[rdkb][common][bsp][Refactor and sync wifi from openwrt]
[Description]
ac60b1ff [MAC80211][misc][Add Filogic 880/860/830/820/630 Release Information]
7eb946a0 [MAC80211][WiFi7][hostapd][sync hostapd patches]
91638fc9 [MAC80211][WiFi7][mac80211][sync backports code]
8e45746b [MAC80211][WiFi7][mt76][sync mt76 patches]
1c564afa [MAC80211][WiFi7][mt76][Add Eagle BE19000 ifem default bin]
[Release-log]
Change-Id: I1d4218d3b1211700acb5937fe310cbd0bf219968
diff --git a/recipes-wifi/hostapd/files/afc_ca.pem b/recipes-wifi/hostapd/files/afc_ca.pem
new file mode 100644
index 0000000..f8199b3
--- /dev/null
+++ b/recipes-wifi/hostapd/files/afc_ca.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDyTCCArGgAwIBAgIURGjnzH/KFgXiBpJHHto1oNCp7Q8wDQYJKoZIhvcNAQEL
+BQAwdDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFDASBgNVBAcM
+C1NhbnRhIENsYXJhMRcwFQYDVQQKDA5XaS1GaSBBbGxpYW5jZTEhMB8GA1UEAwwY
+V0ZBIEFGQyBSb290IENlcnRpZmljYXRlMB4XDTIzMDIwODA1NDgyNVoXDTMzMDIw
+NTA1NDgyNVowdDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFDAS
+BgNVBAcMC1NhbnRhIENsYXJhMRcwFQYDVQQKDA5XaS1GaSBBbGxpYW5jZTEhMB8G
+A1UEAwwYV0ZBIEFGQyBSb290IENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArxJBCpUut883QuQeP6dJMLQo8UUG4XQKN5OBvd7kB6Ff
+fwAI2a1a27LPnbChPwiheZrzFkDWv8xBYcKLlxRhvuAasTsU/lWNyOUBquG3Ut9K
+wpUVdOxmwqY2RH85bzH/x+gk/BnqwMxmavNBgzeD+y4Zf+iqTA4Honxs9tgqehZZ
+CaPsn9ez93Ov1mhMrRvx2RUR4Z6K3LEELVU+vi65pr0DtgdZnD/t3xq7GpHMdpGn
+ugJ4M8wf2ikBGRErkwd6wygZAptNO619LED6o5SyxYXkoyheeJumQQ0x5apsn7GU
+T4DiuBKGXvHeKgRkQUmz+/N8uDIHCAmavHnOqRU8CQIDAQABo1MwUTAdBgNVHQ4E
+FgQUWD0QJblh2IZk0P3GKQxFz3cOtSMwHwYDVR0jBBgwFoAUWD0QJblh2IZk0P3G
+KQxFz3cOtSMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAeccd
+39CM4wznPQuGb0mifPxe6YZo1jSK8XbZ4NBpIW1K0KEDUSbhQ1KZi/c60Si122n6
+eYJziVTXffuDh7d+7RkMPXFi6OEAhaRv/QK5oAQdNUgeTsXjm814+FyOIQyiMZIO
+2v7ILGvDOgpmy4qOzkphkzRA031L3mM6UNRdoVF1fRaZFAYFn9q0zPYzjYORJxmP
+2k+PlPBZ9jtpzB/s2ugcAmlZeWzJApBE+86bFwGhZzv5dBo77hIkgENHtcd8A69I
+ZtJeGnw5QzhIPrYOQiWElmM4cGJcvL1Ti+ETzsfc/P4qGvDiJe9F4lsKzw+ZBtjK
+ZCBXU9R5S8ZB28oH+g==
+-----END CERTIFICATE-----
diff --git a/recipes-wifi/hostapd/files/hostapd-2G-EHT.conf b/recipes-wifi/hostapd/files/hostapd-2G-EHT.conf
index c4ea363..39578a0 100644
--- a/recipes-wifi/hostapd/files/hostapd-2G-EHT.conf
+++ b/recipes-wifi/hostapd/files/hostapd-2G-EHT.conf
@@ -13,6 +13,7 @@
ieee80211d=1
channel=1
+band_idx=0
noscan=1
rnr=1
tx_queue_data2_burst=5.9
diff --git a/recipes-wifi/hostapd/files/hostapd-5G-EHT.conf b/recipes-wifi/hostapd/files/hostapd-5G-EHT.conf
index f9e92e8..3e6fc6a 100644
--- a/recipes-wifi/hostapd/files/hostapd-5G-EHT.conf
+++ b/recipes-wifi/hostapd/files/hostapd-5G-EHT.conf
@@ -12,6 +12,7 @@
ieee80211h=1
channel=36
+band_idx=1
noscan=1
rnr=1
tx_queue_data2_burst=5.9
diff --git a/recipes-wifi/hostapd/files/hostapd-6G-EHT.conf b/recipes-wifi/hostapd/files/hostapd-6G-EHT.conf
index 22162c3..019891f 100644
--- a/recipes-wifi/hostapd/files/hostapd-6G-EHT.conf
+++ b/recipes-wifi/hostapd/files/hostapd-6G-EHT.conf
@@ -12,6 +12,7 @@
ieee80211h=1
channel=37
+band_idx=2
tx_queue_data2_burst=5.9
ieee80211n=1
ht_coex=0
diff --git a/recipes-wifi/hostapd/files/hostapd-init-EHT.sh b/recipes-wifi/hostapd/files/hostapd-init-EHT.sh
new file mode 100644
index 0000000..35c9b08
--- /dev/null
+++ b/recipes-wifi/hostapd/files/hostapd-init-EHT.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+create_hostapdConf() {
+ devidx=0
+ phyidx=0
+ old_path=""
+ pcie7915count=0
+ vap_per_radio=8
+ radio_num="$(iw list | grep Wiphy | wc -l)"
+
+ for _dev in /sys/class/ieee80211/*; do
+ [ -e "$_dev" ] || continue
+
+ dev="${_dev##*/}"
+
+ band="$(uci get wireless.radio${phyidx}.band)"
+ channel="$(uci get wireless.radio${phyidx}.channel)"
+ # Use random MAC to prevent use the same MAC address
+ rand="$(hexdump -C /dev/urandom | head -n 1 | awk '{printf ""$3":"$4""}' &)"
+ killall hexdump
+ MAC="00:0${devidx}:12:34:${rand}"
+ chip="$(cat /sys/class/ieee80211/"$dev"/device/device)"
+
+ if [ $chip == "0x7915" ]; then
+ path="$(realpath /sys/class/ieee80211/"$dev"/device | cut -d/ -f4-)"
+ if [ -n "$path" ]; then
+ if [ "$path" == "$old_path" ] || [ "$old_path" == "" ]; then
+ pcie7915count="1"
+ else
+ pcie7915count="2"
+ fi
+ fi
+ old_path=$path
+ fi
+
+ if [ -e /sys/class/net/wlan$phyidx ]; then
+ iw wlan$phyidx del > /dev/null
+ elif [ -e /sys/class/net/wifi$phyidx ]; then
+ for((i=0;i<$vap_per_radio;i++)); do
+ ifidx=$(($phyidx+$i*$radio_num))
+ ifname="$(cat /nvram/hostapd"$ifidx".conf | grep ^interface= | cut -d '=' -f2 | tr -d '\n')"
+ if [ -n $ifname ]; then
+ hostapd_cli -i global raw REMOVE wifi$ifidx > /dev/null
+ if [ $i -eq 0 ]; then
+ iw wifi$ifidx del > /dev/null
+ fi
+ fi
+ done
+ fi
+
+ if [ "$(uci get wireless.radio${phyidx}.disabled)" == "1" ]; then
+ phyidx=$(($phyidx + 1))
+ continue
+ fi
+
+ if [ ! -f /nvram/hostapd"$devidx".conf ]; then
+ touch /nvram/hostapd"$devidx".conf
+ else
+ for((i=0;i<$vap_per_radio;i++)); do
+ ifidx=$(($phyidx+$i*$radio_num))
+ ifname="$(cat /nvram/hostapd"$ifidx".conf | grep ^interface= | cut -d '=' -f2 | tr -d '\n')"
+ vapstat="$(cat /nvram/vap-status | grep wifi"$ifidx"= | cut -d'=' -f2)"
+ if [ -n $ifname ] && [[ $vapstat -eq "1" ]]; then
+ if [ $i = 0 ]; then
+ ## first interface in this phy
+ iw phy phy0 interface add $ifname type __ap > /dev/null
+ fi
+ touch /nvram/hostapd-acl$ifidx
+ touch /nvram/hostapd$ifidx.psk
+ touch /nvram/hostapd-deny$ifidx
+ if [ $phyidx = $ifidx ]; then
+ touch /tmp/$dev-wifi$ifidx
+ fi
+ hostapd_cli -i global raw ADD bss_config=$dev:/nvram/hostapd"$ifidx".conf
+ fi
+ done
+ devidx=$(($devidx + 1))
+ phyidx=$(($phyidx + 1))
+ continue
+ fi
+
+
+ if [ "$band" == "2g" ]; then
+ cp -f /etc/hostapd-2G.conf /nvram/hostapd"$devidx".conf
+ fi
+
+ if [ "$band" == "5g" ]; then
+
+ if [ $chip == "0x7906" ]; then
+ cp -f /etc/hostapd-5G-7916.conf /nvram/hostapd"$devidx".conf
+ elif [ $chip == "0x7915" ]; then
+ cp -f /etc/hostapd-5G-7915.conf /nvram/hostapd"$devidx".conf
+ else
+ cp -f /etc/hostapd-5G.conf /nvram/hostapd"$devidx".conf
+ fi
+ fi
+
+ if [ "$band" == "6g" ]; then
+ cp -f /etc/hostapd-6G.conf /nvram/hostapd"$devidx".conf
+ fi
+
+ sed -i "/^interface=.*/c\interface=wifi$devidx" /nvram/hostapd"$devidx".conf
+ sed -i "/^bssid=/c\bssid=$MAC" /nvram/hostapd"$devidx".conf
+ echo "wpa_psk_file=/nvram/hostapd$devidx.psk" >> /nvram/hostapd"$devidx".conf
+ iw phy phy0 interface add wifi$devidx type __ap > /dev/null
+ touch /nvram/hostapd-acl$devidx
+ touch /nvram/hostapd$devidx.psk
+ touch /nvram/hostapd-deny$devidx
+ touch /tmp/$dev-wifi$devidx
+ hostapd_cli -i global raw ADD bss_config=$dev:/nvram/hostapd"$devidx".conf && echo -e "wifi"$devidx"=1" >> /nvram/vap-status
+ devidx=$(($devidx + 1))
+ phyidx=$(($phyidx + 1))
+
+ done
+}
+#Creating files for tracking AssociatedDevices
+touch /tmp/AllAssociated_Devices_2G.txt
+touch /tmp/AllAssociated_Devices_5G.txt
+
+#Create wps pin request log file
+touch /var/run/hostapd_wps_pin_requests.log
+
+
+create_hostapdConf
+
+exit 0
diff --git a/recipes-wifi/hostapd/files/hostapd.uc b/recipes-wifi/hostapd/files/hostapd.uc
index 72209ea..7e9fde4 100644
--- a/recipes-wifi/hostapd/files/hostapd.uc
+++ b/recipes-wifi/hostapd/files/hostapd.uc
@@ -28,7 +28,8 @@
return;
for (let bss in cfg.bss)
- wdev_remove(bss.ifname);
+ if (!bss.mld_ap || bss.mld_primary == 1)
+ wdev_remove(bss.ifname);
}
function iface_gen_config(phy, config, start_disabled)
@@ -62,6 +63,7 @@
{
let freq = params.frequency;
let bw320_offset = params.bw320_offset;
+ let band_idx = params.band_idx;
if (!freq)
return null;
@@ -92,7 +94,7 @@
if (freq < 4000)
width = 0;
- return hostapd.freq_info(freq, sec_offset, width, bw320_offset);
+ return hostapd.freq_info(freq, sec_offset, width, bw320_offset, band_idx);
}
function iface_add(phy, config, phy_status)
@@ -154,13 +156,15 @@
pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
return "create_bss";
case "create_bss":
- let err = wdev_create(phy, bss.ifname, { mode: "ap" });
- if (err) {
- hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
- return null;
+ if (!bss.mld_ap || bss.mld_primary == 1) {
+ let err = wdev_create(config.single_hw == 1 ? "phy0" : phy, bss.ifname, { mode: "ap" });
+ if (err) {
+ hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
+ return null;
+ }
}
- pending.call("wpa_supplicant", "phy_status", { phy: phy });
+ pending.call("wpa_supplicant", "phy_status", { phy: bss.mld_ap ? "phy0" : phy });
return "check_phy";
case "check_phy":
let phy_status = data;
@@ -170,12 +174,16 @@
hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
}
- pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
+ pending.call("wpa_supplicant", "phy_set_state", { phy: bss.mld_ap ? "phy0" : phy, stop: true });
return "wpas_stopped";
case "wpas_stopped":
if (!iface_add(phy, config))
hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
- pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
+ let iface = hostapd.interfaces[phy];
+ if (!bss.mld_ap)
+ pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
+ else if (iface.is_mld_finished())
+ pending.call("wpa_supplicant", "phy_set_state", { phy: "phy0", stop: false });
return null;
case "done":
default:
@@ -682,6 +690,9 @@
val[0] == "mbssid")
config[val[0]] = int(val[1]);
+ if (val[0] == "#single_hw")
+ config["single_hw"] = int(val[1]);
+
push(config.radio.data, line);
}
@@ -698,6 +709,12 @@
continue;
}
+ if (val[0] == "mld_ap" && int(val[1]) == 1)
+ bss.mld_ap = 1;
+
+ if (val[0] == "mld_primary" && int(val[1]) == 1)
+ bss.mld_primary = 1;
+
if (val[0] == "nas_identifier")
bss.nasid = val[1];
@@ -752,6 +769,7 @@
sec_chan_offset: 0,
ch_width: -1,
bw320_offset: 1,
+ band_idx: 0,
csa: true,
csa_count: 0,
},
@@ -766,6 +784,7 @@
hostapd.printf(` * sec_chan_offset: ${req.args.sec_chan_offset}`);
hostapd.printf(` * ch_width: ${req.args.ch_width}`);
hostapd.printf(` * bw320_offset: ${req.args.bw320_offset}`);
+ hostapd.printf(` * band_idx: ${req.args.band_idx}`);
hostapd.printf(` * csa: ${req.args.csa}`);
let phy = req.args.phy;
diff --git a/recipes-wifi/hostapd/files/mac80211-EHT.sh b/recipes-wifi/hostapd/files/mac80211-EHT.sh
index c8de5bd..640d958 100644
--- a/recipes-wifi/hostapd/files/mac80211-EHT.sh
+++ b/recipes-wifi/hostapd/files/mac80211-EHT.sh
@@ -136,6 +136,11 @@
[ -n "$band" ] || continue
[ -n "$mode_band" -a "$band" = "6g" ] && return
+ # hardcode for default band selection in MLO codebase
+ [ "$phy" = "phy0" -a "$band" != "2g" ] && continue
+ [ "$phy" = "phy1" -a "$band" != "5g" ] && continue
+ [ "$phy" = "phy2" -a "$band" != "6g" ] && continue
+
mode_band="$band"
channel="$chan"
htmode="$mode"
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
new file mode 100644
index 0000000..c8e7f92
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
@@ -0,0 +1,101 @@
+From f5102b209870065c3e3719dd113892eafd4bb59e Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:31 +0530
+Subject: [PATCH 001/104] hostapd: MLO: fix for_each_mld_link macro
+
+Currently for_each_mld_link macro uses 3 nested for loops. Since now the
+affliated links are linked together via linked list, the logic can
+be improvised by using dl_list_for_each macro instead which uses one for
+loop.
+
+Modify for_each_mld_link macro to use dl_list_for_each instead.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/beacon.c | 10 +---------
+ src/ap/hostapd.h | 17 +++--------------
+ src/ap/sta_info.c | 4 +---
+ 3 files changed, 5 insertions(+), 26 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 32865f667..195c7bbd9 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -945,7 +945,6 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
+ {
+ struct probe_resp_params sta_info_params;
+ struct hostapd_data *link;
+- unsigned int probed_mld_id, i, j;
+
+ params->mld_ap = NULL;
+ params->mld_info = os_zalloc(sizeof(*params->mld_info));
+@@ -956,14 +955,7 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
+ "MLD: Got ML probe request with AP MLD ID %d for links %04x",
+ mld_id, links);
+
+- /*
+- * We want to include the AP MLD ID in the response if it was
+- * included in the request.
+- */
+- probed_mld_id = mld_id != -1 ? mld_id : hostapd_get_mld_id(hapd);
+-
+- for_each_mld_link(link, i, j, hapd->iface->interfaces,
+- probed_mld_id) {
++ for_each_mld_link(link, hapd) {
+ struct mld_link_info *link_info;
+ size_t buflen;
+ u8 mld_link_id = link->mld_link_id;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index affe4f604..d12efb104 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -817,19 +817,8 @@ struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
+
+ bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+
+-#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
+- for (_iface_idx = 0; \
+- _iface_idx < (_ifaces)->count; \
+- _iface_idx++) \
+- for (_bss_idx = 0; \
+- _bss_idx < \
+- (_ifaces)->iface[_iface_idx]->num_bss; \
+- _bss_idx++) \
+- for (_link = \
+- (_ifaces)->iface[_iface_idx]->bss[_bss_idx]; \
+- _link && _link->conf->mld_ap && \
+- hostapd_get_mld_id(_link) == _mld_id; \
+- _link = NULL)
++#define for_each_mld_link(partner, self) \
++ dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
+
+ #else /* CONFIG_IEEE80211BE */
+
+@@ -838,7 +827,7 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+ return true;
+ }
+
+-#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
++#define for_each_mld_link(partner, self) \
+ if (false)
+
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 122880a3d..2423ff189 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -1761,10 +1761,8 @@ static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+ {
+ struct hostapd_data *tmp_hapd;
+- unsigned int i, j;
+
+- for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
+- hostapd_get_mld_id(hapd)) {
++ for_each_mld_link(tmp_hapd, hapd) {
+ struct sta_info *tmp_sta;
+
+ if (hapd == tmp_hapd)
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
new file mode 100644
index 0000000..86c2263
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
@@ -0,0 +1,31 @@
+From 3ca32441ecd9d1a52f736d4a4fffdc24de629e90 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:32 +0530
+Subject: [PATCH 002/104] hostapd: MLO: frame link add command on per BSS basis
+
+Currently function nl80211_link_add() creates the link add NL message on
+drv basis which in turn uses drv's first BSS always. In order to support
+link add for various other interfaces, use BSS handler to create the NL
+message.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 4949de577..042bc97a8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13876,7 +13876,7 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
+ }
+ }
+
+- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
++ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ADD_LINK);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
new file mode 100644
index 0000000..8dfcc5c
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
@@ -0,0 +1,29 @@
+From 695a2dbff28bb259c2b4f8bfdfb9040f81ab7e90 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:33 +0530
+Subject: [PATCH 003/104] nl80211: Print the interface name in debug during
+ link add
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 042bc97a8..98948bfb1 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13900,8 +13900,8 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
+ bss->valid_links |= BIT(link_id);
+ bss->links[link_id].ctx = bss_ctx;
+
+- wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x",
+- bss->valid_links);
++ wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x on %s",
++ bss->valid_links, bss->ifname);
+ return 0;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
new file mode 100644
index 0000000..5f56b72
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
@@ -0,0 +1,29 @@
+From 49a31ee63f482c930e001e2b6a13bf9261fcf5de Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:34 +0530
+Subject: [PATCH 004/104] hostapd: MLO: send link_id on sta_deauth()
+
+Function i802_sta_deauth() already has the link_id passed to it in its
+arguments. Use that to pass it down to send mlme handler.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 98948bfb1..e5fa22b59 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8254,7 +8254,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth), 0, 0, 0, 0,
+- 0, NULL, 0, 0, -1);
++ 0, NULL, 0, 0, link_id);
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
new file mode 100644
index 0000000..e8adbc0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
@@ -0,0 +1,77 @@
+From ec1fdb73853632e9a9003f8c59620d1e12f6d2d0 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:35 +0530
+Subject: [PATCH 005/104] hostapd: MLO: handle auth/assoc on link address
+
+Modify authentication and association frames to be always sent with link
+address as A1 and A3 for ease of Tx status handling.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 25 ++-----------------------
+ 1 file changed, 2 insertions(+), 23 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 5a3132de4..b20300bab 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -416,14 +416,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
+ struct wpabuf *ml_resp = NULL;
+
+ #ifdef CONFIG_IEEE80211BE
+- /*
+- * Once a non-AP MLD is added to the driver, the addressing should use
+- * the MLD MAC address. Thus, use the MLD address instead of translating
+- * the addresses.
+- */
+ if (ap_sta_is_mld(hapd, sta)) {
+- sa = hapd->mld->mld_addr;
+-
+ ml_resp = hostapd_ml_auth_resp(hapd);
+ if (!ml_resp)
+ return -1;
+@@ -444,7 +437,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
+ WLAN_FC_STYPE_AUTH);
+ os_memcpy(reply->da, dst, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
+- os_memcpy(reply->bssid, bssid, ETH_ALEN);
++ os_memcpy(reply->bssid, sa, ETH_ALEN);
+
+ reply->u.auth.auth_alg = host_to_le16(auth_alg);
+ reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
+@@ -3265,14 +3258,9 @@ static void handle_auth(struct hostapd_data *hapd,
+ bssid = mgmt->bssid;
+
+ #ifdef CONFIG_IEEE80211BE
+- /*
+- * Once a non-AP MLD is added to the driver, the addressing should use
+- * the MLD MAC address. It is the responsibility of the driver to
+- * handle the translations.
+- */
+ if (ap_sta_is_mld(hapd, sta)) {
+ dst = sta->addr;
+- bssid = hapd->mld->mld_addr;
++ bssid = hapd->own_addr;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+
+@@ -4823,15 +4811,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+ WLAN_FC_STYPE_ASSOC_RESP));
+
+-#ifdef CONFIG_IEEE80211BE
+- /*
+- * Once a non-AP MLD is added to the driver, the addressing should use
+- * MLD MAC address.
+- */
+- if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
+- sa = hapd->mld->mld_addr;
+-#endif /* CONFIG_IEEE80211BE */
+-
+ os_memcpy(reply->da, addr, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
+ os_memcpy(reply->bssid, sa, ETH_ALEN);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
new file mode 100644
index 0000000..2a7bacd
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
@@ -0,0 +1,93 @@
+From 4be7c245a946016c41a69c7469e00d22aaa32a46 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:36 +0530
+Subject: [PATCH 006/104] hostapd: MLO: reset auth state machine's ML info
+
+Currently auth state machine ML info is set only when the it is created
+newly. However, if the association is tried again, the state machine will
+exist already and hence the ML info will not be refreshed. This leads to
+an issue where if in the subsequent association request, the MLD info is
+different than old info then validation of it will fail.
+
+Fix this issue by refreshing the auth state machine's ML info every time
+association request is handled.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 32 ++++++++++++++++++--------------
+ src/ap/wpa_auth.c | 1 +
+ 2 files changed, 19 insertions(+), 14 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index b20300bab..98398ccdd 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4032,15 +4032,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+
+ if (hapd->conf->wpa && wpa_ie) {
+ enum wpa_validate_result res;
++#ifdef CONFIG_IEEE80211BE
++ struct mld_info *info = &sta->mld_info;
++ bool init = false;
++#endif /* CONFIG_IEEE80211BE */
+
+ wpa_ie -= 2;
+ wpa_ie_len += 2;
+
+ if (!sta->wpa_sm) {
+-#ifdef CONFIG_IEEE80211BE
+- struct mld_info *info = &sta->mld_info;
+-#endif /* CONFIG_IEEE80211BE */
+-
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr,
+ p2p_dev_addr);
+@@ -4050,19 +4050,23 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ "Failed to initialize RSN state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+-
+ #ifdef CONFIG_IEEE80211BE
+- if (ap_sta_is_mld(hapd, sta)) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Set ML info in RSN Authenticator");
+- wpa_auth_set_ml_info(sta->wpa_sm,
+- hapd->mld->mld_addr,
+- sta->mld_assoc_link_id,
+- info);
+- }
+-#endif /* CONFIG_IEEE80211BE */
++ init = true;
+ }
+
++ if (ap_sta_is_mld(hapd, sta)) {
++ wpa_printf(MSG_DEBUG,
++ "MLD: %s ML info in RSN Authenticator",
++ init ? "Set" : "Reset");
++ wpa_auth_set_ml_info(sta->wpa_sm,
++ hapd->mld->mld_addr,
++ sta->mld_assoc_link_id,
++ info);
++ }
++#else
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 01a10b23c..0d15c4209 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -6820,6 +6820,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ return;
+
+ os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
++ sm->n_mld_affiliated_links = 0;
+
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "MLD: Initialization");
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
new file mode 100644
index 0000000..9afd8f4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
@@ -0,0 +1,201 @@
+From d56daa4ebdf544a30f30986097edd6d5f9b8674f Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:37 +0530
+Subject: [PATCH 007/104] hostapd: MLO: add support for cohosted ML BSS
+
+Currently MLO is being supported with an assumption of only single BSS per
+link in the hostapd conf file. This needs to be extended when cohosted ML
+BSS exist in the same config file.
+
+Extend the support for cohosted BSSes. This is required for MBSSID MLO
+support as well.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/main.c | 38 +++++++-------------------
+ src/ap/hostapd.c | 70 +++++++++++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 73 insertions(+), 35 deletions(-)
+
+diff --git a/hostapd/main.c b/hostapd/main.c
+index a43d3a5be..524a10274 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -158,6 +158,9 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 *b = conf->bssid;
+ struct wpa_driver_capa capa;
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_data *h_hapd = NULL;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
+ wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
+@@ -165,35 +168,10 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ }
+
+ #ifdef CONFIG_IEEE80211BE
+- for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
+- struct hostapd_iface *h = iface->interfaces->iface[i];
+- struct hostapd_data *h_hapd = h->bss[0];
+- struct hostapd_bss_config *hconf = h_hapd->conf;
+-
+- if (h == iface) {
+- wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
+- continue;
+- }
+-
+- if (!hconf->mld_ap) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Skip non-MLD");
+- continue;
+- }
+-
+- if (!hostapd_is_ml_partner(hapd, h_hapd)) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Skip non matching MLD vif name");
+- continue;
+- }
+-
+- wpa_printf(MSG_DEBUG, "MLD: Found matching MLD interface");
+- if (!h_hapd->drv_priv) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Matching MLD BSS not initialized yet");
+- continue;
+- }
++ if (conf->mld_ap)
++ h_hapd = hostapd_mld_get_first_bss(hapd);
+
++ if (h_hapd) {
+ hapd->drv_priv = h_hapd->drv_priv;
+ hapd->interface_added = h_hapd->interface_added;
+
+@@ -214,6 +192,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ }
+
+ hostapd_mld_add_link(hapd);
++ wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
+
+ goto setup_mld;
+ }
+@@ -298,6 +278,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ os_memcpy(hapd->own_addr, b, ETH_ALEN);
+
+ hostapd_mld_add_link(hapd);
++ wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
+ }
+
+ setup_mld:
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f8cb6432d..ff1d8f9d0 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1333,6 +1333,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+ int flush_old_stations = 1;
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_data *h_hapd = NULL;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (!hostapd_mld_is_first_bss(hapd))
+ wpa_printf(MSG_DEBUG,
+@@ -1379,6 +1382,21 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ } while (mac_in_conf(hapd->iconf, hapd->own_addr));
+ }
+
++#ifdef CONFIG_IEEE80211BE
++ if (conf->mld_ap) {
++ h_hapd = hostapd_mld_get_first_bss(hapd);
++
++ if (h_hapd) {
++ hapd->drv_priv = h_hapd->drv_priv;
++ hapd->interface_added = h_hapd->interface_added;
++ hostapd_mld_add_link(hapd);
++ wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
++ goto setup_mld;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ hapd->interface_added = 1;
+ if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+ conf->iface, addr, hapd,
+@@ -1393,8 +1411,33 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+
+ if (!addr)
+ os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
++
++#ifdef CONFIG_IEEE80211BE
++ if (hapd->conf->mld_ap) {
++ wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
++ os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN);
++ hostapd_mld_add_link(hapd);
++ }
+ }
+
++setup_mld:
++
++ if (hapd->conf->mld_ap && !first) {
++ wpa_printf(MSG_DEBUG,
++ "MLD: Set link_id=%u, mld_addr=" MACSTR
++ ", own_addr=" MACSTR,
++ hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
++ MAC2STR(hapd->own_addr));
++
++ if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
++ hapd->own_addr))
++ return -1;
++ }
++#else
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ if (conf->wmm_enabled < 0)
+ conf->wmm_enabled = hapd->iconf->ieee80211n |
+ hapd->iconf->ieee80211ax;
+@@ -4679,17 +4722,30 @@ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
+ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ u8 link_id)
+ {
+- unsigned int i;
++ struct hostapd_iface *iface;
++ struct hostapd_data *bss;
++ struct hostapd_bss_config *conf;
++ unsigned int i, j;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
+- struct hostapd_data *h_hapd = h->bss[0];
+-
+- if (!hostapd_is_ml_partner(hapd, h_hapd))
++ iface = hapd->iface->interfaces->iface[i];
++ if (!iface)
+ continue;
+
+- if (h_hapd->mld_link_id == link_id)
+- return h_hapd;
++ for (j = 0; j < iface->num_bss; j++) {
++ bss = iface->bss[j];
++ conf = bss->conf;
++
++ if (!conf->mld_ap ||
++ !hostapd_is_ml_partner(hapd, bss))
++ continue;
++
++ if (!bss->drv_priv)
++ continue;
++
++ if (bss->mld_link_id == link_id)
++ return bss;
++ }
+ }
+
+ return NULL;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
new file mode 100644
index 0000000..42c4b67
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
@@ -0,0 +1,368 @@
+From 27dbd9d9796d656c8cf78d51d48162208080a987 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:38 +0530
+Subject: [PATCH 008/104] hostapd: MLO: extend support for cohosted ML BSS
+
+Modify necessary helper apis to support multiple BSS support for MLO to
+make the changes scalable.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 116 ++++++++++++++++------------------------
+ src/ap/ieee802_11_eht.c | 27 +++-------
+ src/ap/wpa_auth_glue.c | 52 +++++++++++-------
+ 3 files changed, 89 insertions(+), 106 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 98398ccdd..26e3d8356 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4567,7 +4567,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ bool offload)
+ {
+ #ifdef CONFIG_IEEE80211BE
+- unsigned int i, j;
++ unsigned int i;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return 0;
+@@ -4582,25 +4582,25 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+- struct hostapd_iface *iface = NULL;
++ struct hostapd_data *bss = NULL;
+ struct mld_link_info *link = &sta->mld_info.links[i];
++ bool link_bss_found = false;
+
+ if (!link->valid)
+ continue;
+
+- for (j = 0; j < hapd->iface->interfaces->count; j++) {
+- iface = hapd->iface->interfaces->iface[j];
++ for_each_mld_link(bss, hapd) {
++ if (bss == hapd)
++ continue;
+
+- if (hapd->iface == iface)
++ if (bss->mld_link_id != i)
+ continue;
+
+- if (hostapd_is_ml_partner(hapd, iface->bss[0]) &&
+- i == iface->bss[0]->mld_link_id)
+- break;
++ link_bss_found = true;
++ break;
+ }
+
+- if (!iface || j == hapd->iface->interfaces->count ||
+- TEST_FAIL()) {
++ if (!link_bss_found || TEST_FAIL()) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: No link match for link_id=%u", i);
+
+@@ -4613,7 +4613,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
+ } else {
+- if (ieee80211_ml_process_link(iface->bss[0], sta, link,
++ if (ieee80211_ml_process_link(bss, sta, link,
+ ies, ies_len, reassoc,
+ offload))
+ return -1;
+@@ -5777,7 +5777,7 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *assoc_hapd, *tmp_hapd;
+ struct sta_info *assoc_sta;
+- unsigned int i, link_id;
++ struct sta_info *tmp_sta;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return false;
+@@ -5790,45 +5790,27 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
+ if (!assoc_sta)
+ return false;
+
+- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+- for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
+- struct sta_info *tmp_sta;
+-
+- if (!assoc_sta->mld_info.links[link_id].valid)
+- continue;
++ for_each_mld_link(tmp_hapd, assoc_hapd) {
++ if (tmp_hapd == assoc_hapd)
++ continue;
+
+- tmp_hapd =
+- assoc_hapd->iface->interfaces->iface[i]->bss[0];
++ if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
++ continue;
+
+- if (!hostapd_is_ml_partner(assoc_hapd, tmp_hapd))
++ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
++ tmp_sta = tmp_sta->next) {
++ if (tmp_sta->mld_assoc_link_id !=
++ assoc_sta->mld_assoc_link_id ||
++ tmp_sta->aid != assoc_sta->aid)
+ continue;
+
+- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+- tmp_sta = tmp_sta->next) {
+- /*
+- * Remove the station on which the association
+- * was done only after all other link stations
+- * are removed. Since there is only a single
+- * station per struct hostapd_hapd with the
+- * same association link simply break out from
+- * the loop.
+- */
+- if (tmp_sta == assoc_sta)
+- break;
+-
+- if (tmp_sta->mld_assoc_link_id !=
+- assoc_sta->mld_assoc_link_id ||
+- tmp_sta->aid != assoc_sta->aid)
+- continue;
+-
+- if (!disassoc)
+- hostapd_deauth_sta(tmp_hapd, tmp_sta,
+- mgmt);
+- else
+- hostapd_disassoc_sta(tmp_hapd, tmp_sta,
+- mgmt);
+- break;
+- }
++ if (!disassoc)
++ hostapd_deauth_sta(tmp_hapd, tmp_sta,
++ mgmt);
++ else
++ hostapd_disassoc_sta(tmp_hapd, tmp_sta,
++ mgmt);
++ break;
+ }
+ }
+
+@@ -6451,38 +6433,34 @@ static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
+ struct sta_info *sta, bool ok)
+ {
+ #ifdef CONFIG_IEEE80211BE
+- unsigned int i, link_id;
++ struct hostapd_data *tmp_hapd;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+- struct mld_link_info *link = &sta->mld_info.links[link_id];
++ for_each_mld_link(tmp_hapd, hapd) {
++ struct mld_link_info *link =
++ &sta->mld_info.links[tmp_hapd->mld_link_id];
++ struct sta_info *tmp_sta;
+
+- if (!link->valid)
++ if (tmp_hapd == hapd)
+ continue;
+
+- for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- struct sta_info *tmp_sta;
+- struct hostapd_data *tmp_hapd =
+- hapd->iface->interfaces->iface[i]->bss[0];
++ if (!link->valid)
++ continue;
+
+- if (!hostapd_is_ml_partner(tmp_hapd, hapd))
++ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
++ tmp_sta = tmp_sta->next) {
++ if (tmp_sta == sta ||
++ tmp_sta->mld_assoc_link_id !=
++ sta->mld_assoc_link_id ||
++ tmp_sta->aid != sta->aid)
+ continue;
+
+- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+- tmp_sta = tmp_sta->next) {
+- if (tmp_sta == sta ||
+- tmp_sta->mld_assoc_link_id !=
+- sta->mld_assoc_link_id ||
+- tmp_sta->aid != sta->aid)
+- continue;
+-
+- ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
+- tmp_sta, link,
+- ok);
+- break;
+- }
++ ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
++ tmp_sta, link,
++ ok);
++ break;
+ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 7365057ad..353a4116e 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -1029,7 +1029,7 @@ const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
+ static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+ struct sta_info *sta)
+ {
+- u8 i, link_id;
++ u8 link_id;
+ struct mld_info *info = &sta->mld_info;
+
+ if (!ap_sta_is_mld(hapd, sta)) {
+@@ -1049,31 +1049,20 @@ static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct hostapd_data *other_hapd;
+
+- if (!info->links[link_id].valid)
++ if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
+ continue;
+
+- for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+-
+- if (hapd == other_hapd)
+- continue;
+-
+- if (hostapd_is_ml_partner(hapd, other_hapd) &&
+- link_id == other_hapd->mld_link_id)
+- break;
+- }
+-
+- if (i == hapd->iface->interfaces->count &&
+- link_id != hapd->mld_link_id) {
++ other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
++ if (!other_hapd) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
+ link_id);
+ return -1;
+ }
+
+- if (i < hapd->iface->interfaces->count)
+- os_memcpy(info->links[link_id].local_addr,
+- other_hapd->own_addr,
+- ETH_ALEN);
++ os_memcpy(info->links[link_id].local_addr,
++ other_hapd->own_addr,
++ ETH_ALEN);
++
+ }
+
+ return 0;
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index 012f2b803..d3cd44695 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -1537,7 +1537,7 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+ struct wpa_auth_ml_rsn_info *info)
+ {
+ struct hostapd_data *hapd = ctx;
+- unsigned int i, j;
++ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
+ info->n_mld_links);
+@@ -1547,26 +1547,33 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+
+ for (i = 0; i < info->n_mld_links; i++) {
+ unsigned int link_id = info->links[i].link_id;
++ struct hostapd_data *bss = NULL;
++ bool link_bss_found = false;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
+ link_id);
+
+- for (j = 0; j < hapd->iface->interfaces->count; j++) {
+- struct hostapd_iface *iface =
+- hapd->iface->interfaces->iface[j];
++ if (hapd->mld_link_id == link_id) {
++ wpa_auth_ml_get_rsn_info(hapd->wpa_auth,
++ &info->links[i]);
++ continue;
++ }
+
+- if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
+- link_id != iface->bss[0]->mld_link_id ||
+- !iface->bss[0]->wpa_auth)
++ for_each_mld_link(bss, hapd) {
++ if (bss == hapd)
+ continue;
+
+- wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
++ if (bss->mld_link_id != link_id)
++ continue;
++
++ wpa_auth_ml_get_rsn_info(bss->wpa_auth,
+ &info->links[i]);
++ link_bss_found = true;
+ break;
+ }
+
+- if (j == hapd->iface->interfaces->count)
++ if (!link_bss_found)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+@@ -1579,7 +1586,7 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
+ struct wpa_auth_ml_key_info *info)
+ {
+ struct hostapd_data *hapd = ctx;
+- unsigned int i, j;
++ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
+ info->n_mld_links);
+@@ -1588,29 +1595,38 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
+ return -1;
+
+ for (i = 0; i < info->n_mld_links; i++) {
++ struct hostapd_data *bss = NULL;
+ u8 link_id = info->links[i].link_id;
++ bool link_bss_found = false;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link info CB: link_id=%u",
+ link_id);
+
+- for (j = 0; j < hapd->iface->interfaces->count; j++) {
+- struct hostapd_iface *iface =
+- hapd->iface->interfaces->iface[j];
++ if (hapd->mld_link_id == link_id) {
++ wpa_auth_ml_get_key_info(hapd->wpa_auth,
++ &info->links[i],
++ info->mgmt_frame_prot,
++ info->beacon_prot);
++ continue;
++ }
++
++ for_each_mld_link(bss, hapd) {
++ if (bss == hapd)
++ continue;
+
+- if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
+- link_id != iface->bss[0]->mld_link_id ||
+- !iface->bss[0]->wpa_auth)
++ if (bss->mld_link_id != link_id)
+ continue;
+
+- wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
++ wpa_auth_ml_get_key_info(bss->wpa_auth,
+ &info->links[i],
+ info->mgmt_frame_prot,
+ info->beacon_prot);
++ link_bss_found = true;
+ break;
+ }
+
+- if (j == hapd->iface->interfaces->count)
++ if (!link_bss_found)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
new file mode 100644
index 0000000..6b9fc79
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
@@ -0,0 +1,120 @@
+From 30cd94f678f5f85703854812f0deb6467b37df5f Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:39 +0530
+Subject: [PATCH 009/104] hostapd: MLO: pass link_id in get_hapd_bssid helper
+ function
+
+Currently get_hapd_bssid() function matches the given bssid in all bsses
+of its own iface. However with MLO, there is requirement to check its
+own partner BSS at least.
+
+Make changes to compare its link partners as well and if link id passed
+matches with the link id of the partner then return it.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 47 +++++++++++++++++++++++++-----------------
+ 1 file changed, 28 insertions(+), 19 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 2d3206909..adac2d478 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1750,7 +1750,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
+ #define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+- const u8 *bssid)
++ const u8 *bssid, int link_id)
+ {
+ size_t i;
+
+@@ -1761,8 +1761,30 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ return HAPD_BROADCAST;
+
+ for (i = 0; i < iface->num_bss; i++) {
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_data *hapd, *p_hapd;
++
++ hapd = iface->bss[i];
++ if (ether_addr_equal(bssid, hapd->own_addr) ||
++ (hapd->conf->mld_ap &&
++ ether_addr_equal(bssid, hapd->mld->mld_addr) &&
++ link_id == hapd->mld_link_id)) {
++ return hapd;
++ } else if (hapd->conf->mld_ap) {
++ for_each_mld_link(p_hapd, hapd) {
++ if (p_hapd == hapd)
++ continue;
++
++ if (ether_addr_equal(bssid, p_hapd->own_addr) ||
++ (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
++ link_id == p_hapd->mld_link_id))
++ return p_hapd;
++ }
++ }
++#else
+ if (ether_addr_equal(bssid, iface->bss[i]->own_addr))
+ return iface->bss[i];
++#endif /*CONFIG_IEEE80211BE */
+ }
+
+ return NULL;
+@@ -1773,7 +1795,7 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+ const u8 *bssid, const u8 *addr,
+ int wds)
+ {
+- hapd = get_hapd_bssid(hapd->iface, bssid);
++ hapd = get_hapd_bssid(hapd->iface, bssid, -1);
+ if (hapd == NULL || hapd == HAPD_BROADCAST)
+ return;
+
+@@ -1813,14 +1835,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+ if (bssid == NULL)
+ return 0;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hapd->conf->mld_ap &&
+- ether_addr_equal(hapd->mld->mld_addr, bssid))
+- is_mld = true;
+-#endif /* CONFIG_IEEE80211BE */
+-
+- if (!is_mld)
+- hapd = get_hapd_bssid(iface, bssid);
++ hapd = get_hapd_bssid(iface, bssid, rx_mgmt->link_id);
+
+ if (!hapd) {
+ u16 fc = le_to_host16(hdr->frame_control);
+@@ -1872,17 +1887,11 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+ struct ieee80211_hdr *hdr;
+ struct hostapd_data *orig_hapd, *tmp_hapd;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hapd->conf->mld_ap && link_id != -1) {
+- tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
+- if (tmp_hapd)
+- hapd = tmp_hapd;
+- }
+-#endif /* CONFIG_IEEE80211BE */
+ orig_hapd = hapd;
+
+ hdr = (struct ieee80211_hdr *) buf;
+- tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
++ hapd = switch_link_hapd(hapd, link_id);
++ tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
+ if (tmp_hapd) {
+ hapd = tmp_hapd;
+ #ifdef CONFIG_IEEE80211BE
+@@ -1899,7 +1908,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+ if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
+ buf[24] != WLAN_ACTION_PUBLIC)
+ return;
+- hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
++ hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2, link_id);
+ if (!hapd || hapd == HAPD_BROADCAST)
+ return;
+ /*
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/001-wolfssl-init-RNG-with-ECC-key.patch b/recipes-wifi/hostapd/files/patches-2.10.3/001-wolfssl-init-RNG-with-ECC-key.patch
deleted file mode 100644
index 269dcaa..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/001-wolfssl-init-RNG-with-ECC-key.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 21ce83b4ae2b9563175fdb4fc4312096cc399cf8 Mon Sep 17 00:00:00 2001
-From: David Bauer <mail@david-bauer.net>
-Date: Wed, 5 May 2021 00:44:34 +0200
-Subject: [PATCH] wolfssl: add RNG to EC key
-
-Since upstream commit 6467de5a8840 ("Randomize z ordinates in
-scalar mult when timing resistant") WolfSSL requires a RNG for
-the EC key when built hardened which is the default.
-
-Set the RNG for the EC key to fix connections for OWE clients.
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- src/crypto/crypto_wolfssl.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/src/crypto/crypto_wolfssl.c
-+++ b/src/crypto/crypto_wolfssl.c
-@@ -1340,6 +1340,7 @@ int ecc_projective_add_point(ecc_point *
-
- struct crypto_ec {
- ecc_key key;
-+ WC_RNG rng;
- mp_int a;
- mp_int prime;
- mp_int order;
-@@ -1394,6 +1395,8 @@ struct crypto_ec * crypto_ec_init(int gr
- return NULL;
-
- if (wc_ecc_init(&e->key) != 0 ||
-+ wc_InitRng(&e->rng) != 0 ||
-+ wc_ecc_set_rng(&e->key, &e->rng) != 0 ||
- wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
- mp_init(&e->a) != MP_OKAY ||
- mp_init(&e->prime) != MP_OKAY ||
-@@ -1425,6 +1428,7 @@ void crypto_ec_deinit(struct crypto_ec*
- mp_clear(&e->order);
- mp_clear(&e->prime);
- mp_clear(&e->a);
-+ wc_FreeRng(&e->rng);
- wc_ecc_free(&e->key);
- os_free(e);
- }
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
new file mode 100644
index 0000000..de6b62e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
@@ -0,0 +1,64 @@
+From 33c9e4624040e1e0f331260c239fccdccbe52528 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:40 +0530
+Subject: [PATCH 010/104] hostapd: MLO: pass ctx in mlme_event_mgmt()
+
+Add support to pass ctx in mlme_event_mgmt(). This will help in to route
+the event properly to link BSS.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 2 +-
+ src/drivers/driver.h | 8 ++++++++
+ src/drivers/driver_nl80211_event.c | 1 +
+ 3 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index adac2d478..3b24aa4f4 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1810,8 +1810,8 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+ const u8 *bssid;
+ struct hostapd_frame_info fi;
+ int ret;
+- bool is_mld = false;
+
++ hapd = rx_mgmt->ctx ? rx_mgmt->ctx : hapd;
+ hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
+ iface = hapd->iface;
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index d67c949b6..a7455ef6e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -6345,6 +6345,14 @@ union wpa_event_data {
+ */
+ void *drv_priv;
+
++ /**
++ * ctx - Pointer to store ctx of private BSS information
++ *
++ * If not set to NULL, this is used for forwarding the packet
++ * to right link BSS of ML BSS.
++ */
++ void *ctx;
++
+ /**
+ * freq - Frequency (in MHz) on which the frame was received
+ */
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 51b27bd5e..1ca8b5bce 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1367,6 +1367,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ event.rx_mgmt.frame_len = len;
+ event.rx_mgmt.ssi_signal = ssi_signal;
+ event.rx_mgmt.drv_priv = bss;
++ event.rx_mgmt.ctx = bss->ctx;
+ event.rx_mgmt.link_id = link_id;
+
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
new file mode 100644
index 0000000..1d9c2fd
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
@@ -0,0 +1,136 @@
+From 0b72d2a8002e79886433ee85fd23661ec4d3d731 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:41 +0530
+Subject: [PATCH 011/104] hostapd: MLO: move mgmt and control port Tx status to
+ per BSS handling
+
+Currently management and control port transmit status is handled on drv's
+first BSS only. However to support multiple MLDs there is requirement to
+handle it in on a given BSS.
+
+Add changes to use the passed BSS instead of always going with drv's first
+BSS.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211_event.c | 25 +++++++++++++------------
+ 1 file changed, 13 insertions(+), 12 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 1ca8b5bce..f5778cdaf 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -22,7 +22,7 @@
+
+
+ static void
+-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
++nl80211_control_port_frame_tx_status(struct i802_bss *bss,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie);
+
+@@ -1374,12 +1374,13 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ }
+
+
+-static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
++static void mlme_event_mgmt_tx_status(struct i802_bss *bss,
+ struct nlattr *cookie, const u8 *frame,
+ size_t len, struct nlattr *ack)
+ {
+ union wpa_event_data event;
+ const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u16 fc = le_to_host16(hdr->frame_control);
+ u64 cookie_val = 0;
+
+@@ -1398,7 +1399,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+ WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Work around misdelivered control port TX status for EAPOL");
+- nl80211_control_port_frame_tx_status(drv, frame, len, ack,
++ nl80211_control_port_frame_tx_status(bss, frame, len, ack,
+ cookie);
+ return;
+ }
+@@ -1434,7 +1435,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+ event.tx_status.ack = ack != NULL;
+ event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
+ drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
+- wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
++ wpa_supplicant_event(bss->ctx, EVENT_TX_STATUS, &event);
+ }
+
+
+@@ -1742,7 +1743,7 @@ static void mlme_event(struct i802_bss *bss,
+ nla_len(frame), link_id);
+ break;
+ case NL80211_CMD_FRAME_TX_STATUS:
+- mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
++ mlme_event_mgmt_tx_status(bss, cookie, nla_data(frame),
+ nla_len(frame), ack);
+ break;
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+@@ -3652,8 +3653,7 @@ static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
+ }
+
+
+-static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+- struct nlattr **tb)
++static void nl80211_control_port_frame(struct i802_bss *bss, struct nlattr **tb)
+ {
+ u8 *src_addr;
+ u16 ethertype;
+@@ -3682,7 +3682,7 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+ MAC2STR(src_addr));
+ break;
+ case ETH_P_PAE:
+- drv_event_eapol_rx2(drv->ctx, src_addr,
++ drv_event_eapol_rx2(bss->ctx, src_addr,
+ nla_data(tb[NL80211_ATTR_FRAME]),
+ nla_len(tb[NL80211_ATTR_FRAME]),
+ encrypted, link_id);
+@@ -3698,10 +3698,11 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+
+
+ static void
+-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
++nl80211_control_port_frame_tx_status(struct i802_bss *bss,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie)
+ {
++ struct wpa_driver_nl80211_data *drv = bss->drv;
+ union wpa_event_data event;
+
+ if (!cookie || len < ETH_HLEN)
+@@ -3720,7 +3721,7 @@ nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+ nla_get_u64(cookie) == drv->eapol_tx_cookie ?
+ drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
+
+- wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
++ wpa_supplicant_event(bss->ctx, EVENT_EAPOL_TX_STATUS, &event);
+ }
+
+
+@@ -4065,7 +4066,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
+ if (!frame)
+ break;
+- nl80211_control_port_frame_tx_status(drv,
++ nl80211_control_port_frame_tx_status(bss,
+ nla_data(frame),
+ nla_len(frame),
+ tb[NL80211_ATTR_ACK],
+@@ -4238,7 +4239,7 @@ int process_bss_event(struct nl_msg *msg, void *arg)
+ nl80211_external_auth(bss->drv, tb);
+ break;
+ case NL80211_CMD_CONTROL_PORT_FRAME:
+- nl80211_control_port_frame(bss->drv, tb);
++ nl80211_control_port_frame(bss, tb);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
new file mode 100644
index 0000000..960569b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
@@ -0,0 +1,113 @@
+From ac474b8dc6eb9d6a7562a714c0bbdcda47a3d858 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:42 +0530
+Subject: [PATCH 012/104] hostapd: make hostapd_eapol_tx_status() function
+ static
+
+hostapd_eapol_tx_status() function is being used only at one place in
+drv_callbacks. However, it is defined in ieee802_11.c which does not
+suit there.
+
+Hence, being the function definition in drv_callbacks.c and make it static.
+
+No functionality changes.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 25 +++++++++++++++++++++++++
+ src/ap/ieee802_11.c | 28 ----------------------------
+ src/ap/ieee802_11.h | 2 --
+ 3 files changed, 25 insertions(+), 30 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 3b24aa4f4..12e6b3f36 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2348,6 +2348,31 @@ err:
+ }
+ #endif /* CONFIG_OWE */
+
++static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
++ const u8 *data, size_t len, int ack)
++{
++ struct sta_info *sta;
++ struct hostapd_iface *iface = hapd->iface;
++
++ sta = ap_get_sta(hapd, dst);
++ if (sta == NULL && iface->num_bss > 1) {
++ size_t j;
++ for (j = 0; j < iface->num_bss; j++) {
++ hapd = iface->bss[j];
++ sta = ap_get_sta(hapd, dst);
++ if (sta)
++ break;
++ }
++ }
++ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
++ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
++ MACSTR " that is not currently associated",
++ MAC2STR(dst));
++ return;
++ }
++
++ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
++}
+
+ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 26e3d8356..9f7e9afdd 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6874,34 +6874,6 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+ }
+
+-
+-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+- const u8 *data, size_t len, int ack)
+-{
+- struct sta_info *sta;
+- struct hostapd_iface *iface = hapd->iface;
+-
+- sta = ap_get_sta(hapd, dst);
+- if (sta == NULL && iface->num_bss > 1) {
+- size_t j;
+- for (j = 0; j < iface->num_bss; j++) {
+- hapd = iface->bss[j];
+- sta = ap_get_sta(hapd, dst);
+- if (sta)
+- break;
+- }
+- }
+- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+- wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+- MACSTR " that is not currently associated",
+- MAC2STR(dst));
+- return;
+- }
+-
+- ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+-}
+-
+-
+ void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
+ {
+ struct sta_info *sta;
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index a35486d46..262e0ce14 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -132,8 +132,6 @@ int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
+ u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
+ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len, int ack);
+-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+- const u8 *data, size_t len, int ack);
+ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds);
+ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
new file mode 100644
index 0000000..ab69545
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
@@ -0,0 +1,181 @@
+From aa339ee77d60fe9314182cf0e60fa2da4da72b44 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:43 +0530
+Subject: [PATCH 013/104] hostapd: MLO: handle link_id in EAPOL Tx status
+ handler
+
+Add link id support in EAPOL Tx status handler so that event can be
+routed to appropriate link BSS.
+
+In order to support this, modify hostapd_find_by_sta() function to check
+each BSS's other parnter link BSS sta list as well.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 108 +++++++++++++++--------------------------
+ 1 file changed, 38 insertions(+), 70 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 12e6b3f36..064c7abae 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1945,53 +1945,46 @@ static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+
+
+ static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
+- const u8 *src, bool rsn)
++ const u8 *src, bool rsn,
++ struct sta_info **sta_ret)
+ {
++ struct hostapd_data *hapd;
+ struct sta_info *sta;
+ unsigned int j;
+
++ if (sta_ret)
++ *sta_ret = NULL;
++
+ for (j = 0; j < iface->num_bss; j++) {
+- sta = ap_get_sta(iface->bss[j], src);
++ hapd = iface->bss[j];
++ sta = ap_get_sta(hapd, src);
+ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
+- (!rsn || sta->wpa_sm))
+- return iface->bss[j];
+- }
+-
+- return NULL;
+-}
+-
+-
++ (!rsn || sta->wpa_sm)) {
++ if (sta_ret)
++ *sta_ret = sta;
++ return hapd;
+ #ifdef CONFIG_IEEE80211BE
+-static bool search_mld_sta(struct hostapd_data **p_hapd, const u8 *src)
+-{
+- struct hostapd_data *hapd = *p_hapd;
+- unsigned int i;
+-
+- /* Search for STA on other MLO BSSs */
+- for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- struct hostapd_iface *h =
+- hapd->iface->interfaces->iface[i];
+- struct hostapd_data *h_hapd = h->bss[0];
+-
+- if (!hostapd_is_ml_partner(h_hapd, hapd))
+- continue;
++ } else if (hapd->conf->mld_ap) {
++ struct hostapd_data *p_hapd;
+
+- h_hapd = hostapd_find_by_sta(h, src, false);
+- if (h_hapd) {
+- struct sta_info *sta = ap_get_sta(h_hapd, src);
++ for_each_mld_link(p_hapd, hapd) {
++ if (p_hapd == hapd)
++ continue;
+
+- if (sta && sta->mld_info.mld_sta &&
+- sta->mld_assoc_link_id != h_hapd->mld_link_id)
+- continue;
+- *p_hapd = h_hapd;
+- return true;
++ sta = ap_get_sta(p_hapd, src);
++ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
++ (!rsn || sta->wpa_sm)) {
++ if (sta_ret)
++ *sta_ret = sta;
++ return p_hapd;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
+ }
+ }
+
+- return false;
++ return NULL;
+ }
+-#endif /* CONFIG_IEEE80211BE */
+-
+
+ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+ const u8 *data, size_t data_len,
+@@ -2001,28 +1994,10 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+ struct hostapd_data *orig_hapd = hapd;
+
+ #ifdef CONFIG_IEEE80211BE
+- if (link_id != -1) {
+- struct hostapd_data *h_hapd;
+-
+- hapd = switch_link_hapd(hapd, link_id);
+- h_hapd = hostapd_find_by_sta(hapd->iface, src, true);
+- if (!h_hapd)
+- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
+- true);
+- if (!h_hapd)
+- h_hapd = hostapd_find_by_sta(hapd->iface, src, false);
+- if (!h_hapd)
+- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
+- false);
+- if (h_hapd)
+- hapd = h_hapd;
+- } else if (hapd->conf->mld_ap) {
+- search_mld_sta(&hapd, src);
+- } else {
+- hapd = hostapd_find_by_sta(hapd->iface, src, false);
+- }
++ hapd = switch_link_hapd(hapd, link_id);
++ hapd = hostapd_find_by_sta(hapd->iface, src, true, NULL);
+ #else /* CONFIG_IEEE80211BE */
+- hapd = hostapd_find_by_sta(hapd->iface, src, false);
++ hapd = hostapd_find_by_sta(hapd->iface, src, false, NULL);
+ #endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd) {
+@@ -2349,22 +2324,15 @@ err:
+ #endif /* CONFIG_OWE */
+
+ static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+- const u8 *data, size_t len, int ack)
++ const u8 *data, size_t len, int ack,
++ int link_id)
+ {
+ struct sta_info *sta;
+- struct hostapd_iface *iface = hapd->iface;
+
+- sta = ap_get_sta(hapd, dst);
+- if (sta == NULL && iface->num_bss > 1) {
+- size_t j;
+- for (j = 0; j < iface->num_bss; j++) {
+- hapd = iface->bss[j];
+- sta = ap_get_sta(hapd, dst);
+- if (sta)
+- break;
+- }
+- }
+- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
++ hapd = switch_link_hapd(hapd, link_id);
++ hapd = hostapd_find_by_sta(hapd->iface, dst, false, &sta);
++
++ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+ MACSTR " that is not currently associated",
+ MAC2STR(dst));
+@@ -2431,11 +2399,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ break;
+ case EVENT_EAPOL_TX_STATUS:
+- hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
+ hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
+ data->eapol_tx_status.data,
+ data->eapol_tx_status.data_len,
+- data->eapol_tx_status.ack);
++ data->eapol_tx_status.ack,
++ data->eapol_tx_status.link_id);
+ break;
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ hostapd_client_poll_ok(hapd, data->client_poll.addr);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
new file mode 100644
index 0000000..0cec211
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
@@ -0,0 +1,77 @@
+From 4be307ebbc6b94b6a334855a9efe633d77ca98fe Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:44 +0530
+Subject: [PATCH 014/104] hostapd: MLO: update all partner's link beacon
+
+Whenever there is a beacon update of any one of the link, all its other
+partner's link beacon should be refreshed.
+
+Add changes to update all partner's link beacon.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/beacon.c | 27 +++++++++++++++++++--------
+ 1 file changed, 19 insertions(+), 8 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 195c7bbd9..b780d98e4 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2648,7 +2648,7 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ struct hostapd_iface *iface = hapd->iface;
+ int ret;
+ size_t i, j;
+- bool is_6g;
++ bool is_6g, hapd_mld = false;
+
+ ret = __ieee802_11_set_beacon(hapd);
+ if (ret != 0)
+@@ -2657,26 +2657,37 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ if (!iface->interfaces || iface->interfaces->count <= 1)
+ return 0;
+
++#ifdef CONFIG_IEEE80211BE
++ hapd_mld = hapd->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
++
+ /* Update Beacon frames in case of 6 GHz colocation or AP MLD */
+ is_6g = is_6ghz_op_class(iface->conf->op_class);
+ for (j = 0; j < iface->interfaces->count; j++) {
+ struct hostapd_iface *other;
+- bool mld_ap = false;
++ bool other_iface_6g;
+
+ other = iface->interfaces->iface[j];
+ if (other == iface || !other || !other->conf)
+ continue;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hostapd_is_ml_partner(hapd, other->bss[0]))
+- mld_ap = true;
+-#endif /* CONFIG_IEEE80211BE */
++ other_iface_6g = is_6ghz_op_class(other->conf->op_class);
+
+- if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
+- !mld_ap)
++ if (is_6g == other_iface_6g && !hapd_mld)
+ continue;
+
+ for (i = 0; i < other->num_bss; i++) {
++#ifdef CONFIG_IEEE80211BE
++ bool mld_ap = false;
++
++ if (hapd_mld && other->bss[i]->conf->mld_ap &&
++ hostapd_is_ml_partner(hapd, other->bss[i]))
++ mld_ap = true;
++
++ if (is_6g == other_iface_6g && !mld_ap)
++ continue;
++#endif /* CONFIG_IEEE80211BE */
++
+ if (other->bss[i] && other->bss[i]->started)
+ __ieee802_11_set_beacon(other->bss[i]);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
new file mode 100644
index 0000000..3dd5690
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
@@ -0,0 +1,36 @@
+From 6eeca68d65795783243d3634627b4ac8f79e3d15 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:45 +0530
+Subject: [PATCH 015/104] hostapd: MLO: skip assoc link processing in ML info
+
+Currently during processing ML info in association request, all links are
+iterated over. However, the assoc link info will not be present in the
+ML info hence following print is observed during ML association (assoc link
+is 1) -
+
+MLD: No link match for link_id=1
+
+Add changes to skip processing for the assoc link. No functionality
+changes.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 9f7e9afdd..39c63f29b 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4586,7 +4586,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ struct mld_link_info *link = &sta->mld_info.links[i];
+ bool link_bss_found = false;
+
+- if (!link->valid)
++ if (!link->valid || i == sta->mld_assoc_link_id)
+ continue;
+
+ for_each_mld_link(bss, hapd) {
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
new file mode 100644
index 0000000..0c17b68
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
@@ -0,0 +1,335 @@
+From 11cfbaf42eaadf0fd7b50d13f0b7664c1675dc11 Mon Sep 17 00:00:00 2001
+From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:46 +0530
+Subject: [PATCH 016/104] hostapd: MLO: Enhance wpa state machine
+
+Add required ML Specific members in wpa_authenticator and struct
+wpa_state_machine to maintain self and partner link information.
+
+Maintain state machine object in all associated link stations and
+destroy/remove references from the same whenever link stations are getting
+removed.
+
+Increase the wpa_group object reference count for all links in which ML
+station is getting associated and release the same whenever link stations
+are getting removed.
+
+Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 9 ++--
+ src/ap/sta_info.c | 35 ++++++++++++++-
+ src/ap/wpa_auth.c | 101 +++++++++++++++++++++++++++++++++++++++++---
+ src/ap/wpa_auth.h | 16 +++++++
+ src/ap/wpa_auth_i.h | 8 ++++
+ 5 files changed, 159 insertions(+), 10 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 39c63f29b..9d04bdf43 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4467,6 +4467,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ }
+
+ sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
++ sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
++
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
+@@ -4474,7 +4476,6 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ }
+
+ ap_sta_set_mld(sta, true);
+- sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
+ os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+@@ -4501,9 +4502,11 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ ieee802_11_update_beacons(hapd->iface);
+ }
+
+- /* RSN Authenticator should always be the one on the original station */
++ /* Maintain state machine reference on all link STAs, this is needed
++ * during Group rekey handling.
++ */
+ wpa_auth_sta_deinit(sta->wpa_sm);
+- sta->wpa_sm = NULL;
++ sta->wpa_sm = origin_sta->wpa_sm;
+
+ /*
+ * Do not initialize the EAPOL state machine.
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 2423ff189..d483aa9d3 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -199,6 +199,26 @@ static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ }
+
++#ifdef CONFIG_IEEE80211BE
++static void set_for_each_partner_link_sta(struct hostapd_data *hapd,
++ struct sta_info *psta, void *data)
++{
++ struct sta_info *lsta;
++ struct hostapd_data *lhapd;
++
++ if (!ap_sta_is_mld(hapd, psta))
++ return;
++
++ for_each_mld_link(lhapd, hapd) {
++ if (lhapd == hapd)
++ continue;
++
++ lsta = ap_get_sta(lhapd, psta->addr);
++ if (lsta)
++ lsta->wpa_sm = data;
++ }
++}
++#endif /* CONFIG_IEEE80211BE */
+
+ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+@@ -317,8 +337,17 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+
+ #ifdef CONFIG_IEEE80211BE
+ if (!ap_sta_is_mld(hapd, sta) ||
+- hapd->mld_link_id == sta->mld_assoc_link_id)
++ hapd->mld_link_id == sta->mld_assoc_link_id) {
+ wpa_auth_sta_deinit(sta->wpa_sm);
++ /* Remove refrences from partner links. */
++ set_for_each_partner_link_sta(hapd, sta, NULL);
++ }
++
++ /* Release group references in case non assoc link STA is removed
++ * before assoc link STA
++ */
++ if (hostapd_sta_is_link_sta(hapd, sta))
++ wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id);
+ #else
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ #endif /* CONFIG_IEEE80211BE */
+@@ -903,8 +932,10 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd,
+ ieee802_1x_free_station(hapd, sta);
+ #ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf->mld_ap ||
+- hapd->mld_link_id == sta->mld_assoc_link_id)
++ hapd->mld_link_id == sta->mld_assoc_link_id) {
+ wpa_auth_sta_deinit(sta->wpa_sm);
++ set_for_each_partner_link_sta(hapd, sta, NULL);
++ }
+ #else
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 0d15c4209..8c1052c25 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -102,6 +102,59 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+ return sm->addr;
+ }
+
++#ifdef CONFIG_IEEE80211BE
++void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
++{
++ int link_id;
++
++ if (!sm || release_link_id >= MAX_NUM_MLD_LINKS)
++ return;
++
++ for_each_sm_auth(sm, link_id)
++ if (link_id == release_link_id) {
++ wpa_group_put(sm->mld_links[link_id].wpa_auth,
++ sm->mld_links[link_id].wpa_auth->group);
++ sm->mld_links[link_id].wpa_auth = NULL;
++ }
++}
++
++static int wpa_get_link_sta_auth(struct wpa_authenticator *wpa_auth, void *data)
++{
++ struct wpa_get_link_auth_ctx *ctx = data;
++
++ if (os_memcmp(wpa_auth->addr, ctx->addr, ETH_ALEN) != 0)
++ return 0;
++ ctx->wpa_auth = wpa_auth;
++ return 1;
++}
++
++static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void *data)
++{
++ struct wpa_get_link_auth_ctx *ctx = data;
++
++ if (!wpa_auth->is_ml || os_memcmp(wpa_auth->mld_addr, ctx->addr, ETH_ALEN) != 0 ||
++ !wpa_auth->primary_auth)
++ return 0;
++
++ ctx->wpa_auth = wpa_auth;
++ return 1;
++}
++
++static struct wpa_authenticator *
++wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
++{
++ struct wpa_get_link_auth_ctx ctx;
++
++ if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
++ return wpa_auth;
++
++ ctx.addr = wpa_auth->mld_addr;
++ ctx.wpa_auth = NULL;
++ wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
++
++ return ctx.wpa_auth;
++}
++#endif /* CONFIG_IEEE80211BE */
+
+ static inline int wpa_auth_mic_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+@@ -798,6 +851,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+
+ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+ {
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++
+ #ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr)) {
+ wpa_printf(MSG_DEBUG,
+@@ -821,6 +878,13 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+ os_free(sm->last_rx_eapol_key);
+ os_free(sm->wpa_ie);
+ os_free(sm->rsnxe);
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id) {
++ wpa_group_put(sm->mld_links[link_id].wpa_auth,
++ sm->mld_links[link_id].wpa_auth->group);
++ sm->mld_links[link_id].wpa_auth = NULL;
++ }
++#endif /* CONFIG_IEEE80211BE */
+ wpa_group_put(sm->wpa_auth, sm->group);
+ #ifdef CONFIG_DPP2
+ wpabuf_clear_free(sm->dpp_z);
+@@ -831,7 +895,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+
+ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+ {
+- struct wpa_authenticator *wpa_auth;
++ struct wpa_authenticator *wpa_auth, *primary_wpa_auth;
+
+ if (!sm)
+ return;
+@@ -840,10 +904,18 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+ if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "strict rekeying - force GTK rekey since STA is leaving");
++
++#ifdef CONFIG_IEEE80211BE
++ if (wpa_auth->is_ml && !wpa_auth->primary_auth)
++ primary_wpa_auth = wpa_get_primary_wpa_auth(wpa_auth);
++ else
++#endif /* CONFIG_IEEE80211BE */
++ primary_wpa_auth = wpa_auth;
++
+ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
+- wpa_auth, NULL) == -1)
++ primary_wpa_auth, NULL) == -1)
+ eloop_register_timeout(0, 500000, wpa_rekey_gtk,
+- wpa_auth, NULL);
++ primary_wpa_auth, NULL);
+ }
+
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+@@ -6835,6 +6907,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &info->links[link_id];
+ struct mld_link *sm_link = &sm->mld_links[link_id];
++ struct wpa_get_link_auth_ctx ctx;
+
+ sm_link->valid = link->valid;
+ if (!link->valid)
+@@ -6849,10 +6922,28 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ MAC2STR(sm_link->own_addr),
+ MAC2STR(sm_link->peer_addr));
+
+- if (link_id != mld_assoc_link_id)
++ ml_rsn_info.links[i++].link_id = link_id;
++
++ if (link_id != mld_assoc_link_id) {
+ sm->n_mld_affiliated_links++;
++ ctx.addr = link->local_addr;
++ ctx.wpa_auth = NULL;
++ wpa_auth_for_each_auth(sm->wpa_auth, wpa_get_link_sta_auth, &ctx);
++
++ if (ctx.wpa_auth) {
++ sm_link->wpa_auth = ctx.wpa_auth;
++ wpa_group_get(sm_link->wpa_auth,
++ sm_link->wpa_auth->group);
++ }
++ } else {
++ sm_link->wpa_auth = sm->wpa_auth;
++ }
+
+- ml_rsn_info.links[i++].link_id = link_id;
++ if (!sm_link->wpa_auth)
++ wpa_printf(MSG_ERROR, "Unable to find authenticator object for "
++ "ML STA " MACSTR " on link " MACSTR " link id %d",
++ MAC2STR(sm->own_mld_addr), MAC2STR(sm_link->own_addr),
++ link_id);
+ }
+
+ ml_rsn_info.n_mld_links = i;
+diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
+index c74862307..1446872f3 100644
+--- a/src/ap/wpa_auth.h
++++ b/src/ap/wpa_auth.h
+@@ -647,4 +647,20 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_key_info *info,
+ bool mgmt_frame_prot, bool beacon_prot);
+
++#ifdef CONFIG_IEEE80211BE
++void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
++ int release_link_id);
++
++#define for_each_sm_auth(sm, link_id) \
++ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \
++ if (sm->mld_links[link_id].valid && \
++ sm->mld_links[link_id].wpa_auth && \
++ sm->wpa_auth != sm->mld_links[link_id].wpa_auth) \
++
++struct wpa_get_link_auth_ctx {
++ u8 *addr;
++ struct wpa_authenticator *wpa_auth;
++};
++#endif /* CONFIG_IEEE80211BE */
++
+ #endif /* WPA_AUTH_H */
+diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
+index 9ba830415..9ba90749d 100644
+--- a/src/ap/wpa_auth_i.h
++++ b/src/ap/wpa_auth_i.h
+@@ -186,6 +186,7 @@ struct wpa_state_machine {
+ size_t rsne_len;
+ const u8 *rsnxe;
+ size_t rsnxe_len;
++ struct wpa_authenticator *wpa_auth;
+ } mld_links[MAX_NUM_MLD_LINKS];
+ #endif /* CONFIG_IEEE80211BE */
+ };
+@@ -262,6 +263,13 @@ struct wpa_authenticator {
+ #ifdef CONFIG_P2P
+ struct bitfield *ip_pool;
+ #endif /* CONFIG_P2P */
++
++#ifdef CONFIG_IEEE80211BE
++ bool is_ml;
++ u8 mld_addr[ETH_ALEN];
++ u8 link_id;
++ bool primary_auth;
++#endif /* CONFIG_IEEE80211BE */
+ };
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
new file mode 100644
index 0000000..5895635
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
@@ -0,0 +1,799 @@
+From 5e6164cb6143d55409c08ae9bfd859efa188e383 Mon Sep 17 00:00:00 2001
+From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:47 +0530
+Subject: [PATCH 017/104] hostapd: MLO: add support for MLO rekey
+
+Currently wpa group rekey is not supported for ML Stations when non-assoc
+link initiates a group rekey, to support the same following changes have
+been made-
+ * Calculate links specific MLO GTK/IGTK and BIGTK KDE lengths based on
+ corresponding cipher and key instead of taking length of one link and
+ multiplying it by no of associated links.
+ * For MLD, Arm group key rekey timer on one of the links and whenever it
+ fires do group key rekey for all links.
+
+Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Co-developed-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
+Signed-off-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 2 +-
+ src/ap/ieee802_11.c | 13 +-
+ src/ap/wpa_auth.c | 310 +++++++++++++++---
+ src/ap/wpa_auth.h | 9 +-
+ src/ap/wpa_auth_glue.c | 22 ++
+ src/ap/wpa_auth_i.h | 1 +
+ src/ap/wpa_auth_ie.c | 12 +-
+ src/common/wpa_common.h | 1 +
+ tests/fuzzing/eapol-key-auth/eapol-key-auth.c | 2 +-
+ wpa_supplicant/ibss_rsn.c | 2 +-
+ 10 files changed, 317 insertions(+), 57 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 064c7abae..dc21977ff 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -528,7 +528,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ elems.rsnxe ? elems.rsnxe - 2 : NULL,
+ elems.rsnxe ? elems.rsnxe_len + 2 : 0,
+ elems.mdie, elems.mdie_len,
+- elems.owe_dh, elems.owe_dh_len);
++ elems.owe_dh, elems.owe_dh_len, NULL);
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ switch (res) {
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 9d04bdf43..7ee18f4ae 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -1887,7 +1887,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ elems.rsnxe ? elems.rsnxe - 2 : NULL,
+ elems.rsnxe ? elems.rsnxe_len + 2 : 0,
+- elems.mdie, elems.mdie_len, NULL, 0);
++ elems.mdie, elems.mdie_len, NULL, 0, NULL);
+ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+@@ -3778,7 +3778,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd,
+ rsn_ie_len += 2;
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq, rsn_ie, rsn_ie_len,
+- NULL, 0, NULL, 0, owe_dh, owe_dh_len);
++ NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
+ status = wpa_res_to_status_code(res);
+ if (status != WLAN_STATUS_SUCCESS)
+ goto end;
+@@ -3867,6 +3867,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ const u8 *p2p_dev_addr = NULL;
++ struct hostapd_data *assoc_hapd;
++ struct sta_info *assoc_sta = NULL;
+
+ resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+@@ -4041,6 +4043,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ wpa_ie_len += 2;
+
+ if (!sta->wpa_sm) {
++ if (!link)
++ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta,
++ &assoc_hapd);
++
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr,
+ p2p_dev_addr);
+@@ -4076,7 +4082,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ elems->rsnxe ? elems->rsnxe_len + 2 :
+ 0,
+ elems->mdie, elems->mdie_len,
+- elems->owe_dh, elems->owe_dh_len);
++ elems->owe_dh, elems->owe_dh_len,
++ assoc_sta ? assoc_sta->wpa_sm : NULL);
+ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 8c1052c25..7a07dcc4c 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -71,6 +71,9 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+ static int ieee80211w_kde_len(struct wpa_state_machine *sm);
+ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
++static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
++ struct wpa_group *group);
++
+
+ static const u32 eapol_key_timeout_first = 100; /* ms */
+ static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+@@ -102,6 +105,22 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+ return sm->addr;
+ }
+
++static void wpa_update_gkeydone(struct wpa_state_machine *sm, int update)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++ if (!sm || !sm->wpa_auth)
++ return;
++
++ sm->wpa_auth->group->GKeyDoneStations += update;
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations += update;
++#endif /* CONFIG_IEEE80211BE */
++}
++
+ #ifdef CONFIG_IEEE80211BE
+ void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
+ {
+@@ -139,10 +158,12 @@ static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void
+ ctx->wpa_auth = wpa_auth;
+ return 1;
+ }
++#endif /* CONFIG_IEEE80211BE */
+
+ static struct wpa_authenticator *
+ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
+ {
++#ifdef CONFIG_IEEE80211BE
+ struct wpa_get_link_auth_ctx ctx;
+
+ if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
+@@ -153,8 +174,10 @@ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
+ wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
+
+ return ctx.wpa_auth;
+-}
++#else
++ return wpa_auth;
+ #endif /* CONFIG_IEEE80211BE */
++}
+
+ static inline int wpa_auth_mic_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+@@ -420,15 +443,16 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+ }
+ }
+
+-
+-static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
++static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth)
+ {
+- struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct wpa_group *group, *next;
+
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+ group = wpa_auth->group;
+ while (group) {
++ wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator("
++ MACSTR "), group vlan %d",
++ MAC2STR(wpa_auth->addr), group->vlan_id);
+ wpa_group_get(wpa_auth, group);
+
+ group->GTKReKey = true;
+@@ -441,6 +465,80 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+ wpa_group_put(wpa_auth, group);
+ group = next;
+ }
++}
++
++#ifdef CONFIG_IEEE80211BE
++static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth)
++{
++ struct wpa_group *group, *next;
++
++ group = wpa_auth->group;
++ while (group) {
++ wpa_group_get(wpa_auth, group);
++
++ wpa_group_update_gtk(wpa_auth, group);
++ next = group->next;
++ wpa_group_put(wpa_auth, group);
++ group = next;
++ }
++}
++
++static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx)
++{
++ u8 *mld_addr = ctx;
++
++ if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
++ return 0;
++
++ wpa_update_all_gtks(wpa_auth);
++ return 0;
++}
++
++static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth,
++ void *ctx)
++{
++ u8 *mld_addr = ctx;
++
++ if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
++ return 0;
++
++ wpa_rekey_all_groups(wpa_auth);
++ return 0;
++}
++#endif /* CONFIG_IEEE80211BE */
++
++static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
++{
++ struct wpa_authenticator *wpa_auth = eloop_ctx;
++
++#ifdef CONFIG_IEEE80211BE
++ if (wpa_auth->is_ml) {
++ /* Non Primary ML authenticator eloop timer for group rekey is never
++ * started and shouldn't fire too check and warn just in case
++ */
++ if (!wpa_auth->primary_auth) {
++ wpa_printf(MSG_DEBUG,
++ "WPA: Can't start GTK rekey on non-primary ML authenticator");
++ return;
++ }
++ /*
++ * Generate all the new I/BIG/GTKs
++ */
++ wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb,
++ wpa_auth->mld_addr);
++
++ /*
++ * Send all the generated I/BIG/GTKs to the respective
++ * stations via G1 messages
++ */
++ wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb,
++ wpa_auth->mld_addr);
++ } else {
++ wpa_rekey_all_groups(wpa_auth);
++ }
++#else
++ wpa_rekey_all_groups(wpa_auth);
++#endif /* CONFIG_IEEE80211BE */
+
+ if (wpa_auth->conf.wpa_group_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
+@@ -590,8 +688,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
+ wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
+ if (!wpa_auth)
+ return NULL;
++
+ os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+ os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
++
++#ifdef CONFIG_IEEE80211BE
++ if (conf->mld_addr) {
++ wpa_auth->is_ml = true;
++ wpa_auth->link_id = conf->link_id;
++ wpa_auth->primary_auth = !conf->first_link_auth;
++ os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ wpa_auth->cb = cb;
+ wpa_auth->cb_ctx = cb_ctx;
+
+@@ -635,7 +744,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
+ wpa_rekey_gmk, wpa_auth, NULL);
+ }
+
++#ifdef CONFIG_IEEE80211BE
++ /* For ML AP, run Group rekey timer only on one link(first) and whenever
++ * it fires do rekey on all associated ML links at one shot.
++ */
++ if ((!wpa_auth->is_ml || !conf->first_link_auth) &&
++ wpa_auth->conf.wpa_group_rekey) {
++#else
+ if (wpa_auth->conf.wpa_group_rekey) {
++#endif /* CONFIG_IEEE80211BE */
+ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
+ wpa_rekey_gtk, wpa_auth, NULL);
+ }
+@@ -699,6 +816,10 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
+ struct wpa_group *group, *prev;
+
+ eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
++
++ /* TODO: assign ML Primary authenticator to next link auth and
++ * start rekey timer.
++ */
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+@@ -868,7 +989,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+ }
+ #endif /* CONFIG_P2P */
+ if (sm->GUpdateStationKeys) {
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ }
+ #ifdef CONFIG_IEEE80211R_AP
+@@ -1669,12 +1790,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key Request for GTK rekeying");
+- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
++
++ eloop_cancel_timeout(wpa_rekey_gtk,
++ wpa_get_primary_wpa_auth(wpa_auth), NULL);
+ if (wpa_auth_gtk_rekey_in_process(wpa_auth))
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
+ "skip new GTK rekey - already in process");
+ else
+- wpa_rekey_gtk(wpa_auth, NULL);
++ wpa_rekey_gtk(wpa_get_primary_wpa_auth(wpa_auth), NULL);
+ }
+ } else {
+ /* Do not allow the same key replay counter to be reused. */
+@@ -2207,7 +2330,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
+ * Reauthentication cancels the pending group key
+ * update for this STA.
+ */
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ sm->PtkGroupInit = true;
+ }
+@@ -2284,7 +2407,7 @@ SM_STATE(WPA_PTK, INITIALIZE)
+
+ sm->keycount = 0;
+ if (sm->GUpdateStationKeys)
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = false;
+@@ -4058,41 +4181,54 @@ static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
+ wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
+ }
+
++#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN)
+
+ static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+ {
+- struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+- struct wpa_group *gsm = sm->group;
+- size_t gtk_len = gsm->GTK_len;
+- size_t igtk_len;
+- size_t kde_len;
+- unsigned int n_links;
++ struct wpa_authenticator *wpa_auth;
++ size_t kde_len = 0;
++ int link_id;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+- n_links = sm->n_mld_affiliated_links + 1;
++ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++ if (!sm->mld_links[link_id].valid)
++ continue;
++
++ wpa_auth = sm->mld_links[link_id].wpa_auth;
++ if (!wpa_auth || !wpa_auth->group)
++ continue;
+
+- /* MLO GTK KDE for each link */
+- kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
++ /* MLO GTK KDE
++ * Header + Key-idx and Link-id + PN
++ */
++ kde_len += (KDE_HDR_LEN + 1 + WPA_MLO_GTK_KDE_PN_LEN);
++ kde_len += wpa_auth->group->GTK_len;
+
+- if (!sm->mgmt_frame_prot)
+- return kde_len;
++ if (!sm->mgmt_frame_prot)
++ continue;
+
+- /* MLO IGTK KDE for each link */
+- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
++ if (wpa_auth->conf.tx_bss_auth)
++ wpa_auth = wpa_auth->conf.tx_bss_auth;
+
+- if (wpa_auth->conf.tx_bss_auth) {
+- wpa_auth = wpa_auth->conf.tx_bss_auth;
+- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+- }
++ /* MLO IGTK KDE
++ * Header + Key-idx & IPN + Link-id
++ */
++ kde_len += (KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1);
++ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+- if (!wpa_auth->conf.beacon_prot)
+- return kde_len;
++ if (!wpa_auth->conf.beacon_prot)
++ continue;
++
++ /* MLO BIGTK KDE
++ * Header + Key-idx & IPN + Link-id
++ */
++ kde_len += (KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1);
++ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
++ }
+
+- /* MLO BIGTK KDE for each link */
+- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
++ wpa_printf(MSG_DEBUG, "MLO Group kdes len = %zu", kde_len);
+
+ return kde_len;
+ }
+@@ -4102,6 +4238,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ {
+ struct wpa_auth_ml_key_info ml_key_info;
+ unsigned int i, link_id;
++ u8 *start = pos;
+
+ /* First fetch the key information from all the authenticators */
+ os_memset(&ml_key_info, 0, sizeof(ml_key_info));
+@@ -4153,8 +4290,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ i++;
+ }
+
+- if (!sm->mgmt_frame_prot)
++ if (!sm->mgmt_frame_prot) {
++ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
+ return pos;
++ }
+
+ /* Add MLO IGTK KDEs */
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+@@ -4193,8 +4332,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ i++;
+ }
+
+- if (!sm->wpa_auth->conf.beacon_prot)
++ if (!sm->wpa_auth->conf.beacon_prot) {
++ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
+ return pos;
++ }
+
+ /* Add MLO BIGTK KDEs */
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+@@ -4234,6 +4375,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ i++;
+ }
+
++ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
+ return pos;
+ }
+
+@@ -4274,6 +4416,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+ {
+ #ifdef CONFIG_IEEE80211BE
+ u8 link_id;
++ u8 *start = pos;
+
+ if (sm->mld_assoc_link_id < 0)
+ return pos;
+@@ -4324,6 +4467,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+ }
+ }
+
++ wpa_printf(MSG_DEBUG, "RSN: MLO Link kde len = %ld", pos - start);
+ pos = wpa_auth_ml_group_kdes(sm, pos);
+ #endif /* CONFIG_IEEE80211BE */
+
+@@ -5106,7 +5250,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+ #endif /* CONFIG_OCV */
+
+ if (sm->GUpdateStationKeys)
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ sm->GTimeoutCtr = 0;
+ /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+@@ -5121,7 +5265,7 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR)
+ {
+ SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+ if (sm->GUpdateStationKeys)
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ sm->Disconnect = true;
+ sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
+@@ -5415,18 +5559,11 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+
+ #endif /* CONFIG_WNM_AP */
+
+-
+-static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+- struct wpa_group *group)
++static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
++ struct wpa_group *group)
+ {
+ int tmp;
+
+- wpa_printf(MSG_DEBUG,
+- "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
+- group->vlan_id);
+- group->changed = true;
+- group->wpa_group_state = WPA_GROUP_SETKEYS;
+- group->GTKReKey = false;
+ tmp = group->GM;
+ group->GM = group->GN;
+ group->GN = tmp;
+@@ -5440,6 +5577,24 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ * counting the STAs that are marked with GUpdateStationKeys instead of
+ * including all STAs that could be in not-yet-completed state. */
+ wpa_gtk_update(wpa_auth, group);
++}
++
++static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
++ struct wpa_group *group)
++{
++ wpa_printf(MSG_DEBUG,
++ "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
++ group->vlan_id);
++ group->changed = true;
++ group->wpa_group_state = WPA_GROUP_SETKEYS;
++ group->GTKReKey = false;
++
++#ifdef CONFIG_IEEE80211BE
++ if (wpa_auth->is_ml)
++ goto skip_update;
++#endif /* CONFIG_IEEE80211BE */
++
++ wpa_group_update_gtk(wpa_auth, group);
+
+ if (group->GKeyDoneStations) {
+ wpa_printf(MSG_DEBUG,
+@@ -5447,6 +5602,10 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ group->GKeyDoneStations);
+ group->GKeyDoneStations = 0;
+ }
++
++#ifdef CONFIG_IEEE80211BE
++skip_update:
++#endif /* CONFIG_IEEE80211BE */
+ wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
+ wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
+ group->GKeyDoneStations);
+@@ -5564,6 +5723,57 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+ }
+ }
+
++static void wpa_mark_group_change(struct wpa_state_machine *sm, bool change)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++
++ if (!sm || !sm->wpa_auth)
++ return;
++ sm->wpa_auth->group->changed = change;
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ sm->mld_links[link_id].wpa_auth->group->changed = change;
++#endif /* CONFIG_IEEE80211BE */
++}
++
++static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++
++ if (!sm || !sm->wpa_auth)
++ return;
++ wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group);
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ wpa_group_sm_step(sm->mld_links[link_id].wpa_auth,
++ sm->mld_links[link_id].wpa_auth->group);
++#endif /* CONFIG_IEEE80211BE */
++}
++
++static bool wpa_group_sm_changed(struct wpa_state_machine *sm)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++ bool changed;
++
++ if (!sm || !sm->wpa_auth)
++ return false;
++ changed = sm->wpa_auth->group->changed;
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ changed |= sm->mld_links[link_id].wpa_auth->group->changed;
++#endif /* CONFIG_IEEE80211BE */
++
++ return changed;
++}
+
+ static int wpa_sm_step(struct wpa_state_machine *sm)
+ {
+@@ -5584,7 +5794,7 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
+ break;
+
+ sm->changed = false;
+- sm->wpa_auth->group->changed = false;
++ wpa_mark_group_change(sm, false);
+
+ SM_STEP_RUN(WPA_PTK);
+ if (sm->pending_deinit)
+@@ -5592,8 +5802,8 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
+ SM_STEP_RUN(WPA_PTK_GROUP);
+ if (sm->pending_deinit)
+ break;
+- wpa_group_sm_step(sm->wpa_auth, sm->group);
+- } while (sm->changed || sm->wpa_auth->group->changed);
++ wpa_group_sm_step_links(sm);
++ } while (sm->changed || wpa_group_sm_changed(sm));
+ sm->in_step_loop = 0;
+
+ if (sm->pending_deinit) {
+@@ -6807,8 +7017,10 @@ int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+ {
+ if (!wpa_auth)
+ return -1;
+- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+- return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
++ eloop_cancel_timeout(wpa_rekey_gtk,
++ wpa_get_primary_wpa_auth(wpa_auth), NULL);
++ return eloop_register_timeout(0, 0, wpa_rekey_gtk,
++ wpa_get_primary_wpa_auth(wpa_auth), NULL);
+ }
+
+
+diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
+index 1446872f3..331d217b5 100644
+--- a/src/ap/wpa_auth.h
++++ b/src/ap/wpa_auth.h
+@@ -285,6 +285,12 @@ struct wpa_auth_config {
+ * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
+ * and in BSSs that are not part of a Multi-BSSID set. */
+ struct wpa_authenticator *tx_bss_auth;
++
++#ifdef CONFIG_IEEE80211BE
++ u8 *mld_addr;
++ int link_id;
++ struct wpa_authenticator *first_link_auth;
++#endif /* CONFIG_IEEE80211BE */
+ };
+
+ typedef enum {
+@@ -429,7 +435,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsnxe, size_t rsnxe_len,
+ const u8 *mdie, size_t mdie_len,
+- const u8 *owe_dh, size_t owe_dh_len);
++ const u8 *owe_dh, size_t owe_dh_len,
++ struct wpa_state_machine *assoc_sm);
+ int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len);
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index d3cd44695..1726c7201 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -1713,6 +1713,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+
+ hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+ _conf.msg_ctx = hapd->msg_ctx;
++
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ if (tx_bss != hapd)
+ _conf.tx_bss_auth = tx_bss->wpa_auth;
+@@ -1753,6 +1754,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+ !!(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
+
++#ifdef CONFIG_IEEE80211BE
++ _conf.mld_addr = NULL;
++ _conf.link_id = -1;
++ _conf.first_link_auth = NULL;
++
++ if (hapd->conf->mld_ap) {
++ struct hostapd_data *lhapd;
++
++ _conf.mld_addr = hapd->mld->mld_addr;
++ _conf.link_id = hapd->mld_link_id;
++
++ for_each_mld_link(lhapd, hapd) {
++ if (lhapd == hapd)
++ continue;
++
++ if (lhapd->wpa_auth)
++ _conf.first_link_auth = lhapd->wpa_auth;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
+ if (hapd->wpa_auth == NULL) {
+ wpa_printf(MSG_ERROR, "WPA initialization failed.");
+diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
+index 9ba90749d..29bb66733 100644
+--- a/src/ap/wpa_auth_i.h
++++ b/src/ap/wpa_auth_i.h
+@@ -176,6 +176,7 @@ struct wpa_state_machine {
+ u8 peer_mld_addr[ETH_ALEN];
+ s8 mld_assoc_link_id;
+ u8 n_mld_affiliated_links;
++ u16 valid_links;
+
+ struct mld_link {
+ bool valid;
+diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
+index a5f2861c9..bf2303e4f 100644
+--- a/src/ap/wpa_auth_ie.c
++++ b/src/ap/wpa_auth_ie.c
+@@ -608,7 +608,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsnxe, size_t rsnxe_len,
+ const u8 *mdie, size_t mdie_len,
+- const u8 *owe_dh, size_t owe_dh_len)
++ const u8 *owe_dh, size_t owe_dh_len,
++ struct wpa_state_machine *assoc_sm)
+ {
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ struct wpa_ie_data data;
+@@ -956,6 +957,15 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ else
+ sm->wpa = WPA_VERSION_WPA;
+
++ if (assoc_sm) {
++ /* For ML Association Link STA cannot choose a different
++ * akm or pairwise cipher from assoc STA
++ */
++ if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt)
++ return WPA_INVALID_AKMP;
++ if (sm->pairwise != assoc_sm->pairwise)
++ return WPA_INVALID_PAIRWISE;
++ }
+ #if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
+ if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
+diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
+index 01efeea3a..24ceed600 100644
+--- a/src/common/wpa_common.h
++++ b/src/common/wpa_common.h
+@@ -24,6 +24,7 @@
+ #define WPA_PASN_PMK_LEN 32
+ #define WPA_PASN_MAX_MIC_LEN 24
+ #define WPA_MAX_RSNXE_LEN 4
++#define WPA_MLO_GTK_KDE_PN_LEN 6
+
+ #define OWE_DH_GROUP 19
+
+diff --git a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
+index bb46422c6..17f69fd76 100644
+--- a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
++++ b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
+@@ -262,7 +262,7 @@ static int auth_init(struct wpa *wpa)
+ }
+
+ if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie,
+- supp_ie_len, NULL, 0, NULL, 0, NULL, 0) !=
++ supp_ie_len, NULL, 0, NULL, 0, NULL, 0, NULL) !=
+ WPA_IE_OK) {
+ wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+ return -1;
+diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
+index 554268a47..2d06f1a6a 100644
+--- a/wpa_supplicant/ibss_rsn.c
++++ b/wpa_supplicant/ibss_rsn.c
+@@ -484,7 +484,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
+ "\x00\x0f\xac\x04"
+ "\x01\x00\x00\x0f\xac\x04"
+ "\x01\x00\x00\x0f\xac\x02"
+- "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) !=
++ "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, NULL) !=
+ WPA_IE_OK) {
+ wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+ return -1;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
new file mode 100644
index 0000000..ce4a844
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
@@ -0,0 +1,156 @@
+From 8ac142806112477fa012414a2bdea22239e474a4 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:48 +0530
+Subject: [PATCH 018/104] hostapd: MLO: send link id during flushing stations
+
+Currently, whenever a BSS is set up, it sends flush all stations via
+command - NL80211_CMD_DEL_STATION on its interface. However, in case
+of MLO, station could have been connected to other links by the time
+this link is coming up. Since there is no link id currently being
+passed, all those stations entries are also removed in the driver which is
+wrong.
+
+Hence add change to send link id along with the command during MLO so that
+the driver can use this link id and flush only those stations which are
+using the passed link id as one of its links.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ap_drv_ops.c | 10 +++++++++-
+ src/drivers/driver.h | 4 +++-
+ src/drivers/driver_atheros.c | 2 +-
+ src/drivers/driver_bsd.c | 2 +-
+ src/drivers/driver_hostap.c | 2 +-
+ src/drivers/driver_nl80211.c | 17 ++++++++++++++---
+ 6 files changed, 29 insertions(+), 8 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 0d493b837..32722084d 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -624,9 +624,17 @@ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+
+ int hostapd_flush(struct hostapd_data *hapd)
+ {
++ int link_id = -1;
++
+ if (hapd->driver == NULL || hapd->driver->flush == NULL)
+ return 0;
+- return hapd->driver->flush(hapd->drv_priv);
++
++#ifdef CONFIG_IEEE80211BE
++ if (hapd->conf && hapd->conf->mld_ap)
++ link_id = hapd->mld_link_id;
++#endif /* CONFIG_IEEE80211BE */
++
++ return hapd->driver->flush(hapd->drv_priv, link_id);
+ }
+
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index a7455ef6e..e672a1787 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3578,13 +3578,15 @@ struct wpa_driver_ops {
+ /**
+ * flush - Flush all association stations (AP only)
+ * @priv: Private driver interface data
++ * @link_id: In case of MLO, valid link_id on which all associated stations
++ * will be flushed. -1 otherwise.
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function requests the driver to disassociate all associated
+ * stations. This function does not need to be implemented if the
+ * driver does not process association frames internally.
+ */
+- int (*flush)(void *priv);
++ int (*flush)(void *priv, int link_id);
+
+ /**
+ * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
+diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
+index ae7f0e535..71863306a 100644
+--- a/src/drivers/driver_atheros.c
++++ b/src/drivers/driver_atheros.c
+@@ -632,7 +632,7 @@ atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+
+
+ static int
+-atheros_flush(void *priv)
++atheros_flush(void *priv, int link_id)
+ {
+ u8 allsta[IEEE80211_ADDR_LEN];
+ os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
+index 850637f0d..82d8a0186 100644
+--- a/src/drivers/driver_bsd.c
++++ b/src/drivers/driver_bsd.c
+@@ -946,7 +946,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+
+
+ static int
+-bsd_flush(void *priv)
++bsd_flush(void *priv, int link_id)
+ {
+ u8 allsta[IEEE80211_ADDR_LEN];
+
+diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
+index d3520aacc..3aa5860bc 100644
+--- a/src/drivers/driver_hostap.c
++++ b/src/drivers/driver_hostap.c
+@@ -572,7 +572,7 @@ static int hostap_set_ssid(void *priv, const u8 *buf, int len)
+ }
+
+
+-static int hostap_flush(void *priv)
++static int hostap_flush(void *priv, int link_id)
+ {
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index e5fa22b59..9ac621ae6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -7729,25 +7729,36 @@ static int i802_set_frag(void *priv, int frag)
+ }
+
+
+-static int i802_flush(void *priv)
++static int i802_flush(void *priv, int link_id)
+ {
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+ int res;
+
+- wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+- bss->ifname);
++ if (link_id == NL80211_DRV_LINK_ID_NA)
++ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
++ bss->ifname);
++ else
++ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (with link %d)",
++ bss->ifname, link_id);
+
+ /*
+ * XXX: FIX! this needs to flush all VLANs too
+ */
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
++ if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
++ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
++ goto fail;
++
+ res = send_and_recv_cmd(bss->drv, msg);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+ "(%s)", res, strerror(-res));
+ }
+ return res;
++fail:
++ nlmsg_free(msg);
++ return -1;
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
new file mode 100644
index 0000000..77f3ddd
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
@@ -0,0 +1,84 @@
+From 3d12a39b10565a10bec40b53cf6e69b60115a35f Mon Sep 17 00:00:00 2001
+From: Harshitha Prem <quic_hprem@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:49 +0530
+Subject: [PATCH 019/104] hostapd: MLO: display link details in status command
+
+Currently, link id and number of link details of a MLD AP interface is not
+displayed in status command of hostapd_cli.
+
+Add changes to display the link id and number of link details.
+
+The details would be seen as below for a MLD AP interface:
+
+$ hostapd_cli -i wlan0 status | grep link
+num_links=1
+link_id=0
+link_addr=AA:BB:CC:DD:EE:FF
+
+$ hostapd_cli -i wlan1 status | grep link
+num_links=2
+link_id=0
+link_addr=AA:BB:CC:DD:EE:FF
+partner_link_id=1
+partner_link_addr=AA:BB:CC:DD:EE:AA
+
+The above details would not be displayed for non-MLD AP interfaces.
+
+Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
+Co-developed-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
+Signed-off-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ctrl_iface_ap.c | 36 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 272317774..2cfef4bd4 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -887,6 +887,42 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ return len;
+ len += ret;
+ }
++
++ if (hapd->conf->mld_ap) {
++ struct hostapd_data *link_bss;
++
++ ret = os_snprintf(buf + len, buflen - len,
++ "num_links=%d\n",
++ hapd->mld->num_links);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++
++ /* self bss */
++ ret = os_snprintf(buf + len, buflen - len,
++ "link_id=%d\n"
++ "link_addr=" MACSTR "\n",
++ hapd->mld_link_id,
++ MAC2STR(hapd->own_addr));
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++
++ /* partner bss */
++ for_each_mld_link(link_bss, hapd) {
++ if (link_bss == hapd)
++ continue;
++
++ ret = os_snprintf(buf + len, buflen - len,
++ "partner_link_id=%d\n"
++ "partner_link_addr=" MACSTR "\n",
++ link_bss->mld_link_id,
++ MAC2STR(link_bss->own_addr));
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
++ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
new file mode 100644
index 0000000..879adf1
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
@@ -0,0 +1,748 @@
+From 8affcd80f5143fa23d3f21427b6b9f11af35ef5d Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:50 +0530
+Subject: [PATCH 020/104] hostapd: fix RNR building for co-location and MLO
+
+Currently with MLO changes, RNR formation for co-location or MLO
+was not working as expected. Hence make it work as per the
+expectation.
+
+For example, during co-location, if the BSS is also its ML partner
+then there is no need to include a separate TBTT for it.
+
+Also, during co-location, if the BSS is not its partner but it is
+ML capable, then the TBTT length should be 16 bytes and it should
+include the MLD Parameters for it in the RNR.
+
+During co-location, for a given Neighbor AP (operating on a given
+channel and op-class) if it has BSSes which are ML capable as well
+as BSSes which are not, then there should be two Neighbor AP Info
+present. One indicating TBTT length as 13 bytes and one indicating
+TBTT info length as 16 bytes.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/beacon.c | 12 +-
+ src/ap/ieee802_11.c | 387 ++++++++++++++++++++++++++++++++------------
+ src/ap/ieee802_11.h | 5 +-
+ 3 files changed, 290 insertions(+), 114 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index b780d98e4..4354dfae3 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -677,7 +677,7 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
+ params->known_bss,
+ params->known_bss_len, NULL);
+ if (!params->is_ml_sta_info)
+- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
++ buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true);
+ buflen += hostapd_mbo_ie_len(hapd);
+ buflen += hostapd_eid_owe_trans_len(hapd);
+ buflen += hostapd_eid_dpp_cc_len(hapd);
+@@ -797,7 +797,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+
+ if (!params->is_ml_sta_info)
+- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
++ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true);
+ pos = hostapd_eid_fils_indic(hapd, pos, 0);
+ pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
+
+@@ -1946,7 +1946,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
+ total_len += 3;
+ }
+
+- total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
++ total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
+
+ pos = hostapd_eid_fils_indic(hapd, buf, 0);
+ buf_len = pos - buf;
+@@ -2020,7 +2020,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
+ /* Fill in the Length field value */
+ *length_pos = pos - (length_pos + 1);
+
+- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
++ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
+
+ /* FILS Indication element */
+ if (buf_len) {
+@@ -2126,7 +2126,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ hapd == hostapd_mbssid_get_tx_bss(hapd))
+ tail_len += 5; /* Multiple BSSID Configuration element */
+- tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
++ tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
+ tail_len += hostapd_mbo_ie_len(hapd);
+ tail_len += hostapd_eid_owe_trans_len(hapd);
+ tail_len += hostapd_eid_dpp_cc_len(hapd);
+@@ -2262,7 +2262,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+
+ tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+
+- tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
++ tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
+ tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+ tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 7ee18f4ae..9a23c7240 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7273,20 +7273,21 @@ static size_t
+ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ size_t *current_len,
+- struct mbssid_ie_profiles *skip_profiles)
++ struct mbssid_ie_profiles *skip_profiles,
++ bool mld_update)
+ {
+ size_t total_len = 0, len = *current_len;
+- int tbtt_count = 0;
+- size_t i, start = 0;
+- bool ap_mld = false;
++ int tbtt_count, total_tbtt_count = 0;
++ size_t i, start;
++ u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+
+-#ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
+-#endif /* CONFIG_IEEE80211BE */
++repeat_rnr_len:
++ start = 0;
++ tbtt_count = 0;
+
+ while (start < hapd->iface->num_bss) {
+ if (!len ||
+- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
++ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
+ len = RNR_HEADER_LEN;
+ total_len += RNR_HEADER_LEN;
+@@ -7298,10 +7299,15 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+
+ for (i = start; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
++ bool ap_mld = false;
+
+ if (!bss || !bss->conf || !bss->started)
+ continue;
+
++#ifdef CONFIG_IEEE80211BE
++ ap_mld = !!bss->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
++
+ if (bss == reporting_hapd ||
+ bss->conf->ignore_broadcast_ssid)
+ continue;
+@@ -7310,23 +7316,71 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+ i >= skip_profiles->start && i < skip_profiles->end)
+ continue;
+
+- if (len + RNR_TBTT_INFO_LEN > 255 ||
++ /* No need to report if length is for normal TBTT and the BSS
++ * is a MLD. MLD TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
++ continue;
++
++ /* No need to report if length is for MLD TBTT and the BSS
++ * is not MLD. Normal TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
++ continue;
++
++#ifdef CONFIG_IEEE80211BE
++ /* If building for co-location and they are ML partners,
++ * no need to include since the ML RNR will carry this.
++ */
++ if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
++ continue;
++
++ /* If building for ML RNR and they are not ML parnters,
++ * don't include.
++ */
++ if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
++ continue;
++#endif /* CONFIG_IEEE80211BE */
++
++ if (len + tbtt_info_len > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ break;
+
+- if (!ap_mld) {
+- len += RNR_TBTT_INFO_LEN;
+- total_len += RNR_TBTT_INFO_LEN;
+- } else {
+- len += RNR_TBTT_INFO_MLD_LEN;
+- total_len += RNR_TBTT_INFO_MLD_LEN;
+- }
++ len += tbtt_info_len;
++ total_len += tbtt_info_len;
+ tbtt_count++;
+ }
+ start = i;
+ }
+
+- if (!tbtt_count)
++ total_tbtt_count += tbtt_count;
++
++ /* If building for co-location, re-build again but this time include
++ * ML TBTTs.
++ */
++ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
++ tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
++
++ /* If no TBTT was found, then adjust the len and total_len since
++ * it would have incremented before we checked all bss.
++ */
++ if (!tbtt_count) {
++ len -= RNR_TBTT_HEADER_LEN;
++ total_len -= RNR_TBTT_HEADER_LEN;
++ }
++
++ goto repeat_rnr_len;
++ }
++
++ /* this is possible when it re-built and in that no suitable TBTT was
++ * found. Adjust the length accordingly.
++ */
++ if (!tbtt_count && total_tbtt_count) {
++ len -= RNR_TBTT_HEADER_LEN;
++ total_len -= RNR_TBTT_HEADER_LEN;
++ }
++
++ if (!total_tbtt_count)
+ total_len = 0;
+ else
+ *current_len = len;
+@@ -7375,8 +7429,8 @@ static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
+ }
+
+
+-static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+- size_t *current_len)
++static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
++ size_t *current_len)
+ {
+ struct hostapd_iface *iface;
+ size_t len = 0;
+@@ -7387,34 +7441,57 @@ static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+- bool ap_mld = false;
+-
+-#ifdef CONFIG_IEEE80211BE
+- if (hostapd_is_ml_partner(hapd, iface->bss[0]))
+- ap_mld = true;
+-#endif /* CONFIG_IEEE80211BE */
+
+- if (iface == hapd->iface ||
+- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
++ if (!iface || iface == hapd->iface ||
++ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
+ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+- current_len, NULL);
++ current_len, NULL, false);
+ }
+
+ return len;
+ }
+
+-
+-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
++static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
++ size_t *current_len)
+ {
+- size_t total_len = 0, current_len = 0;
+- enum colocation_mode mode = get_colocation_mode(hapd);
+- bool ap_mld = false;
++ size_t len = 0;
+
+ #ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
++ struct hostapd_iface *iface;
++ size_t i;
++
++ if (!hapd->iface || !hapd->iface->interfaces)
++ return 0;
++
++ if (!hapd->conf->mld_ap)
++ return 0;
++
++ /* TODO allow for FILS/Action as well */
++ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
++ return 0;
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ iface = hapd->iface->interfaces->iface[i];
++
++ if (!iface || iface == hapd->iface)
++ continue;
++
++ if (hapd->iface->freq == iface->freq)
++ continue;
++
++ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
++ current_len, NULL, true);
++ }
+ #endif /* CONFIG_IEEE80211BE */
++ return len;
++}
++
++size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params)
++{
++ size_t total_len = 0, current_len = 0;
++ enum colocation_mode mode = get_colocation_mode(hapd);
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+@@ -7423,29 +7500,35 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+- if (mode == COLOCATED_LOWER_BAND || ap_mld)
++ if (mode == COLOCATED_LOWER_BAND)
+ total_len +=
+- hostapd_eid_rnr_multi_iface_len(hapd,
+- ¤t_len);
++ hostapd_eid_rnr_colocation_len(hapd,
++ ¤t_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+ !hapd->iconf->mbssid)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ ¤t_len,
+- NULL);
++ NULL, false);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ ¤t_len,
+- NULL);
++ NULL, false);
+ break;
+
+ default:
+ break;
+ }
+
++ /* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
++ if (include_mld_params &&
++ (type != WLAN_FC_STYPE_BEACON ||
++ hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
++ total_len += hostapd_eid_rnr_mlo_len(hapd, type, ¤t_len);
++
+ return total_len;
+ }
+
+@@ -7509,13 +7592,14 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ struct mbssid_ie_profiles *skip_profiles,
+ size_t i, u8 *tbtt_count, size_t *len,
+- u8 **pos)
++ u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
++ u8 op_class, bool mld_update)
+ {
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_data *bss = iface->bss[i];
+ u8 bss_param = 0;
+- bool ap_mld = false;
+ u8 *eid = *pos;
++ bool ap_mld = false;
+
+ #ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+@@ -7529,10 +7613,47 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ && i >= skip_profiles->start && i < skip_profiles->end)
+ return false;
+
++ /* No need to report if length is for normal TBTT and the BSS
++ * is a MLD. MLD TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
++ return false;
++
++ /* No need to report if length is for MLD TBTT and the BSS
++ * is not MLD. Normal TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
++ return false;
++
++#ifdef CONFIG_IEEE80211BE
++ /* If building for co-location and they are ML partners,
++ * no need to include since the ML RNR will carry this.
++ */
++ if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
++ return false;
++
++ /* If building for ML RNR and they are not ML parnters,
++ * don't include.
++ */
++ if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
++ return false;
++#endif /* CONFIG_IEEE80211BE */
++
+ if (*len + RNR_TBTT_INFO_LEN > 255 ||
+ *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ return true;
+
++ if (!(*tbtt_count)) {
++ /* Add Neighbor report header info only if there is at least
++ * one tbtt info available
++ */
++ *tbtt_count_pos = eid++;
++ *eid++ = tbtt_info_len;
++ *eid++ = op_class;
++ *eid++ = bss->iconf->channel;
++ *len += RNR_TBTT_HEADER_LEN;
++ }
++
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ os_memcpy(eid, bss->own_addr, ETH_ALEN);
+ eid += ETH_ALEN;
+@@ -7556,29 +7677,36 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ *eid++ = bss_param;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
+
+- if (!ap_mld) {
+- *len += RNR_TBTT_INFO_LEN;
+- } else {
+ #ifdef CONFIG_IEEE80211BE
+- u8 param_ch = hapd->eht_mld_bss_param_change;
+-
+- if (hostapd_is_ml_partner(bss, reporting_hapd))
+- *eid++ = 0;
+- else
+- *eid++ = hostapd_get_mld_id(hapd);
+-
+- *eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
+- *eid = (param_ch >> 4) & 0xF;
++ if (ap_mld) {
++ u8 param_ch = bss->eht_mld_bss_param_change;
++ bool is_partner;
++
++ /* If bss is not partner of the reporting_hapd then
++ * a) MLD ID advertised shall be 255.
++ * b) Link ID advertised shall be 15.
++ * c) BPCC advertised shall be 255
++ */
++ is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
++ /* MLD ID */
++ *eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
++ /* Link ID (Bit 3 to Bit 0)
++ * BPCC (Bit 4 to Bit 7)
++ */
++ *eid++ = is_partner ?
++ bss->mld_link_id | ((param_ch & 0xF) << 4) :
++ (MAX_NUM_MLD_LINKS | 0xF0);
++ /* BPCC (Bit 3 to Bit 0) */
++ *eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
+ #ifdef CONFIG_TESTING_OPTIONS
+- if (hapd->conf->mld_indicate_disabled)
++ if (bss->conf->mld_indicate_disabled)
+ *eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ eid++;
+-
+- *len += RNR_TBTT_INFO_MLD_LEN;
+-#endif /* CONFIG_IEEE80211BE */
+ }
++#endif /* CONFIG_IEEE80211BE */
+
++ *len += tbtt_info_len;
+ (*tbtt_count)++;
+ *pos = eid;
+
+@@ -7589,18 +7717,16 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ u8 *eid, size_t *current_len,
+- struct mbssid_ie_profiles *skip_profiles)
++ struct mbssid_ie_profiles *skip_profiles,
++ bool mld_update)
+ {
+ struct hostapd_iface *iface = hapd->iface;
+- size_t i, start = 0;
++ size_t i, start;
+ size_t len = *current_len;
+- u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
+- u8 tbtt_count = 0, op_class, channel;
+- bool ap_mld = false;
+-
+-#ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
+-#endif /* CONFIG_IEEE80211BE */
++ u8 *eid_start = eid, *size_offset = (eid - len) + 1;
++ u8 *tbtt_count_pos = size_offset + 1;
++ u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
++ u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+
+ if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
+ return eid;
+@@ -7612,9 +7738,12 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ NUM_HOSTAPD_MODES)
+ return eid;
+
++repeat_rnr:
++ start = 0;
++ tbtt_count = 0;
+ while (start < iface->num_bss) {
+ if (!len ||
+- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
++ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
+ eid_start = eid;
+ *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+@@ -7623,34 +7752,42 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ tbtt_count = 0;
+ }
+
+- tbtt_count_pos = eid++;
+- *eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+- *eid++ = op_class;
+- *eid++ = hapd->iconf->channel;
+- len += RNR_TBTT_HEADER_LEN;
+-
+ for (i = start; i < iface->num_bss; i++) {
+ if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
+ skip_profiles, i,
+- &tbtt_count, &len, &eid))
++ &tbtt_count, &len, &eid,
++ &tbtt_count_pos, tbtt_info_len,
++ op_class, mld_update))
+ break;
+ }
+
+ start = i;
+- *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+- *size_offset = (eid - size_offset) - 1;
++
++ if (tbtt_count) {
++ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
++ *size_offset = (eid - size_offset) - 1;
++ }
++ }
++
++ total_tbtt_count += tbtt_count;
++
++ /* If building for co-location, re-build again but this time include
++ * ML TBTTs.
++ */
++ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
++ tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
++ goto repeat_rnr;
+ }
+
+- if (tbtt_count == 0)
++ if (!total_tbtt_count)
+ return eid_start;
+
+ *current_len = len;
+ return eid;
+ }
+
+-
+-static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
+- size_t *current_len)
++u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
++ size_t *current_len)
+ {
+ struct hostapd_iface *iface;
+ size_t i;
+@@ -7660,35 +7797,56 @@ static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+- bool ap_mld = false;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hostapd_is_ml_partner(hapd, iface->bss[0]))
+- ap_mld = true;
+-#endif /* CONFIG_IEEE80211BE */
+-
+- if (iface == hapd->iface ||
+- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
++ if (!iface || iface == hapd->iface ||
++ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+- current_len, NULL);
++ current_len, NULL, false);
+ }
+
+ return eid;
+ }
+
++u8 *hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
++ u8 *eid, size_t *current_len)
++{
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_iface *iface;
++ size_t i;
++
++ if (!hapd->iface || !hapd->iface->interfaces)
++ return eid;
++
++ if (!hapd->conf->mld_ap)
++ return eid;
++
++ /* TODO allow for FILS/Action as well */
++ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
++ return eid;
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ iface = hapd->iface->interfaces->iface[i];
++
++ if (!iface || iface == hapd->iface)
++ continue;
++
++ if (hapd->iface->freq == iface->freq)
++ continue;
+
+-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
++ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
++ current_len, NULL, true);
++ }
++#endif /* CONFIG_IEEE80211BE */
++ return eid;
++}
++
++u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, bool include_mld_params)
+ {
+ u8 *eid_start = eid;
+ size_t current_len = 0;
+ enum colocation_mode mode = get_colocation_mode(hapd);
+- bool ap_mld = false;
+-
+-#ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
+-#endif /* CONFIG_IEEE80211BE */
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+@@ -7697,26 +7855,34 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+- if (mode == COLOCATED_LOWER_BAND || ap_mld)
+- eid = hostapd_eid_rnr_multi_iface(hapd, eid,
+- ¤t_len);
++ if (mode == COLOCATED_LOWER_BAND)
++ eid = hostapd_eid_rnr_colocation(hapd, eid,
++ ¤t_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+ !hapd->iconf->mbssid)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+- ¤t_len, NULL);
++ ¤t_len, NULL,
++ false);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+- ¤t_len, NULL);
++ ¤t_len, NULL,
++ false);
+ break;
+
+ default:
+ return eid_start;
+ }
+
++ /* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
++ if (include_mld_params &&
++ (type != WLAN_FC_STYPE_BEACON ||
++ hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
++ eid = hostapd_eid_rnr_mlo(hapd, type, eid, ¤t_len);
++
+ if (eid == eid_start + 2)
+ return eid_start;
+
+@@ -7815,6 +7981,11 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ size_t known_bss_len, size_t *rnr_len)
+ {
+ size_t len = 0, bss_index = 1;
++ bool ap_mld = false;
++
++#ifdef CONFIG_IEEE80211BE
++ ap_mld = !!hapd->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_type != WLAN_FC_STYPE_BEACON &&
+@@ -7847,12 +8018,12 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+
+ *rnr_len += hostapd_eid_rnr_iface_len(
+ hapd, hostapd_mbssid_get_tx_bss(hapd),
+- &rnr_cur_len, &skip_profiles);
++ &rnr_cur_len, &skip_profiles, ap_mld);
+ }
+ }
+
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
+- *rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
++ *rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
+
+ return len;
+ }
+@@ -7978,7 +8149,11 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ {
+ size_t bss_index = 1, cur_len = 0;
+ u8 elem_index = 0, *rnr_start_eid = rnr_eid;
+- bool add_rnr;
++ bool add_rnr, ap_mld = false;
++
++#ifdef CONFIG_IEEE80211BE
++ ap_mld = !!hapd->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_stype != WLAN_FC_STYPE_BEACON &&
+@@ -8023,7 +8198,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ cur_len = 0;
+ rnr_eid = hostapd_eid_rnr_iface(
+ hapd, hostapd_mbssid_get_tx_bss(hapd),
+- rnr_eid, &cur_len, &skip_profiles);
++ rnr_eid, &cur_len, &skip_profiles, ap_mld);
+ }
+ }
+
+@@ -8035,8 +8210,8 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ if (hapd->conf->rnr)
+ rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
+ if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
+- rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
+- &cur_len);
++ rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
++ &cur_len);
+ }
+
+ return eid;
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 262e0ce14..078f4baf9 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -225,8 +225,9 @@ void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
+ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+ u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len);
+-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
+-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
++size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params);
++u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
++ bool include_mld_params);
+ int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int res, struct radius_sta *info);
+ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
new file mode 100644
index 0000000..a4eb061
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
@@ -0,0 +1,271 @@
+From c43241d046e8a6ae75549c23d470b94f16c74ca7 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:51 +0530
+Subject: [PATCH 021/104] tests: MLO: add basic cohosted MLDs functionality
+ testing
+
+Add test case to test basic cohosted MLDs functionality. Add helper
+functions to create the configuration file, start hostapd instance.
+
+Client connectivty test case will be added via a subsequent change.
+
+eht_mld_cohosted_discovery: 2 co-hosted MLDs without non-MLD RNR. Basic
+bring up and beacon, MLD RNR, scan validation.
+
+eht_mld_cohosted_discovery_with_rnr: Same like eht_mld_cohosted_discovery
+but additionally non-MLD RNR (rnr=1) is also enabled. Validate the non-MLD
+RNR as well.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ tests/hwsim/test_eht.py | 230 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 230 insertions(+)
+
+diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
+index a012fe4e7..732406219 100644
+--- a/tests/hwsim/test_eht.py
++++ b/tests/hwsim/test_eht.py
+@@ -15,6 +15,7 @@ from tshark import run_tshark
+ from test_gas import hs20_ap_params
+ from test_dpp import check_dpp_capab, wait_auth_success
+ from test_rrm import build_beacon_request, run_req_beacon, BeaconReport
++import os, subprocess, time, tempfile
+
+ def eht_verify_wifi_version(dev):
+ status = dev.get_status()
+@@ -1823,3 +1824,232 @@ def test_eht_mlo_csa(dev, apdev):
+ traffic_test(wpas, hapd0)
+
+ #TODO: CSA on non-first link
++
++def create_base_conf_file(iface, channel, prefix='hostapd-', hw_mode='g',
++ op_class=None):
++ # Create configuration file and add phy characteristics
++ fd, fname = tempfile.mkstemp(dir='/tmp',
++ prefix=prefix + iface + "-chan-" + str(channel) + "-")
++ f = os.fdopen(fd, 'w')
++
++ f.write("driver=nl80211\n")
++ f.write("hw_mode=" + str(hw_mode) + "\n")
++ f.write("ieee80211n=1\n")
++ if hw_mode == 'a' and \
++ (op_class is None or \
++ op_class not in [131, 132, 133, 134, 135, 136, 137]):
++ f.write("ieee80211ac=1\n")
++ f.write("ieee80211ax=1\n")
++ f.write("ieee80211be=1\n")
++ f.write("channel=" + str(channel) + "\n")
++
++ return f, fname
++
++def append_bss_conf_to_file(f, ifname, params, first=False):
++ # Add BSS specific characteristics
++ config = "bss"
++
++ if first:
++ config = "interface"
++
++ f.write("\n" + config + "=%s\n" % ifname)
++
++ for k, v in list(params.items()):
++ f.write("{}={}\n".format(k,v))
++
++ f.write("mld_ap=1\n")
++
++def dump_config(fname):
++ with open(fname, 'r') as f:
++ cfg = f.read()
++ logger.debug("hostapd config: " + str(fname) + "\n" + cfg)
++
++def get_config(iface, count, ssid, passphrase, channel, bssid_regex,
++ rnr=False, debug=False):
++ f, fname = create_base_conf_file(iface, channel=channel)
++ hapds = []
++
++ for i in range(count):
++ if i == 0:
++ ifname = iface
++ else:
++ ifname = iface + "-" + str(i)
++
++ set_ssid = ssid + str(i)
++ set_passphrase = passphrase + str(i)
++ params = hostapd.wpa2_params(ssid=set_ssid, passphrase=set_passphrase,
++ wpa_key_mgmt="SAE", ieee80211w="2")
++ params['sae_pwe'] = "2"
++ params['group_mgmt_cipher'] = "AES-128-CMAC"
++ params['beacon_prot'] = "1"
++ params["ctrl_interface"] = "/var/run/hostapd/chan_" + str(channel)
++ params["bssid"] = bssid_regex % (i + 1)
++
++ if rnr:
++ params["rnr"]="1"
++
++ append_bss_conf_to_file(f, ifname, params, first=(i == 0))
++
++ hapds.append([ifname, params["ctrl_interface"], i])
++
++ f.close()
++
++ if debug:
++ dump_config(fname)
++
++ return fname, hapds
++
++def start_ap(prefix, configs):
++ pid = prefix + ".hostapd.pid"
++ configs = configs.split()
++
++ cmd = ['../../hostapd/hostapd', '-ddKtB', '-P', pid, '-f',
++ prefix + ".hostapd-log"]
++
++ cmd = cmd + configs
++
++ logger.info("Starting APs")
++ res = subprocess.check_call(cmd)
++ if res != 0:
++ raise Exception("Could not start hostapd: %s" % str(res))
++
++ # Wait for hostapd to complete initialization and daemonize.
++ time.sleep(2)
++
++ if not os.path.exists(pid):
++ raise Exception("hostapd did not create PID file.")
++
++def get_mld_devs(hapd_iface, count, prefix, rnr=False):
++ fname1, hapds1 = get_config(hapd_iface, count=count, ssid="mld-",
++ passphrase="qwertyuiop-", channel=1,
++ bssid_regex="02:00:00:00:07:%02x",
++ rnr=rnr, debug=True)
++ fname2, hapds2 = get_config(hapd_iface, count=count, ssid="mld-",
++ passphrase="qwertyuiop-", channel=6,
++ bssid_regex="02:00:00:00:08:%02x",
++ rnr=rnr, debug=True)
++
++ start_ap(prefix, fname1 + " " + fname2)
++
++ hapd_mld1_link0 = hostapd.Hostapd(ifname=hapds1[0][0], ctrl=hapds1[0][1],
++ bssidx=hapds1[0][2])
++ hapd_mld1_link1 = hostapd.Hostapd(ifname=hapds2[0][0], ctrl=hapds2[0][1],
++ bssidx=hapds2[0][2])
++
++ hapd_mld2_link0 = hostapd.Hostapd(ifname=hapds1[1][0], ctrl=hapds1[1][1],
++ bssidx=hapds1[1][2])
++ hapd_mld2_link1 = hostapd.Hostapd(ifname=hapds2[1][0], ctrl=hapds2[1][1],
++ bssidx=hapds2[1][2])
++
++ if not hapd_mld1_link0.ping():
++ raise Exception("Could not ping hostapd")
++
++ if not hapd_mld1_link1.ping():
++ raise Exception("Could not ping hostapd")
++
++ if not hapd_mld2_link0.ping():
++ raise Exception("Could not ping hostapd")
++
++ if not hapd_mld2_link1.ping():
++ raise Exception("Could not ping hostapd")
++
++ os.remove(fname1)
++ os.remove(fname2)
++
++ return [hapd_mld1_link0, hapd_mld1_link1, hapd_mld2_link0, hapd_mld2_link1]
++
++def stop_mld_devs(hapds, pid):
++ pid = pid + ".hostapd.pid"
++
++ if "OK" not in hapds[0].request("TERMINATE"):
++ raise Exception("Failed to terminate hostapd process")
++
++ ev = hapds[0].wait_event(["CTRL-EVENT-TERMINATING"], timeout=15)
++ if ev is None:
++ raise Exception("CTRL-EVENT-TERMINATING not seen")
++
++ time.sleep(0.5)
++
++ if os.path.exists(pid):
++ raise Exception("PID file exits after process termination")
++
++def eht_parse_rnr(bss, rnr=False, exp_bssid=None):
++ partner_rnr_pattern = re.compile(".*ap_info.*, mld ID=0, link ID=",
++ re.MULTILINE)
++ ml_pattern = re.compile(".*multi-link:.*, MLD addr=.*", re.MULTILINE)
++
++ if partner_rnr_pattern.search(bss) is None:
++ raise Exception("RNR element not found for first link of first MLD")
++
++ if ml_pattern.search(bss) is None:
++ raise Exception("ML element not found for first link of first MLD")
++
++ if not rnr:
++ return
++
++ coloc_rnr_pattern = re.compile(".*ap_info.*, mld ID=255, link ID=..",
++ re.MULTILINE)
++
++ if coloc_rnr_pattern.search(bss) is None:
++ raise Exception("RNR element not found for co-located BSS")
++
++ line = coloc_rnr_pattern.search(bss).group()
++ if line.count('bssid') > 1:
++ raise Exception("More than one BSS found for co-located RNR")
++
++ # Get the BSSID carried in the RNR
++ index = line.rindex('bssid')
++ bssid = line[index+len('bssid')+1:].split(',')[0]
++
++ # Get the MLD ID carried in the RNR
++ index = line.rindex('link ID')
++ link_id = line[index+len('link ID')+1:].split(',')[0]
++
++ if link_id != "15":
++ raise Exception("Unexpected link ID for co-located BSS which is not own partner")
++
++ if bssid != exp_bssid:
++ raise Exception("Unexpected BSSID for co-located BSS")
++
++def eht_mld_cohosted_discovery(dev, apdev, params, rnr=False):
++ with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
++ HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface):
++
++ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
++ wpas.interface_add(wpas_iface)
++
++ hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
++ rnr=rnr)
++
++ # Only scan link 0
++ res = wpas.request("SCAN freq=2412")
++ if "FAIL" in res:
++ raise Exception("Failed to start scan")
++
++ ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"])
++ if ev is None:
++ raise Exception("Scan did not start")
++
++ ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
++ if ev is None:
++ raise Exception("Scan did not complete")
++
++ logger.info("Scan done")
++
++ bss = wpas.request("BSS " + hapds[0].own_addr())
++ logger.info("BSS 0_0: " + str(bss))
++ eht_parse_rnr(bss, rnr, hapds[2].own_addr())
++
++ bss = wpas.request("BSS " + hapds[2].own_addr())
++ logger.info("BSS 1_0: " + str(bss))
++ eht_parse_rnr(bss, rnr, hapds[0].own_addr())
++
++ stop_mld_devs(hapds, params['prefix'])
++
++def test_eht_mld_cohosted_discovery(dev, apdev, params):
++ """EHT 2 AP MLDs discovery"""
++ eht_mld_cohosted_discovery(dev, apdev, params)
++
++def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
++ """EHT 2 AP MLDs discovery (with co-location RNR)"""
++ eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
new file mode 100644
index 0000000..af3c390
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
@@ -0,0 +1,67 @@
+From 29a075f5ea644abdfb9bd93f79b05c72bb9fb78c Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:52 +0530
+Subject: [PATCH 022/104] tests: MLO: add cohosted MLDs connectivity testing
+
+Add test case 'eht_mld_cohosted_connectivity' which creates two 2 link AP
+MLDs and connect 2 links MLD client to each one of them and test data
+traffic.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ tests/hwsim/test_eht.py | 42 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
+index 732406219..f09d31878 100644
+--- a/tests/hwsim/test_eht.py
++++ b/tests/hwsim/test_eht.py
+@@ -2053,3 +2053,45 @@ def test_eht_mld_cohosted_discovery(dev, apdev, params):
+ def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
+ """EHT 2 AP MLDs discovery (with co-location RNR)"""
+ eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
++
++def test_eht_mld_cohosted_connectivity(dev, apdev, params):
++ """EHT 2 AP MLDs with 2 MLD clients connection"""
++ with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
++ HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface), \
++ HWSimRadio(use_mlo=True) as (wpas_radio1, wpas_iface1):
++
++ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
++ wpas.interface_add(wpas_iface)
++
++ wpas1 = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
++ wpas1.interface_add(wpas_iface1)
++
++ hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
++ rnr=False)
++
++ passphrase = "qwertyuiop-"
++ ssid = "mld-"
++
++ # Connect one client to first AP MLD and verify traffic on both links
++ wpas.set("sae_pwe", "1")
++ wpas.connect(ssid+"0", sae_password=passphrase+"0", scan_freq="2412",
++ key_mgmt="SAE", ieee80211w="2")
++
++ eht_verify_status(wpas, hapds[0], 2412, 20, is_ht=True, mld=True,
++ valid_links=3, active_links=3)
++ eht_verify_wifi_version(wpas)
++
++ traffic_test(wpas, hapds[0])
++ traffic_test(wpas, hapds[1])
++
++ # Connect another client to second AP MLD and verify traffic on both links
++ wpas1.set("sae_pwe", "1")
++ wpas1.connect(ssid+"1", sae_password=passphrase+"1", scan_freq="2437",
++ key_mgmt="SAE", ieee80211w="2")
++
++ eht_verify_status(wpas1, hapds[3], 2437, 20, is_ht=True, mld=True,
++ valid_links=3, active_links=3)
++ eht_verify_wifi_version(wpas1)
++
++ traffic_test(wpas1, hapds[3])
++ traffic_test(wpas1, hapds[2])
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
new file mode 100644
index 0000000..9c4f7a4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
@@ -0,0 +1,372 @@
+From 84123bd3df810acd8d463a31d519005cfd0cc8d0 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:13:01 +0800
+Subject: [PATCH 023/104] backport: hostapd: afcd: add AFC daemon support
+
+Introduce Automated Frequency Coordination Daemon (AFCD) support
+for UNII-5 and UNII-7 6GHz bands.
+AFCD will be used by hostapd AFC client in order to forward the AFC
+request to the AFC coordinator and decouple AFC connection management
+from hostapd.
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFCD is tested with AFC DUT Test Harness [0].
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ afc/.gitignore | 1 +
+ afc/Makefile | 31 ++++++
+ afc/afcd.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 324 insertions(+)
+ create mode 100644 afc/.gitignore
+ create mode 100644 afc/Makefile
+ create mode 100644 afc/afcd.c
+
+diff --git a/afc/.gitignore b/afc/.gitignore
+new file mode 100644
+index 000000000..8d8cca905
+--- /dev/null
++++ b/afc/.gitignore
+@@ -0,0 +1 @@
++afcd
+diff --git a/afc/Makefile b/afc/Makefile
+new file mode 100644
+index 000000000..a83bd01db
+--- /dev/null
++++ b/afc/Makefile
+@@ -0,0 +1,31 @@
++ALL=afcd
++
++include ../src/build.rules
++
++CFLAGS += -I../src/utils
++CFLAGS += -I../src
++
++OBJS=afcd.o
++OBJS += ../src/utils/common.o
++OBJS += ../src/utils/wpa_debug.o
++OBJS += ../src/utils/wpabuf.o
++
++ifndef CONFIG_OS
++ifdef CONFIG_NATIVE_WINDOWS
++CONFIG_OS=win32
++else
++CONFIG_OS=unix
++endif
++endif
++OBJS += ../src/utils/os_$(CONFIG_OS).o
++
++LIBS += -lcurl
++
++_OBJS_VAR := OBJS
++include ../src/objs.mk
++afcd: $(OBJS)
++ $(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
++ @$(E) " LD " $@
++
++clean: common-clean
++ rm -f core *~
+diff --git a/afc/afcd.c b/afc/afcd.c
+new file mode 100644
+index 000000000..f502846c5
+--- /dev/null
++++ b/afc/afcd.c
+@@ -0,0 +1,292 @@
++/*
++ * Automated Frequency Coordination Daemon
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <curl/curl.h>
++#include <sys/un.h>
++#include <sys/stat.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#define CURL_TIMEOUT 60
++#define AFCD_SOCK "afcd.sock"
++
++struct curl_ctx {
++ char *buf;
++ size_t buf_len;
++};
++
++static volatile bool exiting;
++
++static char *path = "/var/run";
++static char *bearer_token;
++static char *url;
++static int port = 443;
++
++
++static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
++ void *userdata)
++{
++ struct curl_ctx *ctx = userdata;
++ char *buf;
++
++ buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
++ if (!buf)
++ return 0;
++
++ ctx->buf = buf;
++ os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
++ buf[ctx->buf_len + size * nmemb] = '\0';
++ ctx->buf_len += size * nmemb;
++
++ return size * nmemb;
++}
++
++
++static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
++{
++ struct curl_slist *headers = NULL;
++ CURL *curl;
++ int ret;
++
++ wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
++
++ curl_global_init(CURL_GLOBAL_ALL);
++ curl = curl_easy_init();
++ if (!curl)
++ return -ENOMEM;
++
++ headers = curl_slist_append(headers, "Accept: application/json");
++ headers = curl_slist_append(headers,
++ "Content-Type: application/json");
++ headers = curl_slist_append(headers, "charset: utf-8");
++
++ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
++ curl_easy_setopt(curl, CURLOPT_URL, url);
++ curl_easy_setopt(curl, CURLOPT_PORT, port);
++ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
++ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
++ afcd_curl_cb_write);
++ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
++ curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
++ curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
++ curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
++ if (bearer_token)
++ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
++ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
++ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
++ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
++ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
++ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
++
++ ret = curl_easy_perform(curl);
++ if (ret != CURLE_OK)
++ wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
++ curl_easy_strerror(ret));
++
++ curl_easy_cleanup(curl);
++ curl_global_cleanup();
++
++ return ret == CURLE_OK ? 0 : -EINVAL;
++}
++
++
++static void handle_term(int sig)
++{
++ wpa_printf(MSG_ERROR, "Received signal %d", sig);
++ exiting = true;
++}
++
++
++static void usage(void)
++{
++ wpa_printf(MSG_ERROR,
++ "%s:\n"
++ "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
++ __func__);
++}
++
++
++#define BUFSIZE 8192
++static int afcd_server_run(void)
++{
++ size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
++ struct sockaddr_un addr = {
++ .sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++ .sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++ };
++ int sockfd, ret = 0;
++ char *fname = NULL;
++ unsigned char *buf;
++ fd_set read_set;
++
++ if (len >= sizeof(addr.sun_path))
++ return -EINVAL;
++
++ if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
++ return -EINVAL;
++
++ buf = os_malloc(BUFSIZE);
++ if (!buf)
++ return -ENOMEM;
++
++ fname = os_malloc(len + 1);
++ if (!fname) {
++ ret = -ENOMEM;
++ goto free_buf;
++ }
++
++ os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
++ fname[len] = '\0';
++ os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
++
++ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (sockfd < 0) {
++ wpa_printf(MSG_ERROR, "Failed creating socket");
++ ret = -errno;
++ goto unlink;
++ }
++
++ if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ wpa_printf(MSG_ERROR, "Failed to bind socket");
++ ret = -errno;
++ goto close;
++ }
++
++ if (listen(sockfd, 10) < 0) {
++ wpa_printf(MSG_ERROR, "Failed to listen on socket");
++ ret = -errno;
++ goto close;
++ }
++
++ FD_ZERO(&read_set);
++ while (!exiting) {
++ socklen_t addr_len = sizeof(addr);
++ struct sockaddr_in6 c_addr;
++ struct timeval timeout = {
++ .tv_sec = 1,
++ };
++ struct curl_ctx ctx = {};
++ int fd;
++
++ FD_SET(sockfd, &read_set);
++ if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
++ if (errno != EINTR) {
++ wpa_printf(MSG_ERROR,
++ "Select failed on socket");
++ ret = -errno;
++ break;
++ }
++ continue;
++ }
++
++ if (!FD_ISSET(sockfd, &read_set))
++ continue;
++
++ fd = accept(sockfd, (struct sockaddr *)&c_addr,
++ &addr_len);
++ if (fd < 0) {
++ if (errno != EINTR) {
++ wpa_printf(MSG_ERROR,
++ "Failed accepting connections");
++ ret = -errno;
++ break;
++ }
++ continue;
++ }
++
++ os_memset(buf, 0, BUFSIZE);
++ if (recv(fd, buf, BUFSIZE, 0) <= 0) {
++ close(fd);
++ continue;
++ }
++
++ wpa_printf(MSG_DEBUG, "Received request: %s", buf);
++ if (!afcd_send_request(&ctx, buf)) {
++ wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
++ send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
++ free(ctx.buf);
++ }
++ close(fd);
++ }
++close:
++ close(sockfd);
++unlink:
++ unlink(fname);
++ os_free(fname);
++free_buf:
++ os_free(buf);
++
++ return ret;
++}
++
++
++int main(int argc, char **argv)
++{
++ bool daemonize = false;
++ char *pid_file = NULL;
++
++ if (os_program_init())
++ return -1;
++
++ for (;;) {
++ int c = getopt(argc, argv, "u:p:t:D:P:hdB");
++
++ if (c < 0)
++ break;
++
++ switch (c) {
++ case 'h':
++ usage();
++ return 0;
++ case 'B':
++ daemonize = true;
++ break;
++ case 'D':
++ path = optarg;
++ break;
++ case 'P':
++ os_free(pid_file);
++ pid_file = os_rel2abs_path(optarg);
++ break;
++ case 'u':
++ url = optarg;
++ break;
++ case 'p':
++ port = atoi(optarg);
++ break;
++ case 'd':
++ if (wpa_debug_level > 0)
++ wpa_debug_level--;
++ break;
++ case 't':
++ bearer_token = optarg;
++ break;
++ default:
++ usage();
++ return -EINVAL;
++ }
++ }
++
++ if (!url) {
++ usage();
++ return -EINVAL;
++ }
++
++ if (daemonize && os_daemonize(pid_file)) {
++ wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
++ return -EINVAL;
++ }
++
++ signal(SIGTERM, handle_term);
++ signal(SIGINT, handle_term);
++
++ return afcd_server_run();
++}
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
new file mode 100644
index 0000000..aac2145
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
@@ -0,0 +1,55 @@
+From 524c84524695034b8d531d70b546d5479d59641f Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:17:31 +0800
+Subject: [PATCH 024/104] backport: hostapd: export hostapd_is_usable_chans
+ utility routine
+
+This is a preliminary patch to introduce AFC support.
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/hw_features.c | 2 +-
+ src/ap/hw_features.h | 6 ++++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index fd401d78a..e652d7504 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -995,7 +995,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
+-static int hostapd_is_usable_chans(struct hostapd_iface *iface)
++int hostapd_is_usable_chans(struct hostapd_iface *iface)
+ {
+ int secondary_freq;
+ struct hostapd_channel_data *pri_chan;
+diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
+index c682c6d20..eeffb1abd 100644
+--- a/src/ap/hw_features.h
++++ b/src/ap/hw_features.h
+@@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface);
+ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode);
+ int hostapd_determine_mode(struct hostapd_iface *iface);
++int hostapd_is_usable_chans(struct hostapd_iface *iface);
+ #else /* NEED_AP_MLME */
+ static inline void
+ hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+@@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
+ return 0;
+ }
+
++static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
++{
++ return 1;
++}
++
+ #endif /* NEED_AP_MLME */
+
+ #endif /* HW_FEATURES_H */
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
new file mode 100644
index 0000000..357ca69
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
@@ -0,0 +1,1542 @@
+From c635af2f526c7dc7a862e5c6fed5f2015d8e85b6 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:18:37 +0800
+Subject: [PATCH 025/104] backport: hostapd: ap: add AFC client support
+
+Introduce Automated Frequency Coordination (AFC) support for UNII-5 and
+UNII-7 6GHz bands.
+AFC client will connect to AFCD providing AP related parameter for AFC
+coordinator (e.g. geolocation, supported frequencies, ..).
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFC hostapd client is tested with AFC DUT Test Harness [0].
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ hostapd/Makefile | 6 +
+ hostapd/config_file.c | 262 ++++++++++++
+ hostapd/defconfig | 3 +
+ hostapd/hostapd.conf | 42 ++
+ src/ap/afc.c | 918 ++++++++++++++++++++++++++++++++++++++++++
+ src/ap/ap_config.c | 16 +
+ src/ap/ap_config.h | 47 +++
+ src/ap/hostapd.c | 60 +++
+ src/ap/hostapd.h | 29 ++
+ src/ap/hw_features.c | 2 +
+ 10 files changed, 1385 insertions(+)
+ create mode 100644 src/ap/afc.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index b3cb68673..405e05e5f 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -103,6 +103,12 @@ CFLAGS += -DCONFIG_TAXONOMY
+ OBJS += ../src/ap/taxonomy.o
+ endif
+
++ifdef CONFIG_AFC
++CFLAGS += -DCONFIG_AFC
++OBJS += ../src/ap/afc.o
++LIBS += -ljson-c
++endif
++
+ ifdef CONFIG_MODULE_TESTS
+ CFLAGS += -DCONFIG_MODULE_TESTS
+ OBJS += hapd_module_tests.o
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 56b2df3ae..261905368 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -2436,6 +2436,191 @@ static int get_u16(const char *pos, int line, u16 *ret_val)
+ #endif /* CONFIG_IEEE80211BE */
+
+
++#ifdef CONFIG_AFC
++static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos)
++{
++ struct cert_id *c = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p;
++
++ c = os_realloc_array(c, count + 1, sizeof(*c));
++ if (!c)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ':');
++ if (!p)
++ goto error;
++
++ *p++ = '\0';
++ if (!p || !p[0])
++ goto error;
++
++ c[i].rulset = os_malloc(os_strlen(pos) + 1);
++ if (!c[i].rulset)
++ goto error;
++
++ os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1);
++ pos = p;
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ c[i].id = os_malloc(os_strlen(pos) + 1);
++ if (!c[i].id)
++ goto error;
++
++ os_strlcpy(c[i].id, pos, os_strlen(pos) + 1);
++ pos = p;
++ }
++
++ conf->afc.n_cert_ids = count;
++ conf->afc.cert_ids = c;
++
++ return 0;
++
++error:
++ for (i = 0; i < count; i++) {
++ os_free(c[i].rulset);
++ os_free(c[i].id);
++ }
++ os_free(c);
++
++ return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data,
++ unsigned int *n_linear_polygon_data,
++ char *pos)
++{
++ struct afc_linear_polygon *d = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p, *end;
++
++ d = os_realloc_array(d, count + 1, sizeof(*d));
++ if (!d)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ':');
++ if (!p)
++ goto error;
++
++ *p++ = '\0';
++ if (!p || !p[0])
++ goto error;
++
++ d[i].longitude = strtod(pos, &end);
++ if (*end)
++ goto error;
++
++ pos = p;
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ d[i].latitude = strtod(pos, &end);
++ if (*end)
++ goto error;
++
++ pos = p;
++ }
++
++ *n_linear_polygon_data = count;
++ *data = d;
++
++ return 0;
++
++error:
++ os_free(d);
++ return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos)
++{
++ struct afc_freq_range *f = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p;
++
++ f = os_realloc_array(f, count + 1, sizeof(*f));
++ if (!f)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ':');
++ if (!p)
++ goto error;
++
++ *p++ = '\0';
++ if (!p || !p[0])
++ goto error;
++
++ f[i].low_freq = atoi(pos);
++ pos = p;
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ f[i].high_freq = atoi(pos);
++ pos = p;
++ }
++
++ conf->afc.n_freq_range = count;
++ conf->afc.freq_range = f;
++
++ return 0;
++
++error:
++ os_free(f);
++ return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
++{
++ unsigned int *oc = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p;
++
++ oc = os_realloc_array(oc, count + 1, sizeof(*oc));
++ if (!oc)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ oc[i] = atoi(pos);
++ pos = p;
++ }
++
++ conf->afc.n_op_class = count;
++ conf->afc.op_class = oc;
++
++ return 0;
++}
++#endif /* CONFIG_AFC */
++
++
+ static int hostapd_config_fill(struct hostapd_config *conf,
+ struct hostapd_bss_config *bss,
+ const char *buf, char *pos, int line)
+@@ -3862,6 +4047,83 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ bss->unsol_bcast_probe_resp_interval = val;
++#ifdef CONFIG_AFC
++ } else if (os_strcmp(buf, "afcd_sock") == 0) {
++ conf->afc.socket = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.socket)
++ return 1;
++
++ os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_request_version") == 0) {
++ conf->afc.request.version = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.request.version)
++ return 1;
++
++ os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_request_id") == 0) {
++ conf->afc.request.id = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.request.id)
++ return 1;
++
++ os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_serial_number") == 0) {
++ conf->afc.request.sn = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.request.sn)
++ return 1;
++
++ os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_cert_ids") == 0) {
++ if (hostapd_afc_parse_cert_ids(conf, pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_location_type") == 0) {
++ conf->afc.location.type = atoi(pos);
++ if (conf->afc.location.type != ELLIPSE &&
++ conf->afc.location.type != LINEAR_POLYGON &&
++ conf->afc.location.type != RADIAL_POLYGON)
++ return 1;
++ } else if (os_strcmp(buf, "afc_linear_polygon") == 0) {
++ if (hostapd_afc_parse_position_data(
++ &conf->afc.location.linear_polygon_data,
++ &conf->afc.location.n_linear_polygon_data,
++ pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_radial_polygon") == 0) {
++ if (hostapd_afc_parse_position_data(
++ (struct afc_linear_polygon **)
++ &conf->afc.location.radial_polygon_data,
++ &conf->afc.location.n_radial_polygon_data,
++ pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_major_axis") == 0) {
++ conf->afc.location.major_axis = atoi(pos);
++ } else if (os_strcmp(buf, "afc_minor_axis") == 0) {
++ conf->afc.location.minor_axis = atoi(pos);
++ } else if (os_strcmp(buf, "afc_orientation") == 0) {
++ conf->afc.location.orientation = atoi(pos);
++ } else if (os_strcmp(buf, "afc_height") == 0) {
++ char *end;
++
++ conf->afc.location.height = strtod(pos, &end);
++ if (*end)
++ return 1;
++ } else if (os_strcmp(buf, "afc_height_type") == 0) {
++ conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.location.height_type)
++ return 1;
++
++ os_strlcpy(conf->afc.location.height_type, pos,
++ os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) {
++ conf->afc.location.vertical_tolerance = atoi(pos);
++ } else if (os_strcmp(buf, "afc_min_power") == 0) {
++ conf->afc.min_power = atoi(pos);
++ } else if (os_strcmp(buf, "afc_freq_range") == 0) {
++ if (hostapd_afc_parse_freq_range(conf, pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_op_class") == 0) {
++ if (hostapd_afc_parse_op_class(conf, pos))
++ return 1;
++#endif /* CONFIG_AFC */
+ } else if (os_strcmp(buf, "mbssid") == 0) {
+ int mbssid = atoi(pos);
+ if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
+diff --git a/hostapd/defconfig b/hostapd/defconfig
+index 550db697b..66bf894eb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -425,3 +425,6 @@ CONFIG_DPP2=y
+
+ # Wi-Fi Aware unsynchronized service discovery (NAN USD)
+ #CONFIG_NAN_USD=y
++
++# Enable Automated Frequency Coordination for 6GHz outdoor
++#CONFIG_AFC=y
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index d80abcac0..0d10998af 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0
+ # Valid range: 0..20 TUs; default is 0 (disabled)
+ #unsol_bcast_probe_resp_interval=0
+
++##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands #######
++
++# AFC daemon connection socket
++#afcd_sock=/var/run/afcd.sock
++
++# AFC request identification parameters
++#afc_request_version=1.1
++#afc_request_id=11235813
++#afc_serial_number=abcdefg
++#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000
++#
++# AFC location type:
++# 0 = ellipse
++# 1 = linear polygon
++# 2 = radial polygon
++#afc_location_type=0
++#
++# AFC ellipse or linear polygon coordinations
++#afc_linear_polygon=-122.984157:37.425056
++#
++# AFC radial polygon coordinations
++#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33
++#
++# AFC ellipse major/minor axis and orientation
++#afc_major_axis=100
++#afc_minor_axis=50
++#afc_orientation=70
++#
++# AFC device elevation parameters
++#afc_height=3.0
++#afc_height_type=AGL
++#afc_vertical_tolerance=7
++#
++# AFC minimum desired TX power (dbm)
++#afc_min_power=24
++#
++# AFC request frequency ranges
++#afc_freq_range=5925:6425,6525:6875
++#
++# AFC request operation classes
++#afc_op_class=131,132,133,134,136
++
+ ##### IEEE 802.11be related configuration #####################################
+
+ #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+new file mode 100644
+index 000000000..c75d5d582
+--- /dev/null
++++ b/src/ap/afc.c
+@@ -0,0 +1,918 @@
++/*
++ * Automated Frequency Coordination
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <json-c/json.h>
++#include <sys/un.h>
++#include <time.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "hostapd.h"
++#include "acs.h"
++#include "hw_features.h"
++
++#define HOSTAPD_AFC_RETRY_TIMEOUT 180
++#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */
++#define HOSTAPD_AFC_BUFSIZE 4096
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
++
++
++static struct json_object *
++hostapd_afc_build_location_request(struct hostapd_iface *iface)
++{
++ struct json_object *location_obj, *center_obj, *ellipse_obj;
++ struct json_object *elevation_obj, *str_obj;
++ struct hostapd_config *iconf = iface->conf;
++ bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
++
++ location_obj = json_object_new_object();
++ if (!location_obj)
++ return NULL;
++
++ if (iconf->afc.location.type != LINEAR_POLYGON) {
++ struct afc_linear_polygon *lp =
++ &iconf->afc.location.linear_polygon_data[0];
++
++ ellipse_obj = json_object_new_object();
++ if (!ellipse_obj)
++ goto error;
++
++ center_obj = json_object_new_object();
++ if (!center_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "center", center_obj);
++
++ str_obj = json_object_new_double(lp->longitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "longitude", str_obj);
++ str_obj = json_object_new_double(lp->latitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "latitude", str_obj);
++
++ }
++
++ switch (iconf->afc.location.type) {
++ case LINEAR_POLYGON: {
++ struct json_object *outer_boundary_obj;
++ int i;
++
++ outer_boundary_obj = json_object_new_object();
++ if (!outer_boundary_obj)
++ goto error;
++
++ json_object_object_add(location_obj, "linearPolygon",
++ outer_boundary_obj);
++ ellipse_obj = json_object_new_array();
++ if (!ellipse_obj)
++ goto error;
++
++ json_object_object_add(outer_boundary_obj, "outerBoundary",
++ ellipse_obj);
++ for (i = 0;
++ i < iconf->afc.location.n_linear_polygon_data; i++) {
++ struct afc_linear_polygon *lp =
++ &iconf->afc.location.linear_polygon_data[i];
++
++ center_obj = json_object_new_object();
++ if (!center_obj)
++ goto error;
++
++ json_object_array_add(ellipse_obj, center_obj);
++ str_obj = json_object_new_double(lp->longitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "longitude",
++ str_obj);
++ str_obj = json_object_new_double(lp->latitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "latitude",
++ str_obj);
++ }
++ break;
++ }
++ case RADIAL_POLYGON: {
++ struct json_object *outer_boundary_obj;
++ int i;
++
++ json_object_object_add(location_obj, "radialPolygon",
++ ellipse_obj);
++
++ outer_boundary_obj = json_object_new_array();
++ if (!outer_boundary_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "outerBoundary",
++ outer_boundary_obj);
++ for (i = 0;
++ i < iconf->afc.location.n_radial_polygon_data; i++) {
++ struct afc_radial_polygon *rp =
++ &iconf->afc.location.radial_polygon_data[i];
++ struct json_object *angle_obj;
++
++ angle_obj = json_object_new_object();
++ if (!angle_obj)
++ goto error;
++
++ json_object_array_add(outer_boundary_obj, angle_obj);
++
++ str_obj = json_object_new_double(rp->angle);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(angle_obj, "angle", str_obj);
++ str_obj = json_object_new_double(rp->length);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(angle_obj, "length", str_obj);
++ }
++ break;
++ }
++ case ELLIPSE:
++ default:
++ json_object_object_add(location_obj, "ellipse", ellipse_obj);
++
++ str_obj = json_object_new_int(iconf->afc.location.major_axis);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "majorAxis", str_obj);
++ str_obj = json_object_new_int(iconf->afc.location.minor_axis);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "minorAxis", str_obj);
++ str_obj = json_object_new_int(iconf->afc.location.orientation);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "orientation", str_obj);
++ break;
++ }
++
++ elevation_obj = json_object_new_object();
++ if (!elevation_obj)
++ goto error;
++
++ json_object_object_add(location_obj, "elevation",
++ elevation_obj);
++ str_obj = json_object_new_double(iconf->afc.location.height);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(elevation_obj, "height", str_obj);
++ str_obj = json_object_new_string(iconf->afc.location.height_type);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(elevation_obj, "heightType", str_obj);
++ str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(elevation_obj, "verticalUncertainty",
++ str_obj);
++ str_obj = json_object_new_int(is_ap_indoor);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(location_obj, "indoorDeployment", str_obj);
++
++ return location_obj;
++
++error:
++ json_object_put(location_obj);
++ return NULL;
++}
++
++
++static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
++{
++ struct json_object *chan_list_obj, *str_obj;
++ const struct oper_class_map *oper_class;
++ int chan_offset, chan;
++
++ oper_class = get_oper_class(NULL, op_class);
++ if (!oper_class)
++ return NULL;
++
++ chan_list_obj = json_object_new_array();
++ if (!chan_list_obj)
++ return NULL;
++
++ switch (op_class) {
++ case 132: /* 40MHz */
++ chan_offset = 2;
++ break;
++ case 133: /* 80MHz */
++ chan_offset = 6;
++ break;
++ case 134: /* 160MHz */
++ chan_offset = 14;
++ break;
++ default:
++ chan_offset = 0;
++ break;
++ }
++
++ for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
++ chan += oper_class->inc) {
++ str_obj = json_object_new_int(chan + chan_offset);
++ if (!str_obj) {
++ json_object_put(chan_list_obj);
++ return NULL;
++ }
++ json_object_array_add(chan_list_obj, str_obj);
++ }
++
++ return chan_list_obj;
++}
++
++
++static struct json_object *
++hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
++{
++ struct json_object *op_class_list_obj, *str_obj;
++ struct hostapd_config *iconf = iface->conf;
++ int i;
++
++ op_class_list_obj = json_object_new_array();
++ if (!op_class_list_obj)
++ return NULL;
++
++ for (i = 0; i < iconf->afc.n_op_class; i++) {
++ struct json_object *op_class_obj, *chan_list_obj;
++ u8 op_class = iconf->afc.op_class[i];
++
++ if (!is_6ghz_op_class(op_class))
++ continue;
++
++ op_class_obj = json_object_new_object();
++ if (!op_class_obj)
++ goto error;
++
++ json_object_array_add(op_class_list_obj, op_class_obj);
++ str_obj = json_object_new_int(op_class);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(op_class_obj, "globalOperatingClass",
++ str_obj);
++
++ chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
++ if (!chan_list_obj)
++ goto error;
++
++ json_object_object_add(op_class_obj, "channelCfi",
++ chan_list_obj);
++ }
++
++ return op_class_list_obj;
++
++error:
++ json_object_put(op_class_list_obj);
++ return NULL;
++}
++
++
++static struct json_object *
++hostapd_afc_build_request(struct hostapd_iface *iface)
++{
++ struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
++ struct json_object *s2_obj, *str_obj, *location_obj;
++ struct hostapd_config *iconf = iface->conf;
++ struct json_object *op_class_list_obj;
++ int i;
++
++ l1_obj = json_object_new_object();
++ if (!l1_obj)
++ return NULL;
++
++ if (iconf->afc.request.version) {
++ str_obj = json_object_new_string(iconf->afc.request.version);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(l1_obj, "version", str_obj);
++ }
++
++ la1_obj = json_object_new_array();
++ if (!la1_obj)
++ goto error;
++
++ json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
++ la1_obj);
++ l2_obj = json_object_new_object();
++ if (!l2_obj)
++ goto error;
++
++ json_object_array_add(la1_obj, l2_obj);
++ if (iconf->afc.request.id) {
++ str_obj = json_object_new_string(iconf->afc.request.id);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "requestId", str_obj);
++ }
++
++ s2_obj = json_object_new_object();
++ if (!s2_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
++ if (iconf->afc.request.sn) {
++ str_obj = json_object_new_string(iconf->afc.request.sn);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(s2_obj, "serialNumber", str_obj);
++ }
++
++ la2_obj = json_object_new_array();
++ if (!la2_obj)
++ goto error;
++
++ json_object_object_add(s2_obj, "certificationId", la2_obj);
++ for (i = 0; i < iconf->afc.n_cert_ids; i++) {
++ struct json_object *obj;
++
++ obj = json_object_new_object();
++ if (!obj)
++ goto error;
++
++ json_object_array_add(la2_obj, obj);
++ str_obj =
++ json_object_new_string(iconf->afc.cert_ids[i].rulset);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "rulesetId", str_obj);
++ str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "id", str_obj);
++ }
++
++ location_obj = hostapd_afc_build_location_request(iface);
++ if (!location_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "location", location_obj);
++ str_obj = json_object_new_int(iconf->afc.min_power);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "minDesiredPower", str_obj);
++
++ if (iconf->afc.n_freq_range) {
++ struct json_object *freq_obj;
++
++ freq_obj = json_object_new_array();
++ if (!freq_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "inquiredFrequencyRange",
++ freq_obj);
++ for (i = 0; i < iconf->afc.n_freq_range; i++) {
++ struct afc_freq_range *fr = &iconf->afc.freq_range[i];
++ struct json_object *obj;
++
++ obj = json_object_new_object();
++ if (!obj)
++ goto error;
++
++ json_object_array_add(freq_obj, obj);
++ str_obj = json_object_new_int(fr->low_freq);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "lowFrequency", str_obj);
++ str_obj = json_object_new_int(fr->high_freq);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "highFrequency", str_obj);
++ }
++ }
++
++ op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
++ if (!op_class_list_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
++
++ wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
++ json_object_get_string(l1_obj));
++
++ return l1_obj;
++
++error:
++ json_object_put(l1_obj);
++
++ return NULL;
++}
++
++
++static int
++hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
++ struct json_object *reply_elem_obj)
++{
++ struct afc_freq_range_elem *f = NULL;
++ struct json_object *obj;
++ int i, count = 0;
++
++ if (!json_object_object_get_ex(reply_elem_obj,
++ "availableFrequencyInfo", &obj))
++ return 0;
++
++ for (i = 0; i < json_object_array_length(obj); i++) {
++ struct json_object *range_elem_obj, *freq_range_obj;
++ struct json_object *high_freq_obj, *low_freq_obj;
++ struct json_object *max_psd_obj;
++
++ range_elem_obj = json_object_array_get_idx(obj, i);
++ if (!json_object_object_get_ex(range_elem_obj,
++ "frequencyRange",
++ &freq_range_obj))
++ continue;
++
++ if (!json_object_object_get_ex(freq_range_obj,
++ "lowFrequency",
++ &low_freq_obj))
++ continue;
++
++ if (!json_object_object_get_ex(freq_range_obj,
++ "highFrequency",
++ &high_freq_obj))
++ continue;
++
++ if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
++ &max_psd_obj) &&
++ !json_object_object_get_ex(range_elem_obj, "maxPSD",
++ &max_psd_obj))
++ continue;
++
++ f = os_realloc_array(f, count + 1, sizeof(*f));
++ if (!f)
++ return -ENOMEM;
++
++ f[count].low_freq = json_object_get_int(low_freq_obj);
++ f[count].high_freq = json_object_get_int(high_freq_obj);
++ f[count++].max_psd = json_object_get_int(max_psd_obj);
++ }
++ iface->afc.freq_range = f;
++ iface->afc.num_freq_range = count;
++
++ return 0;
++}
++
++
++static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
++ int *chan_list_size, u8 op_class,
++ int center_chan, int power)
++{
++ int num_low_subchan, ch, count = *chan_list_size;
++ struct afc_chan_info_elem *c = *chan_list;
++
++ switch (op_class) {
++ case 132: /* 40MHz */
++ num_low_subchan = 2;
++ break;
++ case 133: /* 80MHz */
++ num_low_subchan = 6;
++ break;
++ case 134: /* 160MHz */
++ num_low_subchan = 14;
++ break;
++ default:
++ num_low_subchan = 0;
++ break;
++ }
++
++ for (ch = center_chan - num_low_subchan;
++ ch <= center_chan + num_low_subchan; ch += 4) {
++ int i;
++
++ for (i = 0; i < count; i++) {
++ if (c[i].chan == ch)
++ break;
++ }
++
++ if (i == count) {
++ c = os_realloc_array(c, count + 1, sizeof(*c));
++ if (!c)
++ return -ENOMEM;
++
++ c[count].chan = ch;
++ c[count++].power = power;
++ }
++ }
++
++ *chan_list_size = count;
++ *chan_list = c;
++
++ return 0;
++}
++
++
++static int
++hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
++ struct json_object *reply_elem_obj)
++{
++ struct afc_chan_info_elem *c = NULL;
++ struct json_object *obj;
++ int i, count = 0;
++
++ if (!json_object_object_get_ex(reply_elem_obj,
++ "availableChannelInfo", &obj))
++ return 0;
++
++ for (i = 0; i < json_object_array_length(obj); i++) {
++ struct json_object *range_elem_obj, *op_class_obj;
++ struct json_object *chan_cfi_obj, *max_eirp_obj;
++ int ch, op_class;
++
++ range_elem_obj = json_object_array_get_idx(obj, i);
++ if (!json_object_object_get_ex(range_elem_obj,
++ "globalOperatingClass",
++ &op_class_obj))
++ continue;
++
++ if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
++ &max_eirp_obj))
++ continue;
++
++ if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
++ &chan_cfi_obj))
++ continue;
++
++ op_class = json_object_get_int(op_class_obj);
++ for (ch = 0;
++ ch < json_object_array_length(chan_cfi_obj); ch++) {
++ struct json_object *pwr_obj;
++ struct json_object *ch_obj;
++ int channel, power;
++
++ ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
++ if (!ch_obj)
++ continue;
++
++ pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
++ if (!pwr_obj)
++ continue;
++
++ channel = json_object_get_int(ch_obj);
++ power = json_object_get_int(pwr_obj);
++
++ hostad_afc_update_chan_info(&c, &count, op_class,
++ channel, power);
++ }
++ iface->afc.chan_info_list = c;
++ iface->afc.num_chan_info = count;
++ }
++
++ return 0;
++}
++
++
++static int hostad_afc_get_timeout(struct json_object *obj)
++{
++ time_t t, now;
++ struct tm tm;
++
++ if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
++ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
++ &tm.tm_min, &tm.tm_sec) <= 0)
++ return HOSTAPD_AFC_TIMEOUT;
++
++ tm.tm_year -= 1900;
++ tm.tm_mon -= 1;
++ tm.tm_isdst = -1;
++ t = mktime(&tm);
++ time(&now);
++
++ return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
++}
++
++
++static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
++{
++ struct json_object *payload_obj, *reply_obj, *version_obj;
++ struct hostapd_config *iconf = iface->conf;
++ int i, request_timeout = -1, ret = -EINVAL;
++
++ wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
++ payload_obj = json_tokener_parse(reply);
++ if (!payload_obj)
++ return -EINVAL;
++
++ if (!json_object_object_get_ex(payload_obj, "version", &version_obj))
++ return -EINVAL;
++
++ if (iconf->afc.request.version &&
++ os_strcmp(iconf->afc.request.version,
++ json_object_get_string(version_obj)))
++ return -EINVAL;
++
++ if (!json_object_object_get_ex(payload_obj,
++ "availableSpectrumInquiryResponses",
++ &reply_obj))
++ return -EINVAL;
++
++ for (i = 0; i < json_object_array_length(reply_obj); i++) {
++ struct json_object *reply_elem_obj, *obj, *status_obj;
++ int j, status = -EINVAL;
++
++ reply_elem_obj = json_object_array_get_idx(reply_obj, i);
++ if (!reply_elem_obj)
++ continue;
++
++ if (!json_object_object_get_ex(reply_elem_obj, "requestId",
++ &obj))
++ continue;
++
++ if (iconf->afc.request.id &&
++ os_strcmp(iconf->afc.request.id,
++ json_object_get_string(obj)))
++ continue;
++
++ if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
++ &obj))
++ continue;
++
++ for (j = 0; j < iconf->afc.n_cert_ids; j++) {
++ if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
++ json_object_get_string(obj)))
++ break;
++ }
++
++ if (j == iconf->afc.n_cert_ids)
++ continue;
++
++ if (!json_object_object_get_ex(reply_elem_obj, "response",
++ &obj))
++ continue;
++
++ if (json_object_object_get_ex(obj, "shortDescription",
++ &status_obj))
++ wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
++ i, json_object_get_string(status_obj));
++
++ if (json_object_object_get_ex(obj, "responseCode",
++ &status_obj))
++ status = json_object_get_int(status_obj);
++
++ if (status < 0)
++ continue;
++
++ if (hostad_afc_parse_available_freq_info(iface,
++ reply_elem_obj) ||
++ hostad_afc_parse_available_chan_info(iface,
++ reply_elem_obj))
++ continue;
++
++ if (json_object_object_get_ex(reply_elem_obj,
++ "availabilityExpireTime",
++ &obj)) {
++ int timeout = hostad_afc_get_timeout(obj);
++
++ if (request_timeout < 0 || timeout < request_timeout)
++ request_timeout = timeout;
++ }
++
++ ret = status;
++ }
++
++ iface->afc.data_valid = true;
++ iface->afc.timeout = request_timeout;
++ if (iface->afc.timeout < 0)
++ iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++
++ return ret;
++}
++
++
++static int hostapd_afc_send_receive(struct hostapd_iface *iface)
++{
++ struct hostapd_config *iconf = iface->conf;
++ json_object *request_obj = NULL;
++ struct timeval sock_timeout = {
++ .tv_sec = 5,
++ };
++ struct sockaddr_un addr = {
++ .sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++ .sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++ };
++ char buf[HOSTAPD_AFC_BUFSIZE] = {};
++ const char *request;
++ int sockfd, ret;
++ fd_set read_set;
++
++ if (iface->afc.data_valid) {
++ /* AFC data already downloaded from the server */
++ return 0;
++ }
++
++ iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++ if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
++ wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
++ iconf->afc.socket);
++ return -EINVAL;
++ }
++
++ os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
++ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (sockfd < 0) {
++ wpa_printf(MSG_ERROR, "Failed creating AFC socket");
++ return sockfd;
++ }
++
++ if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
++ ret = -EIO;
++ goto close_sock;
++ }
++
++ request_obj = hostapd_afc_build_request(iface);
++ if (!request_obj) {
++ ret = -ENOMEM;
++ goto close_sock;
++ }
++
++ request = json_object_to_json_string(request_obj);
++ if (send(sockfd, request, strlen(request), 0) < 0) {
++ wpa_printf(MSG_ERROR, "Failed sending AFC request");
++ ret = -EIO;
++ goto close_sock;
++ }
++
++ FD_ZERO(&read_set);
++ FD_SET(sockfd, &read_set);
++ if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
++ wpa_printf(MSG_ERROR, "Select failed on AFC socket");
++ ret = -errno;
++ goto close_sock;
++ }
++
++ if (!FD_ISSET(sockfd, &read_set)) {
++ ret = -EIO;
++ goto close_sock;
++ }
++
++ ret = recv(sockfd, buf, sizeof(buf), 0);
++ if (ret <= 0)
++ goto close_sock;
++
++ ret = hostapd_afc_parse_reply(iface, buf);
++close_sock:
++ json_object_put(request_obj);
++ close(sockfd);
++
++ return ret;
++}
++
++
++static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
++{
++ const struct oper_class_map *oper_class;
++ int ch;
++
++ oper_class = get_oper_class(NULL, iface->conf->op_class);
++ if (!oper_class)
++ return false;
++
++ for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
++ ch += oper_class->inc) {
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ int i;
++
++ for (i = 0; i < mode->num_channels; i++) {
++ struct hostapd_channel_data *chan = &mode->channels[i];
++
++ if (chan->chan == ch &&
++ !(chan->flag & HOSTAPD_CHAN_DISABLED))
++ return true;
++ }
++ }
++
++ return false;
++}
++
++
++int hostapd_afc_handle_request(struct hostapd_iface *iface)
++{
++ struct hostapd_config *iconf = iface->conf;
++ int ret;
++
++ /* AFC is required just for standard power AP */
++ if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++ return 1;
++
++ if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
++ return 1;
++
++ if (iface->state == HAPD_IFACE_ACS)
++ return 1;
++
++ ret = hostapd_afc_send_receive(iface);
++ if (ret < 0) {
++ /*
++ * If the connection to the AFCD failed, resched for a
++ * future attempt.
++ */
++ wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
++ if (ret == -EIO)
++ ret = 0;
++ goto resched;
++ }
++
++ hostap_afc_disable_channels(iface);
++ if (!hostapd_afc_has_usable_chans(iface))
++ goto resched;
++
++ /* Trigger an ACS freq scan */
++ iconf->channel = 0;
++ iface->freq = 0;
++
++ if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
++ wpa_printf(MSG_ERROR, "Could not start ACS");
++ ret = -EINVAL;
++ }
++
++resched:
++ eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
++ eloop_register_timeout(iface->afc.timeout, 0,
++ hostapd_afc_timeout_handler, iface, NULL);
++
++ return ret;
++}
++
++
++static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
++{
++ os_free(iface->afc.chan_info_list);
++ os_free(iface->afc.freq_range);
++
++ iface->afc.num_freq_range = 0;
++ iface->afc.num_chan_info = 0;
++
++ iface->afc.chan_info_list = NULL;
++ iface->afc.freq_range = NULL;
++
++ iface->afc.data_valid = false;
++}
++
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_iface *iface = eloop_ctx;
++ bool restart_iface = true;
++
++ hostapd_afc_delete_data_from_server(iface);
++ if (iface->state != HAPD_IFACE_ENABLED) {
++ /* Hostapd is not fully enabled yet, toogle the interface */
++ goto restart_interface;
++ }
++
++ if (hostapd_afc_send_receive(iface) < 0 ||
++ hostapd_get_hw_features(iface)) {
++ restart_iface = false;
++ goto restart_interface;
++ }
++
++ if (hostapd_is_usable_chans(iface))
++ goto resched;
++
++ restart_iface = hostapd_afc_has_usable_chans(iface);
++ if (restart_iface) {
++ /* Trigger an ACS freq scan */
++ iface->conf->channel = 0;
++ iface->freq = 0;
++ }
++
++restart_interface:
++ hostapd_disable_iface(iface);
++ if (restart_iface)
++ hostapd_enable_iface(iface);
++resched:
++ eloop_register_timeout(iface->afc.timeout, 0,
++ hostapd_afc_timeout_handler, iface, NULL);
++}
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 1a18df617..ca67aeb41 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #endif /* CONFIG_ACS */
+ wpabuf_free(conf->lci);
+ wpabuf_free(conf->civic);
++#ifdef CONFIG_AFC
++ os_free(conf->afc.socket);
++ os_free(conf->afc.request.version);
++ os_free(conf->afc.request.id);
++ os_free(conf->afc.request.sn);
++ for (i = 0; i < conf->afc.n_cert_ids; i++) {
++ os_free(conf->afc.cert_ids[i].rulset);
++ os_free(conf->afc.cert_ids[i].id);
++ }
++ os_free(conf->afc.cert_ids);
++ os_free(conf->afc.location.height_type);
++ os_free(conf->afc.location.linear_polygon_data);
++ os_free(conf->afc.location.radial_polygon_data);
++ os_free(conf->afc.freq_range);
++ os_free(conf->afc.op_class);
++#endif /* CONFIG_AFC */
+
+ os_free(conf);
+ }
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 754d55331..2330163c4 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1225,6 +1225,53 @@ struct hostapd_config {
+ MBSSID_ENABLED = 1,
+ ENHANCED_MBSSID_ENABLED = 2,
+ } mbssid;
++
++#ifdef CONFIG_AFC
++ struct {
++ char *socket;
++ struct {
++ char *version;
++ char *id;
++ char *sn;
++ } request;
++ unsigned int n_cert_ids;
++ struct cert_id {
++ char *rulset;
++ char *id;
++ } *cert_ids;
++ struct {
++ enum afc_location_type {
++ ELLIPSE,
++ LINEAR_POLYGON,
++ RADIAL_POLYGON,
++ } type;
++ unsigned int n_linear_polygon_data;
++ struct afc_linear_polygon {
++ double longitude;
++ double latitude;
++ } *linear_polygon_data;
++ unsigned int n_radial_polygon_data;
++ struct afc_radial_polygon {
++ double length;
++ double angle;
++ } *radial_polygon_data;
++ int major_axis;
++ int minor_axis;
++ int orientation;
++ double height;
++ char *height_type;
++ int vertical_tolerance;
++ } location;
++ unsigned int n_freq_range;
++ struct afc_freq_range {
++ int low_freq;
++ int high_freq;
++ } *freq_range;
++ unsigned int n_op_class;
++ unsigned int *op_class;
++ int min_power;
++ } afc;
++#endif /* CONFIG_AFC */
+ };
+
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index ff1d8f9d0..916ac00c4 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2497,6 +2497,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ }
+ #endif /* CONFIG_MESH */
+
++#ifdef CONFIG_AFC
++ /* check AFC for 6GHz channels. */
++ res = hostapd_afc_handle_request(iface);
++ if (res <= 0) {
++ if (res < 0)
++ goto fail;
++ return res;
++ }
++#endif /* CONFIG_AFC */
++
+ if (!delay_apply_cfg &&
+ hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ hapd->iconf->channel,
+@@ -2968,6 +2978,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
+ __func__, iface->bss[j]);
+ os_free(iface->bss[j]);
+ }
++#ifdef CONFIG_AFC
++ os_free(iface->afc.chan_info_list);
++ os_free(iface->afc.freq_range);
++#endif
+ hostapd_cleanup_iface(iface);
+ }
+
+@@ -4888,3 +4902,49 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
+
+ return punct_bitmap;
+ }
++
++
++void hostap_afc_disable_channels(struct hostapd_iface *iface)
++{
++#ifdef CONFIG_AFC
++ struct hostapd_hw_modes *mode;
++ int i;
++
++ if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A)
++ return;
++
++ if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++ return;
++
++ if (!iface->afc.data_valid)
++ return;
++
++ mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A];
++ for (i = 0; i < mode->num_channels; i++) {
++ struct hostapd_channel_data *chan = &mode->channels[i];
++ int j;
++
++ if (!is_6ghz_freq(chan->freq))
++ continue;
++
++ for (j = 0; j < iface->afc.num_freq_range; j++) {
++ if (chan->freq >= iface->afc.freq_range[j].low_freq &&
++ chan->freq <= iface->afc.freq_range[j].high_freq)
++ break;
++ }
++
++ if (j != iface->afc.num_freq_range)
++ continue;
++
++ for (j = 0; j < iface->afc.num_chan_info; j++) {
++ if (chan->chan == iface->afc.chan_info_list[j].chan)
++ break;
++ }
++
++ if (j != iface->afc.num_chan_info)
++ continue;
++
++ chan->flag |= HOSTAPD_CHAN_DISABLED;
++ }
++#endif /* CONFIG_AFC */
++}
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index d12efb104..18bcb82d9 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -700,9 +700,38 @@ struct hostapd_iface {
+
+ /* Configured freq of interface is NO_IR */
+ bool is_no_ir;
++
++#ifdef CONFIG_AFC
++ struct {
++ int timeout;
++ unsigned int num_freq_range;
++ struct afc_freq_range_elem {
++ int low_freq;
++ int high_freq;
++ /**
++ * max eirp power spectral density received from
++ * the AFC coordinator for this band
++ */
++ int max_psd;
++ } *freq_range;
++ unsigned int num_chan_info;
++ struct afc_chan_info_elem {
++ int chan;
++ /**
++ * max eirp power received from the AFC coordinator
++ * for this channel
++ */
++ int power;
++ } *chan_info_list;
++ bool data_valid;
++ } afc;
++#endif /* CONFIG_AFC */
+ };
+
+ /* hostapd.c */
++void hostap_afc_disable_channels(struct hostapd_iface *iface);
++int hostapd_afc_handle_request(struct hostapd_iface *iface);
++
+ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index e652d7504..222f3dc05 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
+ iface->hw_features = modes;
+ iface->num_hw_features = num_modes;
+
++ hostap_afc_disable_channels(iface);
++
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *feature = &modes[i];
+ int dfs_enabled = hapd->iconf->ieee80211h &&
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
new file mode 100644
index 0000000..42ff1d2
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
@@ -0,0 +1,158 @@
+From b2078261e779c949218974a054dc52f3dc5493c7 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:20:01 +0800
+Subject: [PATCH 026/104] backport: hostapd: update TPE IE according to AFC
+
+Update Transmit Power Envelope (TPE) IE according to the reply from AFC
+coordinator on UNII-5 or UNII-7 6GHz bands.
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/hostapd.c | 39 +++++++++++++++++++++++++++++++++++++
+ src/ap/hostapd.h | 2 ++
+ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++-----------------
+ 3 files changed, 70 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 916ac00c4..b899c9831 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4904,6 +4904,45 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
+ }
+
+
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++ int *power)
++{
++#ifdef CONFIG_AFC
++ int i;
++
++ if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++ return -EINVAL;
++
++ if (!iface->afc.data_valid)
++ return -EINVAL;
++
++ if (psd) {
++ for (i = 0; i < iface->afc.num_freq_range; i++) {
++ struct afc_freq_range_elem *f;
++
++ f = &iface->afc.freq_range[i];
++ if (iface->freq >= f->low_freq &&
++ iface->freq <= f->high_freq) {
++ *power = 2 * f->max_psd;
++ return 0;
++ }
++ }
++ } else {
++ for (i = 0; i < iface->afc.num_chan_info; i++) {
++ struct afc_chan_info_elem *c;
++
++ c = &iface->afc.chan_info_list[i];
++ if (c->chan == iface->conf->channel) {
++ *power = 2 * c->power;
++ return 0;
++ }
++ }
++ }
++#endif /* CONFIG_AFC */
++ return -EINVAL;
++}
++
++
+ void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ {
+ #ifdef CONFIG_AFC
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 18bcb82d9..594866fbb 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -729,6 +729,8 @@ struct hostapd_iface {
+ };
+
+ /* hostapd.c */
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++ int *power);
+ void hostap_afc_disable_channels(struct hostapd_iface *iface);
+ int hostapd_afc_handle_request(struct hostapd_iface *iface);
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 9a23c7240..179af5e28 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7047,42 +7047,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+ */
+ if (is_6ghz_op_class(iconf->op_class)) {
+ enum max_tx_pwr_interpretation tx_pwr_intrpn;
++ int err, max_eirp_psd, max_eirp_power;
+
+ /* Same Maximum Transmit Power for all 20 MHz bands */
+ tx_pwr_count = 0;
+ tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
+
+ /* Default Transmit Power Envelope for Global Operating Class */
+- if (hapd->iconf->reg_def_cli_eirp_psd != -1)
+- tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
+- else
+- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++ err = hostap_afc_get_chan_max_eirp_power(iface, true,
++ &max_eirp_psd);
++ if (err < 0) {
++ if (hapd->iconf->reg_def_cli_eirp_psd != -1)
++ max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd;
++ else
++ max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++ }
+
+ eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
+- REG_DEFAULT_CLIENT, tx_pwr);
++ REG_DEFAULT_CLIENT, max_eirp_psd);
+
+ /* Indoor Access Point must include an additional TPE for
+ * subordinate devices */
+ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
+- /* TODO: Extract PSD limits from channel data */
+- if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
+- tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
+- else
+- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++ if (err < 0) {
++ /* non-AFC connection */
++ if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
++ max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd;
++ else
++ max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++ }
+ eid = hostapd_add_tpe_info(eid, tx_pwr_count,
+ tx_pwr_intrpn,
+ REG_SUBORDINATE_CLIENT,
+- tx_pwr);
++ max_eirp_psd);
+ }
+
+- if (iconf->reg_def_cli_eirp != -1 &&
+- he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+- eid = hostapd_add_tpe_info(
+- eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
+- REG_DEFAULT_CLIENT,
+- hapd->iconf->reg_def_cli_eirp);
++ if (hostap_afc_get_chan_max_eirp_power(iface, false,
++ &max_eirp_power)) {
++ max_eirp_power = iconf->reg_def_cli_eirp;
++ if (max_eirp_power == -1 ||
++ !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++ return eid;
++ }
+
+- return eid;
++ return hostapd_add_tpe_info(eid, tx_pwr_count,
++ REGULATORY_CLIENT_EIRP,
++ REG_DEFAULT_CLIENT,
++ max_eirp_power);
+ }
+ #endif /* CONFIG_IEEE80211AX */
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
new file mode 100644
index 0000000..cbe2769
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
@@ -0,0 +1,5128 @@
+From ccb5628a9c5eae2b56cb88f43f850146863cba30 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Tue, 23 Jan 2024 16:46:44 +0800
+Subject: [PATCH 027/104] hostapd: sync 2024-01-18 openwrt/trunk src folder
+
+---
+ hostapd/radius.c | 715 +++++++++++++
+ src/ap/ubus.c | 2005 ++++++++++++++++++++++++++++++++++++
+ src/ap/ubus.h | 154 +++
+ src/ap/ucode.c | 813 +++++++++++++++
+ src/ap/ucode.h | 54 +
+ src/utils/build_features.h | 65 ++
+ src/utils/ucode.c | 502 +++++++++
+ src/utils/ucode.h | 30 +
+ wpa_supplicant/ubus.c | 280 +++++
+ wpa_supplicant/ubus.h | 55 +
+ wpa_supplicant/ucode.c | 299 ++++++
+ wpa_supplicant/ucode.h | 49 +
+ 12 files changed, 5021 insertions(+)
+ create mode 100644 hostapd/radius.c
+ create mode 100644 src/ap/ubus.c
+ create mode 100644 src/ap/ubus.h
+ create mode 100644 src/ap/ucode.c
+ create mode 100644 src/ap/ucode.h
+ create mode 100644 src/utils/build_features.h
+ create mode 100644 src/utils/ucode.c
+ create mode 100644 src/utils/ucode.h
+ create mode 100644 wpa_supplicant/ubus.c
+ create mode 100644 wpa_supplicant/ubus.h
+ create mode 100644 wpa_supplicant/ucode.c
+ create mode 100644 wpa_supplicant/ucode.h
+
+diff --git a/hostapd/radius.c b/hostapd/radius.c
+new file mode 100644
+index 000000000..362a22c27
+--- /dev/null
++++ b/hostapd/radius.c
+@@ -0,0 +1,715 @@
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "crypto/crypto.h"
++#include "crypto/tls.h"
++
++#include "ap/ap_config.h"
++#include "eap_server/eap.h"
++#include "radius/radius.h"
++#include "radius/radius_server.h"
++#include "eap_register.h"
++
++#include <libubox/blobmsg_json.h>
++#include <libubox/blobmsg.h>
++#include <libubox/avl.h>
++#include <libubox/avl-cmp.h>
++#include <libubox/kvlist.h>
++
++#include <sys/stat.h>
++#include <fnmatch.h>
++
++#define VENDOR_ID_WISPR 14122
++#define VENDOR_ATTR_SIZE 6
++
++struct radius_parse_attr_data {
++ unsigned int vendor;
++ u8 type;
++ int size;
++ char format;
++ const char *data;
++};
++
++struct radius_parse_attr_state {
++ struct hostapd_radius_attr *prev;
++ struct hostapd_radius_attr *attr;
++ struct wpabuf *buf;
++ void *attrdata;
++};
++
++struct radius_user_state {
++ struct avl_node node;
++ struct eap_user data;
++};
++
++struct radius_user_data {
++ struct kvlist users;
++ struct avl_tree user_state;
++ struct blob_attr *wildcard;
++};
++
++struct radius_state {
++ struct radius_server_data *radius;
++ struct eap_config eap;
++
++ struct radius_user_data phase1, phase2;
++ const char *user_file;
++ time_t user_file_ts;
++
++ int n_attrs;
++ struct hostapd_radius_attr *attrs;
++};
++
++struct radius_config {
++ struct tls_connection_params tls;
++ struct radius_server_conf radius;
++};
++
++enum {
++ USER_ATTR_PASSWORD,
++ USER_ATTR_HASH,
++ USER_ATTR_SALT,
++ USER_ATTR_METHODS,
++ USER_ATTR_RADIUS,
++ USER_ATTR_VLAN,
++ USER_ATTR_MAX_RATE_UP,
++ USER_ATTR_MAX_RATE_DOWN,
++ __USER_ATTR_MAX
++};
++
++static void radius_tls_event(void *ctx, enum tls_event ev,
++ union tls_event_data *data)
++{
++ switch (ev) {
++ case TLS_CERT_CHAIN_SUCCESS:
++ wpa_printf(MSG_DEBUG, "radius: remote certificate verification success");
++ break;
++ case TLS_CERT_CHAIN_FAILURE:
++ wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
++ data->cert_fail.reason,
++ data->cert_fail.depth,
++ data->cert_fail.subject,
++ data->cert_fail.reason_txt);
++ break;
++ case TLS_PEER_CERTIFICATE:
++ wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s",
++ data->peer_cert.depth,
++ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
++ data->peer_cert.subject);
++ break;
++ case TLS_ALERT:
++ if (data->alert.is_local)
++ wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s",
++ data->alert.description);
++ else
++ wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s",
++ data->alert.description);
++ break;
++ case TLS_UNSAFE_RENEGOTIATION_DISABLED:
++ /* Not applicable to TLS server */
++ break;
++ }
++}
++
++static void radius_userdata_init(struct radius_user_data *u)
++{
++ kvlist_init(&u->users, kvlist_blob_len);
++ avl_init(&u->user_state, avl_strcmp, false, NULL);
++}
++
++static void radius_userdata_free(struct radius_user_data *u)
++{
++ struct radius_user_state *s, *tmp;
++
++ kvlist_free(&u->users);
++ free(u->wildcard);
++ u->wildcard = NULL;
++ avl_remove_all_elements(&u->user_state, s, node, tmp)
++ free(s);
++}
++
++static void
++radius_userdata_load(struct radius_user_data *u, struct blob_attr *data)
++{
++ enum {
++ USERSTATE_USERS,
++ USERSTATE_WILDCARD,
++ __USERSTATE_MAX,
++ };
++ static const struct blobmsg_policy policy[__USERSTATE_MAX] = {
++ [USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE },
++ [USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY },
++ };
++ struct blob_attr *tb[__USERSTATE_MAX], *cur;
++ int rem;
++
++ if (!data)
++ return;
++
++ blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data));
++
++ blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem)
++ kvlist_set(&u->users, blobmsg_name(cur), cur);
++
++ if (tb[USERSTATE_WILDCARD])
++ u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]);
++}
++
++static void
++load_userfile(struct radius_state *s)
++{
++ enum {
++ USERDATA_PHASE1,
++ USERDATA_PHASE2,
++ __USERDATA_MAX
++ };
++ static const struct blobmsg_policy policy[__USERDATA_MAX] = {
++ [USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE },
++ [USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE },
++ };
++ struct blob_attr *tb[__USERDATA_MAX], *cur;
++ static struct blob_buf b;
++ struct stat st;
++ int rem;
++
++ if (stat(s->user_file, &st))
++ return;
++
++ if (s->user_file_ts == st.st_mtime)
++ return;
++
++ s->user_file_ts = st.st_mtime;
++ radius_userdata_free(&s->phase1);
++ radius_userdata_free(&s->phase2);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_json_from_file(&b, s->user_file);
++ blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head));
++ radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]);
++ radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]);
++
++ blob_buf_free(&b);
++}
++
++static struct blob_attr *
++radius_user_get(struct radius_user_data *s, const char *name)
++{
++ struct blob_attr *cur;
++ int rem;
++
++ cur = kvlist_get(&s->users, name);
++ if (cur)
++ return cur;
++
++ blobmsg_for_each_attr(cur, s->wildcard, rem) {
++ static const struct blobmsg_policy policy = {
++ "name", BLOBMSG_TYPE_STRING
++ };
++ struct blob_attr *pattern;
++
++ if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
++ continue;
++
++ blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur));
++ if (!name)
++ continue;
++
++ if (!fnmatch(blobmsg_get_string(pattern), name, 0))
++ return cur;
++ }
++
++ return NULL;
++}
++
++static struct radius_parse_attr_data *
++radius_parse_attr(struct blob_attr *attr)
++{
++ static const struct blobmsg_policy policy[4] = {
++ { .type = BLOBMSG_TYPE_INT32 },
++ { .type = BLOBMSG_TYPE_INT32 },
++ { .type = BLOBMSG_TYPE_STRING },
++ { .type = BLOBMSG_TYPE_STRING },
++ };
++ static struct radius_parse_attr_data data;
++ struct blob_attr *tb[4];
++ const char *format;
++
++ blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr));
++
++ if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
++ return NULL;
++
++ format = blobmsg_get_string(tb[2]);
++ if (strlen(format) != 1)
++ return NULL;
++
++ data.vendor = blobmsg_get_u32(tb[0]);
++ data.type = blobmsg_get_u32(tb[1]);
++ data.format = format[0];
++ data.data = blobmsg_get_string(tb[3]);
++ data.size = strlen(data.data);
++
++ switch (data.format) {
++ case 's':
++ break;
++ case 'x':
++ if (data.size & 1)
++ return NULL;
++ data.size /= 2;
++ break;
++ case 'd':
++ data.size = 4;
++ break;
++ default:
++ return NULL;
++ }
++
++ return &data;
++}
++
++static void
++radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size)
++{
++ struct blob_attr *data = tb[USER_ATTR_RADIUS];
++ struct blob_attr *cur;
++ int rem;
++
++ blobmsg_for_each_attr(cur, data, rem) {
++ struct radius_parse_attr_data *data;
++ size_t prev = *attr_size;
++
++ data = radius_parse_attr(cur);
++ if (!data)
++ continue;
++
++ *attr_size += data->size;
++ if (data->vendor)
++ *attr_size += VENDOR_ATTR_SIZE;
++
++ (*n_attr)++;
++ }
++
++ *n_attr += !!tb[USER_ATTR_VLAN] * 3 +
++ !!tb[USER_ATTR_MAX_RATE_UP] +
++ !!tb[USER_ATTR_MAX_RATE_DOWN];
++ *attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) +
++ !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) +
++ !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE);
++}
++
++static void *
++radius_add_attr(struct radius_parse_attr_state *state,
++ u32 vendor, u8 type, u8 len)
++{
++ struct hostapd_radius_attr *attr;
++ struct wpabuf *buf;
++ void *val;
++
++ val = state->attrdata;
++
++ buf = state->buf++;
++ buf->buf = val;
++
++ attr = state->attr++;
++ attr->val = buf;
++ attr->type = type;
++
++ if (state->prev)
++ state->prev->next = attr;
++ state->prev = attr;
++
++ if (vendor) {
++ u8 *vendor_hdr = val + 4;
++
++ WPA_PUT_BE32(val, vendor);
++ vendor_hdr[0] = type;
++ vendor_hdr[1] = len + 2;
++
++ len += VENDOR_ATTR_SIZE;
++ val += VENDOR_ATTR_SIZE;
++ attr->type = RADIUS_ATTR_VENDOR_SPECIFIC;
++ }
++
++ buf->size = buf->used = len;
++ state->attrdata += len;
++
++ return val;
++}
++
++static void
++radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state)
++{
++ struct blob_attr *data = tb[USER_ATTR_RADIUS];
++ struct hostapd_radius_attr *prev = NULL;
++ struct blob_attr *cur;
++ int len, rem;
++ void *val;
++
++ if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) {
++ char buf[5];
++
++ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4);
++ WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN);
++
++ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4);
++ WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802);
++
++ len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur));
++ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len);
++ memcpy(val, buf, len);
++ }
++
++ if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) {
++ val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4);
++ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
++ }
++
++ if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) {
++ val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4);
++ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
++ }
++
++ blobmsg_for_each_attr(cur, data, rem) {
++ struct radius_parse_attr_data *data;
++ void *val;
++ int size;
++
++ data = radius_parse_attr(cur);
++ if (!data)
++ continue;
++
++ val = radius_add_attr(state, data->vendor, data->type, data->size);
++ switch (data->format) {
++ case 's':
++ memcpy(val, data->data, data->size);
++ break;
++ case 'x':
++ hexstr2bin(data->data, val, data->size);
++ break;
++ case 'd':
++ WPA_PUT_BE32(val, atoi(data->data));
++ break;
++ }
++ }
++}
++
++static void
++radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data)
++{
++ struct blob_attr *cur;
++ int rem, n = 0;
++
++ if (!data)
++ return;
++
++ blobmsg_for_each_attr(cur, data, rem) {
++ const char *method;
++
++ if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
++ continue;
++
++ if (n == EAP_MAX_METHODS)
++ break;
++
++ method = blobmsg_get_string(cur);
++ eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor);
++ if (eap->methods[n].vendor == EAP_VENDOR_IETF &&
++ eap->methods[n].method == EAP_TYPE_NONE) {
++ if (!strcmp(method, "TTLS-PAP")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_PAP;
++ continue;
++ }
++ if (!strcmp(method, "TTLS-CHAP")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_CHAP;
++ continue;
++ }
++ if (!strcmp(method, "TTLS-MSCHAP")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
++ continue;
++ }
++ if (!strcmp(method, "TTLS-MSCHAPV2")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
++ continue;
++ }
++ }
++ n++;
++ }
++}
++
++static struct eap_user *
++radius_user_get_state(struct radius_user_data *u, struct blob_attr *data,
++ const char *id)
++{
++ static const struct blobmsg_policy policy[__USER_ATTR_MAX] = {
++ [USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING },
++ [USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING },
++ [USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING },
++ [USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY },
++ [USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY },
++ [USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 },
++ [USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 },
++ [USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 },
++ };
++ struct blob_attr *tb[__USER_ATTR_MAX], *cur;
++ char *password_buf, *salt_buf, *name_buf;
++ struct radius_parse_attr_state astate = {};
++ struct hostapd_radius_attr *attr;
++ struct radius_user_state *state;
++ int pw_len = 0, salt_len = 0;
++ struct eap_user *eap;
++ struct wpabuf *val;
++ size_t attrsize = 0;
++ void *attrdata;
++ int n_attr = 0;
++
++ state = avl_find_element(&u->user_state, id, state, node);
++ if (state)
++ return &state->data;
++
++ blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
++
++ if ((cur = tb[USER_ATTR_SALT]) != NULL)
++ salt_len = strlen(blobmsg_get_string(cur)) / 2;
++ if ((cur = tb[USER_ATTR_HASH]) != NULL)
++ pw_len = strlen(blobmsg_get_string(cur)) / 2;
++ else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
++ pw_len = blobmsg_len(cur) - 1;
++ radius_count_attrs(tb, &n_attr, &attrsize);
++
++ state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1,
++ &password_buf, pw_len,
++ &salt_buf, salt_len,
++ &astate.attr, n_attr * sizeof(*astate.attr),
++ &astate.buf, n_attr * sizeof(*astate.buf),
++ &astate.attrdata, attrsize);
++ eap = &state->data;
++ eap->salt = salt_len ? salt_buf : NULL;
++ eap->salt_len = salt_len;
++ eap->password = pw_len ? password_buf : NULL;
++ eap->password_len = pw_len;
++ eap->force_version = -1;
++
++ if ((cur = tb[USER_ATTR_SALT]) != NULL)
++ hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len);
++ if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
++ memcpy(password_buf, blobmsg_get_string(cur), pw_len);
++ else if ((cur = tb[USER_ATTR_HASH]) != NULL) {
++ hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len);
++ eap->password_hash = 1;
++ }
++ radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]);
++
++ if (n_attr > 0) {
++ cur = tb[USER_ATTR_RADIUS];
++ eap->accept_attr = astate.attr;
++ radius_parse_attrs(tb, &astate);
++ }
++
++ state->node.key = strcpy(name_buf, id);
++ avl_insert(&u->user_state, &state->node);
++
++ return &state->data;
++
++free:
++ free(state);
++ return NULL;
++}
++
++static int radius_get_eap_user(void *ctx, const u8 *identity,
++ size_t identity_len, int phase2,
++ struct eap_user *user)
++{
++ struct radius_state *s = ctx;
++ struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1;
++ struct blob_attr *entry;
++ struct eap_user *data;
++ char *id;
++
++ if (identity_len > 512)
++ return -1;
++
++ load_userfile(s);
++
++ id = alloca(identity_len + 1);
++ memcpy(id, identity, identity_len);
++ id[identity_len] = 0;
++
++ entry = radius_user_get(u, id);
++ if (!entry)
++ return -1;
++
++ if (!user)
++ return 0;
++
++ data = radius_user_get_state(u, entry, id);
++ if (!data)
++ return -1;
++
++ *user = *data;
++ if (user->password_len > 0)
++ user->password = os_memdup(user->password, user->password_len);
++ if (user->salt_len > 0)
++ user->salt = os_memdup(user->salt, user->salt_len);
++ user->phase2 = phase2;
++
++ return 0;
++}
++
++static int radius_setup(struct radius_state *s, struct radius_config *c)
++{
++ struct eap_config *eap = &s->eap;
++ struct tls_config conf = {
++ .event_cb = radius_tls_event,
++ .tls_flags = TLS_CONN_DISABLE_TLSv1_3,
++ .cb_ctx = s,
++ };
++
++ eap->eap_server = 1;
++ eap->max_auth_rounds = 100;
++ eap->max_auth_rounds_short = 50;
++ eap->ssl_ctx = tls_init(&conf);
++ if (!eap->ssl_ctx) {
++ wpa_printf(MSG_INFO, "TLS init failed\n");
++ return 1;
++ }
++
++ if (tls_global_set_params(eap->ssl_ctx, &c->tls)) {
++ wpa_printf(MSG_INFO, "failed to set TLS parameters\n");
++ return 1;
++ }
++
++ c->radius.eap_cfg = eap;
++ c->radius.conf_ctx = s;
++ c->radius.get_eap_user = radius_get_eap_user;
++ s->radius = radius_server_init(&c->radius);
++ if (!s->radius) {
++ wpa_printf(MSG_INFO, "failed to initialize radius server\n");
++ return 1;
++ }
++
++ return 0;
++}
++
++static int radius_init(struct radius_state *s)
++{
++ memset(s, 0, sizeof(*s));
++ radius_userdata_init(&s->phase1);
++ radius_userdata_init(&s->phase2);
++}
++
++static void radius_deinit(struct radius_state *s)
++{
++ if (s->radius)
++ radius_server_deinit(s->radius);
++
++ if (s->eap.ssl_ctx)
++ tls_deinit(s->eap.ssl_ctx);
++
++ radius_userdata_free(&s->phase1);
++ radius_userdata_free(&s->phase2);
++}
++
++static int usage(const char *progname)
++{
++ fprintf(stderr, "Usage: %s <options>\n",
++ progname);
++}
++
++int radius_main(int argc, char **argv)
++{
++ static struct radius_state state = {};
++ static struct radius_config config = {};
++ const char *progname = argv[0];
++ int ret = 0;
++ int ch;
++
++ wpa_debug_setup_stdout();
++ wpa_debug_level = 0;
++
++ if (eloop_init()) {
++ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
++ return 1;
++ }
++
++ eap_server_register_methods();
++ radius_init(&state);
++
++ while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) {
++ switch (ch) {
++ case '6':
++ config.radius.ipv6 = 1;
++ break;
++ case 'C':
++ config.tls.ca_cert = optarg;
++ break;
++ case 'c':
++ if (config.tls.client_cert2)
++ return usage(progname);
++
++ if (config.tls.client_cert)
++ config.tls.client_cert2 = optarg;
++ else
++ config.tls.client_cert = optarg;
++ break;
++ case 'd':
++ config.tls.dh_file = optarg;
++ break;
++ case 'i':
++ state.eap.server_id = optarg;
++ state.eap.server_id_len = strlen(optarg);
++ break;
++ case 'k':
++ if (config.tls.private_key2)
++ return usage(progname);
++
++ if (config.tls.private_key)
++ config.tls.private_key2 = optarg;
++ else
++ config.tls.private_key = optarg;
++ break;
++ case 'K':
++ if (config.tls.private_key_passwd2)
++ return usage(progname);
++
++ if (config.tls.private_key_passwd)
++ config.tls.private_key_passwd2 = optarg;
++ else
++ config.tls.private_key_passwd = optarg;
++ break;
++ case 'p':
++ config.radius.auth_port = atoi(optarg);
++ break;
++ case 'P':
++ config.radius.acct_port = atoi(optarg);
++ break;
++ case 's':
++ config.radius.client_file = optarg;
++ break;
++ case 'u':
++ state.user_file = optarg;
++ break;
++ default:
++ return usage(progname);
++ }
++ }
++
++ if (!config.tls.client_cert || !config.tls.private_key ||
++ !config.radius.client_file || !state.eap.server_id ||
++ !state.user_file) {
++ wpa_printf(MSG_INFO, "missing options\n");
++ goto out;
++ }
++
++ ret = radius_setup(&state, &config);
++ if (ret)
++ goto out;
++
++ load_userfile(&state);
++ eloop_run();
++
++out:
++ radius_deinit(&state);
++ os_program_deinit();
++
++ return ret;
++}
+diff --git a/src/ap/ubus.c b/src/ap/ubus.c
+new file mode 100644
+index 000000000..f2041a0c9
+--- /dev/null
++++ b/src/ap/ubus.c
+@@ -0,0 +1,2005 @@
++/*
++ * hostapd / ubus support
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "utils/wpabuf.h"
++#include "common/ieee802_11_defs.h"
++#include "common/hw_features_common.h"
++#include "hostapd.h"
++#include "neighbor_db.h"
++#include "wps_hostapd.h"
++#include "sta_info.h"
++#include "ubus.h"
++#include "ap_drv_ops.h"
++#include "beacon.h"
++#include "rrm.h"
++#include "wnm_ap.h"
++#include "taxonomy.h"
++#include "airtime_policy.h"
++#include "hw_features.h"
++
++static struct ubus_context *ctx;
++static struct blob_buf b;
++static int ctx_ref;
++
++static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
++{
++ return container_of(obj, struct hostapd_data, ubus.obj);
++}
++
++struct ubus_banned_client {
++ struct avl_node avl;
++ u8 addr[ETH_ALEN];
++};
++
++static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
++{
++ if (ubus_reconnect(ctx, NULL)) {
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++ return;
++ }
++
++ ubus_add_uloop(ctx);
++}
++
++static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
++{
++ uloop_fd_delete(&ctx->sock);
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++}
++
++static bool hostapd_ubus_init(void)
++{
++ if (ctx)
++ return true;
++
++ eloop_add_uloop();
++ ctx = ubus_connect(NULL);
++ if (!ctx)
++ return false;
++
++ ctx->connection_lost = hostapd_ubus_connection_lost;
++ ubus_add_uloop(ctx);
++
++ return true;
++}
++
++static void hostapd_ubus_ref_inc(void)
++{
++ ctx_ref++;
++}
++
++static void hostapd_ubus_ref_dec(void)
++{
++ ctx_ref--;
++ if (!ctx)
++ return;
++
++ if (ctx_ref)
++ return;
++
++ uloop_fd_delete(&ctx->sock);
++ ubus_free(ctx);
++ ctx = NULL;
++}
++
++void hostapd_ubus_add_iface(struct hostapd_iface *iface)
++{
++ if (!hostapd_ubus_init())
++ return;
++}
++
++void hostapd_ubus_free_iface(struct hostapd_iface *iface)
++{
++ if (!ctx)
++ return;
++}
++
++static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *event)
++{
++ char *event_type;
++
++ if (!ctx || !obj)
++ return;
++
++ if (asprintf(&event_type, "bss.%s", event) < 0)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_string(&b, "name", bssname);
++ ubus_notify(ctx, obj, event_type, b.head, -1);
++ free(event_type);
++}
++
++static void
++hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
++{
++ struct ubus_banned_client *ban = eloop_data;
++ struct hostapd_data *hapd = user_ctx;
++
++ avl_delete(&hapd->ubus.banned, &ban->avl);
++ free(ban);
++}
++
++static void
++hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
++{
++ struct ubus_banned_client *ban;
++
++ if (time < 0)
++ time = 0;
++
++ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
++ if (!ban) {
++ if (!time)
++ return;
++
++ ban = os_zalloc(sizeof(*ban));
++ memcpy(ban->addr, addr, sizeof(ban->addr));
++ ban->avl.key = ban->addr;
++ avl_insert(&hapd->ubus.banned, &ban->avl);
++ } else {
++ eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
++ if (!time) {
++ hostapd_bss_del_ban(ban, hapd);
++ return;
++ }
++ }
++
++ eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
++}
++
++static int
++hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ return hostapd_reload_config(hapd->iface);
++}
++
++
++static void
++hostapd_parse_vht_map_blobmsg(uint16_t map)
++{
++ char label[4];
++ int16_t val;
++ int i;
++
++ for (i = 0; i < 8; i++) {
++ snprintf(label, 4, "%dss", i + 1);
++
++ val = (map & (BIT(1) | BIT(0))) + 7;
++ blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
++ map = map >> 2;
++ }
++}
++
++static void
++hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
++{
++ void *supported_mcs;
++ void *map;
++ int i;
++
++ static const struct {
++ const char *name;
++ uint32_t flag;
++ } vht_capas[] = {
++ { "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
++ { "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
++ };
++
++ for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
++ blobmsg_add_u8(&b, vht_capas[i].name,
++ !!(vhtc->vht_capabilities_info & vht_capas[i].flag));
++
++ supported_mcs = blobmsg_open_table(&b, "mcs_map");
++
++ /* RX map */
++ map = blobmsg_open_table(&b, "rx");
++ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
++ blobmsg_close_table(&b, map);
++
++ /* TX map */
++ map = blobmsg_open_table(&b, "tx");
++ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
++ blobmsg_close_table(&b, map);
++
++ blobmsg_close_table(&b, supported_mcs);
++}
++
++static void
++hostapd_parse_capab_blobmsg(struct sta_info *sta)
++{
++ void *r, *v;
++
++ v = blobmsg_open_table(&b, "capabilities");
++
++ if (sta->vht_capabilities) {
++ r = blobmsg_open_table(&b, "vht");
++ hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
++ blobmsg_close_table(&b, r);
++ }
++
++ /* ToDo: Add HT / HE capability parsing */
++
++ blobmsg_close_table(&b, v);
++}
++
++static int
++hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct hostap_sta_driver_data sta_driver_data;
++ struct sta_info *sta;
++ void *list, *c;
++ char mac_buf[20];
++ static const struct {
++ const char *name;
++ uint32_t flag;
++ } sta_flags[] = {
++ { "auth", WLAN_STA_AUTH },
++ { "assoc", WLAN_STA_ASSOC },
++ { "authorized", WLAN_STA_AUTHORIZED },
++ { "preauth", WLAN_STA_PREAUTH },
++ { "wds", WLAN_STA_WDS },
++ { "wmm", WLAN_STA_WMM },
++ { "ht", WLAN_STA_HT },
++ { "vht", WLAN_STA_VHT },
++ { "he", WLAN_STA_HE },
++ { "wps", WLAN_STA_WPS },
++ { "mfp", WLAN_STA_MFP },
++ };
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
++ list = blobmsg_open_table(&b, "clients");
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ void *r;
++ int i;
++
++ sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
++ c = blobmsg_open_table(&b, mac_buf);
++ for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
++ blobmsg_add_u8(&b, sta_flags[i].name,
++ !!(sta->flags & sta_flags[i].flag));
++
++#ifdef CONFIG_MBO
++ blobmsg_add_u8(&b, "mbo", !!(sta->cell_capa));
++#endif
++
++ r = blobmsg_open_array(&b, "rrm");
++ for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++)
++ blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]);
++ blobmsg_close_array(&b, r);
++
++ r = blobmsg_open_array(&b, "extended_capabilities");
++ /* Check if client advertises extended capabilities */
++ if (sta->ext_capability && sta->ext_capability[0] > 0) {
++ for (i = 0; i < sta->ext_capability[0]; i++) {
++ blobmsg_add_u32(&b, "", sta->ext_capability[1 + i]);
++ }
++ }
++ blobmsg_close_array(&b, r);
++
++ blobmsg_add_u32(&b, "aid", sta->aid);
++#ifdef CONFIG_TAXONOMY
++ r = blobmsg_alloc_string_buffer(&b, "signature", 1024);
++ if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0)
++ blobmsg_add_string_buffer(&b);
++#endif
++
++ /* Driver information */
++ if (hostapd_drv_read_sta_data(hapd, &sta_driver_data, sta->addr) >= 0) {
++ r = blobmsg_open_table(&b, "bytes");
++ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_bytes);
++ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_bytes);
++ blobmsg_close_table(&b, r);
++ r = blobmsg_open_table(&b, "airtime");
++ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_airtime);
++ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_airtime);
++ blobmsg_close_table(&b, r);
++ r = blobmsg_open_table(&b, "packets");
++ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_packets);
++ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_packets);
++ blobmsg_close_table(&b, r);
++ r = blobmsg_open_table(&b, "rate");
++ /* Rate in kbits */
++ blobmsg_add_u32(&b, "rx", sta_driver_data.current_rx_rate * 100);
++ blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
++ blobmsg_close_table(&b, r);
++ blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
++ }
++
++ hostapd_parse_capab_blobmsg(sta);
++
++ blobmsg_close_table(&b, c);
++ }
++ blobmsg_close_array(&b, list);
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features));
++ blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features));
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ void *airtime_table, *dfs_table, *rrm_table, *wnm_table;
++ struct os_reltime now;
++ char ssid[SSID_MAX_LEN + 1];
++ char phy_name[17];
++ size_t ssid_len = SSID_MAX_LEN;
++ u8 channel = 0, op_class = 0;
++
++ if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN)
++ ssid_len = hapd->conf->ssid.ssid_len;
++
++ ieee80211_freq_to_channel_ext(hapd->iface->freq,
++ hapd->iconf->secondary_channel,
++ hostapd_get_oper_chwidth(hapd->iconf),
++ &op_class, &channel);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
++ blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid));
++
++ memset(ssid, 0, SSID_MAX_LEN + 1);
++ memcpy(ssid, hapd->conf->ssid.ssid, ssid_len);
++ blobmsg_add_string(&b, "ssid", ssid);
++
++ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
++ blobmsg_add_u32(&b, "channel", channel);
++ blobmsg_add_u32(&b, "op_class", op_class);
++ blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
++#ifdef CONFIG_IEEE80211AX
++ blobmsg_add_u32(&b, "bss_color", hapd->iface->conf->he_op.he_bss_color_disabled ? -1 :
++ hapd->iface->conf->he_op.he_bss_color);
++#else
++ blobmsg_add_u32(&b, "bss_color", -1);
++#endif
++
++ snprintf(phy_name, 17, "%s", hapd->iface->phy);
++ blobmsg_add_string(&b, "phy", phy_name);
++
++ /* RRM */
++ rrm_table = blobmsg_open_table(&b, "rrm");
++ blobmsg_add_u64(&b, "neighbor_report_tx", hapd->openwrt_stats.rrm.neighbor_report_tx);
++ blobmsg_close_table(&b, rrm_table);
++
++ /* WNM */
++ wnm_table = blobmsg_open_table(&b, "wnm");
++ blobmsg_add_u64(&b, "bss_transition_query_rx", hapd->openwrt_stats.wnm.bss_transition_query_rx);
++ blobmsg_add_u64(&b, "bss_transition_request_tx", hapd->openwrt_stats.wnm.bss_transition_request_tx);
++ blobmsg_add_u64(&b, "bss_transition_response_rx", hapd->openwrt_stats.wnm.bss_transition_response_rx);
++ blobmsg_close_table(&b, wnm_table);
++
++ /* Airtime */
++ airtime_table = blobmsg_open_table(&b, "airtime");
++ blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
++ blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
++ blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
++ blobmsg_close_table(&b, airtime_table);
++
++ /* DFS */
++ dfs_table = blobmsg_open_table(&b, "dfs");
++ blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
++ blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
++ os_reltime_age(&hapd->iface->dfs_cac_start, &now);
++ blobmsg_add_u32(&b, "cac_seconds_left",
++ hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
++ blobmsg_close_table(&b, dfs_table);
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++enum {
++ NOTIFY_RESPONSE,
++ __NOTIFY_MAX
++};
++
++static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
++ [NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__NOTIFY_MAX];
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct wpabuf *elems;
++ const char *pos;
++ size_t len;
++
++ blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
++ blob_data(msg), blob_len(msg));
++
++ if (!tb[NOTIFY_RESPONSE])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
++
++ return UBUS_STATUS_OK;
++}
++
++enum {
++ DEL_CLIENT_ADDR,
++ DEL_CLIENT_REASON,
++ DEL_CLIENT_DEAUTH,
++ DEL_CLIENT_BAN_TIME,
++ __DEL_CLIENT_MAX
++};
++
++static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
++ [DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
++ [DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
++ [DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__DEL_CLIENT_MAX];
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct sta_info *sta;
++ bool deauth = false;
++ int reason;
++ u8 addr[ETH_ALEN];
++
++ blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[DEL_CLIENT_ADDR])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[DEL_CLIENT_REASON])
++ reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
++
++ if (tb[DEL_CLIENT_DEAUTH])
++ deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
++
++ sta = ap_get_sta(hapd, addr);
++ if (sta) {
++ if (deauth) {
++ hostapd_drv_sta_deauth(hapd, addr, reason);
++ ap_sta_deauthenticate(hapd, sta, reason);
++ } else {
++ hostapd_drv_sta_disassoc(hapd, addr, reason);
++ ap_sta_disassociate(hapd, sta, reason);
++ }
++ }
++
++ if (tb[DEL_CLIENT_BAN_TIME])
++ hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
++
++ return 0;
++}
++
++static void
++blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
++{
++ char *s;
++
++ s = blobmsg_alloc_string_buffer(buf, name, 20);
++ sprintf(s, MACSTR, MAC2STR(addr));
++ blobmsg_add_string_buffer(buf);
++}
++
++static int
++hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct ubus_banned_client *ban;
++ void *c;
++
++ blob_buf_init(&b, 0);
++ c = blobmsg_open_array(&b, "clients");
++ avl_for_each_element(&hapd->ubus.banned, ban, avl)
++ blobmsg_add_macaddr(&b, NULL, ban->addr);
++ blobmsg_close_array(&b, c);
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++#ifdef CONFIG_WPS
++static int
++hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ rc = hostapd_wps_button_pushed(hapd, NULL);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++
++
++static const char * pbc_status_enum_str(enum pbc_status status)
++{
++ switch (status) {
++ case WPS_PBC_STATUS_DISABLE:
++ return "Disabled";
++ case WPS_PBC_STATUS_ACTIVE:
++ return "Active";
++ case WPS_PBC_STATUS_TIMEOUT:
++ return "Timed-out";
++ case WPS_PBC_STATUS_OVERLAP:
++ return "Overlap";
++ default:
++ return "Unknown";
++ }
++}
++
++static int
++hostapd_bss_wps_status(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ blob_buf_init(&b, 0);
++
++ blobmsg_add_string(&b, "pbc_status", pbc_status_enum_str(hapd->wps_stats.pbc_status));
++ blobmsg_add_string(&b, "last_wps_result",
++ (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
++ "Success":
++ (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
++ "Failed" : "None")));
++
++ /* If status == Failure - Add possible Reasons */
++ if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
++ hapd->wps_stats.failure_reason > 0)
++ blobmsg_add_string(&b, "reason", wps_ei_str(hapd->wps_stats.failure_reason));
++
++ if (hapd->wps_stats.status)
++ blobmsg_printf(&b, "peer_address", MACSTR, MAC2STR(hapd->wps_stats.peer_addr));
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ rc = hostapd_wps_cancel(hapd);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++#endif /* CONFIG_WPS */
++
++static int
++hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ rc = ieee802_11_set_beacon(hapd);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++
++enum {
++ CONFIG_IFACE,
++ CONFIG_FILE,
++ __CONFIG_MAX
++};
++
++enum {
++ CSA_FREQ,
++ CSA_BCN_COUNT,
++ CSA_CENTER_FREQ1,
++ CSA_CENTER_FREQ2,
++ CSA_BANDWIDTH,
++ CSA_SEC_CHANNEL_OFFSET,
++ CSA_HT,
++ CSA_VHT,
++ CSA_HE,
++ CSA_BLOCK_TX,
++ CSA_FORCE,
++ __CSA_MAX
++};
++
++static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
++ [CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
++ [CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
++ [CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 },
++ [CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 },
++ [CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 },
++ [CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 },
++ [CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL },
++ [CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL },
++ [CSA_HE] = { "he", BLOBMSG_TYPE_BOOL },
++ [CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL },
++ [CSA_FORCE] = { "force", BLOBMSG_TYPE_BOOL },
++};
++
++
++static void switch_chan_fallback_cb(void *eloop_data, void *user_ctx)
++{
++ struct hostapd_iface *iface = eloop_data;
++ struct hostapd_freq_params *freq_params = user_ctx;
++
++ hostapd_switch_channel_fallback(iface, freq_params);
++}
++
++#ifdef NEED_AP_MLME
++static int
++hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__CSA_MAX];
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_config *iconf = hapd->iface->conf;
++ struct hostapd_freq_params *freq_params;
++ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
++ struct csa_settings css = {
++ .freq_params = {
++ .ht_enabled = iconf->ieee80211n,
++ .vht_enabled = iconf->ieee80211ac,
++ .he_enabled = iconf->ieee80211ax,
++ .sec_channel_offset = iconf->secondary_channel,
++ }
++ };
++ u8 chwidth = hostapd_get_oper_chwidth(iconf);
++ u8 seg0 = 0, seg1 = 0;
++ int ret = UBUS_STATUS_OK;
++ int i;
++
++ blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[CSA_FREQ])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ switch (iconf->vht_oper_chwidth) {
++ case CHANWIDTH_USE_HT:
++ if (iconf->secondary_channel)
++ css.freq_params.bandwidth = 40;
++ else
++ css.freq_params.bandwidth = 20;
++ break;
++ case CHANWIDTH_160MHZ:
++ css.freq_params.bandwidth = 160;
++ break;
++ default:
++ css.freq_params.bandwidth = 80;
++ break;
++ }
++
++ css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
++
++#define SET_CSA_SETTING(name, field, type) \
++ do { \
++ if (tb[name]) \
++ css.field = blobmsg_get_ ## type(tb[name]); \
++ } while(0)
++
++ SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32);
++ SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32);
++ SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32);
++ SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32);
++ SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32);
++ SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool);
++ SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
++ SET_CSA_SETTING(CSA_HE, freq_params.he_enabled, bool);
++ SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
++
++ css.freq_params.channel = hostapd_hw_get_channel(hapd, css.freq_params.freq);
++ if (!css.freq_params.channel)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ switch (css.freq_params.bandwidth) {
++ case 160:
++ chwidth = CHANWIDTH_160MHZ;
++ break;
++ case 80:
++ chwidth = css.freq_params.center_freq2 ? CHANWIDTH_80P80MHZ : CHANWIDTH_80MHZ;
++ break;
++ default:
++ chwidth = CHANWIDTH_USE_HT;
++ break;
++ }
++
++ hostapd_set_freq_params(&css.freq_params, iconf->hw_mode,
++ css.freq_params.freq,
++ css.freq_params.channel, iconf->enable_edmg,
++ iconf->edmg_channel,
++ css.freq_params.ht_enabled,
++ css.freq_params.vht_enabled,
++ css.freq_params.he_enabled,
++ css.freq_params.eht_enabled,
++ css.freq_params.sec_channel_offset,
++ chwidth, seg0, seg1,
++ iconf->vht_capab,
++ mode ? &mode->he_capab[IEEE80211_MODE_AP] :
++ NULL,
++ mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
++ NULL, hostapd_get_punct_bitmap(hapd));
++
++ for (i = 0; i < hapd->iface->num_bss; i++) {
++ struct hostapd_data *bss = hapd->iface->bss[i];
++
++ if (hostapd_switch_channel(bss, &css) != 0)
++ ret = UBUS_STATUS_NOT_SUPPORTED;
++ }
++
++ if (!ret || !tb[CSA_FORCE] || !blobmsg_get_bool(tb[CSA_FORCE]))
++ return ret;
++
++ freq_params = malloc(sizeof(*freq_params));
++ memcpy(freq_params, &css.freq_params, sizeof(*freq_params));
++ eloop_register_timeout(0, 1, switch_chan_fallback_cb,
++ hapd->iface, freq_params);
++
++ return 0;
++#undef SET_CSA_SETTING
++}
++#endif
++
++enum {
++ VENDOR_ELEMENTS,
++ __VENDOR_ELEMENTS_MAX
++};
++
++static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
++ /* vendor elements are provided as hex-string */
++ [VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
++};
++
++static int
++hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_bss_config *bss = hapd->conf;
++ struct wpabuf *elems;
++ const char *pos;
++ size_t len;
++
++ blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
++ blob_data(msg), blob_len(msg));
++
++ if (!tb[VENDOR_ELEMENTS])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
++ len = os_strlen(pos);
++ if (len & 0x01)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ len /= 2;
++ if (len == 0) {
++ wpabuf_free(bss->vendor_elements);
++ bss->vendor_elements = NULL;
++ return 0;
++ }
++
++ elems = wpabuf_alloc(len);
++ if (elems == NULL)
++ return 1;
++
++ if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
++ wpabuf_free(elems);
++ return UBUS_STATUS_INVALID_ARGUMENT;
++ }
++
++ wpabuf_free(bss->vendor_elements);
++ bss->vendor_elements = elems;
++
++ /* update beacons if vendor elements were set successfully */
++ if (ieee802_11_update_beacons(hapd->iface) != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++ return UBUS_STATUS_OK;
++}
++
++static void
++hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
++{
++ const u8 *data;
++ char *str;
++ int len;
++
++ blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid));
++
++ str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1);
++ memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len);
++ str[nr->ssid.ssid_len] = 0;
++ blobmsg_add_string_buffer(&b);
++
++ len = wpabuf_len(nr->nr);
++ str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1);
++ wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len);
++ blobmsg_add_string_buffer(&b);
++}
++
++enum {
++ BSS_MGMT_EN_NEIGHBOR,
++ BSS_MGMT_EN_BEACON,
++ BSS_MGMT_EN_LINK_MEASUREMENT,
++#ifdef CONFIG_WNM_AP
++ BSS_MGMT_EN_BSS_TRANSITION,
++#endif
++ __BSS_MGMT_EN_MAX
++};
++
++static bool
++__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
++{
++ struct hostapd_bss_config *bss = hapd->conf;
++ uint32_t flags;
++
++ switch (flag) {
++ case BSS_MGMT_EN_NEIGHBOR:
++ if (bss->radio_measurements[0] &
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT)
++ return false;
++
++ bss->radio_measurements[0] |=
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT;
++ hostapd_neighbor_set_own_report(hapd);
++ return true;
++ case BSS_MGMT_EN_BEACON:
++ flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
++ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
++ WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
++
++ if (bss->radio_measurements[0] & flags == flags)
++ return false;
++
++ bss->radio_measurements[0] |= (u8) flags;
++ return true;
++ case BSS_MGMT_EN_LINK_MEASUREMENT:
++ flags = WLAN_RRM_CAPS_LINK_MEASUREMENT;
++
++ if (bss->radio_measurements[0] & flags == flags)
++ return false;
++
++ bss->radio_measurements[0] |= (u8) flags;
++ return true;
++#ifdef CONFIG_WNM_AP
++ case BSS_MGMT_EN_BSS_TRANSITION:
++ if (bss->bss_transition)
++ return false;
++
++ bss->bss_transition = 1;
++ return true;
++#endif
++ }
++}
++
++static void
++__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
++{
++ bool update = false;
++ int i;
++
++ for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
++ if (!(flags & (1 << i)))
++ continue;
++
++ update |= __hostapd_bss_mgmt_enable_f(hapd, i);
++ }
++
++ if (update)
++ ieee802_11_update_beacons(hapd->iface);
++}
++
++
++static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
++ [BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
++ [BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
++ [BSS_MGMT_EN_LINK_MEASUREMENT] = { "link_measurement", BLOBMSG_TYPE_BOOL },
++#ifdef CONFIG_WNM_AP
++ [BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
++#endif
++};
++
++static int
++hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++
++{
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct blob_attr *tb[__BSS_MGMT_EN_MAX];
++ struct blob_attr *cur;
++ uint32_t flags = 0;
++ int i;
++ bool neigh = false, beacon = false;
++
++ blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
++
++ for (i = 0; i < ARRAY_SIZE(tb); i++) {
++ if (!tb[i] || !blobmsg_get_bool(tb[i]))
++ continue;
++
++ flags |= (1 << i);
++ }
++
++ __hostapd_bss_mgmt_enable(hapd, flags);
++
++ return 0;
++}
++
++
++static void
++hostapd_rrm_nr_enable(struct hostapd_data *hapd)
++{
++ __hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
++}
++
++static int
++hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_neighbor_entry *nr;
++ void *c;
++
++ hostapd_rrm_nr_enable(hapd);
++
++ nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL);
++ if (!nr)
++ return UBUS_STATUS_NOT_FOUND;
++
++ blob_buf_init(&b, 0);
++
++ c = blobmsg_open_array(&b, "value");
++ hostapd_rrm_print_nr(nr);
++ blobmsg_close_array(&b, c);
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_neighbor_entry *nr;
++ void *c;
++
++ hostapd_rrm_nr_enable(hapd);
++ blob_buf_init(&b, 0);
++
++ c = blobmsg_open_array(&b, "list");
++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
++ void *cur;
++
++ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
++ continue;
++
++ cur = blobmsg_open_array(&b, NULL);
++ hostapd_rrm_print_nr(nr);
++ blobmsg_close_array(&b, cur);
++ }
++ blobmsg_close_array(&b, c);
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++enum {
++ NR_SET_LIST,
++ __NR_SET_LIST_MAX
++};
++
++static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = {
++ [NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY },
++};
++
++
++static void
++hostapd_rrm_nr_clear(struct hostapd_data *hapd)
++{
++ struct hostapd_neighbor_entry *nr;
++
++restart:
++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
++ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
++ continue;
++
++ hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid);
++ goto restart;
++ }
++}
++
++static int
++hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ static const struct blobmsg_policy nr_e_policy[] = {
++ { .type = BLOBMSG_TYPE_STRING },
++ { .type = BLOBMSG_TYPE_STRING },
++ { .type = BLOBMSG_TYPE_STRING },
++ };
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct blob_attr *tb_l[__NR_SET_LIST_MAX];
++ struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)];
++ struct blob_attr *cur;
++ int rem;
++
++ hostapd_rrm_nr_enable(hapd);
++
++ blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg));
++ if (!tb_l[NR_SET_LIST])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ hostapd_rrm_nr_clear(hapd);
++ blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) {
++ struct wpa_ssid_value ssid;
++ struct wpabuf *data;
++ u8 bssid[ETH_ALEN];
++ char *s, *nr_s;
++
++ blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
++ if (!tb[0] || !tb[1] || !tb[2])
++ goto invalid;
++
++ /* Neighbor Report binary */
++ nr_s = blobmsg_get_string(tb[2]);
++ data = wpabuf_parse_bin(nr_s);
++ if (!data)
++ goto invalid;
++
++ /* BSSID */
++ s = blobmsg_get_string(tb[0]);
++ if (strlen(s) == 0) {
++ /* Copy BSSID from neighbor report */
++ if (hwaddr_compact_aton(nr_s, bssid))
++ goto invalid;
++ } else if (hwaddr_aton(s, bssid)) {
++ goto invalid;
++ }
++
++ /* SSID */
++ s = blobmsg_get_string(tb[1]);
++ if (strlen(s) == 0) {
++ /* Copy SSID from hostapd BSS conf */
++ memcpy(&ssid, &hapd->conf->ssid, sizeof(ssid));
++ } else {
++ ssid.ssid_len = strlen(s);
++ if (ssid.ssid_len > sizeof(ssid.ssid))
++ goto invalid;
++
++ memcpy(&ssid, s, ssid.ssid_len);
++ }
++
++ hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0, 0);
++ wpabuf_free(data);
++ continue;
++
++invalid:
++ return UBUS_STATUS_INVALID_ARGUMENT;
++ }
++
++ return 0;
++}
++
++enum {
++ BEACON_REQ_ADDR,
++ BEACON_REQ_MODE,
++ BEACON_REQ_OP_CLASS,
++ BEACON_REQ_CHANNEL,
++ BEACON_REQ_DURATION,
++ BEACON_REQ_BSSID,
++ BEACON_REQ_SSID,
++ __BEACON_REQ_MAX,
++};
++
++static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = {
++ [BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING },
++ [BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING },
++};
++
++static int
++hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__BEACON_REQ_MAX];
++ struct blob_attr *cur;
++ struct wpabuf *req;
++ u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++ u8 addr[ETH_ALEN];
++ int mode, rem, ret;
++ int buf_len = 13;
++
++ blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] ||
++ !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[BEACON_REQ_SSID])
++ buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1;
++
++ mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]);
++ if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[BEACON_REQ_BSSID] &&
++ hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ req = wpabuf_alloc(buf_len);
++ if (!req)
++ return UBUS_STATUS_UNKNOWN_ERROR;
++
++ /* 1: regulatory class */
++ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS]));
++
++ /* 2: channel number */
++ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL]));
++
++ /* 3-4: randomization interval */
++ wpabuf_put_le16(req, 0);
++
++ /* 5-6: duration */
++ wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION]));
++
++ /* 7: mode */
++ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE]));
++
++ /* 8-13: BSSID */
++ wpabuf_put_data(req, bssid, ETH_ALEN);
++
++ if ((cur = tb[BEACON_REQ_SSID]) != NULL) {
++ wpabuf_put_u8(req, WLAN_EID_SSID);
++ wpabuf_put_u8(req, blobmsg_data_len(cur) - 1);
++ wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1);
++ }
++
++ ret = hostapd_send_beacon_req(hapd, addr, 0, req);
++ if (ret < 0)
++ return -ret;
++
++ return 0;
++}
++
++enum {
++ LM_REQ_ADDR,
++ LM_REQ_TX_POWER_USED,
++ LM_REQ_TX_POWER_MAX,
++ __LM_REQ_MAX,
++};
++
++static const struct blobmsg_policy lm_req_policy[__LM_REQ_MAX] = {
++ [LM_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [LM_REQ_TX_POWER_USED] = { "tx-power-used", BLOBMSG_TYPE_INT32 },
++ [LM_REQ_TX_POWER_MAX] = { "tx-power-max", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_rrm_lm_req(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__LM_REQ_MAX];
++ struct wpabuf *buf;
++ u8 addr[ETH_ALEN];
++ int ret;
++ int8_t txp_used, txp_max;
++
++ txp_used = 0;
++ txp_max = 0;
++
++ blobmsg_parse(lm_req_policy, __LM_REQ_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[LM_REQ_ADDR])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[LM_REQ_TX_POWER_USED])
++ txp_used = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_USED]);
++
++ if (tb[LM_REQ_TX_POWER_MAX])
++ txp_max = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_MAX]);
++
++ if (hwaddr_aton(blobmsg_data(tb[LM_REQ_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ buf = wpabuf_alloc(5);
++ if (!buf)
++ return UBUS_STATUS_UNKNOWN_ERROR;
++
++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
++ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
++ wpabuf_put_u8(buf, 1);
++ /* TX-Power used */
++ wpabuf_put_u8(buf, txp_used);
++ /* Max TX Power */
++ wpabuf_put_u8(buf, txp_max);
++
++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++ wpabuf_head(buf), wpabuf_len(buf));
++
++ wpabuf_free(buf);
++ if (ret < 0)
++ return -ret;
++
++ return 0;
++}
++
++
++void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
++{
++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) data;
++ const u8 *pos, *end;
++ u8 token;
++
++ end = data + len;
++ token = mgmt->u.action.u.rrm.dialog_token;
++ pos = mgmt->u.action.u.rrm.variable;
++
++ if (end - pos < 8)
++ return;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", mgmt->sa);
++ blobmsg_add_u16(&b, "dialog-token", token);
++ blobmsg_add_u16(&b, "rx-antenna-id", pos[4]);
++ blobmsg_add_u16(&b, "tx-antenna-id", pos[5]);
++ blobmsg_add_u16(&b, "rcpi", pos[6]);
++ blobmsg_add_u16(&b, "rsni", pos[7]);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "link-measurement-report", b.head, -1);
++}
++
++
++#ifdef CONFIG_WNM_AP
++
++static int
++hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
++ u16 disassoc_timer, u8 validity_period, u8 dialog_token,
++ struct blob_attr *neighbors, u8 mbo_reason, u8 cell_pref, u8 reassoc_delay)
++{
++ struct blob_attr *cur;
++ struct sta_info *sta;
++ int nr_len = 0;
++ int rem;
++ u8 *nr = NULL;
++ u8 req_mode = 0;
++ u8 mbo[10];
++ size_t mbo_len = 0;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return UBUS_STATUS_NOT_FOUND;
++
++ if (neighbors) {
++ u8 *nr_cur;
++
++ if (blobmsg_check_array(neighbors,
++ BLOBMSG_TYPE_STRING) < 0)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ blobmsg_for_each_attr(cur, neighbors, rem) {
++ int len = strlen(blobmsg_get_string(cur));
++
++ if (len % 2)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ nr_len += (len / 2) + 2;
++ }
++
++ if (nr_len) {
++ nr = os_zalloc(nr_len);
++ if (!nr)
++ return UBUS_STATUS_UNKNOWN_ERROR;
++ }
++
++ nr_cur = nr;
++ blobmsg_for_each_attr(cur, neighbors, rem) {
++ int len = strlen(blobmsg_get_string(cur)) / 2;
++
++ *nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
++ *nr_cur++ = (u8) len;
++ if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) {
++ free(nr);
++ return UBUS_STATUS_INVALID_ARGUMENT;
++ }
++
++ nr_cur += len;
++ }
++ }
++
++ if (nr)
++ req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
++
++ if (abridged)
++ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
++
++ if (disassoc_imminent)
++ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
++
++#ifdef CONFIG_MBO
++ u8 *mbo_pos = mbo;
++
++ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (reassoc_delay > 65535 || (reassoc_delay && !disassoc_imminent))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = mbo_reason;
++ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = cell_pref;
++
++ if (reassoc_delay) {
++ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
++ *mbo_pos++ = 2;
++ WPA_PUT_LE16(mbo_pos, reassoc_delay);
++ mbo_pos += 2;
++ }
++
++ mbo_len = mbo_pos - mbo;
++#endif
++
++ if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
++ dialog_token, NULL, nr, nr_len, mbo_len ? mbo : NULL, mbo_len))
++ return UBUS_STATUS_UNKNOWN_ERROR;
++
++ return 0;
++}
++
++enum {
++ BSS_TR_ADDR,
++ BSS_TR_DA_IMMINENT,
++ BSS_TR_DA_TIMER,
++ BSS_TR_VALID_PERIOD,
++ BSS_TR_NEIGHBORS,
++ BSS_TR_ABRIDGED,
++ BSS_TR_DIALOG_TOKEN,
++#ifdef CONFIG_MBO
++ BSS_TR_MBO_REASON,
++ BSS_TR_CELL_PREF,
++ BSS_TR_REASSOC_DELAY,
++#endif
++ __BSS_TR_DISASSOC_MAX
++};
++
++static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
++ [BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
++ [BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
++ [BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
++ [BSS_TR_DIALOG_TOKEN] = { "dialog_token", BLOBMSG_TYPE_INT32 },
++#ifdef CONFIG_MBO
++ [BSS_TR_MBO_REASON] = { "mbo_reason", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_CELL_PREF] = { "cell_pref", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_REASSOC_DELAY] = { "reassoc_delay", BLOBMSG_TYPE_INT32 },
++#endif
++};
++
++static int
++hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
++ struct sta_info *sta;
++ u32 da_timer = 0;
++ u32 valid_period = 0;
++ u8 addr[ETH_ALEN];
++ u32 dialog_token = 1;
++ bool abridged;
++ bool da_imminent;
++ u8 mbo_reason;
++ u8 cell_pref;
++ u8 reassoc_delay;
++
++ blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[BSS_TR_ADDR])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[BSS_TR_DA_TIMER])
++ da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
++
++ if (tb[BSS_TR_VALID_PERIOD])
++ valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
++
++ if (tb[BSS_TR_DIALOG_TOKEN])
++ dialog_token = blobmsg_get_u32(tb[BSS_TR_DIALOG_TOKEN]);
++
++ da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
++ abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
++
++#ifdef CONFIG_MBO
++ if (tb[BSS_TR_MBO_REASON])
++ mbo_reason = blobmsg_get_u32(tb[BSS_TR_MBO_REASON]);
++
++ if (tb[BSS_TR_CELL_PREF])
++ cell_pref = blobmsg_get_u32(tb[BSS_TR_CELL_PREF]);
++
++ if (tb[BSS_TR_REASSOC_DELAY])
++ reassoc_delay = blobmsg_get_u32(tb[BSS_TR_REASSOC_DELAY]);
++#endif
++
++ return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
++ dialog_token, tb[BSS_TR_NEIGHBORS], mbo_reason, cell_pref, reassoc_delay);
++}
++#endif
++
++#ifdef CONFIG_AIRTIME_POLICY
++enum {
++ UPDATE_AIRTIME_STA,
++ UPDATE_AIRTIME_WEIGHT,
++ __UPDATE_AIRTIME_MAX,
++};
++
++
++static const struct blobmsg_policy airtime_policy[__UPDATE_AIRTIME_MAX] = {
++ [UPDATE_AIRTIME_STA] = { "sta", BLOBMSG_TYPE_STRING },
++ [UPDATE_AIRTIME_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__UPDATE_AIRTIME_MAX];
++ struct sta_info *sta = NULL;
++ u8 addr[ETH_ALEN];
++ int weight;
++
++ blobmsg_parse(airtime_policy, __UPDATE_AIRTIME_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[UPDATE_AIRTIME_WEIGHT])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ weight = blobmsg_get_u32(tb[UPDATE_AIRTIME_WEIGHT]);
++
++ if (!tb[UPDATE_AIRTIME_STA]) {
++ if (!weight)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ hapd->conf->airtime_weight = weight;
++ return 0;
++ }
++
++ if (hwaddr_aton(blobmsg_data(tb[UPDATE_AIRTIME_STA]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return UBUS_STATUS_NOT_FOUND;
++
++ sta->dyn_airtime_weight = weight;
++ airtime_policy_new_sta(hapd, sta);
++
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_TAXONOMY
++static const struct blobmsg_policy addr_policy[] = {
++ { "address", BLOBMSG_TYPE_STRING }
++};
++
++static bool
++hostapd_add_b64_data(const char *name, const struct wpabuf *buf)
++{
++ char *str;
++
++ if (!buf)
++ return false;
++
++ str = blobmsg_alloc_string_buffer(&b, name, B64_ENCODE_LEN(wpabuf_len(buf)));
++ b64_encode(wpabuf_head(buf), wpabuf_len(buf), str, B64_ENCODE_LEN(wpabuf_len(buf)));
++ blobmsg_add_string_buffer(&b);
++
++ return true;
++}
++
++static int
++hostapd_bss_get_sta_ies(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb;
++ struct sta_info *sta;
++ u8 addr[ETH_ALEN];
++
++ blobmsg_parse(addr_policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
++
++ if (!tb || hwaddr_aton(blobmsg_data(tb), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta || (!sta->probe_ie_taxonomy && !sta->assoc_ie_taxonomy))
++ return UBUS_STATUS_NOT_FOUND;
++
++ blob_buf_init(&b, 0);
++ hostapd_add_b64_data("probe_ie", sta->probe_ie_taxonomy);
++ hostapd_add_b64_data("assoc_ie", sta->assoc_ie_taxonomy);
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++#endif
++
++
++static const struct ubus_method bss_methods[] = {
++ UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
++ UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
++#ifdef CONFIG_TAXONOMY
++ UBUS_METHOD("get_sta_ies", hostapd_bss_get_sta_ies, addr_policy),
++#endif
++ UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
++ UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
++#ifdef CONFIG_AIRTIME_POLICY
++ UBUS_METHOD("update_airtime", hostapd_bss_update_airtime, airtime_policy),
++#endif
++ UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
++#ifdef CONFIG_WPS
++ UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
++ UBUS_METHOD_NOARG("wps_status", hostapd_bss_wps_status),
++ UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
++#endif
++ UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
++ UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features),
++#ifdef NEED_AP_MLME
++ UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
++#endif
++ UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
++ UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
++ UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
++ UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
++ UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list),
++ UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy),
++ UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
++ UBUS_METHOD("link_measurement_req", hostapd_rrm_lm_req, lm_req_policy),
++#ifdef CONFIG_WNM_AP
++ UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
++#endif
++};
++
++static struct ubus_object_type bss_object_type =
++ UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
++
++static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
++{
++ return memcmp(k1, k2, ETH_ALEN);
++}
++
++void hostapd_ubus_add_bss(struct hostapd_data *hapd)
++{
++ struct ubus_object *obj = &hapd->ubus.obj;
++ char *name;
++ int ret;
++
++#ifdef CONFIG_MESH
++ if (hapd->conf->mesh & MESH_ENABLED)
++ return;
++#endif
++
++ if (!hostapd_ubus_init())
++ return;
++
++ if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
++ return;
++
++ avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
++ obj->name = name;
++ obj->type = &bss_object_type;
++ obj->methods = bss_object_type.methods;
++ obj->n_methods = bss_object_type.n_methods;
++ ret = ubus_add_object(ctx, obj);
++ hostapd_ubus_ref_inc();
++}
++
++void hostapd_ubus_free_bss(struct hostapd_data *hapd)
++{
++ struct ubus_object *obj = &hapd->ubus.obj;
++ char *name = (char *) obj->name;
++
++#ifdef CONFIG_MESH
++ if (hapd->conf->mesh & MESH_ENABLED)
++ return;
++#endif
++
++ if (!ctx)
++ return;
++
++ if (obj->id) {
++ ubus_remove_object(ctx, obj);
++ hostapd_ubus_ref_dec();
++ }
++
++ free(name);
++}
++
++static void
++hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++ const char *action)
++{
++ struct vlan_description *desc = &vlan->vlan_desc;
++ void *c;
++ int i;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_string(&b, "ifname", vlan->ifname);
++ blobmsg_add_string(&b, "bridge", vlan->bridge);
++ blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
++
++ if (desc->notempty) {
++ blobmsg_add_u32(&b, "untagged", desc->untagged);
++ c = blobmsg_open_array(&b, "tagged");
++ for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
++ blobmsg_add_u32(&b, "", desc->tagged[i]);
++ blobmsg_close_array(&b, c);
++ }
++
++ ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
++}
++
++void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++ hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
++}
++
++void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++ hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
++}
++
++struct ubus_event_req {
++ struct ubus_notify_request nreq;
++ int resp;
++};
++
++static void
++ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
++{
++ struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
++
++ ureq->resp = ret;
++}
++
++int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
++{
++ struct ubus_banned_client *ban;
++ const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
++ [HOSTAPD_UBUS_PROBE_REQ] = "probe",
++ [HOSTAPD_UBUS_AUTH_REQ] = "auth",
++ [HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
++ };
++ const char *type = "mgmt";
++ struct ubus_event_req ureq = {};
++ const u8 *addr;
++
++ if (req->mgmt_frame)
++ addr = req->mgmt_frame->sa;
++ else
++ addr = req->addr;
++
++ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
++ if (ban)
++ return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return WLAN_STATUS_SUCCESS;
++
++ if (req->type < ARRAY_SIZE(types))
++ type = types[req->type];
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ if (req->mgmt_frame)
++ blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
++ if (req->ssi_signal)
++ blobmsg_add_u32(&b, "signal", req->ssi_signal);
++ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
++
++ if (req->elems) {
++ if(req->elems->ht_capabilities)
++ {
++ struct ieee80211_ht_capabilities *ht_capabilities;
++ void *ht_cap, *ht_cap_mcs_set, *mcs_set;
++
++
++ ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
++ ht_cap = blobmsg_open_table(&b, "ht_capabilities");
++ blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
++ ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
++ blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
++ blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
++ blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
++ blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities);
++ mcs_set = blobmsg_open_array(&b, "supported_mcs_set");
++ for (int i = 0; i < 16; i++) {
++ blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]);
++ }
++ blobmsg_close_array(&b, mcs_set);
++ blobmsg_close_table(&b, ht_cap_mcs_set);
++ blobmsg_close_table(&b, ht_cap);
++ }
++ if(req->elems->vht_capabilities)
++ {
++ struct ieee80211_vht_capabilities *vht_capabilities;
++ void *vht_cap, *vht_cap_mcs_set;
++
++ vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities;
++ vht_cap = blobmsg_open_table(&b, "vht_capabilities");
++ blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info);
++ vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set");
++ blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map);
++ blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest);
++ blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map);
++ blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest);
++ blobmsg_close_table(&b, vht_cap_mcs_set);
++ blobmsg_close_table(&b, vht_cap);
++ }
++ }
++
++ if (!hapd->ubus.notify_response) {
++ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
++ return WLAN_STATUS_SUCCESS;
++ }
++
++ if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
++ return WLAN_STATUS_SUCCESS;
++
++ ureq.nreq.status_cb = ubus_event_cb;
++ ubus_complete_request(ctx, &ureq.nreq.req, 100);
++
++ if (ureq.resp)
++ return ureq.resp;
++
++ return WLAN_STATUS_SUCCESS;
++}
++
++void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
++{
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ if (!addr)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++
++ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
++}
++
++void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
++ const char *auth_alg)
++{
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", sta->addr);
++ if (auth_alg)
++ blobmsg_add_string(&b, "auth-alg", auth_alg);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "sta-authorized", b.head, -1);
++}
++
++void hostapd_ubus_notify_beacon_report(
++ struct hostapd_data *hapd, const u8 *addr, u8 token, u8 rep_mode,
++ struct rrm_measurement_beacon_report *rep, size_t len)
++{
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ if (!addr || !rep)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ blobmsg_add_u16(&b, "op-class", rep->op_class);
++ blobmsg_add_u16(&b, "channel", rep->channel);
++ blobmsg_add_u64(&b, "start-time", rep->start_time);
++ blobmsg_add_u16(&b, "duration", rep->duration);
++ blobmsg_add_u16(&b, "report-info", rep->report_info);
++ blobmsg_add_u16(&b, "rcpi", rep->rcpi);
++ blobmsg_add_u16(&b, "rsni", rep->rsni);
++ blobmsg_add_macaddr(&b, "bssid", rep->bssid);
++ blobmsg_add_u16(&b, "antenna-id", rep->antenna_id);
++ blobmsg_add_u16(&b, "parent-tsf", rep->parent_tsf);
++ blobmsg_add_u16(&b, "rep-mode", rep_mode);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "beacon-report", b.head, -1);
++}
++
++void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
++ int chan_width, int cf1, int cf2)
++{
++ struct hostapd_data *hapd;
++ int i;
++
++ if (!ctx)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u16(&b, "frequency", frequency);
++ blobmsg_add_u16(&b, "width", chan_width);
++ blobmsg_add_u16(&b, "center1", cf1);
++ blobmsg_add_u16(&b, "center2", cf2);
++
++ for (i = 0; i < iface->num_bss; i++) {
++ hapd = iface->bss[i];
++ ubus_notify(ctx, &hapd->ubus.obj, "radar-detected", b.head, -1);
++ }
++}
++
++#ifdef CONFIG_WNM_AP
++static void hostapd_ubus_notify_bss_transition_add_candidate_list(
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++ char *cl_str;
++ int i;
++
++ if (candidate_list_len == 0)
++ return;
++
++ cl_str = blobmsg_alloc_string_buffer(&b, "candidate-list", candidate_list_len * 2 + 1);
++ for (i = 0; i < candidate_list_len; i++)
++ snprintf(&cl_str[i*2], 3, "%02X", candidate_list[i]);
++ blobmsg_add_string_buffer(&b);
++
++}
++#endif
++
++void hostapd_ubus_notify_bss_transition_response(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
++ u8 bss_termination_delay, const u8 *target_bssid,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++#ifdef CONFIG_WNM_AP
++ u16 i;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ if (!addr)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ blobmsg_add_u8(&b, "dialog-token", dialog_token);
++ blobmsg_add_u8(&b, "status-code", status_code);
++ blobmsg_add_u8(&b, "bss-termination-delay", bss_termination_delay);
++ if (target_bssid)
++ blobmsg_add_macaddr(&b, "target-bssid", target_bssid);
++
++ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-response", b.head, -1);
++#endif
++}
++
++int hostapd_ubus_notify_bss_transition_query(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++#ifdef CONFIG_WNM_AP
++ struct ubus_event_req ureq = {};
++ char *cl_str;
++ u16 i;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return 0;
++
++ if (!addr)
++ return 0;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ blobmsg_add_u8(&b, "dialog-token", dialog_token);
++ blobmsg_add_u8(&b, "reason", reason);
++ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
++
++ if (!hapd->ubus.notify_response) {
++ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, -1);
++ return 0;
++ }
++
++ if (ubus_notify_async(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, &ureq.nreq))
++ return 0;
++
++ ureq.nreq.status_cb = ubus_event_cb;
++ ubus_complete_request(ctx, &ureq.nreq.req, 100);
++
++ return ureq.resp;
++#endif
++}
+diff --git a/src/ap/ubus.h b/src/ap/ubus.h
+new file mode 100644
+index 000000000..b0f7c44ab
+--- /dev/null
++++ b/src/ap/ubus.h
+@@ -0,0 +1,154 @@
++/*
++ * hostapd / ubus support
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++#ifndef __HOSTAPD_UBUS_H
++#define __HOSTAPD_UBUS_H
++
++enum hostapd_ubus_event_type {
++ HOSTAPD_UBUS_PROBE_REQ,
++ HOSTAPD_UBUS_AUTH_REQ,
++ HOSTAPD_UBUS_ASSOC_REQ,
++ HOSTAPD_UBUS_TYPE_MAX
++};
++
++struct hostapd_ubus_request {
++ enum hostapd_ubus_event_type type;
++ const struct ieee80211_mgmt *mgmt_frame;
++ const struct ieee802_11_elems *elems;
++ int ssi_signal; /* dBm */
++ const u8 *addr;
++};
++
++struct hostapd_iface;
++struct hostapd_data;
++struct hapd_interfaces;
++struct rrm_measurement_beacon_report;
++
++#ifdef UBUS_SUPPORT
++
++#include <libubox/avl.h>
++#include <libubus.h>
++
++struct hostapd_ubus_bss {
++ struct ubus_object obj;
++ struct avl_tree banned;
++ int notify_response;
++};
++
++void hostapd_ubus_add_iface(struct hostapd_iface *iface);
++void hostapd_ubus_free_iface(struct hostapd_iface *iface);
++void hostapd_ubus_add_bss(struct hostapd_data *hapd);
++void hostapd_ubus_free_bss(struct hostapd_data *hapd);
++void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
++void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
++
++int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
++void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len);
++void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
++void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
++ const u8 *addr, u8 token, u8 rep_mode,
++ struct rrm_measurement_beacon_report *rep,
++ size_t len);
++void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
++ int chan_width, int cf1, int cf2);
++
++void hostapd_ubus_notify_bss_transition_response(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
++ u8 bss_termination_delay, const u8 *target_bssid,
++ const u8 *candidate_list, u16 candidate_list_len);
++void hostapd_ubus_add(struct hapd_interfaces *interfaces);
++void hostapd_ubus_free(struct hapd_interfaces *interfaces);
++int hostapd_ubus_notify_bss_transition_query(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
++ const u8 *candidate_list, u16 candidate_list_len);
++void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
++ const char *auth_alg);
++
++#else
++
++struct hostapd_ubus_bss {};
++
++static inline void hostapd_ubus_add_iface(struct hostapd_iface *iface)
++{
++}
++
++static inline void hostapd_ubus_free_iface(struct hostapd_iface *iface)
++{
++}
++
++static inline void hostapd_ubus_add_bss(struct hostapd_data *hapd)
++{
++}
++
++static inline void hostapd_ubus_free_bss(struct hostapd_data *hapd)
++{
++}
++
++static inline void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++}
++
++static inline void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++}
++
++static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
++{
++ return 0;
++}
++
++static inline void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
++{
++}
++
++static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
++{
++}
++
++static inline void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
++ const u8 *addr, u8 token,
++ u8 rep_mode,
++ struct rrm_measurement_beacon_report *rep,
++ size_t len)
++{
++}
++static inline void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
++ int chan_width, int cf1, int cf2)
++{
++}
++
++static inline void hostapd_ubus_notify_bss_transition_response(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
++ u8 bss_termination_delay, const u8 *target_bssid,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++}
++
++static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
++{
++}
++
++static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
++{
++}
++
++static inline int hostapd_ubus_notify_bss_transition_query(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++ return 0;
++}
++
++static inline void
++hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
++ const char *auth_alg)
++{
++}
++
++#endif
++
++#endif
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+new file mode 100644
+index 000000000..16d1b5153
+--- /dev/null
++++ b/src/ap/ucode.c
+@@ -0,0 +1,813 @@
++#include <sys/un.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "hostapd.h"
++#include "beacon.h"
++#include "hw_features.h"
++#include "ap_drv_ops.h"
++#include "dfs.h"
++#include "acs.h"
++#include <libubox/uloop.h>
++
++static uc_resource_type_t *global_type, *bss_type, *iface_type;
++static struct hapd_interfaces *interfaces;
++static uc_value_t *global, *bss_registry, *iface_registry;
++static uc_vm_t *vm;
++
++static uc_value_t *
++hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ if (hapd->ucode.idx)
++ return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
++
++ val = uc_resource_new(bss_type, hapd);
++ hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
++
++ return val;
++}
++
++static uc_value_t *
++hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
++{
++ uc_value_t *val;
++
++ if (hapd->ucode.idx)
++ return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
++
++ val = uc_resource_new(iface_type, hapd);
++ hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++ return val;
++}
++
++static void
++hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
++{
++ uc_value_t *list;
++ int i;
++
++ list = ucv_array_new(vm);
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *hapd = iface->bss[i];
++ uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
++
++ ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
++ ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
++ }
++ ucv_object_add(if_bss, iface->phy, ucv_get(list));
++}
++
++static void
++hostapd_ucode_update_interfaces(void)
++{
++ uc_value_t *ifs = ucv_object_new(vm);
++ uc_value_t *if_bss = ucv_array_new(vm);
++ uc_value_t *bss = ucv_object_new(vm);
++ int i;
++
++ for (i = 0; i < interfaces->count; i++) {
++ struct hostapd_iface *iface = interfaces->iface[i];
++
++ ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
++ hostapd_ucode_update_bss_list(iface, if_bss, bss);
++ }
++
++ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
++ ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
++ ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
++ ucv_gc(vm);
++}
++
++static uc_value_t *
++uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *iface = uc_fn_arg(0);
++ int ret;
++
++ if (ucv_type(iface) != UC_STRING)
++ return ucv_int64_new(-1);
++
++ ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
++ hostapd_ucode_update_interfaces();
++
++ return ucv_int64_new(ret);
++}
++
++static uc_value_t *
++uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *iface = uc_fn_arg(0);
++
++ if (ucv_type(iface) != UC_STRING)
++ return NULL;
++
++ hostapd_remove_iface(interfaces, ucv_string_get(iface));
++ hostapd_ucode_update_interfaces();
++
++ return NULL;
++}
++
++static struct hostapd_vlan *
++bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
++{
++ struct hostapd_vlan *vlan;
++
++ for (vlan = bss->vlan; vlan; vlan = vlan->next)
++ if (vlan->vlan_id == id)
++ return vlan;
++
++ return NULL;
++}
++
++static int
++bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++ const char *ifname)
++{
++ if (!strcmp(ifname, vlan->ifname))
++ return 0;
++
++ hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
++ os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
++
++ return 0;
++}
++
++static int
++bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
++{
++ struct hostapd_bss_config *old_bss = hapd->conf;
++ struct hostapd_vlan *vlan, *vlan_new, *wildcard;
++ char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
++ int ret;
++
++ vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
++ wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
++ if (!!vlan != !!wildcard)
++ return -1;
++
++ if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
++ strcpy(vlan->ifname, wildcard->ifname);
++ else
++ wildcard = NULL;
++
++ for (vlan = bss->vlan; vlan; vlan = vlan->next) {
++ if (vlan->vlan_id == VLAN_ID_WILDCARD ||
++ vlan->dynamic_vlan > 0)
++ continue;
++
++ if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
++ return -1;
++ }
++
++ for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
++ if (vlan->vlan_id == VLAN_ID_WILDCARD)
++ continue;
++
++ if (vlan->dynamic_vlan == 0) {
++ vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
++ if (!vlan_new)
++ return -1;
++
++ if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
++ return -1;
++
++ continue;
++ }
++
++ if (!wildcard)
++ continue;
++
++ os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
++ pos = os_strchr(ifname, '#');
++ if (!pos)
++ return -1;
++
++ *pos++ = '\0';
++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
++ ifname, vlan->vlan_id, pos);
++ if (os_snprintf_error(sizeof(vlan_ifname), ret))
++ return -1;
++
++ if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
++ return -1;
++ }
++
++ return 0;
++}
++
++static uc_value_t *
++uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ struct hostapd_bss_config *old_bss;
++ struct hostapd_iface *iface;
++ struct hostapd_config *conf;
++ uc_value_t *file = uc_fn_arg(0);
++ uc_value_t *index = uc_fn_arg(1);
++ uc_value_t *files_only = uc_fn_arg(2);
++ unsigned int i, idx = 0;
++ int ret = -1;
++
++ if (!hapd || ucv_type(file) != UC_STRING)
++ goto out;
++
++ if (ucv_type(index) == UC_INTEGER)
++ idx = ucv_int64_get(index);
++
++ iface = hapd->iface;
++ conf = interfaces->config_read_cb(ucv_string_get(file));
++ if (!conf)
++ goto out;
++
++ if (idx > conf->num_bss || !conf->bss[idx])
++ goto free;
++
++ if (ucv_boolean_get(files_only)) {
++ struct hostapd_bss_config *bss = conf->bss[idx];
++ struct hostapd_bss_config *old_bss = hapd->conf;
++
++#define swap_field(name) \
++ do { \
++ void *ptr = old_bss->name; \
++ old_bss->name = bss->name; \
++ bss->name = ptr; \
++ } while (0)
++
++ swap_field(ssid.wpa_psk_file);
++ ret = bss_reload_vlans(hapd, bss);
++ goto done;
++ }
++
++ hostapd_bss_deinit_no_free(hapd);
++ hostapd_drv_stop_ap(hapd);
++ hostapd_free_hapd_data(hapd);
++
++ old_bss = hapd->conf;
++ for (i = 0; i < iface->conf->num_bss; i++)
++ if (iface->conf->bss[i] == hapd->conf)
++ iface->conf->bss[i] = conf->bss[idx];
++ hapd->conf = conf->bss[idx];
++ conf->bss[idx] = old_bss;
++
++ hostapd_setup_bss(hapd, hapd == iface->bss[0], true);
++ hostapd_ucode_update_interfaces();
++
++done:
++ ret = 0;
++free:
++ hostapd_config_free(conf);
++out:
++ return ucv_int64_new(ret);
++}
++
++static void
++hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
++ struct hostapd_bss_config *conf)
++{
++ int i;
++
++ for (i = 0; i < iconf->num_bss; i++)
++ if (iconf->bss[i] == conf)
++ break;
++
++ if (i == iconf->num_bss)
++ return;
++
++ for (i++; i < iconf->num_bss; i++)
++ iconf->bss[i - 1] = iconf->bss[i];
++ iconf->num_bss--;
++}
++
++
++static uc_value_t *
++uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ struct hostapd_iface *iface;
++ int i, idx;
++
++ if (!hapd)
++ return NULL;
++
++ iface = hapd->iface;
++ if (iface->num_bss == 1) {
++ wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
++ return NULL;
++ }
++
++ for (idx = 0; idx < iface->num_bss; idx++)
++ if (iface->bss[idx] == hapd)
++ break;
++
++ if (idx == iface->num_bss)
++ return NULL;
++
++ for (i = idx + 1; i < iface->num_bss; i++)
++ iface->bss[i - 1] = iface->bss[i];
++
++ iface->num_bss--;
++
++ iface->bss[0]->interface_added = 0;
++ hostapd_drv_set_first_bss(iface->bss[0]);
++ hapd->interface_added = 1;
++
++ hostapd_drv_stop_ap(hapd);
++ hostapd_bss_deinit(hapd);
++ hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
++ hostapd_config_free_bss(hapd->conf);
++ os_free(hapd);
++
++ hostapd_ucode_update_interfaces();
++ ucv_gc(vm);
++
++ return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ struct hostapd_bss_config *bss;
++ struct hostapd_config *conf;
++ struct hostapd_data *hapd;
++ uc_value_t *file = uc_fn_arg(0);
++ uc_value_t *index = uc_fn_arg(1);
++ unsigned int idx = 0;
++ uc_value_t *ret = NULL;
++
++ if (!iface || ucv_type(file) != UC_STRING)
++ goto out;
++
++ if (ucv_type(index) == UC_INTEGER)
++ idx = ucv_int64_get(index);
++
++ conf = interfaces->config_read_cb(ucv_string_get(file));
++ if (!conf || idx > conf->num_bss || !conf->bss[idx])
++ goto out;
++
++ bss = conf->bss[idx];
++ hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
++ if (!hapd)
++ goto out;
++
++ hapd->driver = iface->bss[0]->driver;
++ hapd->drv_priv = iface->bss[0]->drv_priv;
++ if (interfaces->ctrl_iface_init &&
++ interfaces->ctrl_iface_init(hapd) < 0)
++ goto free_hapd;
++
++ if (iface->state == HAPD_IFACE_ENABLED &&
++ hostapd_setup_bss(hapd, -1, true))
++ goto deinit_ctrl;
++
++ iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
++ sizeof(*iface->bss));
++ iface->bss[iface->num_bss++] = hapd;
++
++ iface->conf->bss = os_realloc_array(iface->conf->bss,
++ iface->conf->num_bss + 1,
++ sizeof(*iface->conf->bss));
++ iface->conf->bss[iface->conf->num_bss] = bss;
++ conf->bss[idx] = NULL;
++ ret = hostapd_ucode_bss_get_uval(hapd);
++ hostapd_ucode_update_interfaces();
++ goto out;
++
++deinit_ctrl:
++ if (interfaces->ctrl_iface_deinit)
++ interfaces->ctrl_iface_deinit(hapd);
++free_hapd:
++ hostapd_free_hapd_data(hapd);
++ os_free(hapd);
++out:
++ hostapd_config_free(conf);
++ return ret;
++}
++
++static uc_value_t *
++uc_hostapd_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ uc_value_t *bss_list = uc_fn_arg(0);
++ struct hostapd_data **new_bss;
++ struct hostapd_bss_config **new_conf;
++
++ if (!iface)
++ return NULL;
++
++ if (ucv_type(bss_list) != UC_ARRAY ||
++ ucv_array_length(bss_list) != iface->num_bss)
++ return NULL;
++
++ new_bss = calloc(iface->num_bss, sizeof(*new_bss));
++ new_conf = calloc(iface->num_bss, sizeof(*new_conf));
++ for (size_t i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *bss;
++
++ bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
++ if (bss->iface != iface)
++ goto free;
++
++ for (size_t k = 0; k < i; k++)
++ if (new_bss[k] == bss)
++ goto free;
++
++ new_bss[i] = bss;
++ new_conf[i] = bss->conf;
++ }
++
++ new_bss[0]->interface_added = 0;
++ for (size_t i = 1; i < iface->num_bss; i++)
++ new_bss[i]->interface_added = 1;
++
++ free(iface->bss);
++ iface->bss = new_bss;
++
++ free(iface->conf->bss);
++ iface->conf->bss = new_conf;
++ iface->conf->num_bss = iface->num_bss;
++ hostapd_drv_set_first_bss(iface->bss[0]);
++
++ return ucv_boolean_new(true);
++
++free:
++ free(new_bss);
++ free(new_conf);
++ return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ uc_value_t *arg = uc_fn_arg(0);
++ struct sockaddr_storage from = {};
++ static char reply[4096];
++ int reply_len;
++
++ if (!hapd || !interfaces->ctrl_iface_recv ||
++ ucv_type(arg) != UC_STRING)
++ return NULL;
++
++ reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
++ reply, sizeof(reply),
++ &from, sizeof(from));
++ if (reply_len < 0)
++ return NULL;
++
++ if (reply_len && reply[reply_len - 1] == '\n')
++ reply_len--;
++
++ return ucv_string_new_length(reply, reply_len);
++}
++
++static void
++uc_hostapd_disable_iface(struct hostapd_iface *iface)
++{
++ switch (iface->state) {
++ case HAPD_IFACE_DISABLED:
++ break;
++#ifdef CONFIG_ACS
++ case HAPD_IFACE_ACS:
++ acs_cleanup(iface);
++ iface->scan_cb = NULL;
++ /* fallthrough */
++#endif
++ default:
++ hostapd_disable_iface(iface);
++ break;
++ }
++}
++
++static uc_value_t *
++uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ int i;
++
++ if (!iface)
++ return NULL;
++
++ if (iface->state != HAPD_IFACE_ENABLED)
++ uc_hostapd_disable_iface(iface);
++
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *hapd = iface->bss[i];
++
++ hostapd_drv_stop_ap(hapd);
++ hapd->beacon_set_done = 0;
++ }
++
++ return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ uc_value_t *info = uc_fn_arg(0);
++ struct hostapd_config *conf;
++ bool changed = false;
++ uint64_t intval;
++ int i;
++
++ if (!iface)
++ return NULL;
++
++ if (!info) {
++ iface->freq = 0;
++ goto out;
++ }
++
++ if (ucv_type(info) != UC_OBJECT)
++ return NULL;
++
++#define UPDATE_VAL(field, name) \
++ if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) && \
++ !errno && intval != conf->field) do { \
++ conf->field = intval; \
++ changed = true; \
++ } while(0)
++
++ conf = iface->conf;
++ UPDATE_VAL(op_class, "op_class");
++ UPDATE_VAL(hw_mode, "hw_mode");
++ UPDATE_VAL(channel, "channel");
++ UPDATE_VAL(secondary_channel, "sec_channel");
++ if (!changed &&
++ (iface->bss[0]->beacon_set_done ||
++ iface->state == HAPD_IFACE_DFS))
++ return ucv_boolean_new(true);
++
++ intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
++ if (!errno)
++ hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
++
++ intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
++ if (!errno)
++ hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
++
++ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++ if (!errno)
++ hostapd_set_oper_chwidth(conf, intval);
++
++ intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
++ if (!errno)
++ iface->freq = intval;
++ else
++ iface->freq = 0;
++ conf->acs = 0;
++
++out:
++ switch (iface->state) {
++ case HAPD_IFACE_ENABLED:
++ if (!hostapd_is_dfs_required(iface) ||
++ hostapd_is_dfs_chan_available(iface))
++ break;
++ wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
++ /* fallthrough */
++ default:
++ uc_hostapd_disable_iface(iface);
++ break;
++ }
++
++ if (conf->channel && !iface->freq)
++ iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
++
++ if (iface->state != HAPD_IFACE_ENABLED) {
++ hostapd_enable_iface(iface);
++ return ucv_boolean_new(true);
++ }
++
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *hapd = iface->bss[i];
++ int ret;
++
++ hapd->conf->start_disabled = 0;
++ hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
++ conf->channel,
++ conf->enable_edmg,
++ conf->edmg_channel,
++ conf->ieee80211n,
++ conf->ieee80211ac,
++ conf->ieee80211ax,
++ conf->ieee80211be,
++ conf->secondary_channel,
++ hostapd_get_oper_chwidth(conf),
++ hostapd_get_oper_centr_freq_seg0_idx(conf),
++ hostapd_get_oper_centr_freq_seg1_idx(conf));
++
++ ieee802_11_set_beacon(hapd);
++ }
++
++ return ucv_boolean_new(true);
++}
++
++static uc_value_t *
++uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ uc_value_t *info = uc_fn_arg(0);
++ struct hostapd_config *conf;
++ struct csa_settings csa = {};
++ uint64_t intval;
++ int i, ret = 0;
++
++ if (!iface || ucv_type(info) != UC_OBJECT)
++ return NULL;
++
++ conf = iface->conf;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
++ csa.cs_count = intval;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
++ csa.freq_params.sec_channel_offset = intval;
++
++ csa.freq_params.ht_enabled = conf->ieee80211n;
++ csa.freq_params.vht_enabled = conf->ieee80211ac;
++ csa.freq_params.he_enabled = conf->ieee80211ax;
++#ifdef CONFIG_IEEE80211BE
++ csa.freq_params.eht_enabled = conf->ieee80211be;
++#endif
++ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++ if (errno)
++ intval = hostapd_get_oper_chwidth(conf);
++ if (intval)
++ csa.freq_params.bandwidth = 40 << intval;
++ else
++ csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
++
++ if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
++ csa.freq_params.freq = intval;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
++ csa.freq_params.center_freq1 = intval;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
++ csa.freq_params.center_freq2 = intval;
++
++ for (i = 0; i < iface->num_bss; i++)
++ ret = hostapd_switch_channel(iface->bss[i], &csa);
++
++ return ucv_boolean_new(!ret);
++}
++
++static uc_value_t *
++uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ uc_value_t *ifname_arg = uc_fn_arg(0);
++ char prev_ifname[IFNAMSIZ + 1];
++ struct sta_info *sta;
++ const char *ifname;
++ int ret;
++
++ if (!hapd || ucv_type(ifname_arg) != UC_STRING)
++ return NULL;
++
++ os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
++ ifname = ucv_string_get(ifname_arg);
++
++ hostapd_ubus_free_bss(hapd);
++ if (interfaces->ctrl_iface_deinit)
++ interfaces->ctrl_iface_deinit(hapd);
++
++ ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
++ if (ret)
++ goto out;
++
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
++
++ if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
++ continue;
++
++ snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
++ snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
++ hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
++ }
++
++ if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
++ os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
++ os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
++ hostapd_ubus_add_bss(hapd);
++
++ hostapd_ucode_update_interfaces();
++out:
++ if (interfaces->ctrl_iface_init)
++ interfaces->ctrl_iface_init(hapd);
++
++ return ret ? NULL : ucv_boolean_new(true);
++}
++
++
++int hostapd_ucode_init(struct hapd_interfaces *ifaces)
++{
++ static const uc_function_list_t global_fns[] = {
++ { "printf", uc_wpa_printf },
++ { "getpid", uc_wpa_getpid },
++ { "sha1", uc_wpa_sha1 },
++ { "freq_info", uc_wpa_freq_info },
++ { "add_iface", uc_hostapd_add_iface },
++ { "remove_iface", uc_hostapd_remove_iface },
++ { "udebug_set", uc_wpa_udebug_set },
++ };
++ static const uc_function_list_t bss_fns[] = {
++ { "ctrl", uc_hostapd_bss_ctrl },
++ { "set_config", uc_hostapd_bss_set_config },
++ { "rename", uc_hostapd_bss_rename },
++ { "delete", uc_hostapd_bss_delete },
++ };
++ static const uc_function_list_t iface_fns[] = {
++ { "set_bss_order", uc_hostapd_iface_set_bss_order },
++ { "add_bss", uc_hostapd_iface_add_bss },
++ { "stop", uc_hostapd_iface_stop },
++ { "start", uc_hostapd_iface_start },
++ { "switch_channel", uc_hostapd_iface_switch_channel },
++ };
++ uc_value_t *data, *proto;
++
++ interfaces = ifaces;
++ vm = wpa_ucode_create_vm();
++
++ global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
++ bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
++ iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
++
++ bss_registry = ucv_array_new(vm);
++ uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
++
++ iface_registry = ucv_array_new(vm);
++ uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
++
++ global = wpa_ucode_global_init("hostapd", global_type);
++
++ if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
++ goto free_vm;
++ ucv_gc(vm);
++
++ return 0;
++
++free_vm:
++ wpa_ucode_free_vm();
++ return -1;
++}
++
++void hostapd_ucode_free(void)
++{
++ if (wpa_ucode_call_prepare("shutdown") == 0)
++ ucv_put(wpa_ucode_call(0));
++ wpa_ucode_free_vm();
++}
++
++void hostapd_ucode_free_iface(struct hostapd_iface *iface)
++{
++ wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
++}
++
++void hostapd_ucode_add_bss(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ if (wpa_ucode_call_prepare("bss_add"))
++ return;
++
++ val = hostapd_ucode_bss_get_uval(hapd);
++ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ if (wpa_ucode_call_prepare("bss_reload"))
++ return;
++
++ val = hostapd_ucode_bss_get_uval(hapd);
++ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void hostapd_ucode_free_bss(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
++ if (!val)
++ return;
++
++ hapd->ucode.idx = 0;
++ if (wpa_ucode_call_prepare("bss_remove"))
++ return;
++
++ uc_value_push(ucv_string_new(hapd->conf->iface));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
+diff --git a/src/ap/ucode.h b/src/ap/ucode.h
+new file mode 100644
+index 000000000..d00b78716
+--- /dev/null
++++ b/src/ap/ucode.h
+@@ -0,0 +1,54 @@
++#ifndef __HOSTAPD_AP_UCODE_H
++#define __HOSTAPD_AP_UCODE_H
++
++#include "utils/ucode.h"
++
++struct hostapd_data;
++
++struct hostapd_ucode_bss {
++#ifdef UCODE_SUPPORT
++ int idx;
++#endif
++};
++
++struct hostapd_ucode_iface {
++#ifdef UCODE_SUPPORT
++ int idx;
++#endif
++};
++
++#ifdef UCODE_SUPPORT
++
++int hostapd_ucode_init(struct hapd_interfaces *ifaces);
++
++void hostapd_ucode_free(void);
++void hostapd_ucode_free_iface(struct hostapd_iface *iface);
++void hostapd_ucode_add_bss(struct hostapd_data *hapd);
++void hostapd_ucode_free_bss(struct hostapd_data *hapd);
++void hostapd_ucode_reload_bss(struct hostapd_data *hapd);
++
++#else
++
++static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
++{
++ return -EINVAL;
++}
++static inline void hostapd_ucode_free(void)
++{
++}
++static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface)
++{
++}
++static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
++{
++}
++static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd)
++{
++}
++static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd)
++{
++}
++
++#endif
++
++#endif
+diff --git a/src/utils/build_features.h b/src/utils/build_features.h
+new file mode 100644
+index 000000000..553769ece
+--- /dev/null
++++ b/src/utils/build_features.h
+@@ -0,0 +1,65 @@
++#ifndef BUILD_FEATURES_H
++#define BUILD_FEATURES_H
++
++static inline int has_feature(const char *feat)
++{
++#if defined(IEEE8021X_EAPOL) || (defined(HOSTAPD) && !defined(CONFIG_NO_RADIUS))
++ if (!strcmp(feat, "eap"))
++ return 1;
++#endif
++#ifdef CONFIG_IEEE80211AC
++ if (!strcmp(feat, "11ac"))
++ return 1;
++#endif
++#ifdef CONFIG_IEEE80211AX
++ if (!strcmp(feat, "11ax"))
++ return 1;
++#endif
++#ifdef CONFIG_IEEE80211R
++ if (!strcmp(feat, "11r"))
++ return 1;
++#endif
++#ifdef CONFIG_ACS
++ if (!strcmp(feat, "acs"))
++ return 1;
++#endif
++#ifdef CONFIG_SAE
++ if (!strcmp(feat, "sae"))
++ return 1;
++#endif
++#ifdef CONFIG_OWE
++ if (!strcmp(feat, "owe"))
++ return 1;
++#endif
++#ifdef CONFIG_SUITEB192
++ if (!strcmp(feat, "suiteb192"))
++ return 1;
++#endif
++#ifdef CONFIG_WEP
++ if (!strcmp(feat, "wep"))
++ return 1;
++#endif
++#ifdef CONFIG_HS20
++ if (!strcmp(feat, "hs20"))
++ return 1;
++#endif
++#ifdef CONFIG_WPS
++ if (!strcmp(feat, "wps"))
++ return 1;
++#endif
++#ifdef CONFIG_FILS
++ if (!strcmp(feat, "fils"))
++ return 1;
++#endif
++#ifdef CONFIG_OCV
++ if (!strcmp(feat, "ocv"))
++ return 1;
++#endif
++#ifdef CONFIG_MESH
++ if (!strcmp(feat, "mesh"))
++ return 1;
++#endif
++ return 0;
++}
++
++#endif /* BUILD_FEATURES_H */
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+new file mode 100644
+index 000000000..29c753c32
+--- /dev/null
++++ b/src/utils/ucode.c
+@@ -0,0 +1,502 @@
++#include <unistd.h>
++#include "ucode.h"
++#include "utils/eloop.h"
++#include "crypto/crypto.h"
++#include "crypto/sha1.h"
++#include "common/ieee802_11_common.h"
++#include <linux/netlink.h>
++#include <linux/genetlink.h>
++#include <linux/nl80211.h>
++#include <libubox/uloop.h>
++#include <ucode/compiler.h>
++#include <udebug.h>
++
++static uc_value_t *registry;
++static uc_vm_t vm;
++static struct uloop_timeout gc_timer;
++static struct udebug ud;
++static struct udebug_buf ud_log, ud_nl[3];
++static const struct udebug_buf_meta meta_log = {
++ .name = "wpa_log",
++ .format = UDEBUG_FORMAT_STRING,
++};
++static const struct udebug_buf_meta meta_nl_ll = {
++ .name = "wpa_nl_ctrl",
++ .format = UDEBUG_FORMAT_PACKET,
++ .sub_format = UDEBUG_DLT_NETLINK,
++};
++static const struct udebug_buf_meta meta_nl_tx = {
++ .name = "wpa_nl_tx",
++ .format = UDEBUG_FORMAT_PACKET,
++ .sub_format = UDEBUG_DLT_NETLINK,
++};
++#define UDEBUG_FLAG_RX_FRAME (1ULL << 0)
++static const struct udebug_buf_flag rx_flags[] = {
++ { "rx_frame", UDEBUG_FLAG_RX_FRAME },
++};
++static const struct udebug_buf_meta meta_nl_rx = {
++ .name = "wpa_nl_rx",
++ .format = UDEBUG_FORMAT_PACKET,
++ .sub_format = UDEBUG_DLT_NETLINK,
++ .flags = rx_flags,
++ .n_flags = ARRAY_SIZE(rx_flags),
++};
++static struct udebug_ubus_ring udebug_rings[] = {
++ {
++ .buf = &ud_log,
++ .meta = &meta_log,
++ .default_entries = 1024,
++ .default_size = 64 * 1024
++ },
++ {
++ .buf = &ud_nl[0],
++ .meta = &meta_nl_rx,
++ .default_entries = 1024,
++ .default_size = 256 * 1024,
++ },
++ {
++ .buf = &ud_nl[1],
++ .meta = &meta_nl_tx,
++ .default_entries = 1024,
++ .default_size = 64 * 1024,
++ },
++ {
++ .buf = &ud_nl[2],
++ .meta = &meta_nl_ll,
++ .default_entries = 1024,
++ .default_size = 32 * 1024,
++ }
++};
++char *udebug_service;
++struct udebug_ubus ud_ubus;
++
++static void uc_gc_timer(struct uloop_timeout *timeout)
++{
++ ucv_gc(&vm);
++}
++
++uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *level = uc_fn_arg(0);
++ uc_value_t *ret, **args;
++ uc_cfn_ptr_t _sprintf;
++ int l = MSG_INFO;
++ int i, start = 0;
++
++ _sprintf = uc_stdlib_function("sprintf");
++ if (!sprintf)
++ return NULL;
++
++ if (ucv_type(level) == UC_INTEGER) {
++ l = ucv_int64_get(level);
++ start++;
++ }
++
++ if (nargs <= start)
++ return NULL;
++
++ ret = _sprintf(vm, nargs - start);
++ if (ucv_type(ret) != UC_STRING)
++ return NULL;
++
++ wpa_printf(l, "%s", ucv_string_get(ret));
++ ucv_put(ret);
++
++ return NULL;
++}
++
++uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *freq = uc_fn_arg(0);
++ uc_value_t *sec = uc_fn_arg(1);
++ int width = ucv_uint64_get(uc_fn_arg(2));
++ int freq_val, center_idx, center_ofs;
++ enum oper_chan_width chanwidth;
++ enum hostapd_hw_mode hw_mode;
++ u8 op_class, channel, tmp_channel;
++ const char *modestr;
++ int sec_channel = 0;
++ uc_value_t *ret;
++
++ if (ucv_type(freq) != UC_INTEGER)
++ return NULL;
++
++ freq_val = ucv_int64_get(freq);
++ if (ucv_type(sec) == UC_INTEGER)
++ sec_channel = ucv_int64_get(sec);
++ else if (sec)
++ return NULL;
++ else if (freq_val > 4000)
++ sec_channel = (freq_val / 20) & 1 ? 1 : -1;
++ else
++ sec_channel = freq_val < 2442 ? 1 : -1;
++
++ if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
++ return NULL;
++
++ switch (width) {
++ case 0:
++ chanwidth = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ case 1:
++ chanwidth = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case 2:
++ chanwidth = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ default:
++ return NULL;
++ }
++
++ hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
++ chanwidth, &op_class, &channel);
++ switch (hw_mode) {
++ case HOSTAPD_MODE_IEEE80211B:
++ modestr = "b";
++ break;
++ case HOSTAPD_MODE_IEEE80211G:
++ modestr = "g";
++ break;
++ case HOSTAPD_MODE_IEEE80211A:
++ modestr = "a";
++ break;
++ case HOSTAPD_MODE_IEEE80211AD:
++ modestr = "ad";
++ break;
++ default:
++ return NULL;
++ }
++
++ ret = ucv_object_new(vm);
++ ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
++ ucv_object_add(ret, "channel", ucv_int64_new(channel));
++ ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
++ ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
++ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
++ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++
++ if (!sec_channel)
++ return ret;
++
++ if (freq_val >= 5900)
++ center_ofs = 0;
++ else if (freq_val >= 5745)
++ center_ofs = 20;
++ else
++ center_ofs = 35;
++ tmp_channel = channel - center_ofs;
++ tmp_channel &= ~((8 << width) - 1);
++ center_idx = tmp_channel + center_ofs + (4 << width) - 1;
++
++ if (freq_val < 3000)
++ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
++ else
++ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
++ center_idx = (center_idx - channel) * 5 + freq_val;
++ ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
++
++out:
++ return ret;
++}
++
++uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
++{
++ return ucv_int64_new(getpid());
++}
++
++uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
++{
++ u8 hash[SHA1_MAC_LEN];
++ char hash_hex[2 * ARRAY_SIZE(hash) + 1];
++ uc_value_t *val;
++ size_t *lens;
++ const u8 **args;
++ int i;
++
++ if (!nargs)
++ return NULL;
++
++ args = alloca(nargs * sizeof(*args));
++ lens = alloca(nargs * sizeof(*lens));
++ for (i = 0; i < nargs; i++) {
++ val = uc_fn_arg(i);
++ if (ucv_type(val) != UC_STRING)
++ return NULL;
++
++ args[i] = ucv_string_get(val);
++ lens[i] = ucv_string_length(val);
++ }
++
++ if (sha1_vector(nargs, args, lens, hash))
++ return NULL;
++
++ for (i = 0; i < ARRAY_SIZE(hash); i++)
++ sprintf(hash_hex + 2 * i, "%02x", hash[i]);
++
++ return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
++}
++
++uc_vm_t *wpa_ucode_create_vm(void)
++{
++ static uc_parse_config_t config = {
++ .strict_declarations = true,
++ .lstrip_blocks = true,
++ .trim_blocks = true,
++ .raw_mode = true
++ };
++
++ uc_search_path_init(&config.module_search_path);
++ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
++ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
++
++ uc_vm_init(&vm, &config);
++
++ uc_stdlib_load(uc_vm_scope_get(&vm));
++ eloop_add_uloop();
++ gc_timer.cb = uc_gc_timer;
++
++ return &vm;
++}
++
++int wpa_ucode_run(const char *script)
++{
++ uc_source_t *source;
++ uc_program_t *prog;
++ uc_value_t *ops;
++ char *err;
++ int ret;
++
++ source = uc_source_new_file(script);
++ if (!source)
++ return -1;
++
++ prog = uc_compile(vm.config, source, &err);
++ uc_source_put(source);
++ if (!prog) {
++ wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
++ return -1;
++ }
++
++ ret = uc_vm_execute(&vm, prog, &ops);
++ uc_program_put(prog);
++ if (ret || !ops)
++ return -1;
++
++ registry = ucv_array_new(&vm);
++ uc_vm_registry_set(&vm, "hostap.registry", registry);
++ ucv_array_set(registry, 0, ucv_get(ops));
++
++ return 0;
++}
++
++int wpa_ucode_call_prepare(const char *fname)
++{
++ uc_value_t *obj, *func;
++
++ if (!registry)
++ return -1;
++
++ obj = ucv_array_get(registry, 0);
++ if (!obj)
++ return -1;
++
++ func = ucv_object_get(obj, fname, NULL);
++ if (!ucv_is_callable(func))
++ return -1;
++
++ uc_vm_stack_push(&vm, ucv_get(obj));
++ uc_vm_stack_push(&vm, ucv_get(func));
++
++ return 0;
++}
++
++static void udebug_printf_hook(int level, const char *fmt, va_list ap)
++{
++ udebug_entry_init(&ud_log);
++ udebug_entry_vprintf(&ud_log, fmt, ap);
++ udebug_entry_add(&ud_log);
++}
++
++static void udebug_hexdump_hook(int level, const char *title,
++ const void *data, size_t len)
++{
++ char *buf;
++
++ udebug_entry_init(&ud_log);
++ udebug_entry_printf(&ud_log, "%s - hexdump:", title);
++ buf = udebug_entry_append(&ud_log, NULL, 3 * len);
++ for (size_t i = 0; i < len; i++)
++ buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
++ udebug_entry_add(&ud_log);
++}
++
++static void udebug_netlink_hook(int tx, const void *data, size_t len)
++{
++ struct {
++ uint16_t pkttype;
++ uint16_t arphdr;
++ uint16_t _pad[5];
++ uint16_t proto;
++ } hdr = {
++ .pkttype = host_to_be16(tx ? 7 : 6),
++ .arphdr = host_to_be16(824),
++ .proto = host_to_be16(16),
++ };
++ const struct nlmsghdr *nlh = data;
++ const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
++ struct udebug_buf *buf = &ud_nl[!!tx];
++
++ if (nlh->nlmsg_type == 0x10)
++ buf = &ud_nl[2];
++ else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
++ !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
++ return;
++
++ if (!udebug_buf_valid(buf))
++ return;
++
++ udebug_entry_init(buf);
++ udebug_entry_append(buf, &hdr, sizeof(hdr));
++ udebug_entry_append(buf, data, len);
++ udebug_entry_add(buf);
++}
++
++static void
++wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
++ bool enabled)
++{
++ udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
++ data, enabled);
++
++ if (udebug_buf_valid(&ud_log)) {
++ wpa_printf_hook = udebug_printf_hook;
++ wpa_hexdump_hook = udebug_hexdump_hook;
++ } else {
++ wpa_printf_hook = NULL;
++ wpa_hexdump_hook = NULL;
++ }
++
++ if (udebug_buf_valid(&ud_nl[0]) ||
++ udebug_buf_valid(&ud_nl[1]) ||
++ udebug_buf_valid(&ud_nl[2]))
++ wpa_netlink_hook = udebug_netlink_hook;
++ else
++ wpa_netlink_hook = NULL;
++}
++
++uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *name = uc_fn_arg(0);
++ uc_value_t *ubus = uc_fn_arg(1);
++ static bool enabled = false;
++ struct ubus_context *ctx;
++ bool cur_en;
++
++ cur_en = ucv_type(name) == UC_STRING;
++ ctx = ucv_resource_data(ubus, "ubus.connection");
++ if (!ctx)
++ cur_en = false;
++
++ if (enabled == cur_en)
++ return ucv_boolean_new(true);
++
++ enabled = cur_en;
++ if (enabled) {
++ udebug_service = strdup(ucv_string_get(name));
++ udebug_init(&ud);
++ udebug_auto_connect(&ud, NULL);
++ udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
++ } else {
++ udebug_ubus_free(&ud_ubus);
++ for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
++ if (udebug_buf_valid(udebug_rings[i].buf))
++ udebug_buf_free(udebug_rings[i].buf);
++ udebug_free(&ud);
++ free(udebug_service);
++ }
++
++ return ucv_boolean_new(true);
++}
++
++uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
++{
++ uc_value_t *global = uc_resource_new(global_type, NULL);
++ uc_value_t *proto;
++
++ uc_vm_registry_set(&vm, "hostap.global", global);
++ proto = ucv_prototype_get(global);
++ ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
++
++#define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
++ ADD_CONST(MSG_EXCESSIVE);
++ ADD_CONST(MSG_MSGDUMP);
++ ADD_CONST(MSG_DEBUG);
++ ADD_CONST(MSG_INFO);
++ ADD_CONST(MSG_WARNING);
++ ADD_CONST(MSG_ERROR);
++#undef ADD_CONST
++
++ ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
++
++ return global;
++}
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
++{
++ uc_value_t *data;
++ int i = 0;
++
++ while (ucv_array_get(reg, i))
++ i++;
++
++ ucv_array_set(reg, i, ucv_get(val));
++
++ return i + 1;
++}
++
++uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
++{
++ if (!idx)
++ return NULL;
++
++ return ucv_array_get(reg, idx - 1);
++}
++
++uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
++{
++ uc_value_t *val = wpa_ucode_registry_get(reg, idx);
++ void **dataptr;
++
++ if (!val)
++ return NULL;
++
++ ucv_array_set(reg, idx - 1, NULL);
++ dataptr = ucv_resource_dataptr(val, NULL);
++ if (dataptr)
++ *dataptr = NULL;
++
++ return val;
++}
++
++
++uc_value_t *wpa_ucode_call(size_t nargs)
++{
++ if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
++ return NULL;
++
++ if (!gc_timer.pending)
++ uloop_timeout_set(&gc_timer, 10);
++
++ return uc_vm_stack_pop(&vm);
++}
++
++void wpa_ucode_free_vm(void)
++{
++ if (!vm.config)
++ return;
++
++ uc_search_path_free(&vm.config->module_search_path);
++ uc_vm_free(&vm);
++ registry = NULL;
++ vm = (uc_vm_t){};
++}
+diff --git a/src/utils/ucode.h b/src/utils/ucode.h
+new file mode 100644
+index 000000000..c083241e0
+--- /dev/null
++++ b/src/utils/ucode.h
+@@ -0,0 +1,30 @@
++#ifndef __HOSTAPD_UTILS_UCODE_H
++#define __HOSTAPD_UTILS_UCODE_H
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include <ucode/lib.h>
++#include <ucode/vm.h>
++
++#define HOSTAPD_UC_PATH "/usr/share/hostap/"
++
++extern uc_value_t *uc_registry;
++uc_vm_t *wpa_ucode_create_vm(void);
++int wpa_ucode_run(const char *script);
++int wpa_ucode_call_prepare(const char *fname);
++uc_value_t *wpa_ucode_call(size_t nargs);
++void wpa_ucode_free_vm(void);
++
++uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type);
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
++uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx);
++uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx);
++
++uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
++
++#endif
+diff --git a/wpa_supplicant/ubus.c b/wpa_supplicant/ubus.c
+new file mode 100644
+index 000000000..1c477f0c0
+--- /dev/null
++++ b/wpa_supplicant/ubus.c
+@@ -0,0 +1,280 @@
++/*
++ * wpa_supplicant / ubus support
++ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "utils/wpabuf.h"
++#include "common/ieee802_11_defs.h"
++#include "wpa_supplicant_i.h"
++#include "wps_supplicant.h"
++#include "ubus.h"
++
++static struct ubus_context *ctx;
++static struct blob_buf b;
++static int ctx_ref;
++
++static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
++{
++ return container_of(obj, struct wpa_global, ubus_global);
++}
++
++static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
++{
++ return container_of(obj, struct wpa_supplicant, ubus.obj);
++}
++
++static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
++{
++ if (ubus_reconnect(ctx, NULL)) {
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++ return;
++ }
++
++ ubus_add_uloop(ctx);
++}
++
++static void wpas_ubus_connection_lost(struct ubus_context *ctx)
++{
++ uloop_fd_delete(&ctx->sock);
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++}
++
++static bool wpas_ubus_init(void)
++{
++ if (ctx)
++ return true;
++
++ eloop_add_uloop();
++ ctx = ubus_connect(NULL);
++ if (!ctx)
++ return false;
++
++ ctx->connection_lost = wpas_ubus_connection_lost;
++ ubus_add_uloop(ctx);
++
++ return true;
++}
++
++static void wpas_ubus_ref_inc(void)
++{
++ ctx_ref++;
++}
++
++static void wpas_ubus_ref_dec(void)
++{
++ ctx_ref--;
++ if (!ctx)
++ return;
++
++ if (ctx_ref)
++ return;
++
++ uloop_fd_delete(&ctx->sock);
++ ubus_free(ctx);
++ ctx = NULL;
++}
++
++static int
++wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u8(&b, "ht_supported", ht_supported(wpa_s->hw.modes));
++ blobmsg_add_u8(&b, "vht_supported", vht_supported(wpa_s->hw.modes));
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++
++ if (wpa_supplicant_reload_configuration(wpa_s))
++ return UBUS_STATUS_UNKNOWN_ERROR;
++ else
++ return 0;
++}
++
++#ifdef CONFIG_WPS
++enum {
++ WPS_START_MULTI_AP,
++ __WPS_START_MAX
++};
++
++static const struct blobmsg_policy wps_start_policy[] = {
++ [WPS_START_MULTI_AP] = { "multi_ap", BLOBMSG_TYPE_BOOL },
++};
++
++static int
++wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++ struct blob_attr *tb[__WPS_START_MAX], *cur;
++ int multi_ap = 0;
++
++ blobmsg_parse(wps_start_policy, __WPS_START_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
++
++ if (tb[WPS_START_MULTI_AP])
++ multi_ap = blobmsg_get_bool(tb[WPS_START_MULTI_AP]);
++
++ rc = wpas_wps_start_pbc(wpa_s, NULL, 0, multi_ap);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++
++static int
++wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++
++ rc = wpas_wps_cancel(wpa_s);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++#endif
++
++static const struct ubus_method bss_methods[] = {
++ UBUS_METHOD_NOARG("reload", wpas_bss_reload),
++ UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
++#ifdef CONFIG_WPS
++ UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
++ UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
++#endif
++};
++
++static struct ubus_object_type bss_object_type =
++ UBUS_OBJECT_TYPE("wpas_bss", bss_methods);
++
++void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
++{
++ struct ubus_object *obj = &wpa_s->ubus.obj;
++ char *name;
++ int ret;
++
++ if (!wpas_ubus_init())
++ return;
++
++ if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
++ return;
++
++ obj->name = name;
++ obj->type = &bss_object_type;
++ obj->methods = bss_object_type.methods;
++ obj->n_methods = bss_object_type.n_methods;
++ ret = ubus_add_object(ctx, obj);
++ wpas_ubus_ref_inc();
++}
++
++void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
++{
++ struct ubus_object *obj = &wpa_s->ubus.obj;
++ char *name = (char *) obj->name;
++
++ if (!ctx)
++ return;
++
++ if (obj->id) {
++ ubus_remove_object(ctx, obj);
++ wpas_ubus_ref_dec();
++ }
++
++ free(name);
++}
++
++#ifdef CONFIG_WPS
++void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
++{
++ u16 auth_type;
++ char *ifname, *encryption, *ssid, *key;
++ size_t ifname_len;
++
++ if (!cred)
++ return;
++
++ auth_type = cred->auth_type;
++
++ if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))
++ auth_type = WPS_AUTH_WPA2PSK;
++
++ if (auth_type != WPS_AUTH_OPEN &&
++ auth_type != WPS_AUTH_WPAPSK &&
++ auth_type != WPS_AUTH_WPA2PSK) {
++ wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
++ "unsupported authentication type 0x%x",
++ auth_type);
++ return;
++ }
++
++ if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
++ if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
++ wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
++ "invalid Network Key length %lu",
++ (unsigned long) cred->key_len);
++ return;
++ }
++ }
++
++ blob_buf_init(&b, 0);
++
++ ifname_len = strlen(wpa_s->ifname);
++ ifname = blobmsg_alloc_string_buffer(&b, "ifname", ifname_len + 1);
++ memcpy(ifname, wpa_s->ifname, ifname_len + 1);
++ ifname[ifname_len] = '\0';
++ blobmsg_add_string_buffer(&b);
++
++ switch (auth_type) {
++ case WPS_AUTH_WPA2PSK:
++ encryption = "psk2";
++ break;
++ case WPS_AUTH_WPAPSK:
++ encryption = "psk";
++ break;
++ default:
++ encryption = "none";
++ break;
++ }
++
++ blobmsg_add_string(&b, "encryption", encryption);
++
++ ssid = blobmsg_alloc_string_buffer(&b, "ssid", cred->ssid_len + 1);
++ memcpy(ssid, cred->ssid, cred->ssid_len);
++ ssid[cred->ssid_len] = '\0';
++ blobmsg_add_string_buffer(&b);
++
++ if (cred->key_len > 0) {
++ key = blobmsg_alloc_string_buffer(&b, "key", cred->key_len + 1);
++ memcpy(key, cred->key, cred->key_len);
++ key[cred->key_len] = '\0';
++ blobmsg_add_string_buffer(&b);
++ }
++
++// ubus_notify(ctx, &wpa_s->ubus.obj, "wps_credentials", b.head, -1);
++ ubus_send_event(ctx, "wps_credentials", b.head);
++}
++#endif /* CONFIG_WPS */
+diff --git a/wpa_supplicant/ubus.h b/wpa_supplicant/ubus.h
+new file mode 100644
+index 000000000..f6681cb26
+--- /dev/null
++++ b/wpa_supplicant/ubus.h
+@@ -0,0 +1,55 @@
++/*
++ * wpa_supplicant / ubus support
++ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++#ifndef __WPAS_UBUS_H
++#define __WPAS_UBUS_H
++
++struct wpa_supplicant;
++struct wpa_global;
++
++#include "wps_supplicant.h"
++
++#ifdef UBUS_SUPPORT
++#include <libubus.h>
++
++struct wpas_ubus_bss {
++ struct ubus_object obj;
++};
++
++void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
++void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
++
++#ifdef CONFIG_WPS
++void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
++#endif
++
++#else
++struct wpas_ubus_bss {};
++
++static inline void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
++{
++}
++
++static inline void wpas_ubus_add(struct wpa_global *global)
++{
++}
++
++static inline void wpas_ubus_free(struct wpa_global *global)
++{
++}
++#endif
++
++#endif
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+new file mode 100644
+index 000000000..397f85bde
+--- /dev/null
++++ b/wpa_supplicant/ucode.c
+@@ -0,0 +1,299 @@
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "drivers/driver.h"
++#include "ap/hostapd.h"
++#include "wpa_supplicant_i.h"
++#include "wps_supplicant.h"
++#include "bss.h"
++#include "ucode.h"
++
++static struct wpa_global *wpa_global;
++static uc_resource_type_t *global_type, *iface_type;
++static uc_value_t *global, *iface_registry;
++static uc_vm_t *vm;
++
++static uc_value_t *
++wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s)
++{
++ uc_value_t *val;
++
++ if (wpa_s->ucode.idx)
++ return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++
++ val = uc_resource_new(iface_type, wpa_s);
++ wpa_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++ return val;
++}
++
++static void
++wpas_ucode_update_interfaces(void)
++{
++ uc_value_t *ifs = ucv_object_new(vm);
++ struct wpa_supplicant *wpa_s;
++ int i;
++
++ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
++ ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
++
++ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
++{
++ uc_value_t *val;
++
++ if (wpa_ucode_call_prepare("iface_add"))
++ return;
++
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
++{
++ uc_value_t *val;
++
++ val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx);
++ if (!val)
++ return;
++
++ wpa_s->ucode.idx = 0;
++ if (wpa_ucode_call_prepare("iface_remove"))
++ return;
++
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++ const char *state;
++ uc_value_t *val;
++
++ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++ if (!val)
++ return;
++
++ if (wpa_ucode_call_prepare("state"))
++ return;
++
++ state = wpa_supplicant_state_txt(wpa_s->wpa_state);
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(val));
++ uc_value_push(ucv_get(ucv_string_new(state)));
++ ucv_put(wpa_ucode_call(3));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++ const char *state;
++ uc_value_t *val;
++
++ if (event != EVENT_CH_SWITCH_STARTED)
++ return;
++
++ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++ if (!val)
++ return;
++
++ if (wpa_ucode_call_prepare("event"))
++ return;
++
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(val));
++ uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
++ val = ucv_object_new(vm);
++ uc_value_push(ucv_get(val));
++
++ if (event == EVENT_CH_SWITCH_STARTED) {
++ ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
++ ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
++ ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
++ ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++ ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++ }
++
++ ucv_put(wpa_ucode_call(4));
++ ucv_gc(vm);
++}
++
++static const char *obj_stringval(uc_value_t *obj, const char *name)
++{
++ uc_value_t *val = ucv_object_get(obj, name, NULL);
++
++ return ucv_string_get(val);
++}
++
++static uc_value_t *
++uc_wpas_add_iface(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *info = uc_fn_arg(0);
++ uc_value_t *driver = ucv_object_get(info, "driver", NULL);
++ uc_value_t *ifname = ucv_object_get(info, "iface", NULL);
++ uc_value_t *bridge = ucv_object_get(info, "bridge", NULL);
++ uc_value_t *config = ucv_object_get(info, "config", NULL);
++ uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL);
++ struct wpa_interface iface;
++ int ret = -1;
++
++ if (ucv_type(info) != UC_OBJECT)
++ goto out;
++
++ iface = (struct wpa_interface){
++ .driver = "nl80211",
++ .ifname = ucv_string_get(ifname),
++ .bridge_ifname = ucv_string_get(bridge),
++ .confname = ucv_string_get(config),
++ .ctrl_interface = ucv_string_get(ctrl),
++ };
++
++ if (driver) {
++ const char *drvname;
++ if (ucv_type(driver) != UC_STRING)
++ goto out;
++
++ iface.driver = NULL;
++ drvname = ucv_string_get(driver);
++ for (int i = 0; wpa_drivers[i]; i++) {
++ if (!strcmp(drvname, wpa_drivers[i]->name))
++ iface.driver = wpa_drivers[i]->name;
++ }
++
++ if (!iface.driver)
++ goto out;
++ }
++
++ if (!iface.ifname || !iface.confname)
++ goto out;
++
++ ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1;
++ wpas_ucode_update_interfaces();
++
++out:
++ return ucv_int64_new(ret);
++}
++
++static uc_value_t *
++uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs)
++{
++ struct wpa_supplicant *wpa_s = NULL;
++ uc_value_t *ifname_arg = uc_fn_arg(0);
++ const char *ifname = ucv_string_get(ifname_arg);
++ int ret = -1;
++
++ if (!ifname)
++ goto out;
++
++ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
++ if (!strcmp(wpa_s->ifname, ifname))
++ break;
++
++ if (!wpa_s)
++ goto out;
++
++ ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0);
++ wpas_ucode_update_interfaces();
++
++out:
++ return ucv_int64_new(ret);
++}
++
++static uc_value_t *
++uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
++{
++ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++ struct wpa_bss *bss;
++ uc_value_t *ret, *val;
++
++ if (!wpa_s)
++ return NULL;
++
++ ret = ucv_object_new(vm);
++
++ val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
++ ucv_object_add(ret, "state", ucv_get(val));
++
++ bss = wpa_s->current_bss;
++ if (bss) {
++ int sec_chan = 0;
++ const u8 *ie;
++
++ ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
++ if (ie && ie[1] >= 2) {
++ const struct ieee80211_ht_operation *ht_oper;
++ int sec;
++
++ ht_oper = (const void *) (ie + 2);
++ sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
++ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
++ sec_chan = 1;
++ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
++ sec_chan = -1;
++ }
++
++ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
++ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++ }
++
++#ifdef CONFIG_MESH
++ if (wpa_s->ifmsh) {
++ struct hostapd_iface *ifmsh = wpa_s->ifmsh;
++
++ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
++ ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
++ }
++#endif
++
++ return ret;
++}
++
++int wpas_ucode_init(struct wpa_global *gl)
++{
++ static const uc_function_list_t global_fns[] = {
++ { "printf", uc_wpa_printf },
++ { "getpid", uc_wpa_getpid },
++ { "add_iface", uc_wpas_add_iface },
++ { "remove_iface", uc_wpas_remove_iface },
++ { "udebug_set", uc_wpa_udebug_set },
++ };
++ static const uc_function_list_t iface_fns[] = {
++ { "status", uc_wpas_iface_status },
++ };
++ uc_value_t *data, *proto;
++
++ wpa_global = gl;
++ vm = wpa_ucode_create_vm();
++
++ global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL);
++ iface_type = uc_type_declare(vm, "wpas.iface", iface_fns, NULL);
++
++ iface_registry = ucv_array_new(vm);
++ uc_vm_registry_set(vm, "wpas.iface_registry", iface_registry);
++
++ global = wpa_ucode_global_init("wpas", global_type);
++
++ if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc"))
++ goto free_vm;
++
++ ucv_gc(vm);
++ return 0;
++
++free_vm:
++ wpa_ucode_free_vm();
++ return -1;
++}
++
++void wpas_ucode_free(void)
++{
++ if (wpa_ucode_call_prepare("shutdown") == 0)
++ ucv_put(wpa_ucode_call(0));
++ wpa_ucode_free_vm();
++}
+diff --git a/wpa_supplicant/ucode.h b/wpa_supplicant/ucode.h
+new file mode 100644
+index 000000000..a429a0ed8
+--- /dev/null
++++ b/wpa_supplicant/ucode.h
+@@ -0,0 +1,49 @@
++#ifndef __WPAS_UCODE_H
++#define __WPAS_UCODE_H
++
++#include "utils/ucode.h"
++
++struct wpa_global;
++union wpa_event_data;
++struct wpa_supplicant;
++
++struct wpas_ucode_bss {
++#ifdef UCODE_SUPPORT
++ unsigned int idx;
++#endif
++};
++
++#ifdef UCODE_SUPPORT
++int wpas_ucode_init(struct wpa_global *gl);
++void wpas_ucode_free(void);
++void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s);
++void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s);
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
++#else
++static inline int wpas_ucode_init(struct wpa_global *gl)
++{
++ return -EINVAL;
++}
++static inline void wpas_ucode_free(void)
++{
++}
++static inline void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++}
++
++#endif
++
++#endif
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
new file mode 100644
index 0000000..1dff1f3
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
@@ -0,0 +1,14442 @@
+From 2eff271199b22ce44432d531e8b44809368ac5d2 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Tue, 23 Jan 2024 17:07:17 +0800
+Subject: [PATCH 028/104] hostapd: sync 2024-01-18 openwrt/trunk patch folder
+
+---
+ hostapd/Makefile | 149 +-
+ hostapd/config_file.c | 39 +-
+ hostapd/ctrl_iface.c | 4 +
+ hostapd/defconfig | 15 +-
+ hostapd/hostapd_cli.c | 10 +-
+ hostapd/main.c | 21 +-
+ src/ap/acs.c | 5 +-
+ src/ap/airtime_policy.c | 15 +-
+ src/ap/ap_config.h | 8 +
+ src/ap/ap_drv_ops.c | 5 +-
+ src/ap/ap_drv_ops.h | 21 +-
+ src/ap/beacon.c | 14 +-
+ src/ap/ctrl_iface_ap.c | 45 +-
+ src/ap/dfs.c | 22 +-
+ src/ap/drv_callbacks.c | 16 +-
+ src/ap/hostapd.c | 51 +-
+ src/ap/hostapd.h | 42 +
+ src/ap/hw_features.c | 3 +-
+ src/ap/ieee802_11.c | 50 +-
+ src/ap/ieee802_11_ht.c | 15 +
+ src/ap/ieee802_11_shared.c | 2 -
+ src/ap/ieee802_11_vht.c | 17 +
+ src/ap/ieee802_1x.c | 6 +
+ src/ap/ndisc_snoop.c | 1 +
+ src/ap/rrm.c | 8 +
+ src/ap/sta_info.c | 35 +-
+ src/ap/sta_info.h | 12 +-
+ src/ap/vlan_full.c | 4 +
+ src/ap/vlan_init.c | 8 +-
+ src/ap/wnm_ap.c | 16 +-
+ src/ap/wpa_auth.c | 3 +-
+ src/ap/wpa_auth_glue.c | 9 +-
+ src/ap/wps_hostapd.c | 6 +-
+ src/ap/x_snoop.c | 22 +-
+ src/common/dpp_crypto.c | 10 +-
+ src/common/hw_features_common.c | 1 +
+ src/common/ieee802_11_defs.h | 2 +
+ src/common/sae.c | 7 +
+ src/common/wpa_common.c | 40 +-
+ src/common/wpa_ctrl.c | 10 +-
+ src/crypto/Makefile | 129 +-
+ src/crypto/crypto_mbedtls.c | 4228 +++++++++++++++++++++
+ src/crypto/crypto_module_tests.c | 134 +
+ src/crypto/crypto_wolfssl.c | 18 +
+ src/crypto/tls_mbedtls.c | 3313 ++++++++++++++++
+ src/drivers/driver.h | 36 +-
+ src/drivers/driver_nl80211.c | 300 +-
+ src/drivers/driver_nl80211_capa.c | 4 +
+ src/drivers/driver_nl80211_event.c | 5 +
+ src/drivers/driver_nl80211_scan.c | 2 +-
+ src/drivers/drivers.c | 4 +
+ src/drivers/drivers.mak | 4 +-
+ src/drivers/rfkill.h | 16 +
+ src/radius/radius_client.c | 34 +
+ src/radius/radius_client.h | 2 +
+ src/radius/radius_das.c | 201 +-
+ src/radius/radius_das.h | 1 +
+ src/radius/radius_server.c | 61 +-
+ src/rsn_supp/wpa.c | 3 +
+ src/tls/Makefile | 11 +
+ src/utils/eloop.c | 20 +-
+ src/utils/eloop.h | 8 +
+ src/utils/uloop.c | 64 +
+ src/utils/wpa_debug.c | 49 +-
+ src/utils/wpa_debug.h | 73 +-
+ tests/Makefile | 65 +-
+ tests/hwsim/example-hostapd.config | 10 +-
+ tests/hwsim/example-wpa_supplicant.config | 11 +-
+ tests/hwsim/test_ap_eap.py | 112 +-
+ tests/hwsim/test_ap_ft.py | 4 +-
+ tests/hwsim/test_authsrv.py | 9 +-
+ tests/hwsim/test_dpp.py | 19 +-
+ tests/hwsim/test_erp.py | 16 +-
+ tests/hwsim/test_fils.py | 12 +
+ tests/hwsim/test_pmksa_cache.py | 4 +-
+ tests/hwsim/test_sae.py | 7 +
+ tests/hwsim/test_suite_b.py | 3 +
+ tests/hwsim/test_wpas_ctrl.py | 2 +-
+ tests/hwsim/utils.py | 8 +-
+ tests/test-crypto_module.c | 16 +
+ tests/test-https.c | 12 +-
+ tests/test-https_server.c | 12 +-
+ wpa_supplicant/Makefile | 144 +-
+ wpa_supplicant/ap.c | 22 +-
+ wpa_supplicant/config.c | 95 +
+ wpa_supplicant/config_file.c | 8 +-
+ wpa_supplicant/config_ssid.h | 7 +
+ wpa_supplicant/ctrl_iface.c | 12 +-
+ wpa_supplicant/defconfig | 6 +-
+ wpa_supplicant/eapol_test.c | 11 +
+ wpa_supplicant/events.c | 13 +-
+ wpa_supplicant/main.c | 14 +-
+ wpa_supplicant/mesh.c | 3 +
+ wpa_supplicant/wpa_cli.c | 9 +
+ wpa_supplicant/wpa_priv.c | 8 +-
+ wpa_supplicant/wpa_supplicant.c | 75 +-
+ wpa_supplicant/wpa_supplicant_i.h | 6 +
+ wpa_supplicant/wps_supplicant.c | 3 +
+ wpa_supplicant/wps_supplicant.h | 3 +-
+ 99 files changed, 9786 insertions(+), 464 deletions(-)
+ create mode 100644 src/crypto/crypto_mbedtls.c
+ create mode 100644 src/crypto/tls_mbedtls.c
+ create mode 100644 src/utils/uloop.c
+ create mode 100644 tests/test-crypto_module.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index 405e05e5f..f5c1dc029 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -1,6 +1,7 @@
+ ALL=hostapd hostapd_cli
+ CONFIG_FILE = .config
+
++-include $(if $(MULTICALL), ../wpa_supplicant/.config)
+ include ../src/build.rules
+
+ ifdef LIBS
+@@ -62,6 +63,10 @@ endif
+ OBJS += main.o
+ OBJS += config_file.o
+
++ifdef CONFIG_RADIUS_SERVER
++OBJS += radius.o
++endif
++
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/drv_callbacks.o
+@@ -171,6 +176,24 @@ OBJS += ../src/common/hw_features_common.o
+
+ OBJS += ../src/eapol_auth/eapol_auth_sm.o
+
++ifdef CONFIG_UBUS
++CFLAGS += -DUBUS_SUPPORT
++OBJS += ../src/ap/ubus.o
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ../src/ap/ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
++OBJS += ../src/utils/uloop.o
++LIBS += -lubox
++endif
+
+ ifdef CONFIG_CODE_COVERAGE
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+@@ -205,7 +228,8 @@ endif
+
+ ifdef CONFIG_NO_VLAN
+ CFLAGS += -DCONFIG_NO_VLAN
+-else
++endif
++ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
+ OBJS += ../src/ap/vlan_init.o
+ OBJS += ../src/ap/vlan_ifconfig.o
+ OBJS += ../src/ap/vlan.o
+@@ -225,6 +249,9 @@ endif
+ ifdef CONFIG_NO_CTRL_IFACE
+ CFLAGS += -DCONFIG_NO_CTRL_IFACE
+ else
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ ifeq ($(CONFIG_CTRL_IFACE), udp)
+ CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+ else
+@@ -332,6 +359,7 @@ ifdef CONFIG_FILS
+ CFLAGS += -DCONFIG_FILS
+ OBJS += ../src/ap/fils_hlp.o
+ NEED_SHA384=y
++NEED_HMAC_SHA384_KDF=y
+ NEED_AES_SIV=y
+ ifdef CONFIG_FILS_SK_PFS
+ CFLAGS += -DCONFIG_FILS_SK_PFS
+@@ -364,10 +392,14 @@ CFLAGS += -DCONFIG_MBO
+ OBJS += ../src/ap/mbo_ap.o
+ endif
+
++ifndef MULTICALL
++CFLAGS += -DNO_SUPPLICANT
++endif
++
+ include ../src/drivers/drivers.mak
+-OBJS += $(DRV_AP_OBJS)
+-CFLAGS += $(DRV_AP_CFLAGS)
+-LDFLAGS += $(DRV_AP_LDFLAGS)
++OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
++CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
++LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
+ LIBS += $(DRV_AP_LIBS)
+
+ ifdef CONFIG_L2_PACKET
+@@ -714,6 +746,7 @@ CFLAGS += -DCONFIG_TLSV12
+ endif
+
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ CONFIG_CRYPTO=wolfssl
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -734,6 +767,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ CONFIG_CRYPTO=openssl
+ ifdef TLS_FUNCS
+@@ -763,7 +797,39 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls
++ifndef CONFIG_DPP
++LIBS += -lmbedx509
++endif
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++ifdef CONFIG_DPP
++LIBS += -lmbedx509
++LIBS_h += -lmbedx509
++LIBS_n += -lmbedx509
++LIBS_s += -lmbedx509
++endif
++LIBS += -lmbedcrypto
++LIBS_h += -lmbedcrypto
++LIBS_n += -lmbedcrypto
++LIBS_s += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -794,6 +860,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -872,6 +939,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/crypto_internal-rsa.o
+@@ -942,9 +1010,11 @@ endif
+
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -954,38 +1024,48 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_UNWRAP
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_AES_DEC=y
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_DEC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_DEC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-dec.o
+@@ -1000,12 +1080,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1014,16 +1098,22 @@ endif
+ endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+
+ ifdef NEED_SHA1
+ OBJS += $(SHA1OBJS)
+@@ -1033,11 +1123,13 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+@@ -1076,56 +1168,81 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ OBJS += ../src/crypto/sha256-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
++CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ ifdef NEED_SHA384
+ CFLAGS += -DCONFIG_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ CFLAGS += -DCONFIG_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+
+ ifdef CONFIG_INTERNAL_SHA384
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+@@ -1170,11 +1287,13 @@ HOBJS += $(SHA1OBJS)
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+
+ ifdef CONFIG_RADIUS_SERVER
+ CFLAGS += -DRADIUS_SERVER
+@@ -1311,8 +1430,14 @@ install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
+
++hostapd_multi.a: $(BCHECK) $(OBJS)
++ $(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
++ @$(E) " CC " $<
++ @rm -f $@
++ @$(AR) cr $@ hostapd_multi.o $(OBJS)
++
+ hostapd: $(OBJS)
+- $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
++ +$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
+ @$(E) " LD " $@
+
+ ifdef CONFIG_WPA_TRACE
+@@ -1323,7 +1448,7 @@ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+
+ hostapd_cli: $(OBJS_c)
+- $(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
++ +$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
+ @$(E) " LD " $@
+
+ NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+@@ -1347,7 +1472,9 @@ NOBJS += ../src/utils/trace.o
+ endif
+
+ HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/aes-encblock.o
++endif
+ ifdef CONFIG_INTERNAL_AES
+ HOBJS += ../src/crypto/aes-internal.o
+ HOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1370,13 +1497,17 @@ SOBJS += ../src/common/sae.o
+ SOBJS += ../src/common/sae_pk.o
+ SOBJS += ../src/common/dragonfly.o
+ SOBJS += $(AESOBJS)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-prf.o
+ SOBJS += ../src/crypto/sha384-prf.o
+ SOBJS += ../src/crypto/sha512-prf.o
++endif
+ SOBJS += ../src/crypto/dh_groups.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-kdf.o
+ SOBJS += ../src/crypto/sha384-kdf.o
+ SOBJS += ../src/crypto/sha512-kdf.o
++endif
+
+ _OBJS_VAR := NOBJS
+ include ../src/objs.mk
+@@ -1385,6 +1516,12 @@ include ../src/objs.mk
+ _OBJS_VAR := SOBJS
+ include ../src/objs.mk
+
++dump_cflags:
++ @printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ nt_password_hash: $(NOBJS)
+ $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
+ @$(E) " LD " $@
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 261905368..0094db279 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1229,6 +1229,8 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
+ if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
+ conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
++ if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
++ conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
+ return 0;
+ }
+ #endif /* CONFIG_IEEE80211AC */
+@@ -1678,6 +1680,8 @@ static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
+ return 0;
+ }
+
++#endif /* CONFIG_INTERWORKING */
++
+
+ static int parse_qos_map_set(struct hostapd_bss_config *bss,
+ char *buf, int line)
+@@ -1719,8 +1723,6 @@ static int parse_qos_map_set(struct hostapd_bss_config *bss,
+ return 0;
+ }
+
+-#endif /* CONFIG_INTERWORKING */
+-
+
+ #ifdef CONFIG_HS20
+ static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
+@@ -2630,8 +2632,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ sizeof(conf->bss[0]->iface));
+ } else if (os_strcmp(buf, "bridge") == 0) {
+ os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
++ if (!bss->wds_bridge[0])
++ os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+ } else if (os_strcmp(buf, "bridge_hairpin") == 0) {
+ bss->bridge_hairpin = atoi(pos);
++ } else if (os_strcmp(buf, "snoop_iface") == 0) {
++ os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
+ } else if (os_strcmp(buf, "vlan_bridge") == 0) {
+ os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
+ } else if (os_strcmp(buf, "wds_bridge") == 0) {
+@@ -2998,6 +3004,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "iapp_interface") == 0) {
+ wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
+ #endif /* CONFIG_IAPP */
++ } else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
++ bss->dynamic_own_ip_addr = atoi(pos);
+ } else if (os_strcmp(buf, "own_ip_addr") == 0) {
+ if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+ wpa_printf(MSG_ERROR,
+@@ -3222,6 +3230,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ line, bss->max_num_sta, MAX_STA_COUNT);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
++ conf->max_num_sta = atoi(pos);
++ if (conf->max_num_sta < 0 ||
++ conf->max_num_sta > MAX_STA_COUNT) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
++ line, conf->max_num_sta, MAX_STA_COUNT);
++ return 1;
++ }
+ } else if (os_strcmp(buf, "wpa") == 0) {
+ bss->wpa = atoi(pos);
+ } else if (os_strcmp(buf, "extended_key_id") == 0) {
+@@ -3373,6 +3389,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ wpa_printf(MSG_INFO,
+ "Line %d: Obsolete peerkey parameter ignored", line);
+ #ifdef CONFIG_IEEE80211R_AP
++ } else if (os_strcmp(buf, "ft_iface") == 0) {
++ os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
+ } else if (os_strcmp(buf, "mobility_domain") == 0) {
+ if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+ hexstr2bin(pos, bss->mobility_domain,
+@@ -3742,6 +3760,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ #ifndef CONFIG_NO_VLAN
+ } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+ bss->ssid.dynamic_vlan = atoi(pos);
++ } else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
++ bss->ssid.vlan_no_bridge = atoi(pos);
+ } else if (os_strcmp(buf, "per_sta_vif") == 0) {
+ bss->ssid.per_sta_vif = atoi(pos);
+ } else if (os_strcmp(buf, "vlan_file") == 0) {
+@@ -3839,6 +3859,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ if (bss->ocv && !bss->ieee80211w)
+ bss->ieee80211w = 1;
+ #endif /* CONFIG_OCV */
++ } else if (os_strcmp(buf, "noscan") == 0) {
++ conf->noscan = atoi(pos);
++ } else if (os_strcmp(buf, "ht_coex") == 0) {
++ conf->no_ht_coex = !atoi(pos);
+ } else if (os_strcmp(buf, "ieee80211n") == 0) {
+ conf->ieee80211n = atoi(pos);
+ } else if (os_strcmp(buf, "ht_capab") == 0) {
+@@ -3887,6 +3911,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "he_bss_color") == 0) {
+ conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ conf->he_op.he_bss_color_disabled = 0;
++ if (atoi(pos) > 63)
++ conf->he_op.he_bss_color = os_random() % 63 + 1;
+ } else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
+ conf->he_op.he_bss_color_partial = atoi(pos);
+ } else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
+@@ -4520,10 +4546,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->gas_frag_limit = val;
+ } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+ bss->gas_comeback_delay = atoi(pos);
++#endif /* CONFIG_INTERWORKING */
+ } else if (os_strcmp(buf, "qos_map_set") == 0) {
+ if (parse_qos_map_set(bss, pos, line) < 0)
+ return 1;
+-#endif /* CONFIG_INTERWORKING */
+ #ifdef CONFIG_RADIUS_TEST
+ } else if (os_strcmp(buf, "dump_msk_file") == 0) {
+ os_free(bss->dump_msk_file);
+@@ -5347,7 +5373,12 @@ struct hostapd_config * hostapd_config_read(const char *fname)
+ int errors = 0;
+ size_t i;
+
+- f = fopen(fname, "r");
++ if (!strncmp(fname, "data:", 5)) {
++ f = fmemopen((void *)(fname + 5), strlen(fname + 5), "r");
++ fname = "<inline>";
++ } else {
++ f = fopen(fname, "r");
++ }
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+ "for reading.", fname);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 10cb186f1..f76226cf4 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3876,6 +3876,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_size);
+ } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
+ reply_len = hostapd_drv_status(hapd, reply, reply_size);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strcmp(buf, "MIB") == 0) {
+ reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
+ if (reply_len >= 0) {
+@@ -3917,6 +3918,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
+ reply_size);
++#endif
+ } else if (os_strcmp(buf, "ATTACH") == 0) {
+ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
+ reply_len = -1;
+@@ -5464,6 +5466,7 @@ try_again:
+ return -1;
+ }
+
++ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
+ return 0;
+@@ -5565,6 +5568,7 @@ fail:
+ os_free(fname);
+
+ interface->global_ctrl_sock = s;
++ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+ interface, NULL);
+
+diff --git a/hostapd/defconfig b/hostapd/defconfig
+index 66bf894eb..f716553bb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -6,9 +6,21 @@
+ # just setting VARIABLE=n is not disabling that variable.
+ #
+ # This file is included in Makefile, so variables like CFLAGS and LIBS can also
+-# be modified from here. In most cass, these lines should use += in order not
++# be modified from here. In most cases, these lines should use += in order not
+ # to override previous values of the variables.
+
++
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
++#CFLAGS += -I/usr/local/openssl/include
++#LIBS += -L/usr/local/openssl/lib
++
++# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
++# the kerberos files are not in the default include path. Following line can be
++# used to fix build issues on such systems (krb5.h not found).
++#CFLAGS += -I/usr/include/kerberos
++
++
+ # Driver interface for Host AP driver
+ CONFIG_DRIVER_HOSTAP=y
+
+@@ -281,6 +293,7 @@ CONFIG_IPV6=y
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index a9d326de8..a469b1f4d 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -401,7 +401,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
+-#ifdef CONFIG_TAXONOMY
+ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+@@ -414,7 +413,6 @@ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
+ return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_TAXONOMY */
+
+
+ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+@@ -431,7 +429,6 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
+-#ifdef CONFIG_WPS
+ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+@@ -657,7 +654,6 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ ssid_hex, argv[1]);
+ return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_WPS */
+
+
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+@@ -757,7 +753,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+ }
+
+ buf[len] = '\0';
+- if (memcmp(buf, "FAIL", 4) == 0)
++ if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
+ return -1;
+ if (print)
+ printf("%s", buf);
+@@ -1670,13 +1666,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ { "disassociate", hostapd_cli_cmd_disassociate,
+ hostapd_complete_stations,
+ "<addr> = disassociate a station" },
+-#ifdef CONFIG_TAXONOMY
+ { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
+ "<addr> = get taxonomy signature for a station" },
+-#endif /* CONFIG_TAXONOMY */
+ { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
+ "<addr> = send SA Query to a station" },
+-#ifdef CONFIG_WPS
+ { "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
+ "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
+ { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
+@@ -1701,7 +1694,6 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<SSID> <auth> <encr> <key> = configure AP" },
+ { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
+ "= show current WPS status" },
+-#endif /* CONFIG_WPS */
+ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
+ "= send Disassociation Imminent notification" },
+ { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
+diff --git a/hostapd/main.c b/hostapd/main.c
+index 524a10274..0ccd4a5d7 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -31,7 +31,7 @@
+ #include "config_file.h"
+ #include "eap_register.h"
+ #include "ctrl_iface.h"
+-
++#include "build_features.h"
+
+ struct hapd_global {
+ void **drv_priv;
+@@ -40,6 +40,7 @@ struct hapd_global {
+
+ static struct hapd_global global;
+
++extern int radius_main(int argc, char **argv);
+
+ #ifndef CONFIG_NO_HOSTAPD_LOGGER
+ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+@@ -690,6 +691,11 @@ fail:
+ return -1;
+ }
+
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ #ifdef CONFIG_WPS
+ static int gen_uuid(const char *txt_addr)
+@@ -781,6 +787,11 @@ int main(int argc, char *argv[])
+ if (os_program_init())
+ return -1;
+
++#ifdef RADIUS_SERVER
++ if (strstr(argv[0], "radius"))
++ return radius_main(argc, argv);
++#endif
++
+ os_memset(&interfaces, 0, sizeof(interfaces));
+ interfaces.reload_config = hostapd_reload_config;
+ interfaces.config_read_cb = hostapd_config_read;
+@@ -806,8 +817,10 @@ int main(int argc, char *argv[])
+ return -1;
+ #endif /* CONFIG_DPP */
+
++ wpa_supplicant_event = hostapd_wpa_event;
++ wpa_supplicant_event_global = hostapd_wpa_event_global;
+ for (;;) {
+- c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
++ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
+ if (c < 0)
+ break;
+ switch (c) {
+@@ -844,6 +857,8 @@ int main(int argc, char *argv[])
+ break;
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+ case 'v':
++ if (optarg)
++ exit(!has_feature(optarg));
+ show_version();
+ exit(1);
+ case 'g':
+@@ -1013,6 +1028,7 @@ int main(int argc, char *argv[])
+ }
+
+ hostapd_global_ctrl_iface_init(&interfaces);
++ hostapd_ucode_init(&interfaces);
+
+ if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
+ wpa_printf(MSG_ERROR, "Failed to start eloop");
+@@ -1022,6 +1038,7 @@ int main(int argc, char *argv[])
+ ret = 0;
+
+ out:
++ hostapd_ucode_free();
+ hostapd_global_ctrl_iface_deinit(&interfaces);
+ /* Deinitialize all interfaces */
+ for (i = 0; i < interfaces.count; i++) {
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 28b0ba71c..4c4c750ab 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -467,17 +467,17 @@ static int acs_get_bw_center_chan(int freq, enum bw_type bw)
+ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ {
+ if (!(survey->filled & SURVEY_HAS_NF)) {
++ survey->nf = -95;
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing noise floor",
+ survey->freq);
+- return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
++ survey->channel_time = 0;
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing channel time",
+ survey->freq);
+- return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+@@ -485,7 +485,6 @@ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+ survey->freq);
+- return 0;
+ }
+
+ return 1;
+diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
+index 68443115f..26f11ad98 100644
+--- a/src/ap/airtime_policy.c
++++ b/src/ap/airtime_policy.c
+@@ -112,8 +112,14 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+ {
+ struct sta_info *sta;
+
+- for (sta = hapd->sta_list; sta; sta = sta->next)
+- sta_set_airtime_weight(hapd, sta, weight);
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ unsigned int sta_weight = weight;
++
++ if (sta->dyn_airtime_weight)
++ sta_weight = (weight * sta->dyn_airtime_weight) / 256;
++
++ sta_set_airtime_weight(hapd, sta, sta_weight);
++ }
+ }
+
+
+@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ unsigned int weight;
+
+ if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+- weight = get_weight_for_sta(hapd, sta->addr);
++ if (sta->dyn_airtime_weight)
++ weight = sta->dyn_airtime_weight;
++ else
++ weight = get_weight_for_sta(hapd, sta->addr);
+ if (weight)
+ return sta_set_airtime_weight(hapd, sta, weight);
+ }
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 2330163c4..d10b00be9 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -121,6 +121,7 @@ struct hostapd_ssid {
+ #define DYNAMIC_VLAN_OPTIONAL 1
+ #define DYNAMIC_VLAN_REQUIRED 2
+ int dynamic_vlan;
++ int vlan_no_bridge;
+ #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+ #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+ #define DYNAMIC_VLAN_NAMING_END 2
+@@ -282,6 +283,8 @@ struct airtime_sta_weight {
+ struct hostapd_bss_config {
+ char iface[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
++ char ft_iface[IFNAMSIZ + 1];
++ char snoop_iface[IFNAMSIZ + 1];
+ char vlan_bridge[IFNAMSIZ + 1];
+ char wds_bridge[IFNAMSIZ + 1];
+ int bridge_hairpin; /* hairpin_mode on bridge members */
+@@ -307,6 +310,7 @@ struct hostapd_bss_config {
+ unsigned int eap_sim_db_timeout;
+ int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+ struct hostapd_ip_addr own_ip_addr;
++ int dynamic_own_ip_addr;
+ char *nas_identifier;
+ struct hostapd_radius_servers *radius;
+ int acct_interim_interval;
+@@ -1064,6 +1068,8 @@ struct hostapd_config {
+ unsigned int track_sta_max_num;
+ unsigned int track_sta_max_age;
+
++ int max_num_sta;
++
+ char country[3]; /* first two octets: country code as described in
+ * ISO/IEC 3166-1. Third octet:
+ * ' ' (ascii 32): all environments
+@@ -1101,6 +1107,8 @@ struct hostapd_config {
+
+ int ht_op_mode_fixed;
+ u16 ht_capab;
++ int noscan;
++ int no_ht_coex;
+ int ieee80211n;
+ int secondary_channel;
+ int no_pri_sec_switch;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 32722084d..527b2c984 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -387,8 +387,6 @@ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ return -1;
+ if (hapd->conf->wds_bridge[0])
+ bridge = hapd->conf->wds_bridge;
+- else if (hapd->conf->bridge[0])
+- bridge = hapd->conf->bridge;
+ return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ bridge, ifname_wds);
+ }
+@@ -1031,7 +1029,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+ {
+- if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
++ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
++ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
+ return 0;
+ return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+ qos_map_set_len);
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index d7e79c840..f8a8725be 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -371,12 +371,12 @@ static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+
+ static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+ enum drv_br_net_param param,
+- unsigned int val)
++ const char *ifname, unsigned int val)
+ {
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_set_net_param == NULL)
+ return -1;
+- return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
++ return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
+ }
+
+ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+@@ -404,6 +404,23 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+ return hapd->driver->stop_ap(hapd->drv_priv, link_id);
+ }
+
++static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
++ enum wpa_driver_if_type type,
++ const char *ifname,
++ const char *new_name)
++{
++ if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
++ return -1;
++ return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
++}
++
++static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
++ return 0;
++ return hapd->driver->set_first_bss(hapd->drv_priv);
++}
++
+ static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+ struct wpa_channel_info *ci)
+ {
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 4354dfae3..26453cb2c 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -1343,6 +1343,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ int mld_id;
+ u16 links;
+ #endif /* CONFIG_IEEE80211BE */
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_PROBE_REQ,
++ .mgmt_frame = mgmt,
++ .ssi_signal = ssi_signal,
++ .elems = &elems,
++ };
+
+ if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
+ ssi_signal < hapd->iconf->rssi_ignore_probe_request)
+@@ -1529,6 +1535,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ }
+ #endif /* CONFIG_P2P */
+
++ if (hostapd_ubus_handle_event(hapd, &req)) {
++ wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
++ MAC2STR(mgmt->sa));
++ return;
++ }
++
+ /* TODO: verify that supp_rates contains at least one matching rate
+ * with AP configuration */
+
+@@ -1547,7 +1559,7 @@ void handle_probe_req(struct hostapd_data *hapd,
+ if (hapd->conf->no_probe_resp_if_max_sta &&
+ is_multicast_ether_addr(mgmt->da) &&
+ is_multicast_ether_addr(mgmt->bssid) &&
+- hapd->num_sta >= hapd->conf->max_num_sta &&
++ hostapd_check_max_sta(hapd) &&
+ !ap_get_sta(hapd, mgmt->sa)) {
+ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ " since no room for additional STA",
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 2cfef4bd4..cd7db4fc6 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -26,6 +26,26 @@
+ #include "taxonomy.h"
+ #include "wnm_ap.h"
+
++static const char * hw_mode_str(enum hostapd_hw_mode mode)
++{
++ switch (mode) {
++ case HOSTAPD_MODE_IEEE80211B:
++ return "b";
++ case HOSTAPD_MODE_IEEE80211G:
++ return "g";
++ case HOSTAPD_MODE_IEEE80211A:
++ return "a";
++ case HOSTAPD_MODE_IEEE80211AD:
++ return "ad";
++ case HOSTAPD_MODE_IEEE80211ANY:
++ return "any";
++ case NUM_HOSTAPD_MODES:
++ return "invalid";
++ }
++ return "unknown";
++}
++
++#ifdef CONFIG_CTRL_IFACE_MIB
+
+ static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+ size_t curr_len, const u8 *mcs_set)
+@@ -212,26 +232,6 @@ static const char * timeout_next_str(int val)
+ }
+
+
+-static const char * hw_mode_str(enum hostapd_hw_mode mode)
+-{
+- switch (mode) {
+- case HOSTAPD_MODE_IEEE80211B:
+- return "b";
+- case HOSTAPD_MODE_IEEE80211G:
+- return "g";
+- case HOSTAPD_MODE_IEEE80211A:
+- return "a";
+- case HOSTAPD_MODE_IEEE80211AD:
+- return "ad";
+- case HOSTAPD_MODE_IEEE80211ANY:
+- return "any";
+- case NUM_HOSTAPD_MODES:
+- return "invalid";
+- }
+- return "unknown";
+-}
+-
+-
+ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+@@ -539,6 +539,7 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+ return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+ }
+
++#endif
+
+ #ifdef CONFIG_P2P_MANAGER
+ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+@@ -987,12 +988,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ return len;
+ len += ret;
+ }
+-
++#ifdef CONFIG_CTRL_IFACE_MIB
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ mode->mcs_set);
+ }
+-
++#endif /* CONFIG_CTRL_IFACE_MIB */
+ if (iface->current_rates && iface->num_rates) {
+ ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+ if (os_snprintf_error(buflen - len, ret))
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index fc2e8d83c..d14fad136 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -17,6 +17,7 @@
+ #include "ap_drv_ops.h"
+ #include "drivers/driver.h"
+ #include "dfs.h"
++#include "crypto/crypto.h"
+
+
+ enum dfs_channel_type {
+@@ -526,9 +527,14 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ int num_available_chandefs;
+ int chan_idx, chan_idx2;
+ int sec_chan_idx_80p80 = -1;
++ bool is_mesh = false;
+ int i;
+ u32 _rand;
+
++#ifdef CONFIG_MESH
++ is_mesh = iface->mconf;
++#endif
++
+ wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+ *secondary_channel = 0;
+ *oper_centr_freq_seg0_idx = 0;
+@@ -548,8 +554,20 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ if (num_available_chandefs == 0)
+ return NULL;
+
+- if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
++ /* try to use deterministic channel in mesh, so that both sides
++ * have a chance to switch to the same channel */
++ if (is_mesh) {
++#ifdef CONFIG_MESH
++ u64 hash[4];
++ const u8 *meshid[1] = { &iface->mconf->meshid[0] };
++ const size_t meshid_len = iface->mconf->meshid_len;
++
++ sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
++ _rand = hash[0] + hash[1] + hash[2] + hash[3];
++#endif
++ } else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ return NULL;
++
+ chan_idx = _rand % num_available_chandefs;
+ wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ chan_idx, num_available_chandefs);
+@@ -1207,6 +1225,8 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
++ hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
++
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index dc21977ff..e8796f709 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -268,6 +268,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ struct hostapd_iface *iface = hapd->iface;
+ #endif /* CONFIG_OWE */
+ bool updated = false;
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_ASSOC_REQ,
++ .addr = addr,
++ };
+
+ if (addr == NULL) {
+ /*
+@@ -412,6 +416,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ goto fail;
+ }
+
++ if (hostapd_ubus_handle_event(hapd, &req)) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++ MAC2STR(req.addr));
++ goto fail;
++ }
++
+ #ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+@@ -2342,8 +2352,8 @@ static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+ }
+
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data)
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data)
+ {
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+@@ -2675,7 +2685,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+
+
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+ {
+ struct hapd_interfaces *interfaces = ctx;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index b899c9831..7959859b0 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -247,6 +247,29 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+ return 0;
+ }
+
++static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
++{
++ int num_sta = 0;
++ int i;
++
++ for (i = 0; i < iface->num_bss; i++)
++ num_sta += iface->bss[i]->num_sta;
++
++ return num_sta;
++}
++
++
++int hostapd_check_max_sta(struct hostapd_data *hapd)
++{
++ if (hapd->num_sta >= hapd->conf->max_num_sta)
++ return 1;
++
++ if (hapd->iconf->max_num_sta &&
++ hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
++ return 1;
++
++ return 0;
++}
+
+ int hostapd_reload_config(struct hostapd_iface *iface)
+ {
+@@ -255,6 +278,8 @@ int hostapd_reload_config(struct hostapd_iface *iface)
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+
++ hostapd_ucode_reload_bss(hapd);
++
+ if (iface->config_fname == NULL) {
+ /* Only in-memory config in use - assume it has been updated */
+ hostapd_clear_old(iface);
+@@ -475,6 +500,8 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ hapd->beacon_set_done = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
++ hostapd_ucode_free_bss(hapd);
++ hostapd_ubus_free_bss(hapd);
+ accounting_deinit(hapd);
+ hostapd_deinit_wpa(hapd);
+ vlan_deinit(hapd);
+@@ -685,6 +712,7 @@ static void sta_track_deinit(struct hostapd_iface *iface)
+ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+ {
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
++ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ #ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+ #endif /* NEED_AP_MLME */
+@@ -714,7 +742,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+ {
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+- eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
++ hostapd_ucode_free_iface(iface);
+ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ NULL);
+
+@@ -1303,6 +1331,9 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ if (hapd->driver && hapd->driver->set_operstate)
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
++ hostapd_ubus_add_bss(hapd);
++ hostapd_ucode_add_bss(hapd);
++
+ return 0;
+ }
+
+@@ -1324,8 +1355,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+-static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+- bool start_beacon)
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ {
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 ssid[SSID_MAX_LEN + 1];
+@@ -1552,6 +1582,7 @@ setup_mld:
+
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = conf->radius_das_port;
++ das_conf.nas_identifier = conf->nas_identifier;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ conf->radius_das_shared_secret_len;
+@@ -1627,6 +1658,7 @@ setup_mld:
+ wpa_printf(MSG_ERROR, "GAS server initialization failed");
+ return -1;
+ }
++#endif /* CONFIG_INTERWORKING */
+
+ if (conf->qos_map_set_len &&
+ hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
+@@ -1634,7 +1666,6 @@ setup_mld:
+ wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
+ return -1;
+ }
+-#endif /* CONFIG_INTERWORKING */
+
+ if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+ wpa_printf(MSG_ERROR, "BSS Load initialization failed");
+@@ -2447,6 +2478,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ if (err)
+ goto fail;
+
++ hostapd_ubus_add_iface(iface);
+ wpa_printf(MSG_DEBUG, "Completing interface initialization");
+ if (iface->freq) {
+ #ifdef NEED_AP_MLME
+@@ -2676,6 +2708,7 @@ dfs_offload:
+
+ fail:
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
++ hostapd_ubus_free_iface(iface);
+
+ if (iface->is_no_ir) {
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+@@ -2875,7 +2908,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ }
+
+
+-static void hostapd_bss_deinit(struct hostapd_data *hapd)
++void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
+ if (!hapd)
+ return;
+@@ -3395,6 +3428,7 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+ (unsigned int) iface->conf->num_bss);
+ driver = iface->bss[0]->driver;
+ drv_priv = iface->bss[0]->drv_priv;
++ hostapd_ubus_free_iface(iface);
+ hostapd_interface_deinit(iface);
+ wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ __func__, driver, drv_priv);
+@@ -3926,7 +3960,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ hapd_iface = interfaces->iface[i];
+ if (hapd_iface == NULL)
+ return -1;
+- if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
++ if (!os_strcmp(hapd_iface->phy, buf) ||
++ !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
+ wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+@@ -3972,6 +4007,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc)
+ {
++ int mld_assoc_link_id = -1;
++
+ if (hapd->tkip_countermeasures) {
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+@@ -3983,6 +4020,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ sta->mld_assoc_link_id != hapd->mld_link_id)
+ return;
+ #endif /* CONFIG_IEEE80211BE */
++ if (mld_assoc_link_id != -2)
++ hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
+
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ sta->post_csa_sa_query = 0;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 594866fbb..1e4113459 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -13,11 +13,14 @@
+ #include <sqlite3.h>
+ #endif /* CONFIG_SQLITE */
+
++#include "ap/sta_info.h"
+ #include "common/defs.h"
+ #include "common/dpp.h"
+ #include "utils/list.h"
+ #include "ap_config.h"
+ #include "drivers/driver.h"
++#include "ubus.h"
++#include "ucode.h"
+
+ #define OCE_STA_CFON_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_STA_CFON) && \
+@@ -51,6 +54,10 @@ struct hapd_interfaces {
+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
++ int (*ctrl_iface_recv)(struct hostapd_data *hapd,
++ char *buf, char *reply, int reply_size,
++ struct sockaddr_storage *from,
++ socklen_t fromlen);
+ int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+@@ -167,6 +174,21 @@ struct hostapd_sae_commit_queue {
+ u8 msg[];
+ };
+
++/**
++ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
++ */
++struct hostapd_openwrt_stats {
++ struct {
++ u64 neighbor_report_tx;
++ } rrm;
++
++ struct {
++ u64 bss_transition_query_rx;
++ u64 bss_transition_request_tx;
++ u64 bss_transition_response_rx;
++ } wnm;
++};
++
+ /**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+@@ -174,6 +196,8 @@ struct hostapd_data {
+ struct hostapd_iface *iface;
+ struct hostapd_config *iconf;
+ struct hostapd_bss_config *conf;
++ struct hostapd_ubus_bss ubus;
++ struct hostapd_ucode_bss ucode;
+ int interface_added; /* virtual interface added for this BSS */
+ unsigned int started:1;
+ unsigned int disabled:1;
+@@ -181,6 +205,9 @@ struct hostapd_data {
+
+ u8 own_addr[ETH_ALEN];
+
++ /* OpenWrt specific statistics */
++ struct hostapd_openwrt_stats openwrt_stats;
++
+ int num_sta; /* number of entries in sta_list */
+ struct sta_info *sta_list; /* STA info list head */
+ #define STA_HASH_SIZE 256
+@@ -523,6 +550,7 @@ struct hostapd_mld {
+ */
+ struct hostapd_iface {
+ struct hapd_interfaces *interfaces;
++ struct hostapd_ucode_iface ucode;
+ void *owner;
+ char *config_fname;
+ struct hostapd_config *conf;
+@@ -745,6 +773,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_bss_config *bss);
+ int hostapd_setup_interface(struct hostapd_iface *iface);
+ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
++void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
+ void hostapd_interface_deinit(struct hostapd_iface *iface);
+ void hostapd_interface_free(struct hostapd_iface *iface);
+ struct hostapd_iface * hostapd_alloc_iface(void);
+@@ -753,6 +782,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ struct hostapd_iface *
+ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug);
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
++void hostapd_bss_deinit(struct hostapd_data *hapd);
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+@@ -780,6 +811,7 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+ void hostapd_periodic_iface(struct hostapd_iface *iface);
+ int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
+ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
++int hostapd_check_max_sta(struct hostapd_data *hapd);
+
+ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+ void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+@@ -865,4 +897,14 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+
+ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
+
++static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++#ifdef CONFIG_IEEE80211BE
++ return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
++#else /* CONFIG_IEEE80211BE */
++ return false;
++#endif /* CONFIG_IEEE80211BE */
++}
++
+ #endif /* HOSTAPD_H */
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 222f3dc05..672e43a10 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -546,7 +546,8 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+ int ret;
+
+ /* Check that HT40 is used and PRI / SEC switch is allowed */
+- if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
++ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
++ iface->conf->noscan)
+ return 0;
+
+ hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 179af5e28..bda61b998 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -2804,7 +2804,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ u16 auth_alg, auth_transaction, status_code;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct sta_info *sta = NULL;
+- int res, reply_res;
++ int res, reply_res, ubus_resp;
+ u16 fc;
+ const u8 *challenge = NULL;
+ u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+@@ -2815,6 +2815,11 @@ static void handle_auth(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ bool mld_sta = false;
+ #endif /* CONFIG_IEEE80211BE */
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_AUTH_REQ,
++ .mgmt_frame = mgmt,
++ .ssi_signal = rssi,
++ };
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+@@ -3008,6 +3013,13 @@ static void handle_auth(struct hostapd_data *hapd,
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
++ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++ if (ubus_resp) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
++ MAC2STR(mgmt->sa));
++ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
+ if (res == HOSTAPD_ACL_PENDING)
+ return;
+
+@@ -3042,15 +3054,6 @@ static void handle_auth(struct hostapd_data *hapd,
+ seq_ctrl);
+ return;
+ }
+-#ifdef CONFIG_MESH
+- if ((hapd->conf->mesh & MESH_ENABLED) &&
+- sta->plink_state == PLINK_BLOCKED) {
+- wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+- " is blocked - drop Authentication frame",
+- MAC2STR(sa));
+- return;
+- }
+-#endif /* CONFIG_MESH */
+ #ifdef CONFIG_PASN
+ if (auth_alg == WLAN_AUTH_PASN &&
+ (sta->flags & WLAN_STA_ASSOC)) {
+@@ -4698,6 +4701,13 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ * drivers to accept the STA parameter configuration. Since this is
+ * after a new FT-over-DS exchange, a new TK has been derived, so key
+ * reinstallation is not a concern for this case.
++ *
++ * If the STA was associated and authorized earlier, but came for a new
++ * connection (!added_unassoc + !reassoc), remove the existing STA entry
++ * so that it can be re-added. This case is rarely seen when the AP could
++ * not receive the deauth/disassoc frame from the STA. And the STA comes
++ * back with new connection within a short period or before the inactive
++ * STA entry is removed from the list.
+ */
+ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+ " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+@@ -4711,7 +4721,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ (!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
+ (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+- !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
++ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
++ (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
+ set = 0;
+@@ -5273,7 +5284,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ int resp = WLAN_STATUS_SUCCESS;
+ u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ const u8 *pos;
+- int left, i;
++ int left, i, ubus_resp;
+ struct sta_info *sta;
+ u8 *tmp = NULL;
+ #ifdef CONFIG_FILS
+@@ -5515,6 +5526,11 @@ static void handle_assoc(struct hostapd_data *hapd,
+ left = res;
+ }
+ #endif /* CONFIG_FILS */
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_ASSOC_REQ,
++ .mgmt_frame = mgmt,
++ .ssi_signal = rssi,
++ };
+
+ /* followed by SSID and Supported rates; and HT capabilities if 802.11n
+ * is used */
+@@ -5617,6 +5633,13 @@ static void handle_assoc(struct hostapd_data *hapd,
+ if (set_beacon)
+ ieee802_11_set_beacons(hapd->iface);
+
++ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++ if (ubus_resp) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++ MAC2STR(mgmt->sa));
++ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
+ fail:
+
+ /*
+@@ -5848,6 +5871,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
+ (unsigned long) len);
+ return;
+ }
++ hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+@@ -5879,6 +5903,8 @@ static void handle_deauth(struct hostapd_data *hapd,
+ /* Clear the PTKSA cache entries for PASN */
+ ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
+
++ hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
++
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
+index f90f1254e..7f0a00f95 100644
+--- a/src/ap/ieee802_11_ht.c
++++ b/src/ap/ieee802_11_ht.c
+@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ struct ieee80211_ht_operation *oper;
++ le32 vht_capabilities_info;
+ u8 *pos = eid;
++ u8 chwidth;
+
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+ is_6ghz_op_class(hapd->iconf->op_class))
+@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+
++ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
++ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
++ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++ oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
++ }
++
+ pos += sizeof(*oper);
+
+ return pos;
+@@ -230,6 +239,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ return;
+ }
+
++ if (iface->conf->noscan || iface->conf->no_ht_coex)
++ return;
++
+ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore too short 20/40 BSS Coexistence Management frame");
+@@ -390,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return;
+
++ if (iface->conf->noscan || iface->conf->no_ht_coex)
++ return;
++
+ wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+ " in Association Request", MAC2STR(sta->addr));
+
+diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
+index a5716f037..85790c7ed 100644
+--- a/src/ap/ieee802_11_shared.c
++++ b/src/ap/ieee802_11_shared.c
+@@ -1138,13 +1138,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
+ u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+ {
+-#ifdef CONFIG_INTERWORKING
+ /* check for QoS Map support */
+ if (ext_capab_ie_len >= 5) {
+ if (ext_capab_ie[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+-#endif /* CONFIG_INTERWORKING */
+
+ if (ext_capab_ie_len > 0) {
+ sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
+index 4dc325ce8..68880ab64 100644
+--- a/src/ap/ieee802_11_vht.c
++++ b/src/ap/ieee802_11_vht.c
+@@ -26,6 +26,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ struct ieee80211_vht_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ u8 *pos = eid;
++ u8 chwidth;
+
+ if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+@@ -63,6 +64,17 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ }
+
++ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++ if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++ cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
++ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
++ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
++ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
++ } else {
++ cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
++ }
++
+ /* Supported MCS set comes from hw */
+ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+
+@@ -75,6 +87,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ struct ieee80211_vht_operation *oper;
++ le32 vht_capabilities_info;
+ u8 *pos = eid;
+ enum oper_chan_width oper_chwidth =
+ hostapd_get_oper_chwidth(hapd->iconf);
+@@ -110,6 +123,7 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
+
+ oper->vht_op_info_chwidth = oper_chwidth;
++ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
+ /*
+ * Convert 160 MHz channel width to new style as interop
+@@ -123,6 +137,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
+ else
+ oper->vht_op_info_chan_center_freq_seg0_idx += 8;
++
++ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++ oper->vht_op_info_chan_center_freq_seg1_idx = 0;
+ } else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
+ /*
+ * Convert 80+80 MHz channel width to new style as interop
+diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
+index 8e98b6521..8abebbf34 100644
+--- a/src/ap/ieee802_1x.c
++++ b/src/ap/ieee802_1x.c
+@@ -600,6 +600,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *attr;
+ int len;
+
++ if (hapd->conf->dynamic_own_ip_addr)
++ radius_client_get_local_addr(hapd->radius,
++ &hapd->conf->own_ip_addr);
++
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IP_ADDRESS) &&
+ hapd->conf->own_ip_addr.af == AF_INET &&
+@@ -2845,6 +2849,7 @@ static const char * bool_txt(bool val)
+ return val ? "TRUE" : "FALSE";
+ }
+
++#ifdef CONFIG_CTRL_IFACE_MIB
+
+ int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+ {
+@@ -3031,6 +3036,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ return len;
+ }
+
++#endif
+
+ #ifdef CONFIG_HS20
+ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
+diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
+index 788c12fdc..bc1eb6251 100644
+--- a/src/ap/ndisc_snoop.c
++++ b/src/ap/ndisc_snoop.c
+@@ -61,6 +61,7 @@ void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+ dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+ list) {
+ hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
++ dl_list_del(&ip6addr->list);
+ os_free(ip6addr);
+ }
+ }
+diff --git a/src/ap/rrm.c b/src/ap/rrm.c
+index f2d5cd16e..8220590a0 100644
+--- a/src/ap/rrm.c
++++ b/src/ap/rrm.c
+@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+ MAC2STR(addr), token, rep_mode, report);
++ if (len < sizeof(struct rrm_measurement_beacon_report))
++ return;
++ hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
+ }
+
+
+@@ -269,6 +272,8 @@ static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+ }
+ }
+
++ hapd->openwrt_stats.rrm.neighbor_report_tx++;
++
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+@@ -350,6 +355,9 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+ mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+
+ switch (mgmt->u.action.u.rrm.action) {
++ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
++ hostapd_ubus_handle_link_measurement(hapd, buf, len);
++ break;
+ case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+ hostapd_handle_radio_msmt_report(hapd, buf, len);
+ break;
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index d483aa9d3..ee6e20538 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -539,6 +539,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "local deauth request");
++ hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
+ ap_free_sta(hapd, sta);
+ return;
+ }
+@@ -694,6 +695,7 @@ skip_poll:
+ mlme_deauthenticate_indication(
+ hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
++ hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
+ ap_free_sta(hapd, sta);
+ break;
+ }
+@@ -1476,9 +1478,6 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ mld_assoc_link_id = -2;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+- if (mld_assoc_link_id != -2)
+- hostapd_prune_associations(hapd, sta->addr,
+- mld_assoc_link_id);
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ } else {
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+@@ -1515,15 +1514,28 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+
+ if (authorized) {
++ static const char * const auth_algs[] = {
++ [WLAN_AUTH_OPEN] = "open",
++ [WLAN_AUTH_SHARED_KEY] = "shared",
++ [WLAN_AUTH_FT] = "ft",
++ [WLAN_AUTH_SAE] = "sae",
++ [WLAN_AUTH_FILS_SK] = "fils-sk",
++ [WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
++ [WLAN_AUTH_FILS_PK] = "fils-pk",
++ [WLAN_AUTH_PASN] = "pasn",
++ };
++ const char *auth_alg = NULL;
+ const u8 *dpp_pkhash;
+ const char *keyid;
+ char dpp_pkhash_buf[100];
+ char keyid_buf[100];
+ char ip_addr[100];
++ char alg_buf[100];
+
+ dpp_pkhash_buf[0] = '\0';
+ keyid_buf[0] = '\0';
+ ip_addr[0] = '\0';
++ alg_buf[0] = '\0';
+ #ifdef CONFIG_P2P
+ if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+ os_snprintf(ip_addr, sizeof(ip_addr),
+@@ -1534,6 +1546,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ }
+ #endif /* CONFIG_P2P */
+
++ if (sta->auth_alg < ARRAY_SIZE(auth_algs))
++ auth_alg = auth_algs[sta->auth_alg];
++
++ if (auth_alg)
++ os_snprintf(alg_buf, sizeof(alg_buf),
++ " auth_alg=%s", auth_alg);
++
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ os_snprintf(keyid_buf, sizeof(keyid_buf),
+@@ -1552,17 +1571,19 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ dpp_pkhash, SHA256_MAC_LEN);
+ }
+
+- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+- buf, ip_addr, keyid_buf, dpp_pkhash_buf);
++ hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
++ buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
+
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+- AP_STA_CONNECTED "%s%s%s%s",
++ AP_STA_CONNECTED "%s%s%s%s%s",
+ buf, ip_addr, keyid_buf,
+- dpp_pkhash_buf);
++ dpp_pkhash_buf, alg_buf);
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
++ hostapd_ubus_notify(hapd, "disassoc", sta->addr);
+
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 153e4a000..38b80903d 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -17,7 +17,6 @@
+ #include "common/sae.h"
+ #include "crypto/sha384.h"
+ #include "pasn/pasn_common.h"
+-#include "hostapd.h"
+
+ /* STA flags */
+ #define WLAN_STA_AUTH BIT(0)
+@@ -323,6 +322,7 @@ struct sta_info {
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #ifdef CONFIG_AIRTIME_POLICY
+ unsigned int airtime_weight;
++ unsigned int dyn_airtime_weight;
+ struct os_reltime backlogged_until;
+ #endif /* CONFIG_AIRTIME_POLICY */
+
+@@ -420,16 +420,6 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
+
+ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+
+-static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
+- struct sta_info *sta)
+-{
+-#ifdef CONFIG_IEEE80211BE
+- return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
+-#else /* CONFIG_IEEE80211BE */
+- return false;
+-#endif /* CONFIG_IEEE80211BE */
+-}
+-
+ static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
+ {
+ #ifdef CONFIG_IEEE80211BE
+diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
+index 19aa3c649..053d6338e 100644
+--- a/src/ap/vlan_full.c
++++ b/src/ap/vlan_full.c
+@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ if (!vlan)
+ return;
+
++ if (hapd->conf->ssid.vlan_no_bridge)
++ goto out;
++
+ vlan->configured = 1;
+
+ notempty = vlan->vlan_desc.notempty;
+@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ ifname, br_name, tagged[i], hapd);
+ }
+
++out:
+ ifconfig_up(ifname);
+ }
+
+diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
+index 53eacfb45..b69f3de41 100644
+--- a/src/ap/vlan_init.c
++++ b/src/ap/vlan_init.c
+@@ -22,6 +22,7 @@
+ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ int existsok)
+ {
++ bool vlan_exists = iface_exists(vlan->ifname);
+ int ret;
+ #ifdef CONFIG_WEP
+ int i;
+@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ }
+ #endif /* CONFIG_WEP */
+
+- if (!iface_exists(vlan->ifname))
++ if (!vlan_exists)
+ ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+ else if (!existsok)
+ return -1;
+@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ if (hapd->wpa_auth)
+ ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
++ if (!ret && !vlan_exists)
++ hostapd_ubus_add_vlan(hapd, vlan);
++
+ if (ret == 0)
+ return ret;
+
+@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+ "WPA deinitialization for VLAN %d failed (%d)",
+ vlan->vlan_id, ret);
+
++ hostapd_ubus_remove_vlan(hapd, vlan);
++
+ return hostapd_vlan_if_remove(hapd, vlan->ifname);
+ }
+
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index af8cccaef..d259200c9 100644
+--- a/src/ap/wnm_ap.c
++++ b/src/ap/wnm_ap.c
+@@ -410,6 +410,7 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
++ 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 "
+ "validity_interval=%u",
+@@ -478,7 +479,8 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+ MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+ os_free(hex);
+
+- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
++ if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
++ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+ }
+
+
+@@ -500,7 +502,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ size_t len)
+ {
+ u8 dialog_token, status_code, bss_termination_delay;
+- const u8 *pos, *end;
++ const u8 *pos, *end, *target_bssid = NULL;
+ int enabled = hapd->conf->bss_transition;
+ struct sta_info *sta;
+
+@@ -547,6 +549,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+ return;
+ }
++ target_bssid = pos;
+ sta->agreed_to_steer = 1;
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+@@ -566,6 +569,10 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ MAC2STR(addr), status_code, bss_termination_delay);
+ }
+
++ hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
++ status_code, bss_termination_delay,
++ target_bssid, pos, end - pos);
++
+ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ pos, end - pos);
+ }
+@@ -814,10 +821,12 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ plen);
+ return 0;
+ case WNM_BSS_TRANS_MGMT_QUERY:
++ hapd->openwrt_stats.wnm.bss_transition_query_rx++;
+ ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+ case WNM_BSS_TRANS_MGMT_RESP:
++ hapd->openwrt_stats.wnm.bss_transition_response_rx++;
+ ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+@@ -865,6 +874,7 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+ MACSTR, disassoc_timer, MAC2STR(sta->addr));
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+@@ -947,6 +957,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ return -1;
+ }
+
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ if (disassoc_timer) {
+ /* send disassociation frame after time-out */
+ set_disassoc_timer(hapd, sta, disassoc_timer);
+@@ -1028,6 +1039,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+ os_free(buf);
+
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ if (disassoc_timer) {
+ #ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 7a07dcc4c..b23d75444 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -5865,6 +5865,7 @@ static const char * wpa_bool_txt(int val)
+ return val ? "TRUE" : "FALSE";
+ }
+
++#ifdef CONFIG_CTRL_IFACE_MIB
+
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+@@ -6017,7 +6018,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+
+ return len;
+ }
+-
++#endif
+
+ void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+ {
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index 1726c7201..5a9ec6975 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -275,6 +275,7 @@ static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+ struct hostapd_data *hapd = ctx;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+ MAC2STR(addr));
++ hostapd_ubus_notify(hapd, "key-mismatch", addr);
+ }
+
+
+@@ -1812,8 +1813,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+ wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+ const char *ft_iface;
+
+- ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+- hapd->conf->iface;
++ if (hapd->conf->ft_iface[0])
++ ft_iface = hapd->conf->ft_iface;
++ else if (hapd->conf->bridge[0])
++ ft_iface = hapd->conf->bridge;
++ else
++ ft_iface = hapd->conf->iface;
+ hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
+ hostapd_rrb_receive, hapd, 1);
+ if (!hapd->l2) {
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index 82d4d5fdd..dfc5c3ecb 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+ bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+ else
+ bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+- }
+ #ifndef CONFIG_NO_TKIP
+- if (cred->encr_type & WPS_ENCR_TKIP)
++ } else if (cred->encr_type & WPS_ENCR_TKIP)
+ bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+ #endif /* CONFIG_NO_TKIP */
+ bss->rsn_pairwise = bss->wpa_pairwise;
+@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
+ WPA_CIPHER_GCMP_256)) {
+ wps->encr_types |= WPS_ENCR_AES;
+ wps->encr_types_rsn |= WPS_ENCR_AES;
+- }
+- if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
++ } else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+ #ifdef CONFIG_NO_TKIP
+ wpa_printf(MSG_INFO, "WPS: TKIP not supported");
+ goto fail;
+diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
+index 029f4de23..4c20f137f 100644
+--- a/src/ap/x_snoop.c
++++ b/src/ap/x_snoop.c
+@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *hapd)
+
+ hapd->x_snoop_initialized = true;
+
+- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
++ if (!conf->snoop_iface[0] &&
++ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+ 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable hairpin_mode on the bridge port");
+ return -1;
+ }
+
+- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
++ if (!conf->snoop_iface[0] &&
++ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable proxyarp on the bridge port");
+ return -1;
+ }
+
+ if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+- 1)) {
++ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+ return -1;
+ }
+
+ #ifdef CONFIG_IPV6
+- if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
++ if (!conf->snoop_iface[0] &&
++ hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable multicast snooping on the bridge");
+ return -1;
+@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ {
+ struct hostapd_bss_config *conf = hapd->conf;
+ struct l2_packet_data *l2;
++ const char *ifname = conf->bridge;
++
++ if (conf->snoop_iface[0])
++ ifname = conf->snoop_iface;
+
+- l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
++ l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
+ if (l2 == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to initialize L2 packet processing %s",
+@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+
+ void x_snoop_deinit(struct hostapd_data *hapd)
+ {
++ struct hostapd_bss_config *conf = hapd->conf;
++
+ if (!hapd->x_snoop_initialized)
+ return;
+- hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
++ hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
++ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+ hapd->x_snoop_initialized = false;
+diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
+index f17f95a2c..39d39f429 100644
+--- a/src/common/dpp_crypto.c
++++ b/src/common/dpp_crypto.c
+@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
+
+ struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
+ {
++ if (curve == NULL) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: %s curve must be initialized", __func__);
++ return NULL;
++ }
++
+ struct crypto_ec_key *key;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+@@ -1582,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
+ Pr = crypto_ec_key_get_public_key(Pr_key);
+ Qr = crypto_ec_point_init(ec);
+ hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
+- if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
++ if (!Pr || !Qr || !hash_bn ||
++ crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
++ crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
+ goto fail;
+
+ if (crypto_ec_point_is_at_infinity(ec, Qr)) {
+diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
+index 2c47bf812..8bd6e994d 100644
+--- a/src/common/hw_features_common.c
++++ b/src/common/hw_features_common.c
+@@ -898,6 +898,7 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+ VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+ VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
++ VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
+
+ #undef VHT_CAP_CHECK
+ #undef VHT_CAP_CHECK_MAX
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index 5b39a61e1..7a1da3252 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -1397,6 +1397,8 @@ struct ieee80211_ampe_ie {
+ #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27))
+ #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28))
+ #define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT ((u32) BIT(30))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK ((u32) BIT(30) | BIT(31))
+
+ #define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1))
+ #define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \
+diff --git a/src/common/sae.c b/src/common/sae.c
+index f1c164e13..05de737e5 100644
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
+ static int sae_derive_commit_element_ecc(struct sae_data *sae,
+ struct crypto_bignum *mask)
+ {
++ if (sae->tmp->pwe_ecc == NULL) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: %s sae->tmp->pwe_ecc must be initialized",
++ __func__);
++ return -1;
++ }
++
+ /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+ if (!sae->tmp->own_commit_element_ecc) {
+ sae->tmp->own_commit_element_ecc =
+diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
+index 6ea3311ce..7a608c30e 100644
+--- a/src/common/wpa_common.c
++++ b/src/common/wpa_common.c
+@@ -2856,6 +2856,31 @@ u32 wpa_akm_to_suite(int akm)
+ }
+
+
++static void wpa_fixup_wpa_ie_rsn(u8 *assoc_ie, const u8 *wpa_msg_ie,
++ size_t rsn_ie_len)
++{
++ int pos, count;
++
++ pos = sizeof(struct rsn_ie_hdr) + RSN_SELECTOR_LEN;
++ if (rsn_ie_len < pos + 2)
++ return;
++
++ count = WPA_GET_LE16(wpa_msg_ie + pos);
++ pos += 2 + count * RSN_SELECTOR_LEN;
++ if (rsn_ie_len < pos + 2)
++ return;
++
++ count = WPA_GET_LE16(wpa_msg_ie + pos);
++ pos += 2 + count * RSN_SELECTOR_LEN;
++ if (rsn_ie_len < pos + 2)
++ return;
++
++ if (!assoc_ie[pos] && !assoc_ie[pos + 1] &&
++ (wpa_msg_ie[pos] || wpa_msg_ie[pos + 1]))
++ memcpy(&assoc_ie[pos], &wpa_msg_ie[pos], 2);
++}
++
++
+ int wpa_compare_rsn_ie(int ft_initial_assoc,
+ const u8 *ie1, size_t ie1len,
+ const u8 *ie2, size_t ie2len)
+@@ -2863,8 +2888,19 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
+ if (ie1 == NULL || ie2 == NULL)
+ return -1;
+
+- if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
+- return 0; /* identical IEs */
++ if (ie1len == ie2len) {
++ u8 *ie_tmp;
++
++ if (os_memcmp(ie1, ie2, ie1len) == 0)
++ return 0; /* identical IEs */
++
++ ie_tmp = alloca(ie1len);
++ memcpy(ie_tmp, ie1, ie1len);
++ wpa_fixup_wpa_ie_rsn(ie_tmp, ie2, ie1len);
++
++ if (os_memcmp(ie_tmp, ie2, ie1len) == 0)
++ return 0; /* only mismatch in RSN capabilties */
++ }
+
+ #ifdef CONFIG_IEEE80211R
+ if (ft_initial_assoc) {
+diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
+index 7e197f094..791fdbf93 100644
+--- a/src/common/wpa_ctrl.c
++++ b/src/common/wpa_ctrl.c
+@@ -135,7 +135,7 @@ try_again:
+ return NULL;
+ }
+ tries++;
+-#ifdef ANDROID
++
+ /* Set client socket file permissions so that bind() creates the client
+ * socket with these permissions and there is no need to try to change
+ * them with chmod() after bind() which would have potential issues with
+@@ -147,7 +147,7 @@ try_again:
+ * operations to allow the response to go through. Those are using the
+ * no-deference-symlinks version to avoid races. */
+ fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+-#endif /* ANDROID */
++
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local)) < 0) {
+ if (errno == EADDRINUSE && tries < 2) {
+@@ -165,7 +165,11 @@ try_again:
+ return NULL;
+ }
+
+-#ifdef ANDROID
++#ifndef ANDROID
++ /* Set group even if we do not have privileges to change owner */
++ lchown(ctrl->local.sun_path, -1, 101);
++ lchown(ctrl->local.sun_path, 101, 101);
++#else
+ /* Set group even if we do not have privileges to change owner */
+ lchown(ctrl->local.sun_path, -1, AID_WIFI);
+ lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+diff --git a/src/crypto/Makefile b/src/crypto/Makefile
+index ce0997091..96bac9476 100644
+--- a/src/crypto/Makefile
++++ b/src/crypto/Makefile
+@@ -1,10 +1,121 @@
+-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+-CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ #CFLAGS += -DALL_DH_GROUPS
+ CFLAGS += -DCONFIG_SHA256
+ CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++
++# crypto_module_tests.c
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
++ifeq ($(CONFIG_TLS),mbedtls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DCONFIG_DES
++CFLAGS += -DEAP_IKEV2
++CFLAGS += -DEAP_MSCHAPv2
++CFLAGS += -DEAP_SIM
++
++LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
++LIB_OBJS+= \
++ aes-eax.o \
++ aes-siv.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o
++
++else
++ifeq ($(CONFIG_TLS),openssl)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
++ifndef CONFIG_TLS_DEFAULT_CIPHERS
++CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
++endif
++CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DEAP_TLS_OPENSSL
++
++LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
++LIB_OBJS+= \
++ aes-ctr.o \
++ aes-eax.o \
++ aes-encblock.o \
++ aes-siv.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o \
++ sha1-prf.o \
++ sha1-tlsprf.o \
++ sha1-tprf.o \
++ sha256-kdf.o \
++ sha256-prf.o \
++ sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++
++# (wolfssl libraries must be built with ./configure --enable-wpas)
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
++CFLAGS += -DWOLFSSL_DER_LOAD
++CFLAGS += -DCONFIG_DES
++
++LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
++LIB_OBJS+= \
++ aes-ctr.o \
++ aes-eax.o \
++ aes-encblock.o \
++ aes-siv.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o \
++ sha1-prf.o \
++ sha1-tlsprf.o \
++ sha1-tprf.o \
++ sha256-kdf.o \
++ sha256-prf.o \
++ sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),gnutls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
++LIB_OBJS = tls_gnutls.o crypto_gnutls.o
++LIB_OBJS+= \
++ aes-cbc.o \
++ aes-ctr.o \
++ aes-eax.o \
++ aes-encblock.o \
++ aes-omac1.o \
++ aes-siv.o \
++ aes-unwrap.o \
++ aes-wrap.o \
++ dh_group5.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o \
++ rc4.o \
++ sha1-pbkdf2.o \
++ sha1-prf.o \
++ fips_prf_internal.o \
++ sha1-internal.o \
++ sha1-tlsprf.o \
++ sha1-tprf.o \
++ sha256-kdf.o \
++ sha256-prf.o \
++ sha256-tlsprf.o
++
++else
++
++CFLAGS += -DCONFIG_CRYPTO_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+
+ LIB_OBJS= \
+@@ -13,7 +124,6 @@ LIB_OBJS= \
+ aes-ctr.o \
+ aes-eax.o \
+ aes-encblock.o \
+- aes-gcm.o \
+ aes-internal.o \
+ aes-internal-dec.o \
+ aes-internal-enc.o \
+@@ -37,6 +147,7 @@ LIB_OBJS= \
+ sha1-tlsprf.o \
+ sha1-tprf.o \
+ sha256.o \
++ sha256-kdf.o \
+ sha256-prf.o \
+ sha256-tlsprf.o \
+ sha256-internal.o \
+@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
+ LIB_OBJS += crypto_internal-rsa.o
+ LIB_OBJS += tls_internal.o
+ LIB_OBJS += fips_prf_internal.o
++
++endif
++endif
++endif
++endif
++
++
++# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
++LIB_OBJS += aes-gcm.o
++
+ ifndef TEST_FUZZ
+ LIB_OBJS += random.o
+ endif
+diff --git a/src/crypto/crypto_mbedtls.c b/src/crypto/crypto_mbedtls.c
+new file mode 100644
+index 000000000..7a91c965f
+--- /dev/null
++++ b/src/crypto/crypto_mbedtls.c
+@@ -0,0 +1,4228 @@
++/*
++ * crypto wrapper functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/entropy.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/asn1.h>
++#include <mbedtls/asn1write.h>
++#include <mbedtls/aes.h>
++#include <mbedtls/md.h>
++#include <mbedtls/md5.h>
++#include <mbedtls/sha1.h>
++#include <mbedtls/sha256.h>
++#include <mbedtls/sha512.h>
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__ __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__ __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++#include "crypto.h"
++#include "aes_wrap.h"
++#include "aes.h"
++#include "md5.h"
++#include "sha1.h"
++#include "sha256.h"
++#include "sha384.h"
++#include "sha512.h"
++
++
++/*
++ * selective code inclusion based on preprocessor defines
++ *
++ * future: additional code could be wrapped with preprocessor checks if
++ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
++ * setting preprocessor defines for named groups of functionality
++ */
++
++#if defined(CONFIG_FIPS)
++#undef MBEDTLS_MD4_C /* omit md4_vector() */
++#undef MBEDTLS_MD5_C /* omit md5_vector() hmac_md5_vector() hmac_md5() */
++#undef MBEDTLS_DES_C /* omit des_encrypt() */
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#define CRYPTO_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if !defined(CONFIG_FIPS)
++#if defined(EAP_PWD) \
++ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
++ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
++ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
++ || defined(EAP_SERVER_MSCHAPV2)
++#ifndef MBEDTLS_MD4_C /* (MD4 not in mbedtls 3.x) */
++#include "md4-internal.c"/* pull in hostap local implementation */
++#endif /* md4_vector() */
++#else
++#undef MBEDTLS_MD4_C /* omit md4_vector() */
++#endif
++#endif
++
++#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
++#ifndef MBEDTLS_ARC4_C /* (RC4 not in mbedtls 3.x) */
++#include "rc4.c" /* pull in hostap local implementation */
++#endif /* rc4_skip() */
++#else
++#undef MBEDTLS_ARC4_C /* omit rc4_skip() */
++#endif
++
++#if defined(CONFIG_MACSEC) \
++ || defined(CONFIG_NO_RADIUS) \
++ || defined(CONFIG_IEEE80211R) \
++ || defined(EAP_SERVER_FAST) \
++ || defined(EAP_SERVER_TEAP) \
++ || !defined(CONFIG_NO_WPA)
++ /* aes_wrap() aes_unwrap() */
++#else
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#endif
++
++#if !defined(CONFIG_SHA256)
++#undef MBEDTLS_SHA256_C
++#endif
++
++#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
++#undef MBEDTLS_SHA512_C
++#endif
++
++#if defined(CONFIG_HMAC_SHA256_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++#endif
++#if defined(CONFIG_HMAC_SHA384_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++#endif
++#if defined(CONFIG_HMAC_SHA512_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++#endif
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
++/* EAP_SIM=y EAP_AKA=y */
++#define CRYPTO_MBEDTLS_FIPS186_2_PRF
++#endif
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define CRYPTO_MBEDTLS_SHA1_T_PRF
++#endif
++
++#if defined(CONFIG_DES)
++#define CRYPTO_MBEDTLS_DES_ENCRYPT
++#endif /* des_encrypt() */
++
++#if !defined(CONFIG_NO_PBKDF2)
++#define CRYPTO_MBEDTLS_PBKDF2_SHA1
++#endif /* pbkdf2_sha1() */
++
++#if defined(EAP_IKEV2) \
++ || defined(EAP_IKEV2_DYNAMIC) \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
++#endif /* crypto_cipher_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HASH
++#endif /* crypto_hash_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#endif /* crypto_bignum_*() */
++
++#if defined(EAP_PWD) /* CONFIG_EAP_PWD=y */ \
++ || defined(EAP_EKE) /* CONFIG_EAP_EKE=y */ \
++ || defined(EAP_EKE_DYNAMIC) /* CONFIG_EAP_EKE=y */ \
++ || defined(EAP_SERVER_EKE) /* CONFIG_EAP_EKE=y */ \
++ || defined(EAP_IKEV2) /* CONFIG_EAP_IKEV2y */ \
++ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */ \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */ \
++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */ \
++ || defined(CONFIG_WPS) /* CONFIG_WPS=y */
++#define CRYPTO_MBEDTLS_CRYPTO_DH
++#if defined(CONFIG_WPS_NFC)
++#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
++#endif /* dh5_init_fixed() */
++#endif /* crypto_dh_*() */
++
++#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
++#define CRYPTO_MBEDTLS_CRYPTO_ECDH
++#endif /* crypto_ecdh_*() */
++
++#if defined(CONFIG_ECC)
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#define CRYPTO_MBEDTLS_CRYPTO_EC
++#endif /* crypto_ec_*() crypto_ec_key_*() */
++
++#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
++#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
++#define CRYPTO_MBEDTLS_CRYPTO_CSR
++#endif /* crypto_csr_*() */
++
++#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HPKE
++#endif
++
++#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
++#endif /* crypto_pkcs7_*() */
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
++ || defined(CONFIG_AP) || defined(HOSTAPD)
++/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
++#if defined(CRYPTO_RSA_OAEP_SHA256)
++#define CRYPTO_MBEDTLS_CRYPTO_RSA
++#endif
++#endif /* crypto_rsa_*() */
++
++
++static int ctr_drbg_init_state;
++static mbedtls_ctr_drbg_context ctr_drbg;
++static mbedtls_entropy_context entropy;
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#include <mbedtls/bignum.h>
++static mbedtls_mpi mpi_sw_A;
++#endif
++
++__attribute_cold__
++__attribute_noinline__
++static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
++{
++ mbedtls_ctr_drbg_init(&ctr_drbg);
++ mbedtls_entropy_init(&entropy);
++ if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
++ NULL, 0)) {
++ wpa_printf(MSG_ERROR, "Init of random number generator failed");
++ /* XXX: abort? */
++ }
++ else
++ ctr_drbg_init_state = 1;
++
++ return &ctr_drbg;
++}
++
++__attribute_cold__
++void crypto_unload(void)
++{
++ if (ctr_drbg_init_state) {
++ mbedtls_ctr_drbg_free(&ctr_drbg);
++ mbedtls_entropy_free(&entropy);
++ #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++ mbedtls_mpi_free(&mpi_sw_A);
++ #endif
++ ctr_drbg_init_state = 0;
++ }
++}
++
++/* init ctr_drbg on first use
++ * crypto_global_init() and crypto_global_deinit() are not available here
++ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++inline
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
++{
++ return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
++}
++
++#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
++int crypto_get_random(void *buf, size_t len)
++{
++ return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
++}
++#endif
++
++
++#if 1
++
++/* tradeoff: slightly smaller code size here at cost of slight increase
++ * in instructions and function calls at runtime versus the expanded
++ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
++
++__attribute_noinline__
++static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac, mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_starts(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md_update(&ctx, addr[i], len[i]);
++ mbedtls_md_finish(&ctx, mac);
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
++}
++#endif
++
++#else /* expanded per-message-digest functions */
++
++#ifdef MBEDTLS_SHA512_C
++#include <mbedtls/sha512.h>
++__attribute_noinline__
++static int sha384_512_vector(size_t num_elem, const u8 *addr[],
++ const size_t *len, u8 *mac, int is384)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_sha512_context ctx;
++ mbedtls_sha512_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_sha512_starts(&ctx, is384);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha512_update(&ctx, addr[i], len[i]);
++ mbedtls_sha512_finish(&ctx, mac);
++ #else
++ mbedtls_sha512_starts_ret(&ctx, is384);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_sha512_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_sha512_free(&ctx);
++ return 0;
++}
++
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return sha384_512_vector(num_elem, addr, len, mac, 0);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return sha384_512_vector(num_elem, addr, len, mac, 1);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#include <mbedtls/sha256.h>
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_sha256_context ctx;
++ mbedtls_sha256_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_sha256_starts(&ctx, 0);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha256_update(&ctx, addr[i], len[i]);
++ mbedtls_sha256_finish(&ctx, mac);
++ #else
++ mbedtls_sha256_starts_ret(&ctx, 0);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_sha256_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_sha256_free(&ctx);
++ return 0;
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++#include <mbedtls/sha1.h>
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_sha1_context ctx;
++ mbedtls_sha1_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_sha1_starts(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha1_update(&ctx, addr[i], len[i]);
++ mbedtls_sha1_finish(&ctx, mac);
++ #else
++ mbedtls_sha1_starts_ret(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_sha1_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_sha1_free(&ctx);
++ return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++#include <mbedtls/md5.h>
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_md5_context ctx;
++ mbedtls_md5_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_md5_starts(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md5_update(&ctx, addr[i], len[i]);
++ mbedtls_md5_finish(&ctx, mac);
++ #else
++ mbedtls_md5_starts_ret(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_md5_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_md5_free(&ctx);
++ return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_md4_context ctx;
++ mbedtls_md4_init(&ctx);
++ mbedtls_md4_starts_ret(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_md4_finish_ret(&ctx, mac);
++ mbedtls_md4_free(&ctx);
++ return 0;
++}
++#endif
++
++#endif /* expanded per-message-digest functions */
++
++
++__attribute_noinline__
++static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac,
++ mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_hmac_starts(&ctx, key, key_len);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, mac);
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA384);
++}
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA256);
++}
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA1);
++}
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_MD5);
++}
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_MD5);
++}
++#endif
++
++
++#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
++
++#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
++
++#include <mbedtls/hkdf.h>
++
++/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
++
++/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
++/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
++/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
++__attribute_noinline__
++static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
++ const char *label, const u8 *info, size_t info_len,
++ u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++ #ifdef MBEDTLS_HKDF_C
++ if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */
++ return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
++ info_len, okm, okm_len) ? -1 : 0;
++ #endif
++
++ const size_t mac_len = mbedtls_md_get_size(md_info);
++ /* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
++ if (okm_len > ((mac_len << 8) - mac_len))
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_hmac_starts(&ctx, prk, prk_len);
++
++ u8 iter = 1;
++ const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
++ size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
++
++ for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, okm);
++ mbedtls_md_hmac_reset(&ctx);
++ addr[0] = okm;
++ okm += mac_len;
++ len[0] = mac_len; /*(include digest in subsequent rounds)*/
++ }
++
++ if (okm_len) {
++ u8 hash[MBEDTLS_MD_MAX_SIZE];
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, hash);
++ os_memcpy(okm, hash, okm_len);
++ forced_memzero(hash, mac_len);
++ }
++
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++ out, outlen, MBEDTLS_MD_SHA512);
++}
++#endif
++
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++ out, outlen, MBEDTLS_MD_SHA384);
++}
++#endif
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++ out, outlen, MBEDTLS_MD_SHA256);
++}
++#endif
++#endif
++
++#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
++
++
++/* sha256-prf.c sha384-prf.c sha512-prf.c */
++
++/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
++__attribute_noinline__
++static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits, mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_hmac_starts(&ctx, key, key_len);
++
++ u16 ctr, n_le = host_to_le16(buf_len_bits);
++ const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
++ const size_t len[] = { 2, os_strlen(label), data_len, 2 };
++ const size_t mac_len = mbedtls_md_get_size(md_info);
++ size_t buf_len = (buf_len_bits + 7) / 8;
++ for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
++ #if __BYTE_ORDER == __BIG_ENDIAN
++ ctr = host_to_le16(ctr);
++ #endif
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, buf);
++ mbedtls_md_hmac_reset(&ctx);
++ buf += mac_len;
++ #if __BYTE_ORDER == __BIG_ENDIAN
++ ctr = le_to_host16(ctr);
++ #endif
++ }
++
++ if (buf_len) {
++ u8 hash[MBEDTLS_MD_MAX_SIZE];
++ #if __BYTE_ORDER == __BIG_ENDIAN
++ ctr = host_to_le16(ctr);
++ #endif
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, hash);
++ os_memcpy(buf, hash, buf_len);
++ buf += buf_len;
++ forced_memzero(hash, mac_len);
++ }
++
++ /* Mask out unused bits in last octet if it does not use all the bits */
++ if ((buf_len_bits &= 0x7))
++ buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
++
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8, MBEDTLS_MD_SHA512);
++}
++
++int sha384_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8, MBEDTLS_MD_SHA256);
++}
++
++int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len_bits, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
++
++
++#ifdef MBEDTLS_SHA1_C
++
++/* sha1-prf.c */
++
++/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
++
++int sha1_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ /*(note: algorithm differs from hmac_prf_bits() */
++ /*(note: smaller code size instead of expanding hmac_sha1_vector()
++ * as is done in hmac_prf_bits(); not expecting large num of loops) */
++ u8 counter = 0;
++ const u8 *addr[] = { (u8 *)label, data, &counter };
++ const size_t len[] = { os_strlen(label)+1, data_len, 1 };
++
++ for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
++ if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
++ return -1;
++ buf += SHA1_MAC_LEN;
++ }
++
++ if (buf_len) {
++ u8 hash[SHA1_MAC_LEN];
++ if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
++ return -1;
++ os_memcpy(buf, hash, buf_len);
++ forced_memzero(hash, sizeof(hash));
++ }
++
++ return 0;
++}
++
++#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
++
++/* sha1-tprf.c */
++
++/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
++
++int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
++{
++ /*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
++ /*(note: smaller code size instead of expanding hmac_sha1_vector()
++ * as is done in hmac_prf_bits(); not expecting large num of loops) */
++ u8 ctr;
++ u16 olen = host_to_be16(buf_len);
++ const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
++ size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
++
++ for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
++ if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
++ return -1;
++ addr[0] = buf;
++ buf += SHA1_MAC_LEN;
++ len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
++ }
++
++ if (buf_len) {
++ u8 hash[SHA1_MAC_LEN];
++ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
++ return -1;
++ os_memcpy(buf, hash, buf_len);
++ forced_memzero(hash, sizeof(hash));
++ }
++
++ return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
++
++#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
++
++/* fips_prf_internal.c sha1-internal.c */
++
++/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
++ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
++ * where xlen is 160 */
++
++int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
++{
++ /* FIPS 186-2 + change notice 1 */
++
++ mbedtls_sha1_context ctx;
++ u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
++ u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
++ const u32 xstate_init[] =
++ { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
++
++ mbedtls_sha1_init(&ctx);
++ os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
++
++ /* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
++ for (; xlen >= 20; xlen -= 20) {
++ /* XSEED_j = 0 */
++ /* XVAL = (XKEY + XSEED_j) mod 2^b */
++
++ /* w_i = G(t, XVAL) */
++ os_memcpy(xstate, xstate_init, sizeof(xstate_init));
++ mbedtls_internal_sha1_process(&ctx, xkey);
++
++ #if __BYTE_ORDER == __LITTLE_ENDIAN
++ xstate[0] = host_to_be32(xstate[0]);
++ xstate[1] = host_to_be32(xstate[1]);
++ xstate[2] = host_to_be32(xstate[2]);
++ xstate[3] = host_to_be32(xstate[3]);
++ xstate[4] = host_to_be32(xstate[4]);
++ #endif
++ os_memcpy(x, xstate, 20);
++ if (xlen == 20) /*(done; skip prep for next loop)*/
++ break;
++
++ /* XKEY = (1 + XKEY + w_i) mod 2^b */
++ for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
++ xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
++ x += 20;
++ /* x_j = w_0|w_1 (each pair of iterations through loop)*/
++ }
++
++ mbedtls_sha1_free(&ctx);
++ return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
++
++#endif /* MBEDTLS_SHA1_C */
++
++
++#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
++#ifdef MBEDTLS_DES_C
++#include <mbedtls/des.h>
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
++ u8 pkey[8], next, tmp;
++ int i;
++
++ /* Add parity bits to the key */
++ next = 0;
++ for (i = 0; i < 7; i++) {
++ tmp = key[i];
++ pkey[i] = (tmp >> i) | next | 1;
++ next = tmp << (7 - i);
++ }
++ pkey[i] = next | 1;
++
++ mbedtls_des_context des;
++ mbedtls_des_init(&des);
++ int ret = mbedtls_des_setkey_enc(&des, pkey)
++ || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
++ mbedtls_des_free(&des);
++ return ret;
++}
++#else
++#include "des-internal.c"/* pull in hostap local implementation */
++#endif
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
++/* sha1-pbkdf2.c */
++#include <mbedtls/pkcs5.h>
++int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
++ int iterations, u8 *buf, size_t buflen)
++{
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
++ return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
++ (const u8 *)passphrase, os_strlen(passphrase),
++ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++ #else
++ const mbedtls_md_info_t *md_info;
++ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
++ if (md_info == NULL)
++ return -1;
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ int ret = mbedtls_md_setup(&ctx, md_info, 1)
++ || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
++ (const u8 *)passphrase, os_strlen(passphrase),
++ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++ mbedtls_md_free(&ctx);
++ return ret;
++ #endif
++}
++#endif
++
++
++/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
++
++static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
++ if (!aes)
++ return NULL;
++
++ mbedtls_aes_init(aes);
++ if ((mode == MBEDTLS_AES_ENCRYPT
++ ? mbedtls_aes_setkey_enc(aes, key, len * 8)
++ : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
++ return aes;
++
++ mbedtls_aes_free(aes);
++ os_free(aes);
++ return NULL;
++}
++
++void *aes_encrypt_init(const u8 *key, size_t len)
++{
++ return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++{
++ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
++}
++
++void aes_encrypt_deinit(void *ctx)
++{
++ mbedtls_aes_free(ctx);
++ os_free(ctx);
++}
++
++void *aes_decrypt_init(const u8 *key, size_t len)
++{
++ return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
++}
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++{
++ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
++}
++
++void aes_decrypt_deinit(void *ctx)
++{
++ mbedtls_aes_free(ctx);
++ os_free(ctx);
++}
++
++
++#include "aes_wrap.h"
++
++
++#ifdef MBEDTLS_NIST_KW_C
++
++#include <mbedtls/nist_kw.h>
++
++/* aes-wrap.c */
++int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_nist_kw_context ctx;
++ mbedtls_nist_kw_init(&ctx);
++ size_t olen;
++ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++ kek, kek_len*8, 1)
++ || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
++ cipher, &olen, (n+1)*8) ? -1 : 0;
++ mbedtls_nist_kw_free(&ctx);
++ return ret;
++}
++
++/* aes-unwrap.c */
++int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_nist_kw_context ctx;
++ mbedtls_nist_kw_init(&ctx);
++ size_t olen;
++ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++ kek, kek_len*8, 0)
++ || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
++ (n+1)*8, plain, &olen, n*8) ? -1 : 0;
++ mbedtls_nist_kw_free(&ctx);
++ return ret;
++}
++
++#else
++
++#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
++#include "aes-wrap.c" /* pull in hostap local implementation */
++#include "aes-unwrap.c" /* pull in hostap local implementation */
++#endif
++
++#endif /* MBEDTLS_NIST_KW_C */
++
++
++#ifdef MBEDTLS_CMAC_C
++
++/* aes-omac1.c */
++
++#include <mbedtls/cmac.h>
++
++int omac1_aes_vector(
++ const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
++ const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_cipher_type_t cipher_type;
++ switch (key_len) {
++ case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
++ case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
++ case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
++ default: return -1;
++ }
++ const mbedtls_cipher_info_t *cipher_info;
++ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++ if (cipher_info == NULL)
++ return -1;
++
++ mbedtls_cipher_context_t ctx;
++ mbedtls_cipher_init(&ctx);
++ int ret = -1;
++ if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
++ && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
++ ret = 0;
++ for (size_t i = 0; i < num_elem && ret == 0; ++i)
++ ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
++ }
++ if (ret == 0)
++ ret = mbedtls_cipher_cmac_finish(&ctx, mac);
++ mbedtls_cipher_free(&ctx);
++ return ret ? -1 : 0;
++}
++
++int omac1_aes_128_vector(const u8 *key, size_t num_elem,
++ const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
++}
++
++int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
++}
++
++int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
++}
++
++#else
++
++#include "aes-omac1.c" /* pull in hostap local implementation */
++
++#ifndef MBEDTLS_AES_BLOCK_SIZE
++#define MBEDTLS_AES_BLOCK_SIZE 16
++#endif
++
++#endif /* MBEDTLS_CMAC_C */
++
++
++/* These interfaces can be inefficient when used in loops, as the overhead of
++ * initialization each call is large for each block input (e.g. 16 bytes) */
++
++
++/* aes-encblock.c */
++int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_aes_context aes;
++ mbedtls_aes_init(&aes);
++ int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
++ || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
++ ? -1
++ : 0;
++ mbedtls_aes_free(&aes);
++ return ret;
++}
++
++
++/* aes-ctr.c */
++int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
++ u8 *data, size_t data_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
++ unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
++ os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
++
++ mbedtls_aes_context ctx;
++ mbedtls_aes_init(&ctx);
++ size_t nc_off = 0;
++ int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
++ || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
++ counter, stream_block,
++ data, data) ? -1 : 0;
++ forced_memzero(stream_block, sizeof(stream_block));
++ mbedtls_aes_free(&ctx);
++ return ret;
++}
++
++int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
++ u8 *data, size_t data_len)
++{
++ return aes_ctr_encrypt(key, 16, nonce, data, data_len);
++}
++
++
++/* aes-cbc.c */
++static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
++ u8 *data, size_t data_len, int mode)
++{
++ unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
++ os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
++
++ mbedtls_aes_context ctx;
++ mbedtls_aes_init(&ctx);
++ int ret = (mode == MBEDTLS_AES_ENCRYPT
++ ? mbedtls_aes_setkey_enc(&ctx, key, 128)
++ : mbedtls_aes_setkey_dec(&ctx, key, 128))
++ || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
++ mbedtls_aes_free(&ctx);
++ return ret ? -1 : 0;
++}
++
++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
++}
++
++
++/*
++ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
++ * but such comments are not accurate:
++ *
++ * "This function is only used with internal TLSv1 implementation
++ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
++ * to implement this."
++ */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
++
++#include <mbedtls/cipher.h>
++
++struct crypto_cipher
++{
++ mbedtls_cipher_context_t ctx_enc;
++ mbedtls_cipher_context_t ctx_dec;
++};
++
++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
++ const u8 *iv, const u8 *key,
++ size_t key_len)
++{
++ /* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
++ * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
++
++ mbedtls_cipher_type_t cipher_type;
++ size_t iv_len;
++ switch (alg) {
++ #ifdef MBEDTLS_ARC4_C
++ #if 0
++ case CRYPTO_CIPHER_ALG_RC4:
++ cipher_type = MBEDTLS_CIPHER_ARC4_128;
++ iv_len = 0;
++ break;
++ #endif
++ #endif
++ #ifdef MBEDTLS_AES_C
++ case CRYPTO_CIPHER_ALG_AES:
++ if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
++ if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
++ if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
++ iv_len = 16;
++ break;
++ #endif
++ #ifdef MBEDTLS_DES_C
++ case CRYPTO_CIPHER_ALG_3DES:
++ cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
++ iv_len = 8;
++ break;
++ #if 0
++ case CRYPTO_CIPHER_ALG_DES:
++ cipher_type = MBEDTLS_CIPHER_DES_CBC;
++ iv_len = 8;
++ break;
++ #endif
++ #endif
++ default:
++ return NULL;
++ }
++
++ const mbedtls_cipher_info_t *cipher_info;
++ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++ if (cipher_info == NULL)
++ return NULL;
++
++ key_len *= 8; /* key_bitlen */
++ #if 0 /*(were key_bitlen not already available)*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++ key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
++ #else
++ key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
++ #endif
++ #endif
++
++ #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
++ iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
++ #endif
++
++ struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ mbedtls_cipher_init(&ctx->ctx_enc);
++ mbedtls_cipher_init(&ctx->ctx_dec);
++ if ( mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
++ && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
++ && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
++ && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
++ && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
++ && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
++ && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
++ && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
++ return ctx;
++ }
++
++ mbedtls_cipher_free(&ctx->ctx_enc);
++ mbedtls_cipher_free(&ctx->ctx_dec);
++ os_free(ctx);
++ return NULL;
++}
++
++int crypto_cipher_encrypt(struct crypto_cipher *ctx,
++ const u8 *plain, u8 *crypt, size_t len)
++{
++ size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
++ return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
++ || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
++}
++
++int crypto_cipher_decrypt(struct crypto_cipher *ctx,
++ const u8 *crypt, u8 *plain, size_t len)
++{
++ size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
++ return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
++ || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
++}
++
++void crypto_cipher_deinit(struct crypto_cipher *ctx)
++{
++ mbedtls_cipher_free(&ctx->ctx_enc);
++ mbedtls_cipher_free(&ctx->ctx_dec);
++ os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
++
++struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
++ size_t key_len)
++{
++ mbedtls_md_type_t md_type;
++ int is_hmac = 0;
++
++ switch (alg) {
++ #ifdef MBEDTLS_MD5_C
++ case CRYPTO_HASH_ALG_MD5:
++ md_type = MBEDTLS_MD_MD5;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA1_C
++ case CRYPTO_HASH_ALG_SHA1:
++ md_type = MBEDTLS_MD_SHA1;
++ break;
++ #endif
++ #ifdef MBEDTLS_MD5_C
++ case CRYPTO_HASH_ALG_HMAC_MD5:
++ md_type = MBEDTLS_MD_MD5;
++ is_hmac = 1;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA1_C
++ case CRYPTO_HASH_ALG_HMAC_SHA1:
++ md_type = MBEDTLS_MD_SHA1;
++ is_hmac = 1;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA256_C
++ case CRYPTO_HASH_ALG_SHA256:
++ md_type = MBEDTLS_MD_SHA256;
++ break;
++ case CRYPTO_HASH_ALG_HMAC_SHA256:
++ md_type = MBEDTLS_MD_SHA256;
++ is_hmac = 1;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA512_C
++ case CRYPTO_HASH_ALG_SHA384:
++ md_type = MBEDTLS_MD_SHA384;
++ break;
++ case CRYPTO_HASH_ALG_SHA512:
++ md_type = MBEDTLS_MD_SHA512;
++ break;
++ #endif
++ default:
++ return NULL;
++ }
++
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++ if (!md_info)
++ return NULL;
++
++ mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
++ if (mctx == NULL)
++ return NULL;
++
++ mbedtls_md_init(mctx);
++ if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
++ os_free(mctx);
++ return NULL;
++ }
++
++ if (is_hmac)
++ mbedtls_md_hmac_starts(mctx, key, key_len);
++ else
++ mbedtls_md_starts(mctx);
++ return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
++}
++
++void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
++{
++ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++ #if 0
++ /*(mbedtls_md_hmac_update() and mbedtls_md_update()
++ * make same modifications under the hood in mbedtls)*/
++ if ((uintptr_t)ctx & 1uL)
++ mbedtls_md_hmac_update(mctx, data, len);
++ else
++ #endif
++ mbedtls_md_update(mctx, data, len);
++}
++
++int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
++{
++ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++ if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
++ #else
++ const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
++ #endif
++ size_t maclen = mbedtls_md_get_size(md_info);
++ if (*len < maclen) {
++ *len = maclen;
++ /*(note: ctx not freed; can call again with larger *len)*/
++ return -1;
++ }
++ *len = maclen;
++ if ((uintptr_t)ctx & 1uL)
++ mbedtls_md_hmac_finish(mctx, mac);
++ else
++ mbedtls_md_finish(mctx, mac);
++ }
++ mbedtls_md_free(mctx);
++ os_free(mctx);
++
++ if (TEST_FAIL())
++ return -1;
++
++ return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++
++#include <mbedtls/bignum.h>
++
++/* crypto.h bignum interfaces */
++
++struct crypto_bignum *crypto_bignum_init(void)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn)
++ mbedtls_mpi_init(bn);
++ return (struct crypto_bignum *)bn;
++}
++
++struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn) {
++ mbedtls_mpi_init(bn);
++ if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
++ return (struct crypto_bignum *)bn;
++ }
++
++ os_free(bn);
++ return NULL;
++}
++
++struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ #if 0 /*(hostap use of this interface passes int, not uint)*/
++ val = host_to_be32(val);
++ return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
++ #else
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn) {
++ mbedtls_mpi_init(bn);
++ if (mbedtls_mpi_lset(bn, (int)val) == 0)
++ return (struct crypto_bignum *)bn;
++ }
++
++ os_free(bn);
++ return NULL;
++ #endif
++}
++
++void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
++{
++ mbedtls_mpi_free((mbedtls_mpi *)n);
++ os_free(n);
++}
++
++int crypto_bignum_to_bin(const struct crypto_bignum *a,
++ u8 *buf, size_t buflen, size_t padlen)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
++ if (n < padlen)
++ n = padlen;
++ return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
++ ? -1
++ : (int)(n);
++}
++
++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
++ return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++ #else
++ /* (needed by EAP_PWD, SAE, DPP) */
++ wpa_printf(MSG_ERROR,
++ "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
++ return -1;
++ #endif
++}
++
++int crypto_bignum_add(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_mod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_exptmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ const struct crypto_bignum *c,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /* (check if input params match d; d is the result) */
++ /* (a == d) is ok in current mbedtls implementation */
++ if (b == d || c == d) { /*(not ok; store result in intermediate)*/
++ mbedtls_mpi R;
++ mbedtls_mpi_init(&R);
++ int rc = mbedtls_mpi_exp_mod(&R,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b,
++ (const mbedtls_mpi *)c,
++ NULL)
++ || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
++ mbedtls_mpi_free(&R);
++ return rc;
++ }
++ else {
++ return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b,
++ (const mbedtls_mpi *)c,
++ NULL) ? -1 : 0;
++ }
++}
++
++int crypto_bignum_inverse(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_sub(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_div(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /*(most current use of this crypto.h interface has a == c (result),
++ * so store result in an intermediate to avoid overwritten input)*/
++ mbedtls_mpi R;
++ mbedtls_mpi_init(&R);
++ int rc = mbedtls_mpi_div_mpi(&R, NULL,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b)
++ || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
++ mbedtls_mpi_free(&R);
++ return rc;
++}
++
++int crypto_bignum_addmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ const struct crypto_bignum *c,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b)
++ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++ (mbedtls_mpi *)d,
++ (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_mulmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ const struct crypto_bignum *c,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b)
++ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++ (mbedtls_mpi *)d,
++ (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_sqrmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ #if 1
++ return crypto_bignum_mulmod(a, a, b, c);
++ #else
++ mbedtls_mpi bn;
++ mbedtls_mpi_init(&bn);
++ if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
++ return -1;
++ int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a, &bn,
++ (const mbedtls_mpi *)b, NULL) ? -1 : 0;
++ mbedtls_mpi_free(&bn);
++ return ret;
++ #endif
++}
++
++int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
++ struct crypto_bignum *r)
++{
++ return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
++ || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
++}
++
++int crypto_bignum_cmp(const struct crypto_bignum *a,
++ const struct crypto_bignum *b)
++{
++ return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
++}
++
++int crypto_bignum_is_zero(const struct crypto_bignum *a)
++{
++ /* XXX: src/common/sae.c:sswu() contains comment:
++ * "TODO: Make sure crypto_bignum_is_zero() is constant time"
++ * Note: mbedtls_mpi_cmp_int() *is not* constant time */
++ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
++}
++
++int crypto_bignum_is_one(const struct crypto_bignum *a)
++{
++ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
++}
++
++int crypto_bignum_is_odd(const struct crypto_bignum *a)
++{
++ return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
++}
++
++#include "utils/const_time.h"
++int crypto_bignum_legendre(const struct crypto_bignum *a,
++ const struct crypto_bignum *p)
++{
++ if (TEST_FAIL())
++ return -2;
++
++ /* Security Note:
++ * mbedtls_mpi_exp_mod() is not documented to run in constant time,
++ * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
++ * Compare to crypto_openssl.c:crypto_bignum_legendre()
++ * which uses openssl BN_mod_exp_mont_consttime()
++ * mbedtls/library/ecp.c has further countermeasures to timing attacks,
++ * (but ecp.c funcs are not used here) */
++
++ mbedtls_mpi exp, tmp;
++ mbedtls_mpi_init(&exp);
++ mbedtls_mpi_init(&tmp);
++
++ /* exp = (p-1) / 2 */
++ int res;
++ if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
++ && mbedtls_mpi_shift_r(&exp, 1) == 0
++ && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
++ (const mbedtls_mpi *)p, NULL) == 0) {
++ /*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
++ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
++ * to use constant time selection to avoid branches here. */
++ unsigned int mask;
++ res = -1;
++ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
++ res = const_time_select_int(mask, 1, res);
++ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
++ res = const_time_select_int(mask, 0, res);
++ } else {
++ res = -2;
++ }
++
++ mbedtls_mpi_free(&tmp);
++ mbedtls_mpi_free(&exp);
++ return res;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
++
++/* crypto_internal-modexp.c */
++
++#include <mbedtls/bignum.h>
++#include <mbedtls/dhm.h>
++
++#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
++int crypto_mod_exp(const u8 *base, size_t base_len,
++ const u8 *power, size_t power_len,
++ const u8 *modulus, size_t modulus_len,
++ u8 *result, size_t *result_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
++ mbedtls_mpi_init(&bn_base);
++ mbedtls_mpi_init(&bn_exp);
++ mbedtls_mpi_init(&bn_modulus);
++ mbedtls_mpi_init(&bn_result);
++
++ size_t len;
++ int ret = mbedtls_mpi_read_binary(&bn_base, base, base_len)
++ || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
++ || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
++ || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
++ || (len = mbedtls_mpi_size(&bn_result)) > *result_len
++ || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
++ ? -1
++ : 0;
++
++ mbedtls_mpi_free(&bn_base);
++ mbedtls_mpi_free(&bn_exp);
++ mbedtls_mpi_free(&bn_modulus);
++ mbedtls_mpi_free(&bn_result);
++ return ret;
++}
++#endif
++
++static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
++ const u8 *prime, size_t prime_len)
++{
++ /*(could set these directly in MBEDTLS_PRIVATE members)*/
++ mbedtls_mpi P, G;
++ mbedtls_mpi_init(&P);
++ mbedtls_mpi_init(&G);
++ int ret = mbedtls_mpi_lset(&G, generator)
++ || mbedtls_mpi_read_binary(&P, prime, prime_len)
++ || mbedtls_dhm_set_group(ctx, &P, &G);
++ mbedtls_mpi_free(&P);
++ mbedtls_mpi_free(&G);
++ return ret;
++}
++
++__attribute_noinline__
++static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
++ const u8 *prime, size_t prime_len,
++ u8 *privkey, u8 *pubkey)
++{
++ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
++ || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()))
++ return -1;
++
++ /*(enable later when upstream mbedtls interface changes require)*/
++ #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ mbedtls_mpi X;
++ mbedtls_mpi_init(&X);
++ int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
++ || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
++ mbedtls_mpi_free(&X);
++ return ret;
++ #else
++ return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
++ privkey, prime_len) ? -1 : 0;
++ #endif
++}
++
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
++ size_t pubkey_len, pad;
++
++ if (os_get_random(privkey, prime_len) < 0)
++ return -1;
++ if (os_memcmp(privkey, prime, prime_len) > 0) {
++ /* Make sure private value is smaller than prime */
++ privkey[0] = 0;
++ }
++
++ pubkey_len = prime_len;
++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++ pubkey, &pubkey_len) < 0)
++ return -1;
++ if (pubkey_len < prime_len) {
++ pad = prime_len - pubkey_len;
++ os_memmove(pubkey + pad, pubkey, pubkey_len);
++ os_memset(pubkey, 0, pad);
++ }
++
++ return 0;
++ #else
++ /* Prefer to use mbedtls to derive our public/private key, as doing so
++ * leverages mbedtls to properly format output and to perform blinding*/
++ mbedtls_dhm_context ctx;
++ mbedtls_dhm_init(&ctx);
++ int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
++ prime_len, privkey, pubkey);
++ mbedtls_dhm_free(&ctx);
++ return ret;
++ #endif
++}
++
++/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
++ * instead of being reimplemented in each crypto_*.c)*/
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ #if 0
++ if (pubkey_len > prime_len ||
++ (pubkey_len == prime_len &&
++ os_memcmp(pubkey, prime, prime_len) >= 0))
++ return -1;
++
++ int res = 0;
++ mbedtls_mpi pub;
++ mbedtls_mpi_init(&pub);
++ if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
++ || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
++ res = -1;
++ } else if (order) {
++ mbedtls_mpi p, q, tmp;
++ mbedtls_mpi_init(&p);
++ mbedtls_mpi_init(&q);
++ mbedtls_mpi_init(&tmp);
++
++ /* verify: pubkey^q == 1 mod p */
++ res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
++ || mbedtls_mpi_read_binary(&q, order, order_len)
++ || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
++ || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
++
++ mbedtls_mpi_free(&p);
++ mbedtls_mpi_free(&q);
++ mbedtls_mpi_free(&tmp);
++ }
++ mbedtls_mpi_free(&pub);
++
++ return (res == 0)
++ ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++ prime, prime_len, secret, len)
++ : -1;
++ #else
++ /* Prefer to use mbedtls to derive DH shared secret, as doing so
++ * leverages mbedtls to validate params and to perform blinding.
++ *
++ * Attempt to reconstitute DH context to derive shared secret
++ * (due to limitations of the interface, which ought to pass context).
++ * Force provided G (our private key) into context without validation.
++ * Regenerating GX (our public key) not needed to derive shared secret.
++ */
++ /*(older compilers might not support VLAs)*/
++ /*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
++ unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
++ unsigned char *p = buf + 2 + prime_len;
++ if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
++ return -1;
++ WPA_PUT_BE16(buf, prime_len); /*(2-byte big-endian size of prime)*/
++ p[0] = 0; /*(2-byte big-endian size of generator)*/
++ p[1] = 1;
++ p[2] = generator;
++ WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
++ os_memcpy(p+5, pubkey, pubkey_len);
++ os_memcpy(buf+2, prime, prime_len);
++
++ mbedtls_dhm_context ctx;
++ mbedtls_dhm_init(&ctx);
++ p = buf;
++ int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
++ || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
++ privkey, privkey_len)
++ || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++ mbedtls_dhm_free(&ctx);
++ return ret;
++ #endif
++}
++
++/* dh_group5.c */
++
++#include "dh_group5.h"
++
++/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
++
++static const unsigned char RFC3526_PRIME_1536[] = {
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
++ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
++ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
++ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
++ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
++ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
++ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
++ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
++ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
++ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
++ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
++ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
++ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
++ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
++ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
++ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++};
++
++static const unsigned char RFC3526_GENERATOR_1536[] = {
++ 0x02
++};
++
++void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
++{
++ const unsigned char * const prime = RFC3526_PRIME_1536;
++ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++ const u8 generator = *RFC3526_GENERATOR_1536;
++ struct wpabuf *wpubl = NULL, *wpriv = NULL;
++
++ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_dhm_init(ctx);
++
++ if ( (wpubl = wpabuf_alloc(prime_len))
++ && (wpriv = wpabuf_alloc(prime_len))
++ && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
++ wpabuf_put(wpriv, prime_len),
++ wpabuf_put(wpubl, prime_len))==0) {
++ wpabuf_free(*publ);
++ wpabuf_clear_free(*priv);
++ *publ = wpubl;
++ *priv = wpriv;
++ return ctx;
++ }
++
++ wpabuf_clear_free(wpriv);
++ wpabuf_free(wpubl);
++ mbedtls_dhm_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
++void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
++{
++ const unsigned char * const prime = RFC3526_PRIME_1536;
++ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++ const u8 generator = *RFC3526_GENERATOR_1536;
++
++ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_dhm_init(ctx);
++
++ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
++ #if 0 /*(ignore; not required to derive shared secret)*/
++ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
++ wpabuf_head(publ),wpabuf_len(publ))==0
++ #endif
++ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
++ wpabuf_head(priv),wpabuf_len(priv))==0) {
++ return ctx;
++ }
++
++ mbedtls_dhm_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++#endif
++
++struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
++ const struct wpabuf *own_private)
++{
++ /*((mbedtls_dhm_context *)ctx must already contain own_private)*/
++ /* mbedtls 2.x: prime_len = ctx->len; */
++ /* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
++ size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
++ struct wpabuf *buf = wpabuf_alloc(olen);
++ if (buf == NULL)
++ return NULL;
++ if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
++ wpabuf_head(peer_public),
++ wpabuf_len(peer_public)) == 0
++ && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0) {
++ wpabuf_put(buf, olen);
++ return buf;
++ }
++
++ wpabuf_free(buf);
++ return NULL;
++}
++
++void dh5_free(void *ctx)
++{
++ mbedtls_dhm_free(ctx);
++ os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
++
++#include <mbedtls/ecp.h>
++
++#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
++#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
++#define CRYPTO_EC_P(e) (&((mbedtls_ecp_group *)(e))->P)
++#define CRYPTO_EC_N(e) (&((mbedtls_ecp_group *)(e))->N)
++#define CRYPTO_EC_A(e) (&((mbedtls_ecp_group *)(e))->A)
++#define CRYPTO_EC_B(e) (&((mbedtls_ecp_group *)(e))->B)
++#define CRYPTO_EC_G(e) (&((mbedtls_ecp_group *)(e))->G)
++
++static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
++{
++ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++ switch (group) {
++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++ case 19: return MBEDTLS_ECP_DP_SECP256R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++ case 20: return MBEDTLS_ECP_DP_SECP384R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++ case 21: return MBEDTLS_ECP_DP_SECP521R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++ case 25: return MBEDTLS_ECP_DP_SECP192R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++ case 26: return MBEDTLS_ECP_DP_SECP224R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++ case 28: return MBEDTLS_ECP_DP_BP256R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++ case 29: return MBEDTLS_ECP_DP_BP384R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++ case 30: return MBEDTLS_ECP_DP_BP512R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++ case 31: return MBEDTLS_ECP_DP_CURVE25519;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++ case 32: return MBEDTLS_ECP_DP_CURVE448;
++ #endif
++ default: return MBEDTLS_ECP_DP_NONE;
++ }
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
++{
++ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++ /*(for crypto_ec_key_group())*/
++ switch (grp_id) {
++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP256R1: return 19;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP384R1: return 20;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP521R1: return 21;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP192R1: return 25;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP224R1: return 26;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++ case MBEDTLS_ECP_DP_BP256R1: return 28;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++ case MBEDTLS_ECP_DP_BP384R1: return 29;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++ case MBEDTLS_ECP_DP_BP512R1: return 30;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++ case MBEDTLS_ECP_DP_CURVE25519: return 31;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++ case MBEDTLS_ECP_DP_CURVE448: return 32;
++ #endif
++ default: return -1;
++ }
++}
++#endif
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
++
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return -1;
++ const mbedtls_pk_info_t *pk_info =
++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++ if (pk_info == NULL)
++ return -1;
++ return mbedtls_pk_setup(pk, pk_info)
++ || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
++
++#include <mbedtls/ecdh.h>
++#include <mbedtls/ecdsa.h>
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++/* wrap mbedtls_ecdh_context for more future-proof direct access to components
++ * (mbedtls_ecdh_context internal implementation may change between releases)
++ *
++ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
++ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
++ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
++ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
++ * wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
++struct crypto_ecdh {
++ mbedtls_ecdh_context ctx;
++ mbedtls_ecp_group grp;
++ mbedtls_ecp_point Q;
++};
++
++struct crypto_ecdh * crypto_ecdh_init(int group)
++{
++ mbedtls_pk_context pk;
++ mbedtls_pk_init(&pk);
++ struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
++ ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
++ : NULL;
++ mbedtls_pk_free(&pk);
++ return ecdh;
++}
++
++struct crypto_ecdh * crypto_ecdh_init2(int group,
++ struct crypto_ec_key *own_key)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
++ struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
++ if (ecdh == NULL)
++ return NULL;
++ mbedtls_ecdh_init(&ecdh->ctx);
++ mbedtls_ecp_group_init(&ecdh->grp);
++ mbedtls_ecp_point_init(&ecdh->Q);
++ if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
++ && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
++ /* copy grp and Q for later use
++ * (retrieving this info later is more convoluted
++ * even if mbedtls_ecdh_make_public() is considered)*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ mbedtls_mpi d;
++ mbedtls_mpi_init(&d);
++ if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
++ mbedtls_mpi_free(&d);
++ return ecdh;
++ }
++ mbedtls_mpi_free(&d);
++ #else
++ if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
++ && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
++ return ecdh;
++ #endif
++ }
++
++ mbedtls_ecp_point_free(&ecdh->Q);
++ mbedtls_ecp_group_free(&ecdh->grp);
++ mbedtls_ecdh_free(&ecdh->ctx);
++ os_free(ecdh);
++ return NULL;
++}
++
++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
++{
++ mbedtls_ecp_group *grp = &ecdh->grp;
++ size_t prime_len = CRYPTO_EC_plen(grp);
++ size_t output_len = prime_len;
++ u8 output_offset = 0;
++ u8 buf[256];
++
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ /* len */
++ #endif
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
++ output_offset = 1;
++ }
++ #endif
++
++ if (output_len > sizeof(buf))
++ return NULL;
++
++ inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
++ if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
++ buf, output_len) == 0) {
++ return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
++ }
++
++ return NULL;
++}
++
++#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
++ mbedtls_mpi *bn,
++ int parity_bit)
++{
++ /* y^2 = x^3 + ax + b
++ * sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) */
++ mbedtls_mpi *cy2 = (mbedtls_mpi *)
++ crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
++ (const struct crypto_bignum *)bn); /*x*/
++ if (cy2 == NULL)
++ return -1;
++
++ /*mbedtls_mpi_free(bn);*/
++ /*(reuse bn to store result (y))*/
++
++ mbedtls_mpi exp;
++ mbedtls_mpi_init(&exp);
++ int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
++ || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
++ || mbedtls_mpi_add_int(&exp, &grp->P, 1)
++ || mbedtls_mpi_shift_r(&exp, 2)
++ || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
++ || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
++ && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
++ mbedtls_mpi_free(&exp);
++ mbedtls_mpi_free(cy2);
++ os_free(cy2);
++ return ret;
++}
++#endif
++
++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
++ const u8 *key, size_t len)
++{
++ if (len == 0) /*(invalid peer key)*/
++ return NULL;
++
++ mbedtls_ecp_group *grp = &ecdh->grp;
++
++ #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ /* add header for mbedtls_ecdh_read_public() */
++ u8 buf[256];
++ if (sizeof(buf)-1 < len)
++ return NULL;
++ buf[0] = (u8)(len);
++ os_memcpy(buf+1, key, len);
++
++ if (inc_y) {
++ if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
++ if (sizeof(buf)-2 < len)
++ return NULL;
++ buf[0] = (u8)(1+len);
++ buf[1] = 0x04;
++ os_memcpy(buf+2, key, len);
++ }
++ len >>= 1; /*(repurpose len to prime_len)*/
++ } else { /* (inc_y == 0) */
++ /* mbedtls_ecp_point_read_binary() does not currently support
++ * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
++ * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
++
++ /* derive y, amend buf[] with y for UNCOMPRESSED format */
++ if (sizeof(buf)-2 < len*2 || len == 0)
++ return NULL;
++
++ buf[0] = (u8)(1+len*2);
++ buf[1] = 0x04;
++ os_memcpy(buf+2, key, len);
++
++ mbedtls_mpi bn;
++ mbedtls_mpi_init(&bn);
++ int ret = mbedtls_mpi_read_binary(&bn, key, len)
++ || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
++ || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
++ mbedtls_mpi_free(&bn);
++ if (ret != 0)
++ return NULL;
++ }
++
++ if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
++ return NULL;
++ }
++ #endif
++ #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
++ return NULL;
++ }
++ #endif
++
++ struct wpabuf *buf = wpabuf_alloc(len);
++ if (buf == NULL)
++ return NULL;
++
++ if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
++ wpabuf_mhead(buf), len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0) {
++ wpabuf_put(buf, len);
++ return buf;
++ }
++
++ wpabuf_clear_free(buf);
++ return NULL;
++}
++
++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
++{
++ if (ecdh == NULL)
++ return;
++ mbedtls_ecp_point_free(&ecdh->Q);
++ mbedtls_ecp_group_free(&ecdh->grp);
++ mbedtls_ecdh_free(&ecdh->ctx);
++ os_free(ecdh);
++}
++
++size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
++{
++ return CRYPTO_EC_plen(&ecdh->grp);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++
++#include <mbedtls/ecp.h>
++
++struct crypto_ec *crypto_ec_init(int group)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ mbedtls_ecp_group *e = os_malloc(sizeof(*e));
++ if (e == NULL)
++ return NULL;
++ mbedtls_ecp_group_init(e);
++ if (mbedtls_ecp_group_load(e, grp_id) == 0)
++ return (struct crypto_ec *)e;
++
++ mbedtls_ecp_group_free(e);
++ os_free(e);
++ return NULL;
++}
++
++void crypto_ec_deinit(struct crypto_ec *e)
++{
++ mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
++ os_free(e);
++}
++
++size_t crypto_ec_prime_len(struct crypto_ec *e)
++{
++ return CRYPTO_EC_plen(e);
++}
++
++size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
++{
++ return CRYPTO_EC_pbits(e);
++}
++
++size_t crypto_ec_order_len(struct crypto_ec *e)
++{
++ return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
++}
++
++const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *)CRYPTO_EC_P(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *)CRYPTO_EC_N(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
++{
++ static const uint8_t secp256r1_a[] =
++ {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++ static const uint8_t secp384r1_a[] =
++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
++ static const uint8_t secp521r1_a[] =
++ {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xfc};
++ static const uint8_t secp192r1_a[] =
++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++ static const uint8_t secp224r1_a[] =
++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xfe};
++
++ const uint8_t *bin = NULL;
++ size_t len = 0;
++
++ /* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
++ switch (((mbedtls_ecp_group *)e)->id) {
++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP256R1:
++ bin = secp256r1_a;
++ len = sizeof(secp256r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP384R1:
++ bin = secp384r1_a;
++ len = sizeof(secp384r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP521R1:
++ bin = secp521r1_a;
++ len = sizeof(secp521r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP192R1:
++ bin = secp192r1_a;
++ len = sizeof(secp192r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP224R1:
++ bin = secp224r1_a;
++ len = sizeof(secp224r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++ case MBEDTLS_ECP_DP_BP256R1:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++ case MBEDTLS_ECP_DP_BP384R1:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++ case MBEDTLS_ECP_DP_BP512R1:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++ case MBEDTLS_ECP_DP_CURVE25519:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++ case MBEDTLS_ECP_DP_CURVE448:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ default:
++ return NULL;
++ }
++
++ /*(note: not thread-safe; returns file-scoped static storage)*/
++ if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
++ return (const struct crypto_bignum *)&mpi_sw_A;
++ return NULL;
++}
++
++const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *)CRYPTO_EC_B(e);
++}
++
++const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
++{
++ return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
++}
++
++struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++ if (p != NULL)
++ mbedtls_ecp_point_init(p);
++ return (struct crypto_ec_point *)p;
++}
++
++void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
++{
++ mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
++ os_free(p);
++}
++
++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
++ struct crypto_bignum *x)
++{
++ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++ return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
++ ? -1
++ : 0;
++}
++
++int crypto_ec_point_to_bin(struct crypto_ec *e,
++ const struct crypto_ec_point *point, u8 *x, u8 *y)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
++ size_t len = CRYPTO_EC_plen(e);
++ if (x) {
++ mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
++ if (mbedtls_mpi_write_binary(px, x, len))
++ return -1;
++ }
++ if (y) {
++ #if 0 /*(should not be necessary; py mpi should be in initial state)*/
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ os_memset(y, 0, len);
++ return 0;
++ }
++ #endif
++ #endif
++ mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
++ if (mbedtls_mpi_write_binary(py, y, len))
++ return -1;
++ }
++ return 0;
++}
++
++struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
++ const u8 *val)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ size_t len = CRYPTO_EC_plen(e);
++ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++ if (p == NULL)
++ return NULL;
++ mbedtls_ecp_point_init(p);
++
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
++ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++ mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
++
++ if (mbedtls_mpi_read_binary(px, val, len) == 0
++ && mbedtls_mpi_read_binary(py, val + len, len) == 0
++ && mbedtls_mpi_lset(pz, 1) == 0)
++ return (struct crypto_ec_point *)p;
++ #else
++ buf[0] = 0x04;
++ os_memcpy(buf+1, val, len*2);
++ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++ buf, 1+len*2) == 0)
++ return (struct crypto_ec_point *)p;
++ #endif
++ }
++ #endif
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ /* crypto.h interface documents crypto_ec_point_from_bin()
++ * val is length: prime_len * 2 and is big-endian
++ * (Short Weierstrass is assumed by hostap)
++ * Reverse to little-endian format for Montgomery */
++ for (unsigned int i = 0; i < len; ++i)
++ buf[i] = val[len-1-i];
++ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++ buf, len) == 0)
++ return (struct crypto_ec_point *)p;
++ }
++ #endif
++
++ mbedtls_ecp_point_free(p);
++ os_free(p);
++ return NULL;
++}
++
++int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
++ const struct crypto_ec_point *b,
++ struct crypto_ec_point *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /* mbedtls does not provide an mbedtls_ecp_point add function */
++ mbedtls_mpi one;
++ mbedtls_mpi_init(&one);
++ int ret = mbedtls_mpi_lset(&one, 1)
++ || mbedtls_ecp_muladd(
++ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
++ &one, (const mbedtls_ecp_point *)a,
++ &one, (const mbedtls_ecp_point *)b) ? -1 : 0;
++ mbedtls_mpi_free(&one);
++ return ret;
++}
++
++int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
++ const struct crypto_bignum *b,
++ struct crypto_ec_point *res)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_ecp_mul(
++ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
++ (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
++ mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
++ wpa_printf(MSG_ERROR,
++ "%s not implemented for Montgomery curves",__func__);
++ return -1;
++ }
++
++ /* mbedtls does not provide an mbedtls_ecp_point invert function */
++ /* below works for Short Weierstrass; incorrect for Montgomery curves */
++ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
++ || mbedtls_mpi_cmp_int(py, 0) == 0 /*point is its own inverse*/
++ || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
++}
++
++#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++static int
++crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++ mbedtls_mpi *y2)
++{
++ /* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS y^2 = x^3 + a x + b */
++
++ /* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
++ /* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
++ * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
++ * treated as if A = -3. */
++
++ #if 0
++ /* y^2 = x^3 + ax + b */
++ mbedtls_mpi *A = &e->A;
++ mbedtls_mpi t, A_neg3;
++ if (&e->A.p == NULL) {
++ mbedtls_mpi_init(&A_neg3);
++ if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
++ mbedtls_mpi_free(&A_neg3);
++ return -1;
++ }
++ A = &A_neg3;
++ }
++ mbedtls_mpi_init(&t);
++ int ret = /* x^3 */
++ mbedtls_mpi_lset(&t, 3)
++ || mbedtls_mpi_exp_mod(y2, x, &t, &e->P, NULL)
++ /* ax */
++ || mbedtls_mpi_mul_mpi(y2, y2, A)
++ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++ /* ax + b */
++ || mbedtls_mpi_add_mpi(&t, &t, &e->B)
++ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++ /* x^3 + ax + b */
++ || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
++ || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
++ mbedtls_mpi_free(&t);
++ if (A == &A_neg3)
++ mbedtls_mpi_free(&A_neg3);
++ return ret; /* 0: success, non-zero: failure */
++ #else
++ /* y^2 = x^3 + ax + b = (x^2 + a)x + b */
++ return /* x^2 */
++ mbedtls_mpi_mul_mpi(y2, x, x)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* x^2 + a */
++ || (e->A.MBEDTLS_PRIVATE(p)
++ ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
++ : mbedtls_mpi_sub_int(y2, y2, 3))
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x^2 + a)x */
++ || mbedtls_mpi_mul_mpi(y2, y2, x)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x^2 + a)x + b */
++ || mbedtls_mpi_add_mpi(y2, y2, &e->B)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++ #endif
++}
++#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
++
++#if 0 /* not used by hostap */
++#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++static int
++crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++ mbedtls_mpi *y2)
++{
++ /* XXX: !!! must be reviewed and audited for correctness !!! */
++
++ /* MBEDTLS_ECP_TYPE_MONTGOMERY y^2 = x^3 + a x^2 + x */
++
++ /* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
++ mbedtls_mpi x2;
++ mbedtls_mpi_init(&x2);
++ int ret = /* x^2 */
++ mbedtls_mpi_mul_mpi(&x2, x, x)
++ || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
++ /* x + a */
++ || mbedtls_mpi_add_mpi(y2, x, &e->A)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x + a)x^2 */
++ || mbedtls_mpi_mul_mpi(y2, y2, &x2)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x + a)x^2 + x */
++ || mbedtls_mpi_add_mpi(y2, y2, x)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++ mbedtls_mpi_free(&x2);
++ return ret; /* 0: success, non-zero: failure */
++}
++#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
++#endif
++
++struct crypto_bignum *
++crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
++ const struct crypto_bignum *x)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
++ if (y2 == NULL)
++ return NULL;
++ mbedtls_mpi_init(y2);
++
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++ && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
++ (const mbedtls_mpi *)x,
++ y2) == 0)
++ return (struct crypto_bignum *)y2;
++ #endif
++ #if 0 /* not used by hostap */
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY
++ && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
++ (const mbedtls_mpi *)x,
++ y2) == 0)
++ return (struct crypto_bignum *)y2;
++ #endif
++ #endif
++
++ mbedtls_mpi_free(y2);
++ os_free(y2);
++ return NULL;
++}
++
++int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
++ const struct crypto_ec_point *p)
++{
++ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
++}
++
++int crypto_ec_point_is_on_curve(struct crypto_ec *e,
++ const struct crypto_ec_point *p)
++{
++ #if 1
++ return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
++ (const mbedtls_ecp_point *)p) == 0;
++ #else
++ /* compute y^2 mod P and compare to y^2 mod P */
++ /*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
++ const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++ mbedtls_mpi *cy2 = (mbedtls_mpi *)
++ crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
++ if (cy2 == NULL)
++ return 0;
++
++ mbedtls_mpi y2;
++ mbedtls_mpi_init(&y2);
++ const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++ int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
++ || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
++ || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
++
++ mbedtls_mpi_free(&y2);
++ mbedtls_mpi_free(cy2);
++ os_free(cy2);
++ return is_on_curve;
++ #endif
++}
++
++int crypto_ec_point_cmp(const struct crypto_ec *e,
++ const struct crypto_ec_point *a,
++ const struct crypto_ec_point *b)
++{
++ return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
++ (const mbedtls_ecp_point *)b);
++}
++
++#if !defined(CONFIG_NO_STDOUT_DEBUG)
++void crypto_ec_point_debug_print(const struct crypto_ec *e,
++ const struct crypto_ec_point *p,
++ const char *title)
++{
++ u8 x[MBEDTLS_MPI_MAX_SIZE];
++ u8 y[MBEDTLS_MPI_MAX_SIZE];
++ size_t len = CRYPTO_EC_plen(e);
++ /* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
++ struct crypto_ec *ee;
++ *(const struct crypto_ec **)&ee = e; /*(cast away const)*/
++ if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
++ if (title)
++ wpa_printf(MSG_DEBUG, "%s", title);
++ wpa_hexdump(MSG_DEBUG, "x:", x, len);
++ wpa_hexdump(MSG_DEBUG, "y:", y, len);
++ }
++}
++#endif
++
++
++struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
++{
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
++ #else
++ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0)
++ #endif
++ return (struct crypto_ec_key *)ctx;
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++#ifdef CONFIG_MODULE_TESTS
++/*(for crypto_module_tests.c)*/
++struct crypto_ec_key * crypto_ec_key_set_priv(int group,
++ const u8 *raw, size_t raw_len)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ const mbedtls_pk_info_t *pk_info =
++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++ if (pk_info == NULL)
++ return NULL;
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ if (mbedtls_pk_setup(ctx, pk_info) == 0
++ && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
++ return (struct crypto_ec_key *)ctx;
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++#endif
++#endif
++
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
++{
++ /* The following is modified from:
++ * mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
++ * mbedtls/library/pkparse.c:pk_get_pk_alg()
++ * mbedtls/library/pkparse.c:pk_use_ecparams()
++ */
++ mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
++ const mbedtls_pk_info_t *pk_info;
++ int ret;
++ size_t len;
++ const unsigned char *end = der+der_len;
++ unsigned char *p;
++ *(const unsigned char **)&p = der;
++
++ if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
++ {
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
++ }
++
++ end = p + len;
++
++ /*
++ if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
++ return( ret );
++ */
++ mbedtls_asn1_buf alg_oid, params;
++ memset( ¶ms, 0, sizeof(mbedtls_asn1_buf) );
++ if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, ¶ms ) ) != 0 )
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
++ if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
++ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++ if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
++
++ if( p + len != end )
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
++ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
++
++ if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
++ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
++ return( ret );
++
++ /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
++ * has already run with ctx initialized up to pk_get_ecpubkey(),
++ * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
++ *
++ * mbedtls mbedtls_ecp_point_read_binary()
++ * does not handle point in COMPRESSED format
++ *
++ * (validate assumption that algorithm is EC) */
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++ if (ecp_kp == NULL)
++ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ mbedtls_ecp_group_id grp_id;
++
++
++ /* mbedtls/library/pkparse.c:pk_use_ecparams() */
++
++ if( params.tag == MBEDTLS_ASN1_OID )
++ {
++ if( mbedtls_oid_get_ec_grp( ¶ms, &grp_id ) != 0 )
++ return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
++ }
++ else
++ {
++#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
++ /*(large code block not copied from mbedtls; unsupported)*/
++ #if 0
++ if( ( ret = pk_group_id_from_specified( ¶ms, &grp_id ) ) != 0 )
++ return( ret );
++ #endif
++#endif
++ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++ }
++
++ /*
++ * grp may already be initialized; if so, make sure IDs match
++ */
++ if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
++ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++
++ if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
++ return( ret );
++
++
++ /* (validate assumption that EC point is in COMPRESSED format) */
++ len = CRYPTO_EC_plen(ecp_kp_grp);
++ if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++ || (end - p) != 1+len
++ || (*p != 0x02 && *p != 0x03) )
++ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++
++ /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
++ * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
++ mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
++ mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
++ mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
++ ret = mbedtls_mpi_lset(Z, 1);
++ if (ret != 0)
++ return( ret );
++ ret = mbedtls_mpi_read_binary(X, p+1, len);
++ if (ret != 0)
++ return( ret );
++ /* derive Y
++ * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
++ ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
++ || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
++ if (ret != 0)
++ return( ret );
++
++ return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
++}
++
++struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
++{
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ /*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
++ int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
++ if (rc == 0)
++ return (struct crypto_ec_key *)ctx;
++ else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
++ /* mbedtls mbedtls_ecp_point_read_binary()
++ * does not handle point in COMPRESSED format; parse internally */
++ rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
++ if (rc == 0)
++ return (struct crypto_ec_key *)ctx;
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++static struct crypto_ec_key *
++crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
++ const mbedtls_ecp_point *pub,
++ const u8 *buf, size_t len)
++{
++ const mbedtls_pk_info_t *pk_info =
++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++ if (pk_info == NULL)
++ return NULL;
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ if (mbedtls_pk_setup(ctx, pk_info) == 0) {
++ /* (Is private key generation necessary for callers?)
++ * alt: gen key then overwrite Q
++ * mbedtls_ecp_gen_key(grp_id, ecp_kp,
++ * mbedtls_ctr_drbg_random,
++ * crypto_mbedtls_ctr_drbg()) == 0
++ */
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++ if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
++ && (pub
++ ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
++ : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
++ buf, len) == 0)
++ && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0){
++ return (struct crypto_ec_key *)ctx;
++ }
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
++ const u8 *y, size_t len)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ if (len > MBEDTLS_MPI_MAX_SIZE)
++ return NULL;
++ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++ buf[0] = 0x04; /* assume x,y for Short Weierstrass */
++ os_memcpy(buf+1, x, len);
++ os_memcpy(buf+1+len, y, len);
++
++ return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
++}
++
++struct crypto_ec_key *
++crypto_ec_key_set_pub_point(struct crypto_ec *e,
++ const struct crypto_ec_point *pub)
++{
++ mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
++ mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
++ return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
++}
++
++
++struct crypto_ec_key * crypto_ec_key_gen(int group)
++{
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
++ return (struct crypto_ec_key *)ctx;
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++void crypto_ec_key_deinit(struct crypto_ec_key *key)
++{
++ mbedtls_pk_free((mbedtls_pk_context *)key);
++ os_free(key);
++}
++
++struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
++{
++ /* (similar to crypto_ec_key_get_pubkey_point(),
++ * but compressed point format and ASN.1 DER wrapping)*/
++#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++ unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
++ int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
++ buf, sizeof(buf));
++ if (len < 0)
++ return NULL;
++ /* Note: data is written at the end of the buffer! Use the
++ * return value to determine where you should start
++ * using the buffer */
++ unsigned char *p = buf+sizeof(buf)-len;
++
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ /* Note: sae_pk.c expects pubkey point in compressed format,
++ * but mbedtls_pk_write_pubkey_der() writes uncompressed format.
++ * Manually translate format and update lengths in DER format */
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ unsigned char *end = buf+sizeof(buf);
++ size_t n;
++ /* SubjectPublicKeyInfo SEQUENCE */
++ mbedtls_asn1_get_tag(&p, end, &n,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ /* algorithm AlgorithmIdentifier */
++ unsigned char *a = p;
++ size_t alen;
++ mbedtls_asn1_get_tag(&p, end, &alen,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ p += alen;
++ alen = (size_t)(p - a);
++ /* subjectPublicKey BIT STRING */
++ mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
++ /* rewrite into compressed point format and rebuild ASN.1 */
++ p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
++ n = 1 + 1 + (n-2)/2;
++ len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
++ len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
++ os_memmove(p-alen, a, alen);
++ len += alen;
++ p -= alen;
++ len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
++ len += mbedtls_asn1_write_tag(&p, buf,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ }
++ #endif
++ return wpabuf_alloc_copy(p, (size_t)len);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
++ bool include_pub)
++{
++#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++ unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
++ int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
++ priv, sizeof(priv));
++ if (privlen < 0)
++ return NULL;
++
++ struct wpabuf *wbuf;
++
++ /* Note: data is written at the end of the buffer! Use the
++ * return value to determine where you should start
++ * using the buffer */
++ /* mbedtls_pk_write_key_der() includes publicKey in DER */
++ if (include_pub)
++ wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
++ else {
++ /* calculate publicKey offset and skip from end of buffer */
++ unsigned char *p = priv+sizeof(priv)-privlen;
++ unsigned char *end = priv+sizeof(priv);
++ size_t len;
++ /* ECPrivateKey SEQUENCE */
++ mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ /* version INTEGER */
++ unsigned char *v = p;
++ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
++ p += len;
++ /* privateKey OCTET STRING */
++ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
++ p += len;
++ /* parameters ECParameters */
++ mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
++ p += len;
++
++ /* write new SEQUENCE header (we know that it fits in priv[]) */
++ len = (size_t)(p - v);
++ p = v;
++ len += mbedtls_asn1_write_len(&p, priv, len);
++ len += mbedtls_asn1_write_tag(&p, priv,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ wbuf = wpabuf_alloc_copy(p, len);
++ }
++
++ forced_memzero(priv, sizeof(priv));
++ return wbuf;
++}
++
++struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
++ int prefix)
++{
++ /*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ size_t len = CRYPTO_EC_plen(grp);
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ /* len */
++ #endif
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
++ len = len*2+1;
++ #endif
++ struct wpabuf *buf = wpabuf_alloc(len);
++ if (buf == NULL)
++ return NULL;
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
++ MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
++ wpabuf_mhead_u8(buf), len) == 0) {
++ if (!prefix) /* Remove 0x04 prefix if requested */
++ os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
++ wpabuf_put(buf, len);
++ return buf;
++ }
++
++ wpabuf_free(buf);
++ return NULL;
++}
++
++struct crypto_ec_point *
++crypto_ec_key_get_public_key(struct crypto_ec_key *key)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++ if (p != NULL) {
++ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++ mbedtls_ecp_point_init(p);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
++ mbedtls_ecp_point_free(p);
++ os_free(p);
++ p = NULL;
++ }
++ }
++ return (struct crypto_ec_point *)p;
++}
++
++struct crypto_bignum *
++crypto_ec_key_get_private_key(struct crypto_ec_key *key)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn) {
++ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++ mbedtls_mpi_init(bn);
++ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++ if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
++ mbedtls_mpi_free(bn);
++ os_free(bn);
++ bn = NULL;
++ }
++ }
++ return (struct crypto_bignum *)bn;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
++{
++ /* get mbedtls_md_type_t from length of hash data to be signed */
++ switch (len) {
++ case 64: return MBEDTLS_MD_SHA512;
++ case 48: return MBEDTLS_MD_SHA384;
++ case 32: return MBEDTLS_MD_SHA256;
++ case 20: return MBEDTLS_MD_SHA1;
++ case 16: return MBEDTLS_MD_MD5;
++ default: return MBEDTLS_MD_NONE;
++ }
++}
++
++struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
++ size_t len)
++{
++ #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
++ #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
++ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
++ #else
++ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
++ #endif
++ #endif
++ size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
++ struct wpabuf *buf = wpabuf_alloc(sig_len);
++ if (buf == NULL)
++ return NULL;
++ if (mbedtls_pk_sign((mbedtls_pk_context *)key,
++ crypto_ec_key_sign_md(len), data, len,
++ wpabuf_mhead_u8(buf),
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ sig_len,
++ #endif
++ &sig_len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0) {
++ wpabuf_put(buf, sig_len);
++ return buf;
++ }
++
++ wpabuf_free(buf);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
++ const u8 *data, size_t len)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++
++ size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
++ u8 buf[MBEDTLS_ECDSA_MAX_LEN];
++ if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
++ data, len, buf,
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ sig_len,
++ #endif
++ &sig_len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg())) {
++ return NULL;
++ }
++
++ /*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
++ /* parse ASN.1 to get r and s and lengths */
++ u8 *p = buf, *r, *s;
++ u8 *end = p + sig_len;
++ size_t rlen, slen;
++ mbedtls_asn1_get_tag(&p, end, &rlen,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
++ r = p;
++ p += rlen;
++ mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
++ s = p;
++
++ /* write raw r and s into out
++ * (including removal of leading 0 if added for ASN.1 integer)
++ * note: DPP caller expects raw r, s each padded to prime len */
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
++ if (rlen > plen) {
++ r += (rlen - plen);
++ rlen = plen;
++ }
++ if (slen > plen) {
++ s += (slen - plen);
++ slen = plen;
++ }
++ struct wpabuf *out = wpabuf_alloc(plen*2);
++ if (out) {
++ wpabuf_put(out, plen*2);
++ p = wpabuf_mhead_u8(out);
++ os_memset(p, 0, plen*2);
++ os_memcpy(p+plen*1-rlen, r, rlen);
++ os_memcpy(p+plen*2-slen, s, slen);
++ }
++ return out;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
++ size_t len, const u8 *sig, size_t sig_len)
++{
++ switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
++ crypto_ec_key_sign_md(len), data, len,
++ sig, sig_len)) {
++ case 0:
++ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++ return 1;
++ case MBEDTLS_ERR_ECP_VERIFY_FAILED:
++ return 0;
++ default:
++ return -1;
++ }
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
++ const u8 *data, size_t len,
++ const u8 *r, size_t r_len,
++ const u8 *s, size_t s_len)
++{
++ /* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
++ * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return -1;
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++
++ mbedtls_mpi mpi_r;
++ mbedtls_mpi mpi_s;
++ mbedtls_mpi_init(&mpi_r);
++ mbedtls_mpi_init(&mpi_s);
++ int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
++ || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
++ if (ret == 0) {
++ ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
++ ecp_kp_Q, &mpi_r, &mpi_s);
++ ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
++ }
++ mbedtls_mpi_free(&mpi_r);
++ mbedtls_mpi_free(&mpi_s);
++ return ret;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_group(struct crypto_ec_key *key)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return -1;
++ mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
++{
++#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++ (const mbedtls_pk_context *)key2) ? -1 : 0;
++ #else
++ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++ (const mbedtls_pk_context *)key2,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++ #endif
++#else
++ mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
++ mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
++ if (ecp_kp1 == NULL || ecp_kp2 == NULL)
++ return -1;
++ mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
++ mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
++ return ecp_kp1_grp->id != ecp_kp2_grp->id
++ || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
++#endif
++}
++
++void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
++ const char *title)
++{
++ /* TBD: what info is desirable here and in what human readable format?*/
++ /*(crypto_openssl.c prints a human-readably public key and attributes)*/
++ #if 0
++ struct mbedtls_pk_debug_item debug_item;
++ if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
++ return;
++ /* ... */
++ #endif
++ wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
++
++#include <mbedtls/x509_csr.h>
++#include <mbedtls/oid.h>
++
++struct crypto_csr * crypto_csr_init(void)
++{
++ mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
++ if (csr != NULL)
++ mbedtls_x509write_csr_init(csr);
++ return (struct crypto_csr *)csr;
++}
++
++struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
++{
++ /* future: look for alternatives to MBEDTLS_PRIVATE() access */
++
++ /* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
++ * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
++ * so allocate different object (mbedtls_x509_csr *) and special-case
++ * object when used in crypto_csr_get_attribute() and when free()d in
++ * crypto_csr_deinit(). */
++
++ mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
++ if (csr == NULL)
++ return NULL;
++ mbedtls_x509_csr_init(csr);
++ const mbedtls_md_info_t *md_info;
++ unsigned char digest[MBEDTLS_MD_MAX_SIZE];
++ if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
++ && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
++ != NULL
++ && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
++ switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
++ digest, mbedtls_md_get_size(md_info),
++ csr->MBEDTLS_PRIVATE(sig).p,
++ csr->MBEDTLS_PRIVATE(sig).len)) {
++ case 0:
++ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++ return (struct crypto_csr *)((uintptr_t)csr | 1uL);
++ default:
++ break;
++ }
++ }
++
++ mbedtls_x509_csr_free(csr);
++ os_free(csr);
++ return NULL;
++}
++
++void crypto_csr_deinit(struct crypto_csr *csr)
++{
++ if ((uintptr_t)csr & 1uL) {
++ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++ mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
++ }
++ else
++ mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
++ os_free(csr);
++}
++
++int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
++ struct crypto_ec_key *key)
++{
++ mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
++ (mbedtls_pk_context *)key);
++ return 0;
++}
++
++int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
++ const char *name)
++{
++ /* specialized for src/common/dpp_crypto.c */
++
++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr()
++ * calls this function only once, using type == CSR_NAME_CN
++ * (If called more than once, this code would need to append
++ * components to the subject name, which we could do by
++ * appending to (mbedtls_x509write_csr *) private member
++ * mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
++
++ const char *label;
++ switch (type) {
++ case CSR_NAME_CN: label = "CN="; break;
++ case CSR_NAME_SN: label = "SN="; break;
++ case CSR_NAME_C: label = "C="; break;
++ case CSR_NAME_O: label = "O="; break;
++ case CSR_NAME_OU: label = "OU="; break;
++ default: return -1;
++ }
++
++ size_t len = strlen(name);
++ struct wpabuf *buf = wpabuf_alloc(3+len+1);
++ if (buf == NULL)
++ return -1;
++ wpabuf_put_data(buf, label, strlen(label));
++ wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
++ /* Note: 'name' provided is set as given and should be backslash-escaped
++ * by caller when necessary, e.g. literal ',' which are not separating
++ * components should be backslash-escaped */
++
++ int ret =
++ mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
++ wpabuf_head(buf)) ? -1 : 0;
++ wpabuf_free(buf);
++ return ret;
++}
++
++/* OBJ_pkcs9_challengePassword 1 2 840 113549 1 9 7 */
++static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
++
++int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
++ int attr_type, const u8 *value, size_t len)
++{
++ /* specialized for src/common/dpp_crypto.c */
++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++ * attr == CSR_ATTR_CHALLENGE_PASSWORD
++ * attr_type == ASN1_TAG_UTF8STRING */
++
++ const char *oid;
++ size_t oid_len;
++ switch (attr) {
++ case CSR_ATTR_CHALLENGE_PASSWORD:
++ oid = OBJ_pkcs9_challengePassword;
++ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++ break;
++ default:
++ return -1;
++ }
++
++ #if 0 /*(incorrect; sets an extension, not an attribute)*/
++ return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
++ oid, oid_len,
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ 0, /*(critical flag)*/
++ #endif
++ value, len) ? -1 : 0;
++ #else
++ (void)oid;
++ (void)oid_len;
++ #endif
++
++ /* mbedtls does not currently provide way to set an attribute in a CSR:
++ * https://github.com/Mbed-TLS/mbedtls/issues/4886 */
++ wpa_printf(MSG_ERROR,
++ "mbedtls does not currently support setting challengePassword "
++ "attribute in CSR");
++ return -1;
++}
++
++const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
++ const char *oid, size_t oid_len,
++ size_t *vlen, int *vtype)
++{
++ /* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
++ * so validation checks are not repeated here
++ *
++ * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
++ * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
++ * already parsed the rest of CertificationRequestInfo, some of which is
++ * repeated here to step to Attributes. Since csr->subject_raw.p points
++ * into csr->cri.p, which points into csr->raw.p, step over version and
++ * subject of CertificationRequestInfo (SEQUENCE) */
++ unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
++ unsigned char *end = csr->cri.p + csr->cri.len, *ext;
++ size_t len;
++
++ /* step over SubjectPublicKeyInfo */
++ mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ p += len;
++
++ /* Attributes
++ * { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
++ */
++ if (mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
++ return NULL;
++ }
++ while (p < end) {
++ if (mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
++ return NULL;
++ }
++ ext = p;
++ p += len;
++
++ if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
++ return NULL;
++ if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
++ continue;
++
++ /* found oid; return value */
++ *vtype = *ext++; /* tag */
++ return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
++ }
++
++ return NULL;
++}
++
++const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
++ enum crypto_csr_attr attr,
++ size_t *len, int *type)
++{
++ /* specialized for src/common/dpp_crypto.c */
++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++ * attr == CSR_ATTR_CHALLENGE_PASSWORD */
++
++ const char *oid;
++ size_t oid_len;
++ switch (attr) {
++ case CSR_ATTR_CHALLENGE_PASSWORD:
++ oid = OBJ_pkcs9_challengePassword;
++ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++ break;
++ default:
++ return NULL;
++ }
++
++ /* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
++ if (!((uintptr_t)csr & 1uL))
++ return NULL;
++ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++
++ return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
++ oid, oid_len, len, type);
++}
++
++struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
++ struct crypto_ec_key *key,
++ enum crypto_hash_alg algo)
++{
++ mbedtls_md_type_t sig_md;
++ switch (algo) {
++ #ifdef MBEDTLS_SHA256_C
++ case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
++ #endif
++ #ifdef MBEDTLS_SHA512_C
++ case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
++ case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
++ #endif
++ default:
++ return NULL;
++ }
++ mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
++
++ #if 0
++ unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
++ | MBEDTLS_X509_KU_KEY_CERT_SIGN;
++ if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
++ key_usage))
++ return NULL;
++ #endif
++
++ #if 0
++ unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
++ | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
++ if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
++ ns_cert_type))
++ return NULL;
++ #endif
++
++ #if 0
++ /* mbedtls does not currently provide way to set an attribute in a CSR:
++ * https://github.com/Mbed-TLS/mbedtls/issues/4886
++ * XXX: hwsim dpp_enterprise test fails due to this limitation.
++ *
++ * Current usage of this function is solely by dpp_build_csr(),
++ * so as a kludge, might consider custom (struct crypto_csr *)
++ * containing (mbedtls_x509write_csr *) and a list of attributes
++ * (i.e. challengePassword). Might have to totally reimplement
++ * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
++ * handles signing the CSR. (This is more work that appending an
++ * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
++ */
++ #endif
++
++ unsigned char buf[4096]; /* XXX: large enough? too large? */
++ int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
++ buf, sizeof(buf),
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg());
++ if (len < 0)
++ return NULL;
++ /* Note: data is written at the end of the buffer! Use the
++ * return value to determine where you should start
++ * using the buffer */
++ return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
++
++#if 0
++#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
++#include <mbedtls/pem.h>
++#endif
++
++struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
++{
++ /* PKCS7 is not currently supported in mbedtls */
++ return NULL;
++
++#if 0
++ /* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
++ * (??? potential future contribution to mbedtls ???) */
++
++ /* Note: PKCS7 signature *is not* verified by this function.
++ * The function interface does not provide for passing a certificate */
++
++ mbedtls_pkcs7 mpkcs7;
++ mbedtls_pkcs7_init(&mpkcs7);
++ int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
++ wpabuf_len(pkcs7),
++ &mpkcs7);
++ wpabuf *buf = NULL;
++ do {
++ if (pkcs7_type < 0)
++ break;
++
++ /* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
++ * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
++ * (? are adding certificate headers and footers desired ?) */
++
++ /* development-pkcs7 branch does not currently provide
++ * additional interfaces to retrieve the parsed data */
++
++ mbedtls_x509_crt *certs =
++ &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
++ int ncerts =
++ mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
++
++ /* allocate buffer for PEM (base64-encoded DER)
++ * plus header, footer, newlines, and some extra */
++ buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
++ if (buf == NULL)
++ break;
++
++ #define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
++ #define PEM_END_CRT "-----END CERTIFICATE-----\n"
++ size_t olen;
++ for (int i = 0; i < ncerts; ++i) {
++ int ret = mbedtls_pem_write_buffer(
++ PEM_BEGIN_CRT, PEM_END_CRT,
++ certs[i].raw.p, certs[i].raw.len,
++ wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
++ &olen));
++ if (ret == 0)
++ wpabuf_put(buf, olen);
++ } else {
++ if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
++ ret = wpabuf_resize(
++ &buf,olen-wpabuf_tailroom(buf));
++ if (ret == 0) {
++ --i;/*(adjust loop iterator for retry)*/
++ continue;
++ }
++ wpabuf_free(buf);
++ buf = NULL;
++ break;
++ }
++ }
++ } while (0);
++
++ mbedtls_pkcs7_free(&mpkcs7);
++ return buf;
++#endif
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
++
++
++#ifdef MBEDTLS_ARC4_C
++#include <mbedtls/arc4.h>
++int rc4_skip(const u8 *key, size_t keylen, size_t skip,
++ u8 *data, size_t data_len)
++{
++ mbedtls_arc4_context ctx;
++ mbedtls_arc4_init(&ctx);
++ mbedtls_arc4_setup(&ctx, key, keylen);
++
++ if (skip) {
++ /*(prefer [16] on ancient hardware with smaller cache lines)*/
++ unsigned char skip_buf[64]; /*('skip' is generally small)*/
++ /*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
++ size_t len;
++ do {
++ len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
++ mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
++ } while ((skip -= len));
++ }
++
++ int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
++ mbedtls_arc4_free(&ctx);
++ return ret;
++}
++#endif
++
++
++/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
++__attribute_noinline__
++static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++ #if 0 /* #ifdef MBEDTLS_FS_IO */
++ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++ return -1;
++ }
++ #else
++ /*(use os_readfile() so that we can use os_free()
++ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++ * on buf aborts in tests if buf not allocated via os_malloc())*/
++ *buf = (u8 *)os_readfile(path, n);
++ if (!*buf) {
++ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++ return -1;
++ }
++ u8 *buf0 = os_realloc(*buf, *n+1);
++ if (!buf0) {
++ bin_clear_free(*buf, *n);
++ *buf = NULL;
++ return -1;
++ }
++ buf0[(*n)++] = '\0';
++ *buf = buf0;
++ #endif
++ return 0;
++}
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
++#ifdef MBEDTLS_RSA_C
++
++#include <mbedtls/pk.h>
++#include <mbedtls/rsa.h>
++
++struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
++{
++ /* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
++ * require #ifdef MBEDTLS_FS_IO in mbedtls library. Prefer to use
++ * crypto_mbedtls_readfile(), which wraps os_readfile() */
++ u8 *data;
++ size_t len;
++ if (crypto_mbedtls_readfile(file, &data, &len) != 0)
++ return NULL;
++
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL) {
++ bin_clear_free(data, len);
++ return NULL;
++ }
++ mbedtls_pk_init(ctx);
++
++ int rc;
++ rc = (private_key
++ ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ ,mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()
++ #endif
++ )
++ : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
++ && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
++
++ bin_clear_free(data, len);
++
++ if (rc) {
++ /* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
++ /* use MBEDTLS_MD_SHA256 for these hostap interfaces */
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ /*(no return value in mbedtls 2.x)*/
++ mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++ MBEDTLS_RSA_PKCS_V21,
++ MBEDTLS_MD_SHA256);
++ #else
++ if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++ MBEDTLS_RSA_PKCS_V21,
++ MBEDTLS_MD_SHA256) == 0)
++ #endif
++ return (struct crypto_rsa_key *)ctx;
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
++ const struct wpabuf *in)
++{
++ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++ size_t olen = mbedtls_rsa_get_len(pk_rsa);
++ struct wpabuf *buf = wpabuf_alloc(olen);
++ if (buf == NULL)
++ return NULL;
++
++ /* mbedtls_pk_encrypt() takes a few more hops to get to same func */
++ if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg(),
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ MBEDTLS_RSA_PRIVATE,
++ #endif
++ NULL, 0,
++ wpabuf_len(in), wpabuf_head(in),
++ wpabuf_put(buf, olen)) == 0) {
++ return buf;
++ }
++
++ wpabuf_clear_free(buf);
++ return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
++ const struct wpabuf *in)
++{
++ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++ size_t olen = mbedtls_rsa_get_len(pk_rsa);
++ struct wpabuf *buf = wpabuf_alloc(olen);
++ if (buf == NULL)
++ return NULL;
++
++ /* mbedtls_pk_decrypt() takes a few more hops to get to same func */
++ if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg(),
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ MBEDTLS_RSA_PUBLIC,
++ #endif
++ NULL, 0, &olen, wpabuf_head(in),
++ wpabuf_mhead(buf), olen) == 0) {
++ wpabuf_put(buf, olen);
++ return buf;
++ }
++
++ wpabuf_clear_free(buf);
++ return NULL;
++}
++
++void crypto_rsa_key_free(struct crypto_rsa_key *key)
++{
++ mbedtls_pk_free((mbedtls_pk_context *)key);
++ os_free(key);
++}
++
++#endif /* MBEDTLS_RSA_C */
++#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++
++struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
++ enum hpke_kdf_id kdf_id,
++ enum hpke_aead_id aead_id,
++ struct crypto_ec_key *peer_pub,
++ const u8 *info, size_t info_len,
++ const u8 *aad, size_t aad_len,
++ const u8 *pt, size_t pt_len)
++{
++ /* not yet implemented */
++ return NULL;
++}
++
++struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
++ enum hpke_kdf_id kdf_id,
++ enum hpke_aead_id aead_id,
++ struct crypto_ec_key *own_priv,
++ const u8 *info, size_t info_len,
++ const u8 *aad, size_t aad_len,
++ const u8 *enc_ct, size_t enc_ct_len)
++{
++ /* not yet implemented */
++ return NULL;
++}
++
++#endif
+diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
+index ffeddbadd..07c36d850 100644
+--- a/src/crypto/crypto_module_tests.c
++++ b/src/crypto/crypto_module_tests.c
+@@ -2470,6 +2470,139 @@ static int test_hpke(void)
+ }
+
+
++static int test_ecc(void)
++{
++#ifdef CONFIG_ECC
++#ifndef CONFIG_TLS_INTERNAL
++#ifndef CONFIG_TLS_GNUTLS
++#if defined(CONFIG_TLS_MBEDTLS) \
++ || defined(CONFIG_TLS_OPENSSL) \
++ || defined(CONFIG_TLS_WOLFSSL)
++ wpa_printf(MSG_INFO, "Testing ECC");
++ /* Note: some tests below are valid on supported Short Weierstrass
++ * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
++ * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
++ */
++#ifdef CONFIG_TLS_MBEDTLS
++ const int grps[] = {19, 20, 21, 25, 26, 28};
++#endif
++#ifdef CONFIG_TLS_OPENSSL
++ const int grps[] = {19, 20, 21, 26};
++#endif
++#ifdef CONFIG_TLS_WOLFSSL
++ const int grps[] = {19, 20, 21, 26};
++#endif
++ uint32_t i;
++ struct crypto_ec *e = NULL;
++ struct crypto_ec_point *p = NULL, *q = NULL;
++ struct crypto_bignum *x = NULL, *y = NULL;
++#ifdef CONFIG_DPP
++ u8 bin[4096];
++#endif
++ for (i = 0; i < ARRAY_SIZE(grps); ++i) {
++ e = crypto_ec_init(grps[i]);
++ if (e == NULL
++ || crypto_ec_prime_len(e) == 0
++ || crypto_ec_prime_len_bits(e) == 0
++ || crypto_ec_order_len(e) == 0
++ || crypto_ec_get_prime(e) == NULL
++ || crypto_ec_get_order(e) == NULL
++ || crypto_ec_get_a(e) == NULL
++ || crypto_ec_get_b(e) == NULL
++ || crypto_ec_get_generator(e) == NULL) {
++ break;
++ }
++#ifdef CONFIG_DPP
++ struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
++ if (key == NULL)
++ break;
++ p = crypto_ec_key_get_public_key(key);
++ q = crypto_ec_key_get_public_key(key);
++ crypto_ec_key_deinit(key);
++ if (p == NULL || q == NULL)
++ break;
++ if (!crypto_ec_point_is_on_curve(e, p))
++ break;
++
++ /* inverted point should not match original;
++ * double-invert should match */
++ if (crypto_ec_point_invert(e, q) != 0
++ || crypto_ec_point_cmp(e, p, q) == 0
++ || crypto_ec_point_invert(e, q) != 0
++ || crypto_ec_point_cmp(e, p, q) != 0) {
++ break;
++ }
++
++ /* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
++ * imbalanced interfaces? */
++ size_t prime_len = crypto_ec_prime_len(e);
++ if (prime_len * 2 > sizeof(bin))
++ break;
++ if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
++ break;
++ struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
++ if (tmp == NULL)
++ break;
++ if (crypto_ec_point_cmp(e, p, tmp) != 0) {
++ crypto_ec_point_deinit(tmp, 0);
++ break;
++ }
++ crypto_ec_point_deinit(tmp, 0);
++
++ x = crypto_bignum_init();
++ y = crypto_bignum_init_set(bin+prime_len, prime_len);
++ if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
++ break;
++ struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
++ if (y2 == NULL)
++ break;
++ if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
++ || crypto_bignum_cmp(y, y2) != 0) {
++ crypto_bignum_deinit(y2, 0);
++ break;
++ }
++ crypto_bignum_deinit(y2, 0);
++ crypto_bignum_deinit(x, 0);
++ crypto_bignum_deinit(y, 0);
++ x = NULL;
++ y = NULL;
++
++ x = crypto_bignum_init();
++ if (x == NULL)
++ break;
++ if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
++ break;
++ crypto_bignum_deinit(x, 0);
++ x = NULL;
++
++ crypto_ec_point_deinit(p, 0);
++ p = NULL;
++ crypto_ec_point_deinit(q, 0);
++ q = NULL;
++#endif /* CONFIG_DPP */
++ crypto_ec_deinit(e);
++ e = NULL;
++ }
++ if (i != ARRAY_SIZE(grps)) {
++ crypto_bignum_deinit(x, 0);
++ crypto_bignum_deinit(y, 0);
++ crypto_ec_point_deinit(p, 0);
++ crypto_ec_point_deinit(q, 0);
++ crypto_ec_deinit(e);
++ wpa_printf(MSG_INFO,
++ "ECC test case failed tls_id:%d", grps[i]);
++ return -1;
++ }
++
++ wpa_printf(MSG_INFO, "ECC test cases passed");
++#endif
++#endif /* !CONFIG_TLS_GNUTLS */
++#endif /* !CONFIG_TLS_INTERNAL */
++#endif /* CONFIG_ECC */
++ return 0;
++}
++
++
+ static int test_ms_funcs(void)
+ {
+ #ifndef CONFIG_FIPS
+@@ -2591,6 +2724,7 @@ int crypto_module_tests(void)
+ test_fips186_2_prf() ||
+ test_extract_expand_hkdf() ||
+ test_hpke() ||
++ test_ecc() ||
+ test_ms_funcs())
+ ret = -1;
+
+diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
+index 269174321..a554fd8a8 100644
+--- a/src/crypto/crypto_wolfssl.c
++++ b/src/crypto/crypto_wolfssl.c
+@@ -1633,6 +1633,7 @@ struct crypto_ec {
+ #ifdef CONFIG_DPP
+ ecc_point *g; /* Only used in DPP for now */
+ #endif /* CONFIG_DPP */
++ WC_RNG rng;
+ mp_int a;
+ mp_int prime;
+ mp_int order;
+@@ -1666,6 +1667,20 @@ struct crypto_ec * crypto_ec_init(int group)
+ e->key = ecc_key_init();
+ if (!e->key) {
+ LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
++
++ if (wc_ecc_init(e->key) != 0 ||
++ wc_InitRng(e->rng) != 0 ||
++ wc_ecc_set_rng(e->key, e->rng) != 0 ||
++ wc_ecc_set_curve(e->key, 0, curve_id) != 0 ||
++ mp_init(e->a) != MP_OKAY ||
++ mp_init(e->prime) != MP_OKAY ||
++ mp_init(e->order) != MP_OKAY ||
++ mp_init(e->b) != MP_OKAY ||
++ mp_read_radix(e->a, e->key.dp->Af, 16) != MP_OKAY ||
++ mp_read_radix(e->b, e->key.dp->Bf, 16) != MP_OKAY ||
++ mp_read_radix(e->prime, e->key.dp->prime, 16) != MP_OKAY ||
++ mp_read_radix(e->order, e->key.dp->order, 16) != MP_OKAY ||
++ mp_montgomery_setup(e->prime, e->mont_b) != MP_OKAY)
+ goto done;
+ }
+
+@@ -1764,6 +1779,9 @@ void crypto_ec_deinit(struct crypto_ec* e)
+ #endif /* CONFIG_DPP */
+ if (e->own_key)
+ ecc_key_deinit(e->key);
++
++ wc_FreeRng(e->rng);
++ wc_ecc_free(e->key);
+ os_free(e);
+ }
+
+diff --git a/src/crypto/tls_mbedtls.c b/src/crypto/tls_mbedtls.c
+new file mode 100644
+index 000000000..2580a3a27
+--- /dev/null
++++ b/src/crypto/tls_mbedtls.c
+@@ -0,0 +1,3313 @@
++/*
++ * SSL/TLS interface functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ *
++ * template: src/crypto/tls_none.c
++ * reference: src/crypto/tls_*.c
++ *
++ * Known Limitations:
++ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
++ * - no OCSP (not yet available in mbedtls)
++ * - mbedtls does not support all certificate encodings used by hwsim tests
++ * PCKS#5 v1.5
++ * PCKS#12
++ * DH DSA
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - mbedtls does not currently provide way to set an attribute in a CSR
++ * https://github.com/Mbed-TLS/mbedtls/issues/4886
++ * so tests/hwsim dpp_enterprise tests fail
++ * - DPP2 not supported
++ * PKCS#7 parsing is not supported in mbedtls
++ * See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
++ * - DPP3 not supported
++ * hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
++ *
++ * Status:
++ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
++ * (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
++ * (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
++ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
++ * - passes all tests/ crypto module tests (incomplete coverage)
++ * ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
++ * - passes almost all tests/hwsim tests
++ * (hwsim tests skipped for missing features)
++ *
++ * RFE:
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - client/server session resumption, and/or save client session ticket
++ */
++
++#include "includes.h"
++#include "common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++#include <mbedtls/pem.h>
++#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/ssl.h>
++#include <mbedtls/ssl_ticket.h>
++#include <mbedtls/x509.h>
++#include <mbedtls/x509_crt.h>
++
++#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
++#include <mbedtls/net_sockets.h>
++#else
++#include <mbedtls/net.h>
++#endif
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
++ ((ssl)->MBEDTLS_PRIVATE(session) \
++ ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
++ : 0)
++#define mbedtls_ssl_ciphersuite_get_name(info) \
++ (info)->MBEDTLS_PRIVATE(name)
++#endif
++
++#include "crypto.h" /* sha256_vector() */
++#include "tls.h"
++
++#ifndef SHA256_DIGEST_LENGTH
++#define SHA256_DIGEST_LENGTH 32
++#endif
++
++#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
++#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
++#endif
++
++#ifndef MBEDTLS_EXPKEY_RAND_LEN
++#define MBEDTLS_EXPKEY_RAND_LEN 32
++#endif
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
++#else /*(not implemented; return error)*/
++#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
++typedef mbedtls_tls_prf_types int;
++#endif
++
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#ifdef MBEDTLS_SSL_SESSION_TICKETS
++#ifdef MBEDTLS_SSL_TICKET_C
++#define TLS_MBEDTLS_SESSION_TICKETS
++#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#define TLS_MBEDTLS_EAP_TEAP
++#endif
++#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define TLS_MBEDTLS_EAP_FAST
++#endif
++#endif
++#endif
++#endif
++#endif
++
++
++struct tls_conf {
++ mbedtls_ssl_config conf;
++
++ unsigned int verify_peer:1;
++ unsigned int verify_depth0_only:1;
++ unsigned int check_crl:2; /*(needs :2 bits for 0, 1, 2)*/
++ unsigned int check_crl_strict:1; /*(needs :1 bit for 0, 1)*/
++ unsigned int ca_cert_probe:1;
++ unsigned int has_ca_cert:1;
++ unsigned int has_client_cert:1;
++ unsigned int has_private_key:1;
++ unsigned int suiteb128:1;
++ unsigned int suiteb192:1;
++ mbedtls_x509_crl *crl;
++ mbedtls_x509_crt ca_cert;
++ mbedtls_x509_crt client_cert;
++ mbedtls_pk_context private_key;
++
++ uint32_t refcnt;
++
++ unsigned int flags;
++ char *subject_match;
++ char *altsubject_match;
++ char *suffix_match;
++ char *domain_match;
++ char *check_cert_subject;
++ u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
++
++ int *ciphersuites; /* list of ciphersuite ids for mbedtls_ssl_config */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++ mbedtls_ecp_group_id *curves;
++#else
++ uint16_t *curves; /* list of curve ids for mbedtls_ssl_config */
++#endif
++};
++
++
++struct tls_global {
++ struct tls_conf *tls_conf;
++ char *ocsp_stapling_response;
++ mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ mbedtls_ssl_ticket_context ticket_ctx;
++ #endif
++ char *ca_cert_file;
++ struct os_reltime crl_reload_previous;
++ unsigned int crl_reload_interval;
++ uint32_t refcnt;
++ struct tls_config init_conf;
++};
++
++static struct tls_global tls_ctx_global;
++
++
++struct tls_connection {
++ struct tls_conf *tls_conf;
++ struct wpabuf *push_buf;
++ struct wpabuf *pull_buf;
++ size_t pull_buf_offset;
++
++ unsigned int established:1;
++ unsigned int resumed:1;
++ unsigned int verify_peer:1;
++ unsigned int is_server:1;
++
++ mbedtls_ssl_context ssl;
++
++ mbedtls_tls_prf_types tls_prf_type;
++ size_t expkey_keyblock_size;
++ size_t expkey_secret_len;
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
++ #else
++ unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
++ #endif
++ unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
++
++ int read_alerts, write_alerts, failed;
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ tls_session_ticket_cb session_ticket_cb;
++ void *session_ticket_cb_ctx;
++ unsigned char *clienthello_session_ticket;
++ size_t clienthello_session_ticket_len;
++ #endif
++ char *peer_subject; /* peer subject info for authenticated peer */
++ struct wpabuf *success_data;
++};
++
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__ __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__ __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsg(int level, const char * const msg)
++{
++ wpa_printf(level, "MTLS: %s", msg);
++}
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsgrc(int level, const char * const msg, int rc)
++{
++ #ifdef MBEDTLS_ERROR_C
++ /* error logging convenience function that decodes mbedtls result codes */
++ char buf[256];
++ mbedtls_strerror(rc, buf, sizeof(buf));
++ wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
++ #else
++ wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
++ #endif
++}
++
++
++#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
++#define ilog(rc, msg) emsgrc(MSG_INFO, (msg), (rc))
++
++
++struct tls_conf * tls_conf_init(void *tls_ctx)
++{
++ struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
++ if (tls_conf == NULL)
++ return NULL;
++ tls_conf->refcnt = 1;
++
++ mbedtls_ssl_config_init(&tls_conf->conf);
++ mbedtls_ssl_conf_rng(&tls_conf->conf,
++ mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
++ mbedtls_x509_crt_init(&tls_conf->ca_cert);
++ mbedtls_x509_crt_init(&tls_conf->client_cert);
++ mbedtls_pk_init(&tls_conf->private_key);
++
++ return tls_conf;
++}
++
++
++void tls_conf_deinit(struct tls_conf *tls_conf)
++{
++ if (tls_conf == NULL || --tls_conf->refcnt != 0)
++ return;
++
++ mbedtls_x509_crt_free(&tls_conf->ca_cert);
++ mbedtls_x509_crt_free(&tls_conf->client_cert);
++ if (tls_conf->crl) {
++ mbedtls_x509_crl_free(tls_conf->crl);
++ os_free(tls_conf->crl);
++ }
++ mbedtls_pk_free(&tls_conf->private_key);
++ mbedtls_ssl_config_free(&tls_conf->conf);
++ os_free(tls_conf->curves);
++ os_free(tls_conf->ciphersuites);
++ os_free(tls_conf->subject_match);
++ os_free(tls_conf->altsubject_match);
++ os_free(tls_conf->suffix_match);
++ os_free(tls_conf->domain_match);
++ os_free(tls_conf->check_cert_subject);
++ os_free(tls_conf);
++}
++
++
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++
++__attribute_cold__
++void * tls_init(const struct tls_config *conf)
++{
++ /* RFE: review struct tls_config *conf (different from tls_conf) */
++
++ if (++tls_ctx_global.refcnt > 1)
++ return &tls_ctx_global;
++
++ tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
++ mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
++ mbedtls_ctr_drbg_random,
++ tls_ctx_global.ctr_drbg,
++ MBEDTLS_CIPHER_AES_256_GCM,
++ 43200); /* ticket timeout: 12 hours */
++ #endif
++ /* copy struct for future use */
++ tls_ctx_global.init_conf = *conf;
++ if (conf->openssl_ciphers)
++ tls_ctx_global.init_conf.openssl_ciphers =
++ os_strdup(conf->openssl_ciphers);
++
++ tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
++ os_get_reltime(&tls_ctx_global.crl_reload_previous);
++
++ return &tls_ctx_global;
++}
++
++
++__attribute_cold__
++void tls_deinit(void *tls_ctx)
++{
++ if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
++ return;
++
++ tls_conf_deinit(tls_ctx_global.tls_conf);
++ os_free(tls_ctx_global.ca_cert_file);
++ os_free(tls_ctx_global.ocsp_stapling_response);
++ char *openssl_ciphers; /*(allocated in tls_init())*/
++ *(const char **)&openssl_ciphers =
++ tls_ctx_global.init_conf.openssl_ciphers;
++ os_free(openssl_ciphers);
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
++ #endif
++ os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
++}
++
++
++int tls_get_errors(void *tls_ctx)
++{
++ return 0;
++}
++
++
++static void tls_connection_deinit_expkey(struct tls_connection *conn)
++{
++ conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
++ conn->expkey_keyblock_size = 0;
++ conn->expkey_secret_len = 0;
++ forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
++ forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
++{
++ if (conn->clienthello_session_ticket) {
++ mbedtls_platform_zeroize(conn->clienthello_session_ticket,
++ conn->clienthello_session_ticket_len);
++ mbedtls_free(conn->clienthello_session_ticket);
++ conn->clienthello_session_ticket = NULL;
++ conn->clienthello_session_ticket_len = 0;
++ }
++}
++#endif
++
++
++void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
++{
++ if (conn == NULL)
++ return;
++
++ #if 0 /*(good intention, but never sent since we destroy self below)*/
++ if (conn->established)
++ mbedtls_ssl_close_notify(&conn->ssl);
++ #endif
++
++ if (conn->tls_prf_type)
++ tls_connection_deinit_expkey(conn);
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ if (conn->clienthello_session_ticket)
++ tls_connection_deinit_clienthello_session_ticket(conn);
++ #endif
++
++ os_free(conn->peer_subject);
++ wpabuf_free(conn->success_data);
++ wpabuf_free(conn->push_buf);
++ wpabuf_free(conn->pull_buf);
++ mbedtls_ssl_free(&conn->ssl);
++ tls_conf_deinit(conn->tls_conf);
++ os_free(conn);
++}
++
++
++static void tls_mbedtls_refresh_crl(void);
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
++
++struct tls_connection * tls_connection_init(void *tls_ctx)
++{
++ struct tls_connection *conn = os_zalloc(sizeof(*conn));
++ if (conn == NULL)
++ return NULL;
++
++ mbedtls_ssl_init(&conn->ssl);
++
++ conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
++ if (conn->tls_conf) {
++ ++conn->tls_conf->refcnt;
++ /* check for CRL refresh if inheriting from global config */
++ tls_mbedtls_refresh_crl();
++
++ conn->verify_peer = conn->tls_conf->verify_peer;
++ if (tls_mbedtls_ssl_setup(conn) != 0) {
++ tls_connection_deinit(&tls_ctx_global, conn);
++ return NULL;
++ }
++ }
++
++ return conn;
++}
++
++
++int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? conn->established : 0;
++}
++
++
++__attribute_noinline__
++char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
++{
++ /* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
++ * colons, so generate the hex serial number here. The func
++ * wpa_snprintf_hex_uppercase() is similarly inefficient. */
++ size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
++ while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
++ if (i == crt->serial.len) --i;
++
++ const unsigned char *s = crt->serial.p + i;
++ const size_t e = (crt->serial.len - i) * 2;
++ if (e >= len)
++ return NULL;
++ #if 0
++ wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
++ #else
++ for (i = 0; i < e; i+=2, ++s) {
++ serial_num[i+0] = "0123456789ABCDEF"[(*s >> 4)];
++ serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
++ }
++ serial_num[e] = '\0';
++ #endif
++ return serial_num;
++}
++
++
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
++ if (crt == NULL)
++ return NULL;
++ size_t len = crt->serial.len * 2 + 1;
++ char *serial_num = os_malloc(len);
++ if (!serial_num)
++ return NULL;
++ return tls_mbedtls_peer_serial_num(crt, serial_num, len);
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn);
++
++int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
++{
++ /* Note: this function called from eap_peer_tls_reauth_init()
++ * for session resumption, not for connection shutdown */
++
++ if (conn == NULL)
++ return -1;
++
++ tls_pull_buf_reset(conn);
++ wpabuf_free(conn->push_buf);
++ conn->push_buf = NULL;
++ conn->established = 0;
++ conn->resumed = 0;
++ if (conn->tls_prf_type)
++ tls_connection_deinit_expkey(conn);
++
++ /* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
++
++ return mbedtls_ssl_session_reset(&conn->ssl);
++}
++
++
++static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
++ const unsigned char *data, size_t dlen)
++{
++ if (wpabuf_resize(buf, dlen) < 0)
++ return 0;
++ wpabuf_put_data(*buf, data, dlen);
++ return 1;
++}
++
++
++static int tls_pull_buf_append(struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ /*(interface does not lend itself to move semantics)*/
++ return tls_wpabuf_resize_put_data(&conn->pull_buf,
++ wpabuf_head(in_data),
++ wpabuf_len(in_data));
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn)
++{
++ /*(future: might consider reusing conn->pull_buf)*/
++ wpabuf_free(conn->pull_buf);
++ conn->pull_buf = NULL;
++ conn->pull_buf_offset = 0;
++}
++
++
++__attribute_cold__
++static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
++{
++ size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++ if (discard)
++ wpa_printf(MSG_DEBUG,
++ "%s - %zu bytes remaining in pull_buf; discarding",
++ func, discard);
++ tls_pull_buf_reset(conn);
++}
++
++
++static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
++{
++ struct tls_connection *conn = (struct tls_connection *) ptr;
++ if (conn->pull_buf == NULL)
++ return MBEDTLS_ERR_SSL_WANT_READ;
++ const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++ if (dlen == 0)
++ return MBEDTLS_ERR_SSL_WANT_READ;
++
++ if (len > dlen)
++ len = dlen;
++ os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
++
++ if (len == dlen) {
++ tls_pull_buf_reset(conn);
++ /*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
++ }
++ else {
++ conn->pull_buf_offset += len;
++ /*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
++ __func__, dlen - len);*/
++ }
++ return (int)len;
++}
++
++
++static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
++{
++ struct tls_connection *conn = (struct tls_connection *) ptr;
++ return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
++ ? (int)len
++ : MBEDTLS_ERR_SSL_ALLOC_FAILED;
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
++
++
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
++{
++ #if 0
++ /* mbedtls_ssl_setup() must be called only once */
++ /* If this func might be called multiple times (e.g. via set_params),
++ * then we should set a flag in conn that ssl was initialized */
++ if (conn->ssl_is_init) {
++ mbedtls_ssl_free(&conn->ssl);
++ mbedtls_ssl_init(&conn->ssl);
++ }
++ #endif
++
++ int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
++ if (ret != 0) {
++ elog(ret, "mbedtls_ssl_setup");
++ return -1;
++ }
++
++ mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ mbedtls_ssl_set_export_keys_cb(
++ &conn->ssl, tls_connection_export_keys_cb, conn);
++ #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++ mbedtls_ssl_conf_export_keys_ext_cb(
++ &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
++ #endif
++ if (conn->verify_peer)
++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++ return 0;
++}
++
++
++static int tls_mbedtls_data_is_pem(const u8 *data)
++{
++ return (NULL != os_strstr((char *)data, "-----"));
++}
++
++
++static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
++ mbedtls_ssl_config *conf)
++{
++ #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
++ #endif
++
++ /* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
++ if (tls_conf->flags & TLS_CONN_SUITEB) {
++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
++ }
++
++ const unsigned int flags = tls_conf->flags;
++
++ /* attempt to map flags to min and max TLS protocol version */
++
++ int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_3)
++ ? 4
++ : 3
++ : 2
++ : 1
++ : 0;
++
++ int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_0)
++ ? -1
++ : 0
++ : 1
++ : 2
++ : 3;
++
++ if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
++ if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
++ if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
++ if (max < min) {
++ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++ return;
++ }
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ /* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
++ if (min < 2 || max < 2) {
++ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++ if (min < 2) min = 2;
++ if (max < 2) max = 2;
++ }
++ #endif
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ /* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
++ /* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
++ min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++ max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++ mbedtls_ssl_conf_min_tls_version(conf, min);
++ mbedtls_ssl_conf_max_tls_version(conf, max);
++ #else
++ #ifndef MBEDTLS_SSL_MINOR_VERSION_4
++ if (min == 3) min = 2;
++ if (max == 3) max = 2;
++ #endif
++ /* MBEDTLS_SSL_MINOR_VERSION_0 0 *//*!< SSL v3.0 */
++ /* MBEDTLS_SSL_MINOR_VERSION_1 1 *//*!< TLS v1.0 */
++ /* MBEDTLS_SSL_MINOR_VERSION_2 2 *//*!< TLS v1.1 */
++ /* MBEDTLS_SSL_MINOR_VERSION_3 3 *//*!< TLS v1.2 */
++ /* MBEDTLS_SSL_MINOR_VERSION_4 4 *//*!< TLS v1.3 */
++ mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
++ mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
++ #endif
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
++
++
++static int
++tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
++{
++ size_t len;
++ u8 *data;
++ if (tls_mbedtls_readfile(dh_file, &data, &len))
++ return 0;
++
++ /* parse only if DH parameters if in PEM format */
++ if (tls_mbedtls_data_is_pem(data)
++ && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
++ if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
++ wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
++ else
++ wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
++ forced_memzero(data, len);
++ os_free(data);
++ return 0;
++ }
++
++ /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
++ mbedtls_dhm_context dhm;
++ mbedtls_dhm_init(&dhm);
++ int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
++ if (0 == rc)
++ rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
++ if (0 != rc)
++ elog(rc, dh_file);
++ mbedtls_dhm_free(&dhm);
++
++ forced_memzero(data, len);
++ os_free(data);
++ return (0 == rc);
++}
++
++
++/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
++ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++static int
++tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
++{
++ if (1 >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR, "error: too many curves during list expand");
++ return -1;
++ }
++ ids[++nids] = id;
++ return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++ mbedtls_ecp_group_id ids[512];
++ int nids = -1;
++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++ for (const char *e = curvelist-1; e; ) {
++ const char * const n = e+1;
++ e = os_strchr(n, ':');
++ size_t len = e ? (size_t)(e - n) : os_strlen(n);
++ mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
++ switch (len) {
++ case 5:
++ if (0 == os_memcmp("P-521", n, 5))
++ grp_id = MBEDTLS_ECP_DP_SECP521R1;
++ else if (0 == os_memcmp("P-384", n, 5))
++ grp_id = MBEDTLS_ECP_DP_SECP384R1;
++ else if (0 == os_memcmp("P-256", n, 5))
++ grp_id = MBEDTLS_ECP_DP_SECP256R1;
++ break;
++ case 6:
++ if (0 == os_memcmp("BP-521", n, 6))
++ grp_id = MBEDTLS_ECP_DP_BP512R1;
++ else if (0 == os_memcmp("BP-384", n, 6))
++ grp_id = MBEDTLS_ECP_DP_BP384R1;
++ else if (0 == os_memcmp("BP-256", n, 6))
++ grp_id = MBEDTLS_ECP_DP_BP256R1;
++ break;
++ default:
++ break;
++ }
++ if (grp_id != MBEDTLS_ECP_DP_NONE) {
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
++ if (-1 == nids) return 0;
++ continue;
++ }
++ /* similar to mbedtls_ecp_curve_info_from_name() */
++ const mbedtls_ecp_curve_info *info;
++ for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
++ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++ break;
++ }
++ if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
++ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++ return 0;
++ }
++
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
++ if (-1 == nids) return 0;
++ }
++
++ /* mod_openssl configures "prime256v1" if curve list not specified,
++ * but mbedtls provides a list of supported curves if not explicitly set */
++ if (-1 == nids) return 1; /* empty list; no-op */
++
++ ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
++ ++nids;
++
++ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++ tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
++ if (tls_conf->curves == NULL)
++ return 0;
++ os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
++
++ mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
++ return 1;
++}
++#else
++static int
++tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
++{
++ if (1 >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR, "error: too many curves during list expand");
++ return -1;
++ }
++ ids[++nids] = id;
++ return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++ /* TLS Supported Groups (renamed from "EC Named Curve Registry")
++ * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
++ */
++ uint16_t ids[512];
++ int nids = -1;
++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++ for (const char *e = curvelist-1; e; ) {
++ const char * const n = e+1;
++ e = os_strchr(n, ':');
++ size_t len = e ? (size_t)(e - n) : os_strlen(n);
++ uint16_t tls_id = 0;
++ switch (len) {
++ case 5:
++ if (0 == os_memcmp("P-521", n, 5))
++ tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
++ else if (0 == os_memcmp("P-384", n, 5))
++ tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
++ else if (0 == os_memcmp("P-256", n, 5))
++ tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
++ break;
++ case 6:
++ if (0 == os_memcmp("BP-521", n, 6))
++ tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
++ else if (0 == os_memcmp("BP-384", n, 6))
++ tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
++ else if (0 == os_memcmp("BP-256", n, 6))
++ tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
++ break;
++ default:
++ break;
++ }
++ if (tls_id != 0) {
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
++ if (-1 == nids) return 0;
++ continue;
++ }
++ /* similar to mbedtls_ecp_curve_info_from_name() */
++ const mbedtls_ecp_curve_info *info;
++ for (info = curve_info; info->tls_id != 0; ++info) {
++ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++ break;
++ }
++ if (info->tls_id == 0) {
++ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++ return 0;
++ }
++
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
++ if (-1 == nids) return 0;
++ }
++
++ /* mod_openssl configures "prime256v1" if curve list not specified,
++ * but mbedtls provides a list of supported curves if not explicitly set */
++ if (-1 == nids) return 1; /* empty list; no-op */
++
++ ids[++nids] = 0; /* terminate list */
++ ++nids;
++
++ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++ tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
++ if (tls_conf->curves == NULL)
++ return 0;
++ os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
++
++ mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
++ return 1;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
++
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_256_ephemeral[] = {
++ /* All AES-256 ephemeral suites */
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_128_ephemeral[] = {
++ /* All AES-128 ephemeral suites */
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++/* HIGH cipher list (mapped from openssl list to mbedtls) */
++static const int suite_HIGH[] = {
++ MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
++};
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
++{
++ if (xsz >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR, "error: too many ciphers during list expand");
++ return -1;
++ }
++
++ for (int i = 0; i < xsz; ++i)
++ ids[++nids] = x[i];
++
++ return nids;
++}
++
++
++static int
++tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
++{
++ const mbedtls_ssl_ciphersuite_t *info =
++ mbedtls_ssl_ciphersuite_from_id(id);
++ if (info == NULL)
++ return 0;
++ const char *name = mbedtls_ssl_ciphersuite_get_name(info);
++ const size_t len = os_strlen(name);
++ if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
++ return 0;
++ if (len >= buflen)
++ return 0;
++ os_strlcpy(buf, name, buflen);
++
++ /* attempt to translate mbedtls string to openssl string
++ * (some heuristics; incomplete) */
++ size_t i = 0, j = 0;
++ if (buf[0] == 'T') {
++ if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
++ buf[3] = '-';
++ j = 4; /* remove "1-3" from "TLS1-3-" prefix */
++ i = 7;
++ }
++ else if (os_strncmp(buf, "TLS-", 4) == 0)
++ i = 4; /* remove "TLS-" prefix */
++ }
++ for (; buf[i]; ++i) {
++ if (buf[i] == '-') {
++ if (i >= 3) {
++ if (0 == os_memcmp(buf+i-3, "AES", 3))
++ continue; /* "AES-" -> "AES" */
++ }
++ if (i >= 4) {
++ if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
++ j -= 4; /* remove "WITH-" */
++ continue;
++ }
++ }
++ }
++ buf[j++] = buf[i];
++ }
++ buf[j] = '\0';
++
++ return j;
++}
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
++{
++ /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
++ os_free(tls_conf->ciphersuites);
++ tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
++ if (tls_conf->ciphersuites == NULL)
++ return 0;
++ os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
++ mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
++ return 1;
++}
++
++
++static int
++tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
++{
++ char buf[64];
++ int ids[512];
++ int nids = -1;
++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++ const char *next;
++ size_t blen, clen;
++ do {
++ next = os_strchr(ciphers, ':');
++ clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
++ if (!clen)
++ continue;
++
++ /* special-case a select set of openssl group names for hwsim tests */
++ /* (review; remove excess code if tests are not run for non-OpenSSL?) */
++ if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
++ static int ssl_preset_suiteb192_ciphersuites[] = {
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++ 0
++ };
++ return tls_mbedtls_set_ciphersuites(tls_conf,
++ ssl_preset_suiteb192_ciphersuites,
++ 2);
++ }
++ if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
++ static int ssl_preset_suiteb128_ciphersuites[] = {
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++ 0
++ };
++ return tls_mbedtls_set_ciphersuites(tls_conf,
++ ssl_preset_suiteb128_ciphersuites,
++ 2);
++ }
++ if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
++ continue;
++ if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++ suite_AES_128_ephemeral,
++ (int)ARRAY_SIZE(suite_AES_128_ephemeral));
++ if (nids == -1)
++ return 0;
++ continue;
++ }
++ if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++ suite_AES_256_ephemeral,
++ (int)ARRAY_SIZE(suite_AES_256_ephemeral));
++ if (nids == -1)
++ return 0;
++ continue;
++ }
++ if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
++ (int)ARRAY_SIZE(suite_HIGH));
++ if (nids == -1)
++ return 0;
++ continue;
++ }
++ /* ignore anonymous cipher group names (?not supported by mbedtls?) */
++ if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
++ continue;
++ if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
++ continue;
++ if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
++ continue;
++
++ /* attempt to match mbedtls cipher names
++ * nb: does not support openssl group names or list manipulation syntax
++ * (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
++ * mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
++ * note: not efficient to rewrite list for each ciphers entry,
++ * but this code is expected to run only at startup
++ */
++ const int *list = mbedtls_ssl_list_ciphersuites();
++ for (; *list; ++list) {
++ blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
++ if (!blen)
++ continue;
++
++ /* matching heuristics additional to translate_ciphername above */
++ if (blen == clen+4) {
++ char *cbc = os_strstr(buf, "CBC-");
++ if (cbc) {
++ os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
++ blen -= 4;
++ }
++ }
++ if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
++ && (blen == clen
++ || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
++ if (1 >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR,
++ "error: too many ciphers during list expand");
++ return 0;
++ }
++ ids[++nids] = *list;
++ break;
++ }
++ }
++ if (*list == 0) {
++ wpa_printf(MSG_ERROR,
++ "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
++ return 0;
++ }
++ } while ((ciphers = next ? next+1 : NULL));
++
++ if (-1 == nids) return 1; /* empty list; no-op */
++
++ ids[++nids] = 0; /* terminate list */
++ ++nids;
++
++ return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_set_item(char **config_item, const char *item)
++{
++ os_free(*config_item);
++ *config_item = NULL;
++ return item ? (*config_item = os_strdup(item)) != NULL : 1;
++}
++
++
++static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ int rc = 1;
++ rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
++ params->subject_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
++ params->altsubject_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
++ params->suffix_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
++ params->domain_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
++ params->check_cert_subject);
++ return rc;
++}
++
++
++/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++ #if 0 /* #ifdef MBEDTLS_FS_IO */
++ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++ return -1;
++ }
++ #else
++ /*(use os_readfile() so that we can use os_free()
++ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++ * on buf aborts in tests if buf not allocated via os_malloc())*/
++ *buf = (u8 *)os_readfile(path, n);
++ if (!*buf) {
++ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++ return -1;
++ }
++ u8 *buf0 = os_realloc(*buf, *n+1);
++ if (!buf0) {
++ bin_clear_free(*buf, *n);
++ *buf = NULL;
++ return -1;
++ }
++ buf0[(*n)++] = '\0';
++ *buf = buf0;
++ #endif
++ return 0;
++}
++
++
++static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
++{
++ /* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
++ if (len && data[len-1] == '\0'
++ && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
++ && tls_mbedtls_data_is_pem(data))
++ return 0;
++
++ mbedtls_x509_crl crl;
++ mbedtls_x509_crl_init(&crl);
++ int rc = mbedtls_x509_crl_parse(&crl, data, len);
++ if (rc < 0) {
++ mbedtls_x509_crl_free(&crl);
++ return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
++ }
++
++ mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
++ if (crl_new == NULL) {
++ mbedtls_x509_crl_free(&crl);
++ return MBEDTLS_ERR_X509_ALLOC_FAILED;
++ }
++ os_memcpy(crl_new, &crl, sizeof(crl));
++
++ mbedtls_x509_crl *crl_old = tls_conf->crl;
++ tls_conf->crl = crl_new;
++ if (crl_old) {
++ mbedtls_x509_crl_free(crl_old);
++ os_free(crl_old);
++ }
++ return 0;
++}
++
++
++static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
++{
++ /* load crt struct onto stack and then copy into tls_conf in
++ * order to preserve existing tls_conf value if error occurs
++ *
++ * hostapd is not threaded, or else should allocate memory and swap in
++ * pointer reduce race condition. (If threaded, would also need to
++ * keep reference count of use to avoid freeing while still in use.) */
++
++ mbedtls_x509_crt crt;
++ mbedtls_x509_crt_init(&crt);
++ int rc = mbedtls_x509_crt_parse(&crt, data, len);
++ if (rc < 0) {
++ mbedtls_x509_crt_free(&crt);
++ return rc;
++ }
++
++ mbedtls_x509_crt_free(&tls_conf->ca_cert);
++ os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
++ return 0;
++}
++
++
++static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
++{
++ size_t len;
++ u8 *data;
++ if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
++ return -1;
++
++ int rc;
++ if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
++ && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
++ || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
++ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++ &tls_conf->ca_cert,
++ tls_conf->crl);
++ }
++ else {
++ elog(rc, __func__);
++ emsg(MSG_ERROR, ca_cert_file);
++ }
++
++ forced_memzero(data, len);
++ os_free(data);
++ return rc;
++}
++
++
++static void tls_mbedtls_refresh_crl(void)
++{
++ /* check for CRL refresh
++ * continue even if error occurs; continue with previous cert, CRL */
++ unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
++ const char *ca_cert_file = tls_ctx_global.ca_cert_file;
++ if (!crl_reload_interval || !ca_cert_file)
++ return;
++
++ struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
++ struct os_reltime now;
++ if (os_get_reltime(&now) != 0
++ || !os_reltime_expired(&now, previous, crl_reload_interval))
++ return;
++
++ /* Note: modifying global state is not thread-safe
++ * if in use by existing connections
++ *
++ * src/utils/os.h does not provide a portable stat()
++ * or else it would be a good idea to check mtime and size,
++ * and avoid reloading if file has not changed */
++
++ if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
++ *previous = now;
++}
++
++
++static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ if (params->ca_cert) {
++ if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
++ tls_conf->ca_cert_probe = 1;
++ tls_conf->has_ca_cert = 1;
++ return 0;
++ }
++
++ if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
++ const char *pos = params->ca_cert + 7;
++ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
++ emsg(MSG_ERROR, "unsupported ca_cert hash value");
++ return -1;
++ }
++ pos += 14;
++ if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
++ emsg(MSG_ERROR, "unexpected ca_cert hash length");
++ return -1;
++ }
++ if (hexstr2bin(pos, tls_conf->ca_cert_hash,
++ SHA256_DIGEST_LENGTH) < 0) {
++ emsg(MSG_ERROR, "invalid ca_cert hash value");
++ return -1;
++ }
++ emsg(MSG_DEBUG, "checking only server certificate match");
++ tls_conf->verify_depth0_only = 1;
++ tls_conf->has_ca_cert = 1;
++ return 0;
++ }
++
++ if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
++ return -1;
++ }
++ if (params->ca_cert_blob) {
++ size_t len = params->ca_cert_blob_len;
++ int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
++ if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
++ ++len; /*(include '\0' in len for PEM)*/
++ int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
++ params->ca_cert_blob, len);
++ if (ret != 0) {
++ elog(ret, "mbedtls_x509_crt_parse");
++ return -1;
++ }
++ if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
++ ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
++ if (ret != 0) {
++ elog(ret, "mbedtls_x509_crl_parse");
++ return -1;
++ }
++ }
++ }
++
++ if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
++ || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
++ emsg(MSG_WARNING, "ca_cert expired or not yet valid");
++ if (params->ca_cert)
++ emsg(MSG_WARNING, params->ca_cert);
++ }
++
++ tls_conf->has_ca_cert = 1;
++ return 0;
++}
++
++
++static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ int ret;
++
++ if (params->ca_cert || params->ca_cert_blob) {
++ if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
++ return -1;
++ }
++ else if (params->ca_path) {
++ emsg(MSG_INFO, "ca_path support not implemented");
++ return -1;
++ }
++
++ if (!tls_conf->has_ca_cert)
++ mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
++ else {
++ /* Initial setting: REQUIRED for client, OPTIONAL for server
++ * (see also tls_connection_set_verify()) */
++ tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
++ int authmode = tls_conf->verify_peer
++ ? MBEDTLS_SSL_VERIFY_REQUIRED
++ : MBEDTLS_SSL_VERIFY_OPTIONAL;
++ mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
++ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++ &tls_conf->ca_cert,
++ tls_conf->crl);
++
++ if (!tls_connection_set_subject_match(tls_conf, params))
++ return -1;
++ }
++
++ if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
++ emsg(MSG_INFO, "server_cert2 support not implemented");
++
++ if (params->client_cert) {
++ size_t len;
++ u8 *data;
++ if (tls_mbedtls_readfile(params->client_cert, &data, &len))
++ return -1;
++ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
++ forced_memzero(data, len);
++ os_free(data);
++ }
++ if (params->client_cert_blob) {
++ size_t len = params->client_cert_blob_len;
++ if (len && params->client_cert_blob[len-1] != '\0'
++ && tls_mbedtls_data_is_pem(params->client_cert_blob))
++ ++len; /*(include '\0' in len for PEM)*/
++ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
++ params->client_cert_blob, len);
++ }
++ if (params->client_cert || params->client_cert_blob) {
++ if (ret < 0) {
++ elog(ret, "mbedtls_x509_crt_parse");
++ if (params->client_cert)
++ emsg(MSG_ERROR, params->client_cert);
++ return -1;
++ }
++ if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
++ || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
++ emsg(MSG_WARNING, "cert expired or not yet valid");
++ if (params->client_cert)
++ emsg(MSG_WARNING, params->client_cert);
++ }
++ tls_conf->has_client_cert = 1;
++ }
++
++ if (params->private_key || params->private_key_blob) {
++ size_t len = params->private_key_blob_len;
++ u8 *data;
++ *(const u8 **)&data = params->private_key_blob;
++ if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
++ ++len; /*(include '\0' in len for PEM)*/
++ if (params->private_key
++ && tls_mbedtls_readfile(params->private_key, &data, &len)) {
++ return -1;
++ }
++ const char *pwd = params->private_key_passwd;
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++ data, len,
++ (const unsigned char *)pwd,
++ pwd ? os_strlen(pwd) : 0,
++ mbedtls_ctr_drbg_random,
++ tls_ctx_global.ctr_drbg);
++ #else
++ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++ data, len,
++ (const unsigned char *)pwd,
++ pwd ? os_strlen(pwd) : 0);
++ #endif
++ if (params->private_key) {
++ forced_memzero(data, len);
++ os_free(data);
++ }
++ if (ret < 0) {
++ elog(ret, "mbedtls_pk_parse_key");
++ return -1;
++ }
++ tls_conf->has_private_key = 1;
++ }
++
++ if (tls_conf->has_client_cert && tls_conf->has_private_key) {
++ ret = mbedtls_ssl_conf_own_cert(
++ &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
++ if (ret < 0) {
++ elog(ret, "mbedtls_ssl_conf_own_cert");
++ return -1;
++ }
++ }
++
++ return 0;
++}
++
++
++/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
++{
++ /* Only SHA-256 and 384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++ /* Only ECDSA */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++ /* Only NIST P-256 and P-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++ 0,
++#endif
++ 2048,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
++{
++ /* Only SHA-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++ /* Only ECDSA */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++ /* Only NIST P-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++ 0,
++#endif
++ 3072,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
++{
++ /* Only SHA-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++ 0xFFFFFFF, /* Any PK alg */
++#if defined(MBEDTLS_ECP_C)
++ /* Only NIST P-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++ 0,
++#endif
++ 3072,
++};
++
++
++static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ tls_conf->flags = params->flags;
++
++ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
++ emsg(MSG_INFO, "ocsp=3 not supported");
++ return -1;
++ }
++
++ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
++ emsg(MSG_INFO, "ocsp not supported");
++ return -1;
++ }
++
++ int suiteb128 = 0;
++ int suiteb192 = 0;
++ if (params->openssl_ciphers) {
++ if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
++ suiteb192 = 1;
++ tls_conf->flags |= TLS_CONN_SUITEB;
++ }
++ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
++ suiteb128 = 1;
++ tls_conf->flags |= TLS_CONN_SUITEB;
++ }
++ }
++
++ int ret = mbedtls_ssl_config_defaults(
++ &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
++ : MBEDTLS_SSL_IS_CLIENT,
++ MBEDTLS_SSL_TRANSPORT_STREAM,
++ (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
++ : MBEDTLS_SSL_PRESET_DEFAULT);
++ if (ret != 0) {
++ elog(ret, "mbedtls_ssl_config_defaults");
++ return -1;
++ }
++
++ if (suiteb128) {
++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++ &tls_mbedtls_crt_profile_suiteb128);
++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
++ }
++ else if (suiteb192) {
++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++ &tls_mbedtls_crt_profile_suiteb192);
++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++ }
++ else if (tls_conf->flags & TLS_CONN_SUITEB) {
++ /* treat as suiteb192 while allowing any PK algorithm */
++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++ &tls_mbedtls_crt_profile_suiteb192_anypk);
++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++ }
++
++ tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
++ ret = tls_mbedtls_set_certs(tls_conf, params);
++ if (ret != 0)
++ return -1;
++
++ if (params->dh_file
++ && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
++ return -1;
++ }
++
++ if (params->openssl_ecdh_curves
++ && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
++ return -1;
++ }
++
++ if (params->openssl_ciphers) {
++ if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
++ return -1;
++ }
++ else if (tls_conf->flags & TLS_CONN_SUITEB) {
++ /* special-case a select set of ciphers for hwsim tests */
++ if (!tls_mbedtls_set_ciphers(tls_conf,
++ (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
++ ? "DHE-RSA-AES256-GCM-SHA384"
++ : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
++ return -1;
++ }
++
++ return 0;
++}
++
++
++int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
++ const struct tls_connection_params *params)
++{
++ if (conn == NULL || params == NULL)
++ return -1;
++
++ tls_conf_deinit(conn->tls_conf);
++ struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
++ if (tls_conf == NULL)
++ return -1;
++
++ if (tls_ctx_global.tls_conf) {
++ tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
++ tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
++ /*(tls_openssl.c inherits check_cert_subject from global conf)*/
++ if (tls_ctx_global.tls_conf->check_cert_subject) {
++ tls_conf->check_cert_subject =
++ os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
++ if (tls_conf->check_cert_subject == NULL)
++ return -1;
++ }
++ }
++
++ if (tls_mbedtls_set_params(tls_conf, params) != 0)
++ return -1;
++ conn->verify_peer = tls_conf->verify_peer;
++
++ return tls_mbedtls_ssl_setup(conn);
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
++ const u8 *data, size_t len)
++{
++ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++ return -1;
++ if (conn->clienthello_session_ticket)
++ tls_connection_deinit_clienthello_session_ticket(conn);
++ if (len) {
++ conn->clienthello_session_ticket = mbedtls_calloc(1, len);
++ if (conn->clienthello_session_ticket == NULL)
++ return -1;
++ conn->clienthello_session_ticket_len = len;
++ os_memcpy(conn->clienthello_session_ticket, data, len);
++ }
++ return 0;
++}
++
++
++static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
++{
++ mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
++ if (sess->MBEDTLS_PRIVATE(ticket)) {
++ mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
++ sess->MBEDTLS_PRIVATE(ticket_len));
++ mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
++ }
++ sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
++ sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
++ sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
++
++ conn->clienthello_session_ticket = NULL;
++ conn->clienthello_session_ticket_len = 0;
++}
++
++
++static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
++ const mbedtls_ssl_session *session,
++ unsigned char *start,
++ const unsigned char *end,
++ size_t *tlen,
++ uint32_t *lifetime)
++{
++ struct tls_connection *conn = p_ticket;
++ if (conn && conn->session_ticket_cb) {
++ /* see tls_mbedtls_clienthello_session_ticket_prep() */
++ /* see tls_mbedtls_clienthello_session_ticket_set() */
++ return 0;
++ }
++
++ return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
++ session, start, end, tlen, lifetime);
++}
++
++
++static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
++ mbedtls_ssl_session *session,
++ unsigned char *buf,
++ size_t len)
++{
++ /* XXX: TODO: not implemented in client;
++ * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
++
++ if (len == 0)
++ return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
++
++ struct tls_connection *conn = p_ticket;
++ if (conn && conn->session_ticket_cb) {
++ /* XXX: have random and secret been initialized yet?
++ * or must keys first be exported?
++ * EAP-FAST uses all args, EAP-TEAP only uses secret */
++ struct tls_random data;
++ if (tls_connection_get_random(NULL, conn, &data) != 0)
++ return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
++ int ret =
++ conn->session_ticket_cb(conn->session_ticket_cb_ctx,
++ buf, len,
++ data.client_random,
++ data.server_random,
++ conn->expkey_secret);
++ if (ret == 1) {
++ conn->resumed = 1;
++ return 0;
++ }
++ emsg(MSG_ERROR, "EAP session ticket ext not implemented");
++ return MBEDTLS_ERR_SSL_INVALID_MAC;
++ /*(non-zero return used for mbedtls debug logging)*/
++ }
++
++ /* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
++ int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
++ session, buf, len);
++ if (conn)
++ conn->resumed = (rc == 0);
++ return rc;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++__attribute_cold__
++int tls_global_set_params(void *tls_ctx,
++ const struct tls_connection_params *params)
++{
++ /* XXX: why might global_set_params be called more than once? */
++ if (tls_ctx_global.tls_conf)
++ tls_conf_deinit(tls_ctx_global.tls_conf);
++ tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
++ if (tls_ctx_global.tls_conf == NULL)
++ return -1;
++
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ #ifdef MBEDTLS_SSL_TICKET_C
++ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++ tls_mbedtls_ssl_ticket_write,
++ tls_mbedtls_ssl_ticket_parse,
++ NULL);
++ #else
++ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++ mbedtls_ssl_ticket_write,
++ mbedtls_ssl_ticket_parse,
++ &tls_ctx_global.ticket_ctx);
++ #endif
++ #endif
++ #endif
++
++ os_free(tls_ctx_global.ocsp_stapling_response);
++ tls_ctx_global.ocsp_stapling_response = NULL;
++ if (params->ocsp_stapling_response)
++ tls_ctx_global.ocsp_stapling_response =
++ os_strdup(params->ocsp_stapling_response);
++
++ os_free(tls_ctx_global.ca_cert_file);
++ tls_ctx_global.ca_cert_file = NULL;
++ if (params->ca_cert)
++ tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
++ return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
++}
++
++
++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
++{
++ tls_ctx_global.tls_conf->check_crl = check_crl;
++ tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
++ return 0;
++}
++
++
++int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
++ int verify_peer, unsigned int flags,
++ const u8 *session_ctx, size_t session_ctx_len)
++{
++ /*(EAP server-side calls this from eap_server_tls_ssl_init())*/
++ if (conn == NULL)
++ return -1;
++
++ conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
++
++ int authmode;
++ switch (verify_peer) {
++ case 2: authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
++ case 1: authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
++ default: authmode = MBEDTLS_SSL_VERIFY_NONE; break;
++ }
++ mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
++
++ if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++ else
++ mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
++
++ return 0;
++}
++
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static void tls_connection_export_keys_cb(
++ void *p_expkey, mbedtls_ssl_key_export_type secret_type,
++ const unsigned char *secret, size_t secret_len,
++ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++ mbedtls_tls_prf_types tls_prf_type)
++{
++ struct tls_connection *conn = p_expkey;
++ conn->tls_prf_type = tls_prf_type;
++ if (!tls_prf_type)
++ return;
++ if (secret_len > sizeof(conn->expkey_secret)) {
++ emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
++ conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
++ return;
++ }
++ conn->expkey_secret_len = secret_len;
++ os_memcpy(conn->expkey_secret, secret, secret_len);
++ os_memcpy(conn->expkey_randbytes,
++ client_random, MBEDTLS_EXPKEY_RAND_LEN);
++ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++ server_random, MBEDTLS_EXPKEY_RAND_LEN);
++}
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static int tls_connection_export_keys_cb(
++ void *p_expkey,
++ const unsigned char *ms,
++ const unsigned char *kb,
++ size_t maclen,
++ size_t keylen,
++ size_t ivlen,
++ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++ mbedtls_tls_prf_types tls_prf_type )
++{
++ struct tls_connection *conn = p_expkey;
++ conn->tls_prf_type = tls_prf_type;
++ if (!tls_prf_type)
++ return -1; /*(return value ignored by mbedtls)*/
++ conn->expkey_keyblock_size = maclen + keylen + ivlen;
++ conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
++ os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
++ os_memcpy(conn->expkey_randbytes,
++ client_random, MBEDTLS_EXPKEY_RAND_LEN);
++ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++ server_random, MBEDTLS_EXPKEY_RAND_LEN);
++ return 0;
++}
++#endif
++
++
++int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
++ struct tls_random *data)
++{
++ if (!conn || !conn->tls_prf_type)
++ return -1;
++ data->client_random = conn->expkey_randbytes;
++ data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++ data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
++ data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++ return 0;
++}
++
++
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, u8 *out, size_t out_len)
++{
++ /* (EAP-PEAP EAP-TLS EAP-TTLS) */
++ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++ return (conn && conn->established && conn->tls_prf_type)
++ ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
++ conn->expkey_secret, conn->expkey_secret_len, label,
++ conn->expkey_randbytes,
++ sizeof(conn->expkey_randbytes), out, out_len)
++ : -1;
++ #else
++ /* not implemented here for mbedtls < 2.18.0 */
++ return -1;
++ #endif
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++/* keyblock size info is not exposed in mbed TLS 3.0.0 */
++/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
++#include <mbedtls/ssl_ciphersuites.h>
++#include <mbedtls/cipher.h>
++static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
++{
++ #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
++ #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
++ if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3)
++ return 0; /* (calculation not extracted) */
++ #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
++
++ int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
++ const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
++ mbedtls_ssl_ciphersuite_from_id(ciphersuite);
++ if (ciphersuite_info == NULL)
++ return 0;
++
++ const mbedtls_cipher_info_t *cipher_info =
++ mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
++ if (cipher_info == NULL)
++ return 0;
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++ size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
++ mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
++ #else
++ size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
++ mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
++ #endif
++ #if defined(MBEDTLS_GCM_C) || \
++ defined(MBEDTLS_CCM_C) || \
++ defined(MBEDTLS_CHACHAPOLY_C)
++ if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
++ return keylen + 4;
++ else if (mode == MBEDTLS_MODE_CHACHAPOLY)
++ return keylen + 12;
++ else
++ #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
++ #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
++ {
++ const mbedtls_md_info_t *md_info =
++ mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
++ if (md_info == NULL)
++ return 0;
++ size_t mac_key_len = mbedtls_md_get_size(md_info);
++ size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
++ return keylen + mac_key_len + ivlen;
++ }
++ #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
++ #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
++ return 0;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
++
++
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++ /* XXX: has export keys callback been run? */
++ if (!conn || !conn->tls_prf_type)
++ return -1;
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
++ if (conn->expkey_keyblock_size == 0)
++ return -1;
++ #endif
++ size_t skip = conn->expkey_keyblock_size * 2;
++ unsigned char *tmp_out = os_malloc(skip + out_len);
++ if (!tmp_out)
++ return -1;
++
++ /* server_random and then client_random */
++ unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
++ os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++ MBEDTLS_EXPKEY_RAND_LEN);
++ os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
++ MBEDTLS_EXPKEY_RAND_LEN);
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++ int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
++ conn->expkey_secret, conn->expkey_secret_len,
++ "key expansion", seed, sizeof(seed),
++ tmp_out, skip + out_len);
++ if (ret == 0)
++ os_memcpy(out, tmp_out + skip, out_len);
++ #else
++ int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
++ #endif
++
++ bin_clear_free(tmp_out, skip + out_len);
++ forced_memzero(seed, sizeof(seed));
++ return ret;
++}
++
++#endif /* TLS_MBEDTLS_EAP_FAST */
++
++
++__attribute_cold__
++static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
++{
++ /* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
++ if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
++ return;
++ if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
++ return;
++ #if 0
++ /*(info not available on client;
++ * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
++ if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
++ #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++ mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
++ #else
++ mbedtls_ssl_get_ciphersuite_id(
++ mbedtls_ssl_get_ciphersuite(&conn->ssl))
++ #endif
++ && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
++ < 384 /*(3072/8)*/)
++ #endif
++ {
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (init_conf->event_cb) {
++ union tls_event_data ev;
++ os_memset(&ev, 0, sizeof(ev));
++ ev.alert.is_local = 1;
++ ev.alert.type = "fatal";
++ /*"internal error" string for tests/hwsim/test_suiteb.py */
++ ev.alert.description = "internal error: handshake failure";
++ /*ev.alert.description = "insufficient security";*/
++ init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
++ }
++ }
++}
++
++
++struct wpabuf * tls_connection_handshake(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ struct wpabuf **appl_data)
++{
++ if (appl_data)
++ *appl_data = NULL;
++
++ if (in_data && wpabuf_len(in_data)) {
++ /*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
++ if (conn->pull_buf && 0) /* disable; appears unwise */
++ tls_pull_buf_discard(conn, __func__);
++ if (!tls_pull_buf_append(conn, in_data))
++ return NULL;
++ }
++
++ if (conn->tls_conf == NULL) {
++ struct tls_connection_params params;
++ os_memset(¶ms, 0, sizeof(params));
++ params.openssl_ciphers =
++ tls_ctx_global.init_conf.openssl_ciphers;
++ params.flags = tls_ctx_global.tls_conf->flags;
++ if (tls_connection_set_params(tls_ctx, conn, ¶ms) != 0)
++ return NULL;
++ }
++
++ if (conn->verify_peer) /*(call here might be redundant; nbd)*/
++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ if (conn->clienthello_session_ticket)
++ /*(starting handshake for EAP-FAST and EAP-TEAP)*/
++ tls_mbedtls_clienthello_session_ticket_set(conn);
++
++ /* (not thread-safe due to need to set userdata 'conn' for callback) */
++ /* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
++ * since ticket write and parse callbacks take (mbedtls_ssl_session *)
++ * param instead of (mbedtls_ssl_context *) param) */
++ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++ NULL, NULL, NULL);
++ else
++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++ tls_mbedtls_ssl_ticket_write,
++ tls_mbedtls_ssl_ticket_parse,
++ conn);
++ #endif
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ int ret = mbedtls_ssl_handshake(&conn->ssl);
++ #else
++ int ret = 0;
++ while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
++ ret = mbedtls_ssl_handshake_step(&conn->ssl);
++ if (ret != 0)
++ break;
++ }
++ #endif
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++ tls_mbedtls_ssl_ticket_write,
++ tls_mbedtls_ssl_ticket_parse,
++ NULL);
++ #endif
++
++ switch (ret) {
++ case 0:
++ conn->established = 1;
++ if (conn->push_buf == NULL)
++ /* Need to return something to get final TLS ACK. */
++ conn->push_buf = wpabuf_alloc(0);
++
++ if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
++ *appl_data = NULL; /* RFE: check for application data */
++ break;
++ case MBEDTLS_ERR_SSL_WANT_WRITE:
++ case MBEDTLS_ERR_SSL_WANT_READ:
++ case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
++ case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
++ if (tls_ctx_global.tls_conf /*(is server)*/
++ && conn->established && conn->push_buf == NULL)
++ /* Need to return something to trigger completion of EAP-TLS. */
++ conn->push_buf = wpabuf_alloc(0);
++ break;
++ default:
++ ++conn->failed;
++ switch (ret) {
++ case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
++ case MBEDTLS_ERR_NET_CONN_RESET:
++ case MBEDTLS_ERR_NET_SEND_FAILED:
++ ++conn->write_alerts;
++ break;
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
++ #else
++ case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
++ #endif
++ tls_mbedtls_suiteb_handshake_alert(conn);
++ /* fall through */
++ case MBEDTLS_ERR_NET_RECV_FAILED:
++ case MBEDTLS_ERR_SSL_CONN_EOF:
++ case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
++ case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
++ ++conn->read_alerts;
++ break;
++ default:
++ break;
++ }
++
++ ilog(ret, "mbedtls_ssl_handshake");
++ break;
++ }
++
++ struct wpabuf *out_data = conn->push_buf;
++ conn->push_buf = NULL;
++ return out_data;
++}
++
++
++struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ struct wpabuf **appl_data)
++{
++ conn->is_server = 1;
++ return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
++}
++
++
++struct wpabuf * tls_connection_encrypt(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ int res = mbedtls_ssl_write(&conn->ssl,
++ wpabuf_head_u8(in_data), wpabuf_len(in_data));
++ if (res < 0) {
++ elog(res, "mbedtls_ssl_write");
++ return NULL;
++ }
++
++ struct wpabuf *buf = conn->push_buf;
++ conn->push_buf = NULL;
++ return buf;
++}
++
++
++struct wpabuf * tls_connection_decrypt(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ int res;
++ struct wpabuf *out;
++
++ /*assert(in_data != NULL);*/
++ if (!tls_pull_buf_append(conn, in_data))
++ return NULL;
++
++ #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
++ /* Add extra buffer space to handle the possibility of decrypted
++ * data being longer than input data due to TLS compression. */
++ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
++ #else /* TLS compression is disabled in mbedtls 3.x */
++ out = wpabuf_alloc(wpabuf_len(in_data));
++ #endif
++ if (out == NULL)
++ return NULL;
++
++ res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
++ if (res < 0) {
++ #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
++ if (res == MBEDTLS_ERR_SSL_WANT_READ)
++ return out;
++ #endif
++ elog(res, "mbedtls_ssl_read");
++ wpabuf_free(out);
++ return NULL;
++ }
++ wpabuf_put(out, res);
++
++ return out;
++}
++
++
++int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
++{
++ /* XXX: might need to detect if session resumed from TLS session ticket
++ * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
++ /* (?ssl->handshake->resume during session ticket validation?) */
++ return conn && conn->resumed;
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
++ u8 *ciphers)
++{
++ /* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
++ int ids[7];
++ const int idsz = (int)sizeof(ids);
++ int nids = -1, id;
++ for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
++ switch (*ciphers) {
++ case TLS_CIPHER_RC4_SHA:
++ #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
++ id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
++ break;
++ #else
++ continue; /*(not supported in mbedtls 3.x; ignore)*/
++ #endif
++ case TLS_CIPHER_AES128_SHA:
++ id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
++ break;
++ case TLS_CIPHER_RSA_DHE_AES128_SHA:
++ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
++ break;
++ case TLS_CIPHER_ANON_DH_AES128_SHA:
++ continue; /*(not supported in mbedtls; ignore)*/
++ case TLS_CIPHER_RSA_DHE_AES256_SHA:
++ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
++ break;
++ case TLS_CIPHER_AES256_SHA:
++ id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
++ break;
++ default:
++ return -1; /* should not happen */
++ }
++ if (++nids == idsz)
++ return -1; /* should not happen */
++ ids[nids] = id;
++ }
++ if (nids < 0)
++ return 0; /* nothing to do */
++ if (++nids == idsz)
++ return -1; /* should not happen */
++ ids[nids] = 0; /* terminate list */
++ ++nids;
++
++ return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
++}
++#endif
++
++
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++ char *buf, size_t buflen)
++{
++ if (conn == NULL)
++ return -1;
++ os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
++ return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
++{
++ if (conn == NULL)
++ return 0;
++ return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++}
++#endif
++
++
++int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
++ char *buf, size_t buflen)
++{
++ if (conn == NULL)
++ return -1;
++ const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++ return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++int tls_connection_enable_workaround(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ /* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
++ /* XXX: is there a relevant setting for this in mbed TLS? */
++ /* (do we even care that much about older CBC ciphers?) */
++ return 0;
++}
++
++
++int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
++ int ext_type, const u8 *data,
++ size_t data_len)
++{
++ /* (EAP-FAST and EAP-TEAP) */
++ if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
++ return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
++ data_len);
++
++ return -1;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? conn->failed : -1;
++}
++
++
++int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? conn->read_alerts : -1;
++}
++
++
++int tls_connection_get_write_alerts(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ return conn ? conn->write_alerts : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++int tls_connection_set_session_ticket_cb(
++ void *tls_ctx, struct tls_connection *conn,
++ tls_session_ticket_cb cb, void *ctx)
++{
++ if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
++ /* (EAP-FAST and EAP-TEAP) */
++ conn->session_ticket_cb = cb;
++ conn->session_ticket_cb_ctx = ctx;
++ return 0;
++ }
++ return -1;
++}
++#endif
++
++
++int tls_get_library_version(char *buf, size_t buf_len)
++{
++ #ifndef MBEDTLS_VERSION_C
++ const char * const ver = "n/a";
++ #else
++ char ver[9];
++ mbedtls_version_get_string(ver);
++ #endif
++ return os_snprintf(buf, buf_len,
++ "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
++}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++ struct wpabuf *data)
++{
++ wpabuf_free(conn->success_data);
++ conn->success_data = data;
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++ return conn->success_data;
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
++{
++ #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
++ /* data from TLS handshake Finished message */
++ size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
++ char *verify_data = (conn->is_server ^ conn->resumed)
++ ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
++ : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
++ if (verify_len && verify_len <= max_len) {
++ os_memcpy(buf, verify_data, verify_len);
++ return (int)verify_len;
++ }
++ #endif
++ return -1;
++}
++#endif
++
++
++__attribute_noinline__
++static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
++{
++ if (conn->peer_subject)
++ return;
++ char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++ int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
++ if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
++ os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++const char * tls_connection_get_peer_subject(struct tls_connection *conn)
++{
++ if (!conn)
++ return NULL;
++ if (!conn->peer_subject) { /*(if not set during cert verify)*/
++ const mbedtls_x509_crt *peer_cert =
++ mbedtls_ssl_get_peer_cert(&conn->ssl);
++ if (peer_cert)
++ tls_mbedtls_set_peer_subject(conn, peer_cert);
++ }
++ return conn->peer_subject;
++}
++#endif
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++bool tls_connection_get_own_cert_used(struct tls_connection *conn)
++{
++ /* XXX: availability of cert does not necessary mean that client
++ * received certificate request from server and then sent cert.
++ * ? step handshake in tls_connection_handshake() looking for
++ * MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
++ const struct tls_conf * const tls_conf = conn->tls_conf;
++ return (tls_conf->has_client_cert && tls_conf->has_private_key);
++}
++#endif
++
++
++#if defined(CONFIG_FIPS)
++#define TLS_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if defined(CONFIG_SHA256)
++#define TLS_MBEDTLS_TLS_PRF_SHA256
++#endif
++
++#if defined(CONFIG_SHA384)
++#define TLS_MBEDTLS_TLS_PRF_SHA384
++#endif
++
++
++#ifndef TLS_MBEDTLS_CONFIG_FIPS
++#if defined(CONFIG_MODULE_TESTS)
++/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
++ && MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++/* sha1-tlsprf.c */
++#include "sha1.h"
++int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
++ secret, secret_len, label,
++ seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha1-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
++/* sha256-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha256.h"
++int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
++ secret, secret_len, label,
++ seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha256-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
++/* sha384-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha384.h"
++int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
++ secret, secret_len, label,
++ seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha384-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
++ ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
++#endif
++
++struct mlist { const char *p; size_t n; };
++
++
++static int
++tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
++{
++ /* RFE: this could be pre-parsed into structured data at config time */
++ struct mlist list[256]; /*(much larger than expected)*/
++ int nlist = 0;
++ if ( os_strncmp(match, "EMAIL:", 6) != 0
++ && os_strncmp(match, "DNS:", 4) != 0
++ && os_strncmp(match, "URI:", 4) != 0 ) {
++ wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
++ return 0;
++ }
++ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++ do { } while ((tok = os_strchr(s, ';'))
++ && os_strncmp(tok+1, "EMAIL:", 6) != 0
++ && os_strncmp(tok+1, "DNS:", 4) != 0
++ && os_strncmp(tok+1, "URI:", 4) != 0);
++ list[nlist].p = s;
++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++ wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
++ match);
++ break; /* truncate huge list and continue */
++ }
++ }
++
++ if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++ return 0;
++
++ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++ for (; cur != NULL; cur = cur->next) {
++ const unsigned char san_type = (unsigned char)cur->buf.tag
++ & MBEDTLS_ASN1_TAG_VALUE_MASK;
++ char t;
++ size_t step = 4;
++ switch (san_type) { /* "EMAIL:" or "DNS:" or "URI:" */
++ case MBEDTLS_X509_SAN_RFC822_NAME: step = 6; t = 'E'; break;
++ case MBEDTLS_X509_SAN_DNS_NAME: t = 'D'; break;
++ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
++ default: continue;
++ }
++
++ for (int i = 0; i < nlist; ++i) {
++ /* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
++ /* Note: v is not '\0'-terminated, but is a known length vlen,
++ * so okay to pass to os_strncasecmp() even though not z-string */
++ if (cur->buf.len == list[i].n - step && t == *list[i].p
++ && 0 == os_strncasecmp((char *)cur->buf.p,
++ list[i].p+step, cur->buf.len)) {
++ return 1; /* match */
++ }
++ }
++ }
++ return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffix(const char *v, size_t vlen,
++ const struct mlist *list, int nlist, int full)
++{
++ /* Note: v is not '\0'-terminated, but is a known length vlen,
++ * so okay to pass to os_strncasecmp() even though not z-string */
++ for (int i = 0; i < nlist; ++i) {
++ size_t n = list[i].n;
++ if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
++ && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
++ return 1; /* match */
++ }
++ return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
++{
++ /* RFE: this could be pre-parsed into structured data at config time */
++ struct mlist list[256]; /*(much larger than expected)*/
++ int nlist = 0;
++ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++ tok = os_strchr(s, ';');
++ list[nlist].p = s;
++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++ wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
++ break; /* truncate huge list and continue */
++ }
++ }
++
++ /* check subjectAltNames */
++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
++ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++ for (; cur != NULL; cur = cur->next) {
++ const unsigned char san_type = (unsigned char)cur->buf.tag
++ & MBEDTLS_ASN1_TAG_VALUE_MASK;
++ if (san_type == MBEDTLS_X509_SAN_DNS_NAME
++ && tls_mbedtls_match_suffix((char *)cur->buf.p,
++ cur->buf.len,
++ list, nlist, full)) {
++ return 1; /* match */
++ }
++ }
++ }
++
++ /* check subject CN */
++ const mbedtls_x509_name *name = &crt->subject;
++ for (; name != NULL; name = name->next) {
++ if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
++ break;
++ }
++ if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
++ list, nlist, full)) {
++ return 1; /* match */
++ }
++
++ return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
++{
++ /* RFE: this could be pre-parsed into structured data at config time */
++ struct mlistoid { const char *p; size_t n;
++ const char *oid; size_t olen;
++ int prefix; };
++ struct mlistoid list[32]; /*(much larger than expected)*/
++ int nlist = 0;
++ for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
++ tok = os_strchr(s, '/');
++ list[nlist].oid = NULL;
++ list[nlist].olen = 0;
++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++ e = memchr(s, '=', list[nlist].n);
++ if (e == NULL) {
++ if (list[nlist].n == 0)
++ continue; /* skip consecutive, repeated '/' */
++ if (list[nlist].n == 1 && *s == '*') {
++ /* special-case "*" to match any OID and value */
++ s = e = "=*";
++ list[nlist].n = 2;
++ list[nlist].oid = "";
++ }
++ else {
++ wpa_printf(MSG_INFO,
++ "MTLS: invalid check_cert_subject '%s' missing '='",
++ match);
++ return 0;
++ }
++ }
++ switch (e - s) {
++ case 1:
++ if (*s == 'C') {
++ list[nlist].oid = MBEDTLS_OID_AT_COUNTRY;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
++ }
++ else if (*s == 'L') {
++ list[nlist].oid = MBEDTLS_OID_AT_LOCALITY;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
++ }
++ else if (*s == 'O') {
++ list[nlist].oid = MBEDTLS_OID_AT_ORGANIZATION;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
++ }
++ break;
++ case 2:
++ if (s[0] == 'C' && s[1] == 'N') {
++ list[nlist].oid = MBEDTLS_OID_AT_CN;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
++ }
++ else if (s[0] == 'S' && s[1] == 'T') {
++ list[nlist].oid = MBEDTLS_OID_AT_STATE;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
++ }
++ else if (s[0] == 'O' && s[1] == 'U') {
++ list[nlist].oid = MBEDTLS_OID_AT_ORG_UNIT;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
++ }
++ break;
++ case 12:
++ if (os_memcmp(s, "emailAddress", 12) == 0) {
++ list[nlist].oid = MBEDTLS_OID_PKCS9_EMAIL;
++ list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
++ }
++ break;
++ default:
++ break;
++ }
++ if (list[nlist].oid == NULL) {
++ wpa_printf(MSG_INFO,
++ "MTLS: Unknown field in check_cert_subject '%s'",
++ match);
++ return 0;
++ }
++ list[nlist].n -= (size_t)(++e - s);
++ list[nlist].p = e;
++ if (list[nlist].n && e[list[nlist].n-1] == '*') {
++ --list[nlist].n;
++ list[nlist].prefix = 1;
++ }
++ /*(could easily add support for suffix matches if value begins with '*',
++ * but suffix match is not currently supported by other TLS modules)*/
++
++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++ wpa_printf(MSG_INFO,
++ "MTLS: excessive check_cert_subject match '%s'",
++ match);
++ break; /* truncate huge list and continue */
++ }
++ }
++
++ /* each component in match string must match cert Subject in order listed
++ * The behavior below preserves ordering but is slightly different than
++ * the grossly inefficient contortions implemented in tls_openssl.c */
++ const mbedtls_x509_name *name = &crt->subject;
++ for (int i = 0; i < nlist; ++i) {
++ int found = 0;
++ for (; name != NULL && !found; name = name->next) {
++ if (!name->oid.p)
++ continue;
++ /* special-case "*" to match any OID and value */
++ if (list[i].olen == 0) {
++ found = 1;
++ continue;
++ }
++ /* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
++ if (list[i].olen != name->oid.len
++ || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
++ continue;
++ /* Note: v is not '\0'-terminated, but is a known length vlen,
++ * so okay to pass to os_strncasecmp() even though not z-string */
++ if ((list[i].prefix
++ ? list[i].n <= name->val.len /* prefix match */
++ : list[i].n == name->val.len) /* full match */
++ && 0 == os_strncasecmp((char *)name->val.p,
++ list[i].p, list[i].n)) {
++ found = 1;
++ continue;
++ }
++ }
++ if (!found)
++ return 0; /* no match */
++ }
++ return 1; /* match */
++}
++
++
++__attribute_cold__
++static void
++tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
++ const char *errmsg, enum tls_fail_reason reason)
++{
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (init_conf->event_cb == NULL)
++ return;
++
++ struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++ subject[0] = '\0';
++ union tls_event_data ev;
++ os_memset(&ev, 0, sizeof(ev));
++ ev.cert_fail.reason = reason;
++ ev.cert_fail.depth = depth;
++ ev.cert_fail.subject = subject;
++ ev.cert_fail.reason_txt = errmsg;
++ ev.cert_fail.cert = certbuf;
++
++ init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
++
++ wpabuf_free(certbuf);
++}
++
++
++__attribute_noinline__
++static void
++tls_mbedtls_verify_cert_event (struct tls_connection *conn,
++ mbedtls_x509_crt *crt, int depth)
++{
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (init_conf->event_cb == NULL)
++ return;
++
++ struct wpabuf *certbuf = NULL;
++ union tls_event_data ev;
++ os_memset(&ev, 0, sizeof(ev));
++
++ #ifdef MBEDTLS_SHA256_C
++ u8 hash[SHA256_DIGEST_LENGTH];
++ const u8 *addr[] = { (u8 *)crt->raw.p };
++ if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
++ ev.peer_cert.hash = hash;
++ ev.peer_cert.hash_len = sizeof(hash);
++ }
++ #endif
++ ev.peer_cert.depth = depth;
++ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++ if (depth == 0)
++ ev.peer_cert.subject = conn->peer_subject;
++ if (ev.peer_cert.subject == NULL) {
++ ev.peer_cert.subject = subject;
++ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++ subject[0] = '\0';
++ }
++
++ char serial_num[128+1];
++ ev.peer_cert.serial_num =
++ tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
++
++ const mbedtls_x509_sequence *cur;
++
++ cur = NULL;
++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++ cur = &crt->subject_alt_names;
++ for (; cur != NULL; cur = cur->next) {
++ const unsigned char san_type = (unsigned char)cur->buf.tag
++ & MBEDTLS_ASN1_TAG_VALUE_MASK;
++ size_t prelen = 4;
++ const char *pre;
++ switch (san_type) {
++ case MBEDTLS_X509_SAN_RFC822_NAME: prelen = 6; pre = "EMAIL:";break;
++ case MBEDTLS_X509_SAN_DNS_NAME: pre = "DNS:"; break;
++ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:"; break;
++ default: continue;
++ }
++
++ char *pos = os_malloc(prelen + cur->buf.len + 1);
++ if (pos == NULL)
++ break;
++ ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
++ os_memcpy(pos, pre, prelen);
++ /* data should be properly backslash-escaped if needed,
++ * so code below does not re-escape, but does replace CTLs */
++ /*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
++ /*pos[prelen+cur->buf.len] = '\0';*/
++ pos += prelen;
++ for (size_t i = 0; i < cur->buf.len; ++i) {
++ unsigned char c = cur->buf.p[i];
++ *pos++ = (c >= 32 && c != 127) ? c : '?';
++ }
++ *pos = '\0';
++
++ if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
++ break;
++ }
++
++ cur = NULL;
++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
++ cur = &crt->certificate_policies;
++ for (; cur != NULL; cur = cur->next) {
++ if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
++ continue;
++ /* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
++ /* TOD-TOFU "1.3.6.1.4.1.40808.1.3.2" */
++ #define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
++ #define OID_TOD_TOFU "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
++ if (os_memcmp(cur->buf.p,
++ OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
++ ev.peer_cert.tod = 1; /* TOD-STRICT */
++ break;
++ }
++ if (os_memcmp(cur->buf.p,
++ OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
++ ev.peer_cert.tod = 2; /* TOD-TOFU */
++ break;
++ }
++ }
++
++ struct tls_conf *tls_conf = conn->tls_conf;
++ if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
++ || init_conf->cert_in_cb) {
++ certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++ ev.peer_cert.cert = certbuf;
++ }
++
++ init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
++
++ wpabuf_free(certbuf);
++ char **altsubject;
++ *(const char ***)&altsubject = ev.peer_cert.altsubject;
++ for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
++ os_free(altsubject[i]);
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
++{
++ /* XXX: N.B. verify code not carefully tested besides hwsim tests
++ *
++ * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
++ * RFE: review and add support for additional TLS_CONN_* flags
++ * not handling OCSP (not available in mbedtls)
++ * ... */
++
++ struct tls_connection *conn = (struct tls_connection *)arg;
++ struct tls_conf *tls_conf = conn->tls_conf;
++ uint32_t flags_in = *flags;
++
++ if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
++ emsg(MSG_WARNING, "client cert chain too long");
++ *flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "client cert chain too long",
++ TLS_FAIL_BAD_CERTIFICATE);
++ }
++ else if (tls_conf->verify_depth0_only) {
++ if (depth > 0)
++ *flags = 0;
++ else {
++ #ifdef MBEDTLS_SHA256_C
++ u8 hash[SHA256_DIGEST_LENGTH];
++ const u8 *addr[] = { (u8 *)crt->raw.p };
++ if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
++ || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
++ *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "cert hash mismatch",
++ TLS_FAIL_UNTRUSTED);
++ }
++ else /* hash matches; ignore other issues *except* if revoked)*/
++ *flags &= MBEDTLS_X509_BADCERT_REVOKED;
++ #endif
++ }
++ }
++ else if (depth == 0) {
++ if (!conn->peer_subject)
++ tls_mbedtls_set_peer_subject(conn, crt);
++ /*(use same labels to tls_mbedtls_verify_fail_event() as used in
++ * other TLS modules so that hwsim tests find exact string match)*/
++ if (!conn->peer_subject) { /* error copying subject string */
++ *flags |= MBEDTLS_X509_BADCERT_OTHER;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "internal error",
++ TLS_FAIL_UNSPECIFIED);
++ }
++ /*(use os_strstr() for subject match as is done in tls_mbedtls.c
++ * to follow the same behavior, even though a suffix match would
++ * make more sense. Also, note that strstr match does not
++ * normalize whitespace (between components) for comparison)*/
++ else if (tls_conf->subject_match
++ && os_strstr(conn->peer_subject,
++ tls_conf->subject_match) == NULL) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: Subject '%s' did not match with '%s'",
++ conn->peer_subject, tls_conf->subject_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Subject mismatch",
++ TLS_FAIL_SUBJECT_MISMATCH);
++ }
++ if (tls_conf->altsubject_match
++ && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: altSubjectName match '%s' not found",
++ tls_conf->altsubject_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "AltSubject mismatch",
++ TLS_FAIL_ALTSUBJECT_MISMATCH);
++ }
++ if (tls_conf->suffix_match
++ && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: Domain suffix match '%s' not found",
++ tls_conf->suffix_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Domain suffix mismatch",
++ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
++ }
++ if (tls_conf->domain_match
++ && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: Domain match '%s' not found",
++ tls_conf->domain_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Domain mismatch",
++ TLS_FAIL_DOMAIN_MISMATCH);
++ }
++ if (tls_conf->check_cert_subject
++ && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Distinguished Name",
++ TLS_FAIL_DN_MISMATCH);
++ }
++ if (tls_conf->flags & TLS_CONN_SUITEB) {
++ /* check RSA modulus size (public key bitlen) */
++ const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
++ if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
++ && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
++ /* hwsim suite_b RSA tests expect 3072
++ * suite_b_192_rsa_ecdhe_radius_rsa2048_client
++ * suite_b_192_rsa_dhe_radius_rsa2048_client */
++ *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Insufficient RSA modulus size",
++ TLS_FAIL_INSUFFICIENT_KEY_LEN);
++ }
++ }
++ if (tls_conf->check_crl && tls_conf->crl == NULL) {
++ /* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
++ emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
++ *flags |= MBEDTLS_X509_BADCERT_OTHER;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "check_crl set but no CRL loaded; "
++ "reject all?",
++ TLS_FAIL_BAD_CERTIFICATE);
++ }
++ }
++ else {
++ if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
++ *flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
++ }
++
++ if (!tls_conf->check_crl_strict) {
++ *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
++ *flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
++ }
++
++ if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
++ *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
++ *flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
++ }
++
++ tls_mbedtls_verify_cert_event(conn, crt, depth);
++
++ if (*flags) {
++ if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
++ |MBEDTLS_X509_BADCERT_CN_MISMATCH
++ |MBEDTLS_X509_BADCERT_REVOKED)) {
++ emsg(MSG_WARNING, "client cert not trusted");
++ }
++ /* report event if flags set but no additional flags set above */
++ /* (could translate flags to more detailed TLS_FAIL_* if needed) */
++ if (!(*flags & ~flags_in)) {
++ enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
++ const char *errmsg = "cert verify fail unspecified";
++ if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
++ reason = TLS_FAIL_UNTRUSTED;
++ errmsg = "certificate not trusted";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
++ reason = TLS_FAIL_REVOKED;
++ errmsg = "certificate has been revoked";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
++ reason = TLS_FAIL_NOT_YET_VALID;
++ errmsg = "certificate not yet valid";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
++ reason = TLS_FAIL_EXPIRED;
++ errmsg = "certificate has expired";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
++ reason = TLS_FAIL_BAD_CERTIFICATE;
++ errmsg = "certificate uses insecure algorithm";
++ }
++ tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
++ }
++ #if 0
++ /* ??? send (again) cert events for all certs in chain ???
++ * (should already have been called for greater depths) */
++ /* tls_openssl.c:tls_verify_cb() sends cert events for all certs
++ * in chain if certificate validation fails, but sends all events
++ * with depth set to 0 (might be a bug) */
++ if (depth > 0) {
++ int pdepth = depth + 1;
++ for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
++ tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
++ }
++ }
++ #endif
++ /*(do not preserve subject if verification failed but was optional)*/
++ if (depth == 0 && conn->peer_subject) {
++ os_free(conn->peer_subject);
++ conn->peer_subject = NULL;
++ }
++ }
++ else if (depth == 0) {
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (tls_conf->ca_cert_probe) {
++ /* reject server certificate on probe-only run */
++ *flags |= MBEDTLS_X509_BADCERT_OTHER;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "server chain probe",
++ TLS_FAIL_SERVER_CHAIN_PROBE);
++ }
++ else if (init_conf->event_cb) {
++ /* ??? send event as soon as depth == 0 is verified ???
++ * What about rest of chain?
++ * Follows tls_mbedtls.c behavior: */
++ init_conf->event_cb(init_conf->cb_ctx,
++ TLS_CERT_CHAIN_SUCCESS, NULL);
++ }
++ }
++
++ return 0;
++}
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index e672a1787..3e3e309f4 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -971,6 +971,9 @@ struct wpa_driver_associate_params {
+ * responsible for selecting with which BSS to associate. */
+ const u8 *bssid;
+
++ unsigned char rates[32];
++ int mcast_rate;
++
+ /**
+ * bssid_hint - BSSID of a proposed AP
+ *
+@@ -1873,6 +1876,7 @@ struct wpa_driver_mesh_join_params {
+ #define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008
+ unsigned int flags;
+ bool handle_dfs;
++ int mcast_rate;
+ };
+
+ struct wpa_driver_set_key_params {
+@@ -2340,6 +2344,9 @@ struct wpa_driver_capa {
+ /** Maximum number of iterations in a single scan plan */
+ u32 max_sched_scan_plan_iterations;
+
++ /** Maximum number of extra IE bytes for scans */
++ u16 max_scan_ie_len;
++
+ /** Whether sched_scan (offloaded scanning) is supported */
+ int sched_scan_supported;
+
+@@ -3861,6 +3868,25 @@ struct wpa_driver_ops {
+ int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname);
+
++ /**
++ * if_rename - Rename a virtual interface
++ * @priv: Private driver interface data
++ * @type: Interface type
++ * @ifname: Interface name of the virtual interface to be renamed
++ * (NULL when renaming the AP BSS interface)
++ * @new_name: New interface name of the virtual interface
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*if_rename)(void *priv, enum wpa_driver_if_type type,
++ const char *ifname, const char *new_name);
++
++ /**
++ * set_first_bss - Make a virtual interface the first (primary) bss
++ * @priv: Private driver interface data
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*set_first_bss)(void *priv);
++
+ /**
+ * set_sta_vlan - Bind a station into a specific interface (AP only)
+ * @priv: Private driver interface data
+@@ -4265,7 +4291,7 @@ struct wpa_driver_ops {
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+- unsigned int val);
++ const char *ifname, unsigned int val);
+
+ /**
+ * get_wowlan - Get wake-on-wireless status
+@@ -6559,6 +6585,7 @@ union wpa_event_data {
+
+ /**
+ * struct ch_switch
++ * @count: Count until channel switch activates
+ * @freq: Frequency of new channel in MHz
+ * @ht_enabled: Whether this is an HT channel
+ * @ch_offset: Secondary channel offset
+@@ -6569,6 +6596,7 @@ union wpa_event_data {
+ * @punct_bitmap: Puncturing bitmap
+ */
+ struct ch_switch {
++ int count;
+ int freq;
+ int ht_enabled;
+ int ch_offset;
+@@ -6816,8 +6844,8 @@ union wpa_event_data {
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data);
++extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ /**
+ * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
+@@ -6829,7 +6857,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ * Same as wpa_supplicant_event(), but we search for the interface in
+ * wpa_global.
+ */
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+
+ /*
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 9ac621ae6..6778ad369 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -78,6 +78,16 @@ enum nlmsgerr_attrs {
+
+ #endif /* ANDROID */
+
++static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
++{
++ const struct nlmsghdr *nlh;
++
++ if (!wpa_netlink_hook)
++ return;
++
++ nlh = nlmsg_hdr(msg);
++ wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
++}
+
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+@@ -432,6 +442,11 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
+ return NL_OK;
+ }
+
++static int debug_handler(struct nl_msg *msg, void *arg)
++{
++ handle_nl_debug_hook(msg, 0);
++ return NL_OK;
++}
+
+ static void nl80211_nlmsg_clear(struct nl_msg *msg)
+ {
+@@ -505,6 +520,7 @@ int send_and_recv(struct nl80211_global *global,
+ if (!msg)
+ return -ENOMEM;
+
++ handle_nl_debug_hook(msg, 1);
+ err.err = -ENOMEM;
+
+ s_nl_cb = nl_socket_get_cb(nl_handle);
+@@ -539,6 +555,7 @@ int send_and_recv(struct nl80211_global *global,
+ err.orig_msg = msg;
+ err.err_info = err_info;
+
++ nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
+ if (ack_handler_custom) {
+@@ -942,6 +959,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+ os_free(w);
+ return NULL;
+ }
++ nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -1356,7 +1374,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+ namebuf, ifname);
+- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++ if (drv->first_bss->ifindex != ifi->ifi_index) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface down",
+ drv->first_bss->ifname);
+@@ -1392,7 +1410,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+ namebuf, ifname);
+- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++ if (drv->first_bss->ifindex != ifi->ifi_index) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface up",
+ drv->first_bss->ifname);
+@@ -2038,6 +2056,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+ genl_family_put(family);
+ nl_cache_free(cache);
+
++ nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -2208,6 +2227,7 @@ static int nl80211_init_bss(struct i802_bss *bss)
+ if (!bss->nl_cb)
+ return -1;
+
++ nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -5485,7 +5505,7 @@ static int nl80211_set_channel(struct i802_bss *bss,
+ freq->he_enabled, freq->eht_enabled, freq->bandwidth,
+ freq->center_freq1, freq->center_freq2);
+
+- msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
++ msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+ NL80211_CMD_SET_WIPHY);
+ if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+ nlmsg_free(msg);
+@@ -5858,26 +5878,29 @@ fail:
+
+ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+ {
+-#ifdef CONFIG_LIBNL3_ROUTE
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+- struct rtnl_neigh *rn;
+- struct nl_addr *nl_addr;
++ struct ndmsg nhdr = {
++ .ndm_state = NUD_PERMANENT,
++ .ndm_ifindex = bss->ifindex,
++ .ndm_family = AF_BRIDGE,
++ };
++ struct nl_msg *msg;
+ int err;
+
+- rn = rtnl_neigh_alloc();
+- if (!rn)
++ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
++ if (!msg)
+ return;
+
+- rtnl_neigh_set_family(rn, AF_BRIDGE);
+- rtnl_neigh_set_ifindex(rn, bss->ifindex);
+- nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
+- if (!nl_addr) {
+- rtnl_neigh_put(rn);
+- return;
+- }
+- rtnl_neigh_set_lladdr(rn, nl_addr);
++ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
++ goto errout;
+
+- err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
++ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
++ goto errout;
++
++ if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
++ goto errout;
++
++ err = nl_wait_for_ack(drv->rtnl_sk);
+ if (err < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
+ MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
+@@ -5887,9 +5910,8 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+ MACSTR, MAC2STR(addr));
+ }
+
+- nl_addr_put(nl_addr);
+- rtnl_neigh_put(rn);
+-#endif /* CONFIG_LIBNL3_ROUTE */
++errout:
++ nlmsg_free(msg);
+ }
+
+
+@@ -6178,6 +6200,8 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
+ nl80211_put_wiphy_data_ap(bss);
+ if (bss->flink)
+ bss->flink->beacon_set = 0;
++
++ wpa_driver_nl80211_del_beacon_all(bss);
+ }
+
+
+@@ -8566,6 +8590,7 @@ static void *i802_init(struct hostapd_data *hapd,
+ char master_ifname[IFNAMSIZ];
+ int ifindex, br_ifindex = 0;
+ int br_added = 0;
++ int err;
+
+ bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+ params->global_priv, 1,
+@@ -8625,24 +8650,18 @@ static void *i802_init(struct hostapd_data *hapd,
+ (params->num_bridge == 0 || !params->bridge[0]))
+ add_ifidx(drv, br_ifindex, drv->ifindex);
+
+-#ifdef CONFIG_LIBNL3_ROUTE
+- if (bss->added_if_into_bridge || bss->already_in_bridge) {
+- int err;
+-
+- drv->rtnl_sk = nl_socket_alloc();
+- if (drv->rtnl_sk == NULL) {
+- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+- goto failed;
+- }
++ drv->rtnl_sk = nl_socket_alloc();
++ if (drv->rtnl_sk == NULL) {
++ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
++ goto failed;
++ }
+
+- err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+- if (err) {
+- wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+- nl_geterror(err));
+- goto failed;
+- }
++ err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
++ if (err) {
++ wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
++ nl_geterror(err));
++ goto failed;
+ }
+-#endif /* CONFIG_LIBNL3_ROUTE */
+
+ if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
+ wpa_printf(MSG_DEBUG,
+@@ -9000,6 +9019,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ if (drv->first_bss->next) {
+ drv->first_bss = drv->first_bss->next;
+ drv->ctx = drv->first_bss->ctx;
++ drv->ifindex = drv->first_bss->ifindex;
+ os_free(bss);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
+@@ -9009,6 +9029,50 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ return 0;
+ }
+
++static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
++ enum wpa_driver_if_type type,
++ const char *ifname, const char *new_name)
++{
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct ifinfomsg ifi = {
++ .ifi_family = AF_UNSPEC,
++ .ifi_index = bss->ifindex,
++ };
++ struct nl_msg *msg;
++ int res = -ENOMEM;
++
++ if (ifname)
++ ifi.ifi_index = if_nametoindex(ifname);
++
++ msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
++ if (!msg)
++ return res;
++
++ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
++ goto out;
++
++ if (nla_put_string(msg, IFLA_IFNAME, new_name))
++ goto out;
++
++ res = nl_send_auto_complete(drv->rtnl_sk, msg);
++ if (res < 0)
++ goto out;
++
++ res = nl_wait_for_ack(drv->rtnl_sk);
++ if (res) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Renaming device %s to %s failed: %s",
++ ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
++ goto out;
++ }
++
++ if (type == WPA_IF_AP_BSS && !ifname)
++ os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
++
++out:
++ nlmsg_free(msg);
++ return res;
++}
+
+ static int cookie_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -10792,6 +10856,37 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+ #endif /* CONFIG_IEEE80211BE */
+
+
++static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
++ const char *ifname, const char *new_name)
++{
++ struct i802_bss *bss = priv;
++ return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
++}
++
++
++static int driver_nl80211_set_first_bss(void *priv)
++{
++ struct i802_bss *bss = priv, *tbss;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++
++ if (drv->first_bss == bss)
++ return 0;
++
++ for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
++ if (tbss->next != bss)
++ continue;
++
++ tbss->next = bss->next;
++ bss->next = drv->first_bss;
++ drv->first_bss = bss;
++ drv->ctx = bss->ctx;
++ return 0;
++ }
++
++ return -1;
++}
++
++
+ static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq,
+@@ -11294,6 +11389,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ if (ret)
+ goto error;
+
++ if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
++ nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
++ }
++
+ /* beacon_csa params */
+ beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+ if (!beacon_csa)
+@@ -11940,6 +12039,18 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+ }
+
+
++static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
++{
++ if (mcast_rate > 0) {
++ wpa_printf(MSG_DEBUG, " * mcast_rate=%.1f",
++ (double)mcast_rate / 10);
++ return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
++ }
++
++ return 0;
++}
++
++
+ static int nl80211_put_mesh_config(struct nl_msg *msg,
+ struct wpa_driver_mesh_bss_params *params)
+ {
+@@ -12001,6 +12112,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
+ nl80211_put_basic_rates(msg, params->basic_rates) ||
+ nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+ nl80211_put_beacon_int(msg, params->beacon_int) ||
++ nl80211_put_mcast_rate(msg, params->mcast_rate) ||
+ nl80211_put_dtim_period(msg, params->dtim_period))
+ goto fail;
+
+@@ -12156,13 +12268,14 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+ const u8 *ipaddr, int prefixlen,
+ const u8 *addr)
+ {
+-#ifdef CONFIG_LIBNL3_ROUTE
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+- struct rtnl_neigh *rn;
+- struct nl_addr *nl_ipaddr = NULL;
+- struct nl_addr *nl_lladdr = NULL;
+- int family, addrsize;
++ struct ndmsg nhdr = {
++ .ndm_state = NUD_PERMANENT,
++ .ndm_ifindex = bss->br_ifindex,
++ };
++ struct nl_msg *msg;
++ int addrsize;
+ int res;
+
+ if (!ipaddr || prefixlen == 0 || !addr)
+@@ -12181,85 +12294,66 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+ }
+
+ if (version == 4) {
+- family = AF_INET;
++ nhdr.ndm_family = AF_INET;
+ addrsize = 4;
+ } else if (version == 6) {
+- family = AF_INET6;
++ nhdr.ndm_family = AF_INET6;
+ addrsize = 16;
+ } else {
+ return -EINVAL;
+ }
+
+- rn = rtnl_neigh_alloc();
+- if (rn == NULL)
++ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
++ if (!msg)
+ return -ENOMEM;
+
+- /* set the destination ip address for neigh */
+- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+- if (nl_ipaddr == NULL) {
+- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+- res = -ENOMEM;
++ res = -ENOMEM;
++ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+ goto errout;
+- }
+- nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
+- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+- if (res) {
+- wpa_printf(MSG_DEBUG,
+- "nl80211: neigh set destination addr failed");
++
++ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+ goto errout;
+- }
+
+- /* set the corresponding lladdr for neigh */
+- nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
+- if (nl_lladdr == NULL) {
+- wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
+- res = -ENOMEM;
++ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
+ goto errout;
+- }
+- rtnl_neigh_set_lladdr(rn, nl_lladdr);
+
+- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+- rtnl_neigh_set_state(rn, NUD_PERMANENT);
++ res = nl_send_auto_complete(drv->rtnl_sk, msg);
++ if (res < 0)
++ goto errout;
+
+- res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
++ res = nl_wait_for_ack(drv->rtnl_sk);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Adding bridge ip neigh failed: %s",
+ nl_geterror(res));
+ }
+ errout:
+- if (nl_lladdr)
+- nl_addr_put(nl_lladdr);
+- if (nl_ipaddr)
+- nl_addr_put(nl_ipaddr);
+- if (rn)
+- rtnl_neigh_put(rn);
++ nlmsg_free(msg);
+ return res;
+-#else /* CONFIG_LIBNL3_ROUTE */
+- return -1;
+-#endif /* CONFIG_LIBNL3_ROUTE */
+ }
+
+
+ static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+ const u8 *ipaddr)
+ {
+-#ifdef CONFIG_LIBNL3_ROUTE
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+- struct rtnl_neigh *rn;
+- struct nl_addr *nl_ipaddr;
+- int family, addrsize;
++ struct ndmsg nhdr = {
++ .ndm_state = NUD_PERMANENT,
++ .ndm_ifindex = bss->br_ifindex,
++ };
++ struct nl_msg *msg;
++ int addrsize;
+ int res;
+
+ if (!ipaddr)
+ return -EINVAL;
+
+ if (version == 4) {
+- family = AF_INET;
++ nhdr.ndm_family = AF_INET;
+ addrsize = 4;
+ } else if (version == 6) {
+- family = AF_INET6;
++ nhdr.ndm_family = AF_INET6;
+ addrsize = 16;
+ } else {
+ return -EINVAL;
+@@ -12277,41 +12371,30 @@ static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+ return -1;
+ }
+
+- rn = rtnl_neigh_alloc();
+- if (rn == NULL)
++ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
++ if (!msg)
+ return -ENOMEM;
+
+- /* set the destination ip address for neigh */
+- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+- if (nl_ipaddr == NULL) {
+- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+- res = -ENOMEM;
++ res = -ENOMEM;
++ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+ goto errout;
+- }
+- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+- if (res) {
+- wpa_printf(MSG_DEBUG,
+- "nl80211: neigh set destination addr failed");
++
++ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+ goto errout;
+- }
+
+- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
++ res = nl_send_auto_complete(drv->rtnl_sk, msg);
++ if (res < 0)
++ goto errout;
+
+- res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
++ res = nl_wait_for_ack(drv->rtnl_sk);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Deleting bridge ip neigh failed: %s",
+ nl_geterror(res));
+ }
+ errout:
+- if (nl_ipaddr)
+- nl_addr_put(nl_ipaddr);
+- if (rn)
+- rtnl_neigh_put(rn);
++ nlmsg_free(msg);
+ return res;
+-#else /* CONFIG_LIBNL3_ROUTE */
+- return -1;
+-#endif /* CONFIG_LIBNL3_ROUTE */
+ }
+
+
+@@ -12389,7 +12472,7 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param)
+
+
+ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+- unsigned int val)
++ const char *ifname, unsigned int val)
+ {
+ struct i802_bss *bss = priv;
+ char path[128];
+@@ -12415,8 +12498,11 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+ return -EINVAL;
+ }
+
++ if (!ifname)
++ ifname = bss->brname;
++
+ os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+- ip_version, bss->brname, param_txt);
++ ip_version, ifname, param_txt);
+
+ set_val:
+ if (linux_write_system_file(path, val))
+@@ -14019,6 +14105,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .set_acl = wpa_driver_nl80211_set_acl,
+ .if_add = wpa_driver_nl80211_if_add,
+ .if_remove = driver_nl80211_if_remove,
++ .if_rename = driver_nl80211_if_rename,
++ .set_first_bss = driver_nl80211_set_first_bss,
+ .send_mlme = driver_nl80211_send_mlme,
+ .get_hw_feature_data = nl80211_get_hw_feature_data,
+ .sta_add = wpa_driver_nl80211_sta_add,
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 65389d206..d6a887cef 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -976,6 +976,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+ }
+
++ if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
++ capa->max_scan_ie_len =
++ nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
++
+ if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+ capa->max_match_sets =
+ nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index f5778cdaf..4a12d749c 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1196,6 +1196,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *bw, struct nlattr *cf1,
+ struct nlattr *cf2,
+ struct nlattr *punct_bitmap,
++ struct nlattr *count,
+ int finished)
+ {
+ struct i802_bss *bss;
+@@ -1259,6 +1260,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ data.ch_switch.cf1 = nla_get_u32(cf1);
+ if (cf2)
+ data.ch_switch.cf2 = nla_get_u32(cf2);
++ if (count)
++ data.ch_switch.count = nla_get_u32(count);
+
+ if (link)
+ data.ch_switch.link_id = nla_get_u8(link);
+@@ -3972,6 +3975,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
++ tb[NL80211_ATTR_CH_SWITCH_COUNT],
+ 0);
+ break;
+ case NL80211_CMD_CH_SWITCH_NOTIFY:
+@@ -3984,6 +3988,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
++ NULL,
+ 1);
+ break;
+ case NL80211_CMD_DISCONNECT:
+diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
+index 577f84fef..c352a88bc 100644
+--- a/src/drivers/driver_nl80211_scan.c
++++ b/src/drivers/driver_nl80211_scan.c
+@@ -221,7 +221,7 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+ wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
+ }
+
+- if (params->extra_ies) {
++ if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ params->extra_ies, params->extra_ies_len);
+ if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
+index e95df6ddb..9071da3cf 100644
+--- a/src/drivers/drivers.c
++++ b/src/drivers/drivers.c
+@@ -10,6 +10,10 @@
+ #include "utils/common.h"
+ #include "driver.h"
+
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ const struct wpa_driver_ops *const wpa_drivers[] =
+ {
+diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
+index a03d4a034..8da44d9f5 100644
+--- a/src/drivers/drivers.mak
++++ b/src/drivers/drivers.mak
+@@ -54,7 +54,6 @@ NEED_SME=y
+ NEED_AP_MLME=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ NEED_RADIOTAP=y
+ NEED_LIBNL=y
+ endif
+@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+ CONFIG_WIRELESS_EXTENSION=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ endif
+
+ ifdef CONFIG_DRIVER_NDIS
+@@ -137,7 +135,6 @@ endif
+ ifdef CONFIG_WIRELESS_EXTENSION
+ DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+ DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+-NEED_RFKILL=y
+ endif
+
+ ifdef NEED_NETLINK
+@@ -146,6 +143,7 @@ endif
+
+ ifdef NEED_RFKILL
+ DRV_OBJS += ../src/drivers/rfkill.o
++DRV_WPA_CFLAGS += -DCONFIG_RFKILL
+ endif
+
+ ifdef NEED_RADIOTAP
+diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
+index 0412ac330..e27565375 100644
+--- a/src/drivers/rfkill.h
++++ b/src/drivers/rfkill.h
+@@ -18,8 +18,24 @@ struct rfkill_config {
+ void (*unblocked_cb)(void *ctx);
+ };
+
++#ifdef CONFIG_RFKILL
+ struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+ void rfkill_deinit(struct rfkill_data *rfkill);
+ int rfkill_is_blocked(struct rfkill_data *rfkill);
++#else
++static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
++{
++ return (void *) 1;
++}
++
++static inline void rfkill_deinit(struct rfkill_data *rfkill)
++{
++}
++
++static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
++{
++ return 0;
++}
++#endif
+
+ #endif /* RFKILL_H */
+diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
+index 2a7f36170..8e8903051 100644
+--- a/src/radius/radius_client.c
++++ b/src/radius/radius_client.c
+@@ -165,6 +165,8 @@ struct radius_client_data {
+ */
+ void *ctx;
+
++ struct hostapd_ip_addr local_ip;
++
+ /**
+ * conf - RADIUS client configuration (list of RADIUS servers to use)
+ */
+@@ -818,6 +820,30 @@ static void radius_close_acct_socket(struct radius_client_data *radius)
+ }
+
+
++/**
++ * radius_client_send - Get local address for the RADIUS auth socket
++ * @radius: RADIUS client context from radius_client_init()
++ * @addr: pointer to store the address
++ *
++ * This function returns the local address for the connection to the RADIUS
++ * auth server. It also opens the socket if it's not available yet.
++ */
++int radius_client_get_local_addr(struct radius_client_data *radius,
++ struct hostapd_ip_addr *addr)
++{
++ struct hostapd_radius_servers *conf = radius->conf;
++
++ if (conf->auth_server && radius->auth_sock < 0)
++ radius_client_init_auth(radius);
++
++ if (radius->auth_sock < 0)
++ return -1;
++
++ memcpy(addr, &radius->local_ip, sizeof(*addr));
++
++ return 0;
++}
++
+ /**
+ * radius_client_send - Send a RADIUS request
+ * @radius: RADIUS client context from radius_client_init()
+@@ -1711,6 +1737,10 @@ radius_change_server(struct radius_client_data *radius,
+ wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+ inet_ntoa(claddr.sin_addr),
+ ntohs(claddr.sin_port));
++ if (auth) {
++ radius->local_ip.af = AF_INET;
++ radius->local_ip.u.v4 = claddr.sin_addr;
++ }
+ }
+ break;
+ #ifdef CONFIG_IPV6
+@@ -1722,6 +1752,10 @@ radius_change_server(struct radius_client_data *radius,
+ inet_ntop(AF_INET6, &claddr6.sin6_addr,
+ abuf, sizeof(abuf)),
+ ntohs(claddr6.sin6_port));
++ if (auth) {
++ radius->local_ip.af = AF_INET6;
++ radius->local_ip.u.v6 = claddr6.sin6_addr;
++ }
+ }
+ break;
+ }
+diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
+index db40637ea..9a89b0382 100644
+--- a/src/radius/radius_client.h
++++ b/src/radius/radius_client.h
+@@ -274,6 +274,8 @@ int radius_client_register(struct radius_client_data *radius,
+ void radius_client_set_interim_error_cb(struct radius_client_data *radius,
+ void (*cb)(const u8 *addr, void *ctx),
+ void *ctx);
++int radius_client_get_local_addr(struct radius_client_data *radius,
++ struct hostapd_ip_addr * addr);
+ int radius_client_send(struct radius_client_data *radius,
+ struct radius_msg *msg,
+ RadiusType msg_type, const u8 *addr);
+diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
+index aaa3fc267..327782f62 100644
+--- a/src/radius/radius_das.c
++++ b/src/radius/radius_das.c
+@@ -12,13 +12,26 @@
+ #include "utils/common.h"
+ #include "utils/eloop.h"
+ #include "utils/ip_addr.h"
++#include "utils/list.h"
+ #include "radius.h"
+ #include "radius_das.h"
+
+
+-struct radius_das_data {
++static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
++
++struct radius_das_port {
++ struct dl_list list;
++ struct dl_list das_data;
++
++ int port;
+ int sock;
++};
++
++struct radius_das_data {
++ struct dl_list list;
++ struct radius_das_port *port;
+ u8 *shared_secret;
++ u8 *nas_identifier;
+ size_t shared_secret_len;
+ struct hostapd_ip_addr client_addr;
+ unsigned int time_window;
+@@ -378,56 +391,17 @@ fail:
+ }
+
+
+-static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++static void
++radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
++ struct sockaddr *from, socklen_t fromlen,
++ char *abuf, int from_port)
+ {
+- struct radius_das_data *das = eloop_ctx;
+- u8 buf[1500];
+- union {
+- struct sockaddr_storage ss;
+- struct sockaddr_in sin;
+-#ifdef CONFIG_IPV6
+- struct sockaddr_in6 sin6;
+-#endif /* CONFIG_IPV6 */
+- } from;
+- char abuf[50];
+- int from_port = 0;
+- socklen_t fromlen;
+- int len;
+- struct radius_msg *msg, *reply = NULL;
++ struct radius_msg *reply = NULL;
+ struct radius_hdr *hdr;
+ struct wpabuf *rbuf;
++ struct os_time now;
+ u32 val;
+ int res;
+- struct os_time now;
+-
+- fromlen = sizeof(from);
+- len = recvfrom(sock, buf, sizeof(buf), 0,
+- (struct sockaddr *) &from.ss, &fromlen);
+- if (len < 0) {
+- wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+- return;
+- }
+-
+- os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+- from_port = ntohs(from.sin.sin_port);
+-
+- wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+- len, abuf, from_port);
+- if (das->client_addr.u.v4.s_addr &&
+- das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+- wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+- return;
+- }
+-
+- msg = radius_msg_parse(buf, len);
+- if (msg == NULL) {
+- wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+- "from %s:%d failed", abuf, from_port);
+- return;
+- }
+-
+- if (wpa_debug_level <= MSG_MSGDUMP)
+- radius_msg_dump(msg);
+
+ if (radius_msg_verify_das_req(msg, das->shared_secret,
+ das->shared_secret_len,
+@@ -494,9 +468,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+ radius_msg_dump(reply);
+
+ rbuf = radius_msg_get_buf(reply);
+- res = sendto(das->sock, wpabuf_head(rbuf),
+- wpabuf_len(rbuf), 0,
+- (struct sockaddr *) &from.ss, fromlen);
++ res = sendto(das->port->sock, wpabuf_head(rbuf),
++ wpabuf_len(rbuf), 0, from, fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+ abuf, from_port, strerror(errno));
+@@ -508,6 +481,72 @@ fail:
+ radius_msg_free(reply);
+ }
+
++static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++{
++ struct radius_das_port *p = eloop_ctx;
++ struct radius_das_data *das;
++ u8 buf[1500];
++ union {
++ struct sockaddr_storage ss;
++ struct sockaddr_in sin;
++#ifdef CONFIG_IPV6
++ struct sockaddr_in6 sin6;
++#endif /* CONFIG_IPV6 */
++ } from;
++ struct radius_msg *msg;
++ size_t nasid_len = 0;
++ u8 *nasid_buf = NULL;
++ char abuf[50];
++ int from_port = 0;
++ socklen_t fromlen;
++ int found = 0;
++ int len;
++
++ fromlen = sizeof(from);
++ len = recvfrom(sock, buf, sizeof(buf), 0,
++ (struct sockaddr *) &from.ss, &fromlen);
++ if (len < 0) {
++ wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
++ return;
++ }
++
++ os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
++ from_port = ntohs(from.sin.sin_port);
++
++ msg = radius_msg_parse(buf, len);
++ if (msg == NULL) {
++ wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
++ "from %s:%d failed", abuf, from_port);
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
++ len, abuf, from_port);
++
++ if (wpa_debug_level <= MSG_MSGDUMP)
++ radius_msg_dump(msg);
++
++ radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
++ &nasid_buf, &nasid_len, NULL);
++ dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
++ if (das->client_addr.u.v4.s_addr &&
++ das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
++ continue;
++
++ if (das->nas_identifier && nasid_buf &&
++ (nasid_len != os_strlen(das->nas_identifier) ||
++ os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
++ continue;
++
++ found = 1;
++ radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
++ fromlen, abuf, from_port);
++ }
++
++ if (!found)
++ wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
++}
++
+
+ static int radius_das_open_socket(int port)
+ {
+@@ -533,6 +572,49 @@ static int radius_das_open_socket(int port)
+ }
+
+
++static struct radius_das_port *
++radius_das_open_port(int port)
++{
++ struct radius_das_port *p;
++
++ dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
++ if (p->port == port)
++ return p;
++ }
++
++ p = os_zalloc(sizeof(*p));
++ if (p == NULL)
++ return NULL;
++
++ dl_list_init(&p->das_data);
++ p->port = port;
++ p->sock = radius_das_open_socket(port);
++ if (p->sock < 0)
++ goto free_port;
++
++ if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
++ goto close_port;
++
++ dl_list_add(&das_ports, &p->list);
++
++ return p;
++
++close_port:
++ close(p->sock);
++free_port:
++ os_free(p);
++
++ return NULL;
++}
++
++static void radius_das_close_port(struct radius_das_port *p)
++{
++ dl_list_del(&p->list);
++ eloop_unregister_read_sock(p->sock);
++ close(p->sock);
++ free(p);
++}
++
+ struct radius_das_data *
+ radius_das_init(struct radius_das_conf *conf)
+ {
+@@ -553,6 +635,8 @@ radius_das_init(struct radius_das_conf *conf)
+ das->ctx = conf->ctx;
+ das->disconnect = conf->disconnect;
+ das->coa = conf->coa;
++ if (conf->nas_identifier)
++ das->nas_identifier = os_strdup(conf->nas_identifier);
+
+ os_memcpy(&das->client_addr, conf->client_addr,
+ sizeof(das->client_addr));
+@@ -565,19 +649,15 @@ radius_das_init(struct radius_das_conf *conf)
+ }
+ das->shared_secret_len = conf->shared_secret_len;
+
+- das->sock = radius_das_open_socket(conf->port);
+- if (das->sock < 0) {
++ das->port = radius_das_open_port(conf->port);
++ if (!das->port) {
+ wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+ "DAS");
+ radius_das_deinit(das);
+ return NULL;
+ }
+
+- if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+- {
+- radius_das_deinit(das);
+- return NULL;
+- }
++ dl_list_add(&das->port->das_data, &das->list);
+
+ return das;
+ }
+@@ -588,11 +668,14 @@ void radius_das_deinit(struct radius_das_data *das)
+ if (das == NULL)
+ return;
+
+- if (das->sock >= 0) {
+- eloop_unregister_read_sock(das->sock);
+- close(das->sock);
++ if (das->port) {
++ dl_list_del(&das->list);
++
++ if (dl_list_empty(&das->port->das_data))
++ radius_das_close_port(das->port);
+ }
+
++ os_free(das->nas_identifier);
+ os_free(das->shared_secret);
+ os_free(das);
+ }
+diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
+index 233d662f6..80dc13fc8 100644
+--- a/src/radius/radius_das.h
++++ b/src/radius/radius_das.h
+@@ -44,6 +44,7 @@ struct radius_das_attrs {
+ struct radius_das_conf {
+ int port;
+ const u8 *shared_secret;
++ const u8 *nas_identifier;
+ size_t shared_secret_len;
+ const struct hostapd_ip_addr *client_addr;
+ unsigned int time_window;
+diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
+index e02c21540..57a47263e 100644
+--- a/src/radius/radius_server.c
++++ b/src/radius/radius_server.c
+@@ -63,6 +63,12 @@ struct radius_server_counters {
+ u32 unknown_acct_types;
+ };
+
++struct radius_accept_attr {
++ u8 type;
++ u16 len;
++ void *data;
++};
++
+ /**
+ * struct radius_session - Internal RADIUS server data for a session
+ */
+@@ -90,7 +96,7 @@ struct radius_session {
+ unsigned int macacl:1;
+ unsigned int t_c_filtering:1;
+
+- struct hostapd_radius_attr *accept_attr;
++ struct radius_accept_attr *accept_attr;
+
+ u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
+ };
+@@ -394,6 +400,7 @@ static void radius_server_session_free(struct radius_server_data *data,
+ radius_msg_free(sess->last_reply);
+ os_free(sess->username);
+ os_free(sess->nas_ip);
++ os_free(sess->accept_attr);
+ os_free(sess);
+ data->num_sess--;
+ }
+@@ -554,6 +561,36 @@ radius_server_erp_find_key(struct radius_server_data *data, const char *keyname)
+ }
+ #endif /* CONFIG_ERP */
+
++static struct radius_accept_attr *
++radius_server_copy_attr(const struct hostapd_radius_attr *data)
++{
++ const struct hostapd_radius_attr *attr;
++ struct radius_accept_attr *attr_new;
++ size_t data_size = 0;
++ void *data_buf;
++ int n_attr = 1;
++
++ for (attr = data; attr; attr = attr->next) {
++ n_attr++;
++ data_size += wpabuf_len(attr->val);
++ }
++
++ attr_new = os_zalloc(n_attr * sizeof(*attr) + data_size);
++ if (!attr_new)
++ return NULL;
++
++ data_buf = &attr_new[n_attr];
++ for (n_attr = 0, attr = data; attr; attr = attr->next) {
++ struct radius_accept_attr *cur = &attr_new[n_attr++];
++
++ cur->type = attr->type;
++ cur->len = wpabuf_len(attr->val);
++ cur->data = memcpy(data_buf, wpabuf_head(attr->val), cur->len);
++ data_buf += cur->len;
++ }
++
++ return attr_new;
++}
+
+ static struct radius_session *
+ radius_server_get_new_session(struct radius_server_data *data,
+@@ -607,7 +644,7 @@ radius_server_get_new_session(struct radius_server_data *data,
+ eap_user_free(tmp);
+ return NULL;
+ }
+- sess->accept_attr = tmp->accept_attr;
++ sess->accept_attr = radius_server_copy_attr(tmp->accept_attr);
+ sess->macacl = tmp->macacl;
+ eap_user_free(tmp);
+
+@@ -1118,11 +1155,10 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+- struct hostapd_radius_attr *attr;
+- for (attr = sess->accept_attr; attr; attr = attr->next) {
+- if (!radius_msg_add_attr(msg, attr->type,
+- wpabuf_head(attr->val),
+- wpabuf_len(attr->val))) {
++ struct radius_accept_attr *attr;
++ for (attr = sess->accept_attr; attr->data; attr++) {
++ if (!radius_msg_add_attr(msg, attr->type, attr->data,
++ attr->len)) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ radius_msg_free(msg);
+ return NULL;
+@@ -1211,11 +1247,10 @@ radius_server_macacl(struct radius_server_data *data,
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+- struct hostapd_radius_attr *attr;
+- for (attr = sess->accept_attr; attr; attr = attr->next) {
+- if (!radius_msg_add_attr(msg, attr->type,
+- wpabuf_head(attr->val),
+- wpabuf_len(attr->val))) {
++ struct radius_accept_attr *attr;
++ for (attr = sess->accept_attr; attr->data; attr++) {
++ if (!radius_msg_add_attr(msg, attr->type, attr->data,
++ attr->len)) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ radius_msg_free(msg);
+ return NULL;
+@@ -2512,7 +2547,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+ ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+ phase2, user);
+ if (ret == 0 && user) {
+- sess->accept_attr = user->accept_attr;
++ sess->accept_attr = radius_server_copy_attr(user->accept_attr);
+ sess->remediation = user->remediation;
+ sess->macacl = user->macacl;
+ sess->t_c_timestamp = user->t_c_timestamp;
+diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
+index 8956c4072..e669858d8 100644
+--- a/src/rsn_supp/wpa.c
++++ b/src/rsn_supp/wpa.c
+@@ -3943,6 +3943,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+ }
+
+
++#ifdef CONFIG_CTRL_IFACE_MIB
++
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+@@ -4024,6 +4026,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+
+ return (int) len;
+ }
++#endif
+ #endif /* CONFIG_CTRL_IFACE */
+
+
+diff --git a/src/tls/Makefile b/src/tls/Makefile
+index c84fbe859..e974a41f0 100644
+--- a/src/tls/Makefile
++++ b/src/tls/Makefile
+@@ -1,3 +1,10 @@
++LIB_OBJS= asn1.o
++
++ifneq ($(CONFIG_TLS),gnutls)
++ifneq ($(CONFIG_TLS),mbedtls)
++ifneq ($(CONFIG_TLS),openssl)
++ifneq ($(CONFIG_TLS),wolfssl)
++
+ CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ CFLAGS += -DCONFIG_TLSV11
+@@ -21,5 +28,9 @@ LIB_OBJS= \
+ tlsv1_server_read.o \
+ tlsv1_server_write.o \
+ x509v3.o
++endif
++endif
++endif
++endif
+
+ include ../lib.rules
+diff --git a/src/utils/eloop.c b/src/utils/eloop.c
+index 00b0beff0..50dd1beda 100644
+--- a/src/utils/eloop.c
++++ b/src/utils/eloop.c
+@@ -77,6 +77,9 @@ struct eloop_sock_table {
+ struct eloop_data {
+ int max_sock;
+
++ eloop_timeout_poll_handler timeout_poll_cb;
++ eloop_poll_handler poll_cb;
++
+ size_t count; /* sum of all table counts */
+ #ifdef CONFIG_ELOOP_POLL
+ size_t max_pollfd_map; /* number of pollfds_map currently allocated */
+@@ -1121,6 +1124,12 @@ void eloop_run(void)
+ os_reltime_sub(&timeout->time, &now, &tv);
+ else
+ tv.sec = tv.usec = 0;
++ }
++
++ if (eloop.timeout_poll_cb && eloop.timeout_poll_cb(&tv, !!timeout))
++ timeout = (void *)1;
++
++ if (timeout) {
+ #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
+ timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+ #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+@@ -1190,7 +1199,8 @@ void eloop_run(void)
+ eloop.exceptions.changed = 0;
+
+ eloop_process_pending_signals();
+-
++ if (eloop.poll_cb)
++ eloop.poll_cb();
+
+ /* check if some registered timeouts have occurred */
+ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+@@ -1252,6 +1262,14 @@ out:
+ return;
+ }
+
++int eloop_register_cb(eloop_poll_handler poll_cb,
++ eloop_timeout_poll_handler timeout_cb)
++{
++ eloop.poll_cb = poll_cb;
++ eloop.timeout_poll_cb = timeout_cb;
++
++ return 0;
++}
+
+ void eloop_terminate(void)
+ {
+diff --git a/src/utils/eloop.h b/src/utils/eloop.h
+index 04ee6d183..5452ea589 100644
+--- a/src/utils/eloop.h
++++ b/src/utils/eloop.h
+@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
+ */
+ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
++typedef bool (*eloop_timeout_poll_handler)(struct os_reltime *tv, bool tv_set);
++typedef void (*eloop_poll_handler)(void);
++
+ /**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+@@ -73,6 +76,9 @@ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+ */
+ int eloop_init(void);
+
++int eloop_register_cb(eloop_poll_handler poll_cb,
++ eloop_timeout_poll_handler timeout_cb);
++
+ /**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+@@ -320,6 +326,8 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ */
+ int eloop_sock_requeue(void);
+
++void eloop_add_uloop(void);
++
+ /**
+ * eloop_run - Start the event loop
+ *
+diff --git a/src/utils/uloop.c b/src/utils/uloop.c
+new file mode 100644
+index 000000000..c0d26db93
+--- /dev/null
++++ b/src/utils/uloop.c
+@@ -0,0 +1,64 @@
++#include <libubox/uloop.h>
++#include "includes.h"
++#include "common.h"
++#include "eloop.h"
++
++static void eloop_uloop_event_cb(int sock, void *eloop_ctx, void *sock_ctx)
++{
++}
++
++static void eloop_uloop_fd_cb(struct uloop_fd *fd, unsigned int events)
++{
++ unsigned int changed = events ^ fd->flags;
++
++ if (changed & ULOOP_READ) {
++ if (events & ULOOP_READ)
++ eloop_register_sock(fd->fd, EVENT_TYPE_READ, eloop_uloop_event_cb, fd, fd);
++ else
++ eloop_unregister_sock(fd->fd, EVENT_TYPE_READ);
++ }
++
++ if (changed & ULOOP_WRITE) {
++ if (events & ULOOP_WRITE)
++ eloop_register_sock(fd->fd, EVENT_TYPE_WRITE, eloop_uloop_event_cb, fd, fd);
++ else
++ eloop_unregister_sock(fd->fd, EVENT_TYPE_WRITE);
++ }
++}
++
++static bool uloop_timeout_poll_handler(struct os_reltime *tv, bool tv_set)
++{
++ struct os_reltime tv_uloop;
++ int timeout_ms = uloop_get_next_timeout();
++
++ if (timeout_ms < 0)
++ return false;
++
++ tv_uloop.sec = timeout_ms / 1000;
++ tv_uloop.usec = (timeout_ms % 1000) * 1000;
++
++ if (!tv_set || os_reltime_before(&tv_uloop, tv)) {
++ *tv = tv_uloop;
++ return true;
++ }
++
++ return false;
++}
++
++static void uloop_poll_handler(void)
++{
++ uloop_run_timeout(0);
++}
++
++void eloop_add_uloop(void)
++{
++ static bool init_done = false;
++
++ if (!init_done) {
++ uloop_init();
++ uloop_fd_set_cb = eloop_uloop_fd_cb;
++ init_done = true;
++ }
++
++ eloop_register_cb(uloop_poll_handler, uloop_timeout_poll_handler);
++}
+diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
+index 7f3dd185f..627575e39 100644
+--- a/src/utils/wpa_debug.c
++++ b/src/utils/wpa_debug.c
+@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NULL;
+ #define WPAS_TRACE_PFX "wpas <%d>: "
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+
++void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
++ size_t len);
++void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+
+ int wpa_debug_level = MSG_INFO;
+ int wpa_debug_show_keys = 0;
+@@ -206,10 +210,16 @@ void wpa_debug_close_linux_tracing(void)
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ {
+ va_list ap;
+
++ if (wpa_printf_hook) {
++ va_start(ap, fmt);
++ wpa_printf_hook(level, fmt, ap);
++ va_end(ap);
++ }
++
+ if (level >= wpa_debug_level) {
+ #ifdef CONFIG_ANDROID_LOG
+ va_start(ap, fmt);
+@@ -255,11 +265,14 @@ void wpa_printf(int level, const char *fmt, ...)
+ }
+
+
+-static void _wpa_hexdump(int level, const char *title, const u8 *buf,
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ size_t len, int show, int only_syslog)
+ {
+ size_t i;
+
++ if (wpa_hexdump_hook)
++ wpa_hexdump_hook(level, title, buf, len);
++
+ #ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+@@ -382,19 +395,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ #endif /* CONFIG_ANDROID_LOG */
+ }
+
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
+-{
+- _wpa_hexdump(level, title, buf, len, 1, 0);
+-}
+-
+-
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
+-{
+- _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
+-}
+-
+-
+-static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len, int show)
+ {
+ size_t i, llen;
+@@ -507,20 +508,6 @@ file_done:
+ }
+
+
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+- size_t len)
+-{
+- _wpa_hexdump_ascii(level, title, buf, len, 1);
+-}
+-
+-
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+- size_t len)
+-{
+- _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+-}
+-
+-
+ #ifdef CONFIG_DEBUG_FILE
+ static char *last_path = NULL;
+ #endif /* CONFIG_DEBUG_FILE */
+@@ -644,7 +631,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+ }
+
+
+-void wpa_msg(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg(void *ctx, int level, const char *fmt, ...)
+ {
+ va_list ap;
+ char *buf;
+@@ -682,7 +669,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
+ }
+
+
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ {
+ va_list ap;
+ char *buf;
+diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
+index 4c02ad3c7..854520bfe 100644
+--- a/src/utils/wpa_debug.h
++++ b/src/utils/wpa_debug.h
+@@ -11,6 +11,10 @@
+
+ #include "wpabuf.h"
+
++extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++extern void (*wpa_hexdump_hook)(int level, const char *title,
++ const void *buf, size_t len);
++extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+ extern int wpa_debug_level;
+ extern int wpa_debug_show_keys;
+ extern int wpa_debug_timestamp;
+@@ -51,6 +55,17 @@ void wpa_debug_close_file(void);
+ void wpa_debug_setup_stdout(void);
+ void wpa_debug_stop_log(void);
+
++/* internal */
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
++ size_t len, int show, int only_syslog);
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++ size_t len, int show);
++extern int wpa_debug_show_keys;
++
++#ifndef CONFIG_MSG_MIN_PRIORITY
++#define CONFIG_MSG_MIN_PRIORITY 0
++#endif
++
+ /**
+ * wpa_debug_printf_timestamp - Print timestamp for debug output
+ *
+@@ -71,9 +86,15 @@ void wpa_debug_print_timestamp(void);
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ PRINTF_FORMAT(2, 3);
+
++#define wpa_printf(level, ...) \
++ do { \
++ if (level >= CONFIG_MSG_MIN_PRIORITY) \
++ _wpa_printf(level, __VA_ARGS__); \
++ } while(0)
++
+ /**
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+@@ -85,7 +106,13 @@ PRINTF_FORMAT(2, 3);
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump.
+ */
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump(level, title, buf, len, 1, 1);
++}
+
+ static inline void wpa_hexdump_buf(int level, const char *title,
+ const struct wpabuf *buf)
+@@ -107,7 +134,13 @@ static inline void wpa_hexdump_buf(int level, const char *title,
+ * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+ * etc.) in debug output.
+ */
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
++}
+
+ static inline void wpa_hexdump_buf_key(int level, const char *title,
+ const struct wpabuf *buf)
+@@ -129,8 +162,14 @@ static inline void wpa_hexdump_buf_key(int level, const char *title,
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown.
+ */
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+- size_t len);
++static inline void wpa_hexdump_ascii(int level, const char *title,
++ const u8 *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump_ascii(level, title, buf, len, 1);
++}
+
+ /**
+ * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+@@ -146,8 +185,14 @@ void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
+ * default, does not include secret keys (passwords, etc.) in debug output.
+ */
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+- size_t len);
++static inline void wpa_hexdump_ascii_key(int level, const char *title,
++ const u8 *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
++}
+
+ /*
+ * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+@@ -184,7 +229,12 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+-void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++#define wpa_msg(ctx, level, ...) \
++ do { \
++ if (level >= CONFIG_MSG_MIN_PRIORITY) \
++ _wpa_msg(ctx, level, __VA_ARGS__); \
++ } while(0)
+
+ /**
+ * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
+@@ -198,8 +248,13 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+ * attached ctrl_iface monitors. In other words, it can be used for frequent
+ * events that do not need to be sent to syslog.
+ */
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ PRINTF_FORMAT(3, 4);
++#define wpa_msg_ctrl(ctx, level, ...) \
++ do { \
++ if (level >= CONFIG_MSG_MIN_PRIORITY) \
++ _wpa_msg_ctrl(ctx, level, __VA_ARGS__); \
++ } while(0)
+
+ /**
+ * wpa_msg_global - Global printf for ctrl_iface monitors
+diff --git a/tests/Makefile b/tests/Makefile
+index 8ec154bb3..58287f56f 100644
+--- a/tests/Makefile
++++ b/tests/Makefile
+@@ -5,6 +5,14 @@ ALL=test-base64 test-md4 test-milenage \
+ test-sha256 test-aes test-x509v3 test-list test-rc4 \
+ test-bss
+
++RUN_TESTS= \
++ test-list \
++ test-md4 test-rc4 test-sha1 test-sha256 \
++ test-milenage test-aes \
++ test-crypto_module
++
++ALL=$(RUN_TESTS) test-base64 test-https test-https_server
++
+ include ../src/build.rules
+
+ ifdef LIBFUZZER
+@@ -25,13 +33,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
+ CFLAGS += -DCONFIG_IEEE80211R
+ CFLAGS += -DCONFIG_TDLS
+
++# test-crypto_module
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DCONFIG_SHA256
++CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
+ CFLAGS += -I../src
+ CFLAGS += -I../src/utils
+
+ SLIBS = ../src/utils/libutils.a
+
+-DLIBS = ../src/crypto/libcrypto.a \
+- ../src/tls/libtls.a
++DLIBS = ../src/tls/libtls.a \
++ ../src/crypto/libcrypto.a
+
+ _OBJS_VAR := LLIBS
+ include ../src/objs.mk
+@@ -43,12 +65,43 @@ include ../src/objs.mk
+ LIBS = $(SLIBS) $(DLIBS)
+ LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
+
++ifeq ($(CONFIG_TLS),mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
++else
++ifeq ($(CONFIG_TLS),openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
++LLIBS += -lssl -lcrypto
++else
++ifeq ($(CONFIG_TLS),gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
++LLIBS += -lgnutls -lgpg-error -lgcrypt
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
++LLIBS += -lwolfssl -lm
++else
++CFLAGS += -DCONFIG_TLS_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
++ALL += test-rsa-sig-ver
++ALL += test-x509v3
++clean-config_tls_internal:
++ rm -f test_x509v3_nist.out.*
++ rm -f test_x509v3_nist2.out.*
++endif
++endif
++endif
++endif
++
+ # glibc < 2.17 needs -lrt for clock_gettime()
+ LLIBS += -lrt
+
+ test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
++test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
++ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
++
+ test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+@@ -149,10 +202,12 @@ run-tests: $(ALL)
+ ./test-sha1
+ ./test-sha256
+ ./test-bss
++
++ @set -ex; for i in $(RUN_TESTS); do ./$$i; done
+ @echo
+ @echo All tests completed successfully.
+
+-clean: common-clean
++clean: common-clean clean-config_tls_internal
+ rm -f *~
+- rm -f test_x509v3_nist.out.*
+- rm -f test_x509v3_nist2.out.*
++
++.PHONY: run-tests clean-config_tls_internal
+diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
+index 210b7fb86..608e20eed 100644
+--- a/tests/hwsim/example-hostapd.config
++++ b/tests/hwsim/example-hostapd.config
+@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
+ CONFIG_DRIVER_NL80211=y
+ CONFIG_RSN_PREAUTH=y
+
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -33,12 +34,7 @@ CONFIG_EAP_TNC=y
+ CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+ LIBS += -rdynamic
+ CONFIG_EAP_UNAUTH_TLS=y
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+ CONFIG_EAP_EKE=y
+ CONFIG_PKCS12=y
+ CONFIG_RADIUS_SERVER=y
+@@ -88,7 +84,7 @@ CFLAGS += -DCONFIG_RADIUS_TEST
+ CONFIG_MODULE_TESTS=y
+
+ CONFIG_SUITEB=y
+-CONFIG_SUITEB192=y
++CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
+
+ # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+ # This can be used as a more efficient memory error detector than valgrind
+diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
+index 123f397e3..da0dde659 100644
+--- a/tests/hwsim/example-wpa_supplicant.config
++++ b/tests/hwsim/example-wpa_supplicant.config
+@@ -2,6 +2,7 @@
+
+ CONFIG_TLS=openssl
+ #CONFIG_TLS=wolfssl
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -34,13 +35,7 @@ LIBS += -rdynamic
+ CONFIG_EAP_FAST=y
+ CONFIG_EAP_TEAP=y
+ CONFIG_EAP_IKEV2=y
+-
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+
+ CONFIG_USIM_SIMULATOR=y
+ CONFIG_SIM_SIMULATOR=y
+@@ -136,7 +131,7 @@ CONFIG_TESTING_OPTIONS=y
+ CONFIG_MODULE_TESTS=y
+
+ CONFIG_SUITEB=y
+-CONFIG_SUITEB192=y
++CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
+
+ # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+ # This can be used as a more efficient memory error detector than valgrind
+diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py
+index a20140316..027a60b25 100644
+--- a/tests/hwsim/test_ap_eap.py
++++ b/tests/hwsim/test_ap_eap.py
+@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
+ res = dev.get_capability("eap")
+ if method not in res:
+ raise HwsimSkip("EAP method %s not supported in the build" % method)
++ if method == "FAST" or method == "TEAP":
++ tls = dev.request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
+
+ def check_subject_match_support(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
+
+ def check_check_cert_subject_support(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
+
+ def check_altsubject_match_support(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
+
+ def check_domain_match(dev):
+@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
+
+ def check_domain_match_full(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
+
+ def check_cert_probe_support(dev):
+@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
+ raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
+
+ def check_ext_cert_check_support(dev):
++ if not openssl_imported:
++ raise HwsimSkip("OpenSSL python method not available")
++
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
+
+ def check_ocsp_support(dev):
+@@ -91,10 +126,12 @@ def check_ocsp_support(dev):
+ # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+ #if tls.startswith("wolfSSL"):
+ # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+
+ def check_pkcs5_v15_support(dev):
+ tls = dev.request("GET tls_library")
+- if "BoringSSL" in tls or "GnuTLS" in tls:
++ if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
+ raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
+
+ def check_tls13_support(dev):
+@@ -122,11 +159,15 @@ def check_pkcs12_support(dev):
+ # raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+ if tls.startswith("wolfSSL"):
+ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+
+ def check_dh_dsa_support(dev):
+ tls = dev.request("GET tls_library")
+ if tls.startswith("internal"):
+ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
+
+ def check_ec_support(dev):
+ tls = dev.request("GET tls_library")
+@@ -1741,7 +1782,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+- subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
++ check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
+ altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
+ eap_reauth(dev[0], "TTLS")
+
+@@ -2976,6 +3017,7 @@ def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
+
+ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+ """WPA2-Enterprise negative test - subject mismatch"""
++ check_subject_match_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+@@ -3036,6 +3078,7 @@ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+
+ def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
+ """WPA2-Enterprise negative test - altsubject mismatch"""
++ check_altsubject_match_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+@@ -3582,7 +3625,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
+ dev[0].request("REMOVE_NETWORK all")
+
+ tls = dev[0].request("GET tls_library")
+- if not tls.startswith("wolfSSL"):
++ if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
+ tests = [(1, "os_get_random;dh_init")]
+ else:
+ tests = [(1, "crypto_dh_init;dh_init")]
+@@ -4896,7 +4939,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
+ params["private_key"] = "auth_serv/iCA-server/server.key"
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -4962,6 +5005,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
+ run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
+
+ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
++ check_ocsp_support(dev[0])
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+ params["server_cert"] = "auth_serv/iCA-server/server.pem"
+@@ -4971,7 +5015,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5007,7 +5051,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5057,7 +5101,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, par
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5124,7 +5168,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
+
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5382,6 +5426,7 @@ def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
+
+ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
++ check_pkcs12_support(dev[0])
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ del params["server_cert"]
+@@ -5394,6 +5439,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+
+ def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
+ """EAP-TTLS and server PKCS#12 file with extra certs"""
++ check_pkcs12_support(dev[0])
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ del params["server_cert"]
+@@ -5416,6 +5462,7 @@ def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
+
+ def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
++ check_dh_dsa_support(dev[0])
+ params = int_eap_server_params()
+ params["dh_file"] = "auth_serv/dsaparam.pem"
+ hapd = hostapd.add_ap(apdev[0], params)
+@@ -5727,8 +5774,8 @@ def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
+ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+ """OpenSSL cipher suite configuration on wpa_supplicant"""
+ tls = dev[0].request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
+- raise HwsimSkip("TLS library is not OpenSSL: " + tls)
++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++ raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+@@ -5754,14 +5801,14 @@ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+ def test_openssl_cipher_suite_config_hapd(dev, apdev):
+ """OpenSSL cipher suite configuration on hostapd"""
+ tls = dev[0].request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
+- raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++ raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
+ params = int_eap_server_params()
+ params['openssl_ciphers'] = "AES256"
+ hapd = hostapd.add_ap(apdev[0], params)
+ tls = hapd.request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
+- raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++ raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+@@ -6207,14 +6254,26 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev):
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
+ "TLSv1.2")
+- elif tls.startswith("internal"):
++ elif tls.startswith("internal") or tls.startswith("mbed TLS"):
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
++<<<<<<< HEAD
+ check_tls_ver(dev[1], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
+ check_tls_ver(dev[2], hapd,
+ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
+ if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls:
++=======
++ if tls.startswith("mbed TLS"):
++ check_tls_ver(dev[2], hapd,
++ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
++ else:
++ check_tls_ver(dev[1], hapd,
++ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
++ check_tls_ver(dev[2], hapd,
++ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
++ if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3.0" in tls:
++>>>>>>> 585bc9ada (hostapd: sync 2024-01-18 openwrt/trunk patch folder)
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
+
+@@ -6235,6 +6294,11 @@ def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
+ tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+ ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++ #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
+ for exp, flags in tests:
+ hapd.disable()
+ hapd.set("tls_flags", flags)
+@@ -7305,6 +7369,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
+ def test_eap_tls_ext_cert_check(dev, apdev):
+ """EAP-TLS and external server certification validation"""
+ # With internal server certificate chain validation
++ check_ext_cert_check_support(dev[0])
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert="auth_serv/ca.pem",
+@@ -7317,6 +7382,7 @@ def test_eap_tls_ext_cert_check(dev, apdev):
+ def test_eap_ttls_ext_cert_check(dev, apdev):
+ """EAP-TTLS and external server certification validation"""
+ # Without internal server certificate chain validation
++ check_ext_cert_check_support(dev[0])
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+@@ -7327,6 +7393,7 @@ def test_eap_ttls_ext_cert_check(dev, apdev):
+ def test_eap_peap_ext_cert_check(dev, apdev):
+ """EAP-PEAP and external server certification validation"""
+ # With internal server certificate chain validation
++ check_ext_cert_check_support(dev[0])
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user", anonymous_identity="peap",
+ ca_cert="auth_serv/ca.pem",
+@@ -7337,6 +7404,7 @@ def test_eap_peap_ext_cert_check(dev, apdev):
+
+ def test_eap_fast_ext_cert_check(dev, apdev):
+ """EAP-FAST and external server certification validation"""
++ check_ext_cert_check_support(dev[0])
+ check_eap_capa(dev[0], "FAST")
+ # With internal server certificate chain validation
+ dev[0].request("SET blob fast_pac_auth_ext ")
+@@ -7351,10 +7419,6 @@ def test_eap_fast_ext_cert_check(dev, apdev):
+ run_ext_cert_check(dev, apdev, id)
+
+ def run_ext_cert_check(dev, apdev, net_id):
+- check_ext_cert_check_support(dev[0])
+- if not openssl_imported:
+- raise HwsimSkip("OpenSSL python method not available")
+-
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
+index 3d07d21f7..a708412de 100644
+--- a/tests/hwsim/test_ap_ft.py
++++ b/tests/hwsim/test_ap_ft.py
+@@ -2486,11 +2486,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+- with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++ with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+- with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++ with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+diff --git a/tests/hwsim/test_authsrv.py b/tests/hwsim/test_authsrv.py
+index e0665bcb2..02ec301e5 100644
+--- a/tests/hwsim/test_authsrv.py
++++ b/tests/hwsim/test_authsrv.py
+@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+- with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
+- if "FAIL" not in authsrv.request("ENABLE"):
+- raise Exception("ENABLE succeeded during OOM")
++ # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
++ tls = dev[0].request("GET tls_library")
++ if not tls.startswith("mbed TLS"):
++ with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
++ if "FAIL" not in authsrv.request("ENABLE"):
++ raise Exception("ENABLE succeeded during OOM")
+
+ for count in range(1, 3):
+ with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
+diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py
+index 518983bd0..077de58c9 100644
+--- a/tests/hwsim/test_dpp.py
++++ b/tests/hwsim/test_dpp.py
+@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False, min_ver=1):
+ raise HwsimSkip("DPP not supported")
+ if brainpool:
+ tls = dev.request("GET tls_library")
+- if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
++ if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
++ and not tls.startswith("mbed TLS"):
+ raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
+ capa = dev.request("GET_CAPABILITY dpp")
+ ver = 1
+@@ -3902,6 +3903,9 @@ def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
+
+ def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
+ """DPP protocol testing - invalid I-proto key in Auth Req"""
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+ run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
+
+ def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
+@@ -3997,7 +4001,12 @@ def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
+
+ def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
+ """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
+- run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
++ run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
++ else:
++ run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
+
+ def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
+ """DPP protocol testing - no R-nonce in Auth Resp"""
+@@ -4359,11 +4368,17 @@ def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
+
+ def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+ run_dpp_proto_pkex_req_missing(dev, 47,
+ "Peer bootstrapping key is invalid")
+
+ def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+ run_dpp_proto_pkex_resp_missing(dev, 48,
+ "Peer bootstrapping key is invalid")
+
+diff --git a/tests/hwsim/test_erp.py b/tests/hwsim/test_erp.py
+index d083993e8..262e9f095 100644
+--- a/tests/hwsim/test_erp.py
++++ b/tests/hwsim/test_erp.py
+@@ -12,7 +12,7 @@ import time
+
+ import hostapd
+ from utils import *
+-from test_ap_eap import int_eap_server_params, check_tls13_support
++from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
+ from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+ def test_erp_initiate_reauth_start(dev, apdev):
+@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+ params['erp_domain'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
++ tls = dev[0].request("GET tls_library")
+
+ erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+ erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
+ password="hello")
+- if "FAST" in eap_methods:
++ if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
+ erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=GTC",
+@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apdev):
+ password="password")
+ erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+- if "MSCHAPV2" in eap_methods:
++ if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
+ erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=MSCHAPV2")
+- erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
+- password="password", ca_cert="auth_serv/ca.pem",
+- phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
++ if check_eap_capa(dev[0], "TEAP"):
++ erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
++ password="password", ca_cert="auth_serv/ca.pem",
++ phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
+ erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ if "PWD" in eap_methods:
+@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+- for count in range(1, 6):
++ for count in range(1, 4):
+ dev[0].request("ERP_FLUSH")
+ with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py
+index 5cdc28734..17110c5c2 100644
+--- a/tests/hwsim/test_fils.py
++++ b/tests/hwsim/test_fils.py
+@@ -1484,6 +1484,18 @@ def run_fils_sk_pfs(dev, apdev, group, params):
+ check_erp_capa(dev[0])
+ check_ec_group(dev[0], group)
+
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ if int(group) == 27:
++ raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
++ elif not tls.startswith("wolfSSL"):
++ if int(group) in [25]:
++ if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
++ raise HwsimSkip("EC group not supported")
++ if int(group) in [27, 28, 29, 30]:
++ if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
++ raise HwsimSkip("Brainpool EC group not supported")
++
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+diff --git a/tests/hwsim/test_pmksa_cache.py b/tests/hwsim/test_pmksa_cache.py
+index 4a3b444ff..4f7f7f760 100644
+--- a/tests/hwsim/test_pmksa_cache.py
++++ b/tests/hwsim/test_pmksa_cache.py
+@@ -958,7 +958,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ bssid=apdev[0]['bssid'])
+- for i in range(1, 11):
++ for i in range(1, 10):
+ with alloc_fail(dev[0], i, "rsn_preauth_init"):
+ res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
+ logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
+@@ -966,7 +966,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ break
+- time.sleep(0.05)
++ time.sleep(0.10)
+
+ def test_pmksa_cache_ctrl(dev, apdev):
+ """PMKSA cache control interface operations"""
+diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
+index aceb92751..6f9ee5669 100644
+--- a/tests/hwsim/test_sae.py
++++ b/tests/hwsim/test_sae.py
+@@ -178,6 +178,11 @@ def test_sae_groups(dev, apdev):
+ if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
+ logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+ sae_groups += [27, 28, 29, 30]
++ if tls.startswith("mbed TLS"):
++ # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
++ # does not have code to derive y from compressed format for those curves
++ sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
++ sae_groups += [27, 28, 29, 30]
+ heavy_groups = [14, 15, 16]
+ suitable_groups = [15, 16, 17, 18, 19, 20, 21]
+ groups = [str(g) for g in sae_groups]
+@@ -2194,6 +2199,8 @@ def run_sae_pwe_group(dev, apdev, group):
+ logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+ elif tls.startswith("wolfSSL"):
+ logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
++ elif tls.startswith("mbed TLS"):
++ logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
+ else:
+ raise HwsimSkip("Brainpool curve not supported")
+ start_sae_pwe_ap(apdev[0], group, 2)
+diff --git a/tests/hwsim/test_suite_b.py b/tests/hwsim/test_suite_b.py
+index d03a39dee..d703dee95 100644
+--- a/tests/hwsim/test_suite_b.py
++++ b/tests/hwsim/test_suite_b.py
+@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False, level128=False):
+ return
+ if tls.startswith("wolfSSL"):
+ return
++ if tls.startswith("mbed TLS"):
++ return
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("TLS library not supported for Suite B: " + tls)
+ supported = False
+@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev):
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
++ openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
+ phase1="tls_suiteb=1",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/rsa3072-ca.pem",
+diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py
+index 44eb00444..fbe0fb794 100644
+--- a/tests/hwsim/test_wpas_ctrl.py
++++ b/tests/hwsim/test_wpas_ctrl.py
+@@ -1856,7 +1856,7 @@ def _test_wpas_ctrl_oom(dev):
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("internal"):
+ tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+- 4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
++ 3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
+ for cmd, exp, count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ res = dev[0].request(cmd)
+diff --git a/tests/hwsim/utils.py b/tests/hwsim/utils.py
+index 7e3608284..b23c1ee0b 100644
+--- a/tests/hwsim/utils.py
++++ b/tests/hwsim/utils.py
+@@ -145,7 +145,13 @@ def check_imsi_privacy_support(dev):
+
+ def check_tls_tod(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("internal"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
+
+ def vht_supported():
+diff --git a/tests/test-crypto_module.c b/tests/test-crypto_module.c
+new file mode 100644
+index 000000000..0f1156142
+--- /dev/null
++++ b/tests/test-crypto_module.c
+@@ -0,0 +1,16 @@
++/*
++ * crypto module tests - test program
++ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/module_tests.h"
++#include "crypto/crypto_module_tests.c"
++
++int main(int argc, char *argv[])
++{
++ return crypto_module_tests();
++}
+diff --git a/tests/test-https.c b/tests/test-https.c
+index a72e56f9d..e9df82f1d 100644
+--- a/tests/test-https.c
++++ b/tests/test-https.c
+@@ -75,7 +75,7 @@ static int https_client(int s, const char *path)
+ struct tls_connection *conn;
+ struct wpabuf *in, *out, *appl;
+ int res = -1;
+- int need_more_data;
++ int need_more_data = 0;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.event_cb = https_tls_event_cb;
+@@ -93,8 +93,12 @@ static int https_client(int s, const char *path)
+
+ for (;;) {
+ appl = NULL;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ out = tls_connection_handshake2(tls, conn, in, &appl,
+ &need_more_data);
++#else
++ out = tls_connection_handshake(tls, conn, in, &appl);
++#endif
+ wpabuf_free(in);
+ in = NULL;
+ if (out == NULL) {
+@@ -152,11 +156,15 @@ static int https_client(int s, const char *path)
+
+ wpa_printf(MSG_INFO, "Reading HTTP response");
+ for (;;) {
+- int need_more_data;
++ int need_more_data = 0;
+ in = https_recv(s);
+ if (in == NULL)
+ goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++ out = tls_connection_decrypt(tls, conn, in);
++#endif
+ if (need_more_data)
+ wpa_printf(MSG_DEBUG, "HTTP: Need more data");
+ wpabuf_free(in);
+diff --git a/tests/test-https_server.c b/tests/test-https_server.c
+index 33b448682..9dcca5596 100644
+--- a/tests/test-https_server.c
++++ b/tests/test-https_server.c
+@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s, int timeout_ms)
+ }
+
+
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ static void https_tls_log_cb(void *ctx, const char *msg)
+ {
+ wpa_printf(MSG_DEBUG, "TLS: %s", msg);
+ }
++#endif
+
+
+ static int https_server(int s)
+@@ -79,7 +81,7 @@ static int https_server(int s)
+ void *tls;
+ struct tls_connection_params params;
+ struct tls_connection *conn;
+- struct wpabuf *in, *out, *appl;
++ struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
+ int res = -1;
+
+ os_memset(&conf, 0, sizeof(conf));
+@@ -106,7 +108,9 @@ static int https_server(int s)
+ return -1;
+ }
+
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
++#endif
+
+ for (;;) {
+ in = https_recv(s, 5000);
+@@ -147,12 +151,16 @@ static int https_server(int s)
+
+ wpa_printf(MSG_INFO, "Reading HTTP request");
+ for (;;) {
+- int need_more_data;
++ int need_more_data = 0;
+
+ in = https_recv(s, 5000);
+ if (!in)
+ goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++ out = tls_connection_decrypt(tls, conn, in);
++#endif
+ wpabuf_free(in);
+ in = NULL;
+ if (need_more_data) {
+diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
+index dd13308f7..c65acab94 100644
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.service
+ EXTRA_TARGETS=dynamic_eap_methods
+
+ CONFIG_FILE=.config
++-include $(if $(MULTICALL),../hostapd/.config)
+ include ../src/build.rules
+
+ ifdef CONFIG_BUILD_PASN_SO
+@@ -188,6 +189,25 @@ ifdef CONFIG_EAPOL_TEST
+ CFLAGS += -Werror -DEAPOL_TEST
+ endif
+
++ifdef CONFIG_UBUS
++CFLAGS += -DUBUS_SUPPORT
++OBJS += ubus.o
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
++OBJS += ../src/utils/uloop.o
++LIBS += -lubox
++endif
++
+ ifdef CONFIG_CODE_COVERAGE
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+ LIBS += -lgcov
+@@ -334,6 +354,7 @@ endif
+ ifdef CONFIG_FILS
+ CFLAGS += -DCONFIG_FILS
+ NEED_SHA384=y
++NEED_HMAC_SHA384_KDF=y
+ NEED_AES_SIV=y
+ ifdef CONFIG_FILS_SK_PFS
+ CFLAGS += -DCONFIG_FILS_SK_PFS
+@@ -388,7 +409,9 @@ endif
+ ifdef CONFIG_IBSS_RSN
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_IBSS_RSN
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ibss_rsn.o
+ endif
+
+@@ -980,6 +1003,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
+ CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+ LIBS += -ldl -rdynamic
+ endif
++else
++ ifdef MULTICALL
++ OBJS += ../src/eap_common/eap_common.o
++ endif
+ endif
+
+ ifdef CONFIG_AP
+@@ -987,9 +1014,11 @@ NEED_EAP_COMMON=y
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_AP
+ OBJS += ap.o
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
+ CFLAGS += -DCONFIG_NO_ACCOUNTING
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/utils.o
+@@ -1029,7 +1058,16 @@ ifdef CONFIG_FILS
+ OBJS += ../src/ap/fils_hlp.o
+ endif
+ ifdef CONFIG_CTRL_IFACE
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ OBJS += ../src/ap/ctrl_iface_ap.o
++ifdef CONFIG_UBUS
++OBJS += ../src/ap/ubus.o
++endif
++ifdef CONFIG_UCODE
++OBJS += ../src/ap/ucode.o
++endif
+ endif
+
+ CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+@@ -1080,6 +1118,12 @@ endif
+ ifdef CONFIG_HS20
+ OBJS += ../src/ap/hs20.o
+ endif
++else
++ ifdef MULTICALL
++ OBJS += ../src/eap_server/eap_server.o
++ OBJS += ../src/eap_server/eap_server_identity.o
++ OBJS += ../src/eap_server/eap_server_methods.o
++ endif
+ endif
+
+ ifdef CONFIG_MBO
+@@ -1089,7 +1133,9 @@ NEED_GAS=y
+ endif
+
+ ifdef NEED_RSN_AUTHENTICATOR
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
++endif
+ NEED_AES_WRAP=y
+ OBJS += ../src/ap/wpa_auth.o
+ OBJS += ../src/ap/wpa_auth_ie.o
+@@ -1188,6 +1234,7 @@ TLS_FUNCS=y
+ endif
+
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ ifdef TLS_FUNCS
+ CFLAGS += -DWOLFSSL_DER_LOAD
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -1203,6 +1250,7 @@ LIBS_p += -lwolfssl -lm
+ endif
+
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ ifdef TLS_FUNCS
+ CFLAGS += -DEAP_TLS_OPENSSL
+@@ -1229,7 +1277,28 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls -lmbedx509
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++LIBS += -lmbedcrypto
++LIBS_p += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -1260,6 +1329,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -1340,6 +1410,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ OBJS_p += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+@@ -1421,9 +1492,11 @@ endif
+
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_INTERNAL_AES_WRAP=y
+ endif
+ endif
++endif
+ ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+ # Seems to be needed at least with BoringSSL
+ NEED_INTERNAL_AES_WRAP=y
+@@ -1437,9 +1510,11 @@ endif
+
+ ifdef NEED_INTERNAL_AES_WRAP
+ ifneq ($(CONFIG_TLS), linux)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -1449,35 +1524,45 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_WRAP
+ NEED_AES_ENC=y
+ ifdef NEED_INTERNAL_AES_WRAP
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_ENC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1492,12 +1577,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1509,29 +1598,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
+ else
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+
+ ifndef CONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ MD5OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+ MD5OBJS += ../src/crypto/md5-internal.o
+@@ -1586,12 +1683,17 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ SHA256OBJS += ../src/crypto/sha256-internal.o
+ endif
+@@ -1604,50 +1706,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
+ SHA256OBJS += ../src/crypto/sha512-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ OBJS += $(SHA256OBJS)
+ ifdef NEED_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA512
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+
+ ifdef NEED_ASN1
+ OBJS += ../src/tls/asn1.o
+@@ -1822,10 +1942,12 @@ ifdef CONFIG_FIPS
+ CFLAGS += -DCONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
+ endif
+ endif
+ endif
++endif
+
+ OBJS += $(SHA1OBJS) $(DESOBJS)
+
+@@ -2003,32 +2125,38 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
+
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
++wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
++ $(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
++ @$(E) " CC " $<
++ @rm -f $@
++ @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
++
+ wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
+- $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_t
+ include ../src/objs.mk
+ eapol_test: $(OBJS_t)
+- $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_t2
+ include ../src/objs.mk
+ preauth_test: $(OBJS_t2)
+- $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_p
+ include ../src/objs.mk
+ wpa_passphrase: $(OBJS_p)
+- $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+ wpa_cli: $(OBJS_c)
+- $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
++ +$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+ @$(E) " LD " $@
+
+ LIBCTRL += ../src/common/wpa_ctrl.o
+@@ -2135,6 +2263,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
+ $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+ @$(E) " sed" $<
+
++dump_cflags:
++ @printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ wpa_supplicant.exe: wpa_supplicant
+ mv -f $< $@
+ wpa_cli.exe: wpa_cli
+diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
+index 69a0e5ee1..43c39d7ce 100644
+--- a/wpa_supplicant/ap.c
++++ b/wpa_supplicant/ap.c
+@@ -1520,7 +1520,7 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ #endif /* CONFIG_WPS */
+
+
+-#ifdef CONFIG_CTRL_IFACE
++#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
+
+ int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+@@ -1846,11 +1846,31 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s,
+
+
+ #ifdef CONFIG_CTRL_IFACE
++
++static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
++ struct csa_settings *settings)
++{
++#ifdef NEED_AP_MLME
++ if (!iface || !iface->bss[0])
++ return 0;
++
++ return hostapd_switch_channel(iface->bss[0], settings);
++#else
++ return -1;
++#endif
++}
++
++
+ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
+ {
+ struct csa_settings settings;
+ int ret = hostapd_parse_csa_settings(pos, &settings);
+
++ if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
++ !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
++ return -1;
++
++ ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
+ if (ret)
+ return ret;
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index 2c756136c..c3943355d 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -18,6 +18,7 @@
+ #include "eap_peer/eap.h"
+ #include "p2p/p2p.h"
+ #include "fst/fst.h"
++#include "ap/sta_info.h"
+ #include "config.h"
+
+
+@@ -2421,6 +2422,97 @@ static char * wpa_config_write_mac_value(const struct parse_data *data,
+ #endif /* NO_CONFIG_WRITE */
+
+
++static int wpa_config_parse_mcast_rate(const struct parse_data *data,
++ struct wpa_ssid *ssid, int line,
++ const char *value)
++{
++ ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
++
++ return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_mcast_rate(const struct parse_data *data,
++ struct wpa_ssid *ssid)
++{
++ char *value;
++ int res;
++
++ if (!ssid->mcast_rate == 0)
++ return NULL;
++
++ value = os_malloc(6); /* longest: 300.0 */
++ if (value == NULL)
++ return NULL;
++ res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
++ if (res < 0) {
++ os_free(value);
++ return NULL;
++ }
++ return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
++static int wpa_config_parse_rates(const struct parse_data *data,
++ struct wpa_ssid *ssid, int line,
++ const char *value)
++{
++ int i;
++ char *pos, *r, *sptr, *end;
++ double rate;
++
++ pos = (char *)value;
++ r = strtok_r(pos, ",", &sptr);
++ i = 0;
++ while (pos && i < WLAN_SUPP_RATES_MAX) {
++ rate = 0.0;
++ if (r)
++ rate = strtod(r, &end);
++ ssid->rates[i] = rate * 2;
++ if (*end != '\0' || rate * 2 != ssid->rates[i])
++ return 1;
++
++ i++;
++ r = strtok_r(NULL, ",", &sptr);
++ }
++
++ return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_rates(const struct parse_data *data,
++ struct wpa_ssid *ssid)
++{
++ char *value, *pos;
++ int res, i;
++
++ if (ssid->rates[0] <= 0)
++ return NULL;
++
++ value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
++ if (value == NULL)
++ return NULL;
++ pos = value;
++ for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
++ res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
++ if (res < 0) {
++ os_free(value);
++ return NULL;
++ }
++ pos += res;
++ }
++ res = os_snprintf(pos, 6, "%.1f",
++ (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
++ if (res < 0) {
++ os_free(value);
++ return NULL;
++ }
++
++ value[6 * WLAN_SUPP_RATES_MAX] = '\0';
++ return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
+ /* Helper macros for network block parser */
+
+ #ifdef OFFSET
+@@ -2639,6 +2731,7 @@ static const struct parse_data ssid_fields[] = {
+ #else /* CONFIG_MESH */
+ { INT_RANGE(mode, 0, 4) },
+ #endif /* CONFIG_MESH */
++ { INT_RANGE(noscan, 0, 1) },
+ { INT_RANGE(proactive_key_caching, 0, 1) },
+ { INT_RANGE(disabled, 0, 2) },
+ { STR(id_str) },
+@@ -2712,6 +2805,8 @@ static const struct parse_data ssid_fields[] = {
+ { INT(ap_max_inactivity) },
+ { INT(dtim_period) },
+ { INT(beacon_int) },
++ { FUNC(rates) },
++ { FUNC(mcast_rate) },
+ #ifdef CONFIG_MACSEC
+ { INT_RANGE(macsec_policy, 0, 1) },
+ { INT_RANGE(macsec_integ_only, 0, 1) },
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index 1a2c0c9be..7a3ed6373 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ while (cred_tail && cred_tail->next)
+ cred_tail = cred_tail->next;
+
++ if (!strncmp(name, "data:", 5)) {
++ f = fmemopen((void *)(name + 5), strlen(name + 5), "r");
++ name = "<inline>";
++ } else {
++ f = fopen(name, "r");
++ }
+ wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
+- f = fopen(name, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+ "error: %s", name, strerror(errno));
+@@ -775,6 +780,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
+ #endif /* IEEE8021X_EAPOL */
+ INT(mode);
+ INT(no_auto_peer);
++ INT(noscan);
+ INT(mesh_fwding);
+ INT(frequency);
+ INT(enable_edmg);
+diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
+index e40650c27..de79972b6 100644
+--- a/wpa_supplicant/config_ssid.h
++++ b/wpa_supplicant/config_ssid.h
+@@ -10,8 +10,10 @@
+ #define CONFIG_SSID_H
+
+ #include "common/defs.h"
++#include "ap/sta_info.h"
+ #include "utils/list.h"
+ #include "eap_peer/eap_config.h"
++#include "drivers/nl80211_copy.h"
+
+
+ #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
+@@ -879,6 +881,9 @@ struct wpa_ssid {
+ */
+ void *parent_cred;
+
++ unsigned char rates[WLAN_SUPP_RATES_MAX];
++ double mcast_rate;
++
+ #ifdef CONFIG_MACSEC
+ /**
+ * macsec_policy - Determines the policy for MACsec secure session
+@@ -1035,6 +1040,8 @@ struct wpa_ssid {
+ */
+ int no_auto_peer;
+
++ int noscan;
++
+ /**
+ * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
+ *
+diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
+index d0fda4cd9..ec45f29bb 100644
+--- a/wpa_supplicant/ctrl_iface.c
++++ b/wpa_supplicant/ctrl_iface.c
+@@ -2355,7 +2355,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+ pos += ret;
+ }
+
+-#ifdef CONFIG_AP
++#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
+ if (wpa_s->ap_iface) {
+ pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
+ end - pos,
+@@ -12542,6 +12542,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strcmp(buf, "MIB") == 0) {
+ reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+ if (reply_len >= 0) {
+@@ -12554,6 +12555,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ reply_size - reply_len);
+ #endif /* CONFIG_MACSEC */
+ }
++#endif
+ } else if (os_strncmp(buf, "STATUS", 6) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_status(
+ wpa_s, buf + 6, reply, reply_size);
+@@ -13042,6 +13044,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ reply_len = wpa_supplicant_ctrl_iface_bss(
+ wpa_s, buf + 4, reply, reply_size);
+ #ifdef CONFIG_AP
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strcmp(buf, "STA-FIRST") == 0) {
+ reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "STA ", 4) == 0) {
+@@ -13050,12 +13053,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
+ reply_size);
++#endif
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+ if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+ if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
+ reply_len = -1;
++#endif
+ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+ if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+ reply_len = -1;
+@@ -13214,7 +13220,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
+ reply_len = -1;
+ #endif /* CONFIG_WNM */
+-#ifdef CONFIG_WNM_AP
++#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
+ } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+ if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
+ reply_len = -1;
+@@ -13224,7 +13230,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+ if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
+ reply_len = -1;
+-#endif /* CONFIG_WNM_AP */
++#endif /* CONFIG_AP && CONFIG_WNM_AP */
+ } else if (os_strcmp(buf, "FLUSH") == 0) {
+ wpa_supplicant_ctrl_iface_flush(wpa_s);
+ } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
+index 52befd8f1..ace6c5530 100644
+--- a/wpa_supplicant/defconfig
++++ b/wpa_supplicant/defconfig
+@@ -10,8 +10,8 @@
+ # to override previous values of the variables.
+
+
+-# Uncomment following two lines and fix the paths if you have installed OpenSSL
+-# or GnuTLS in non-default location
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
+ #CFLAGS += -I/usr/local/openssl/include
+ #LIBS += -L/usr/local/openssl/lib
+
+@@ -20,6 +20,7 @@
+ # used to fix build issues on such systems (krb5.h not found).
+ #CFLAGS += -I/usr/include/kerberos
+
++
+ # Driver interface for generic Linux wireless extensions
+ # Note: WEXT is deprecated in the current Linux kernel version and no new
+ # functionality is added to it. nl80211-based interface is the new
+@@ -329,6 +330,7 @@ CONFIG_BACKEND=file
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
+index 95953de92..673c3cc11 100644
+--- a/wpa_supplicant/eapol_test.c
++++ b/wpa_supplicant/eapol_test.c
+@@ -31,7 +31,12 @@
+ #include "ctrl_iface.h"
+ #include "pcsc_funcs.h"
+ #include "wpas_glue.h"
++#include "drivers/driver.h"
+
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+
+@@ -1325,6 +1330,10 @@ static void usage(void)
+ "option several times.\n");
+ }
+
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ int main(int argc, char *argv[])
+ {
+@@ -1348,6 +1357,8 @@ int main(int argc, char *argv[])
+ if (os_program_init())
+ return -1;
+
++ wpa_supplicant_event = supplicant_event;
++ wpa_supplicant_event_global = supplicant_event_global;
+ hostapd_logger_register_cb(hostapd_logger_cb);
+
+ os_memset(&eapol_test, 0, sizeof(eapol_test));
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index ca2794638..2a9342318 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -2935,8 +2935,6 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
+ }
+
+
+-#ifdef CONFIG_INTERWORKING
+-
+ static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
+ size_t len)
+ {
+@@ -2969,8 +2967,6 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
+ }
+ }
+
+-#endif /* CONFIG_INTERWORKING */
+-
+
+ static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
+ {
+@@ -3349,10 +3345,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
+ wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+ #endif /* CONFIG_WNM */
+-#ifdef CONFIG_INTERWORKING
+ interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+-#endif /* CONFIG_INTERWORKING */
+ if (wpa_s->hw_capab == CAPAB_VHT &&
+ get_ie(data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
+@@ -5928,8 +5922,8 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
+ }
+
+
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data)
++void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data)
+ {
+ struct wpa_supplicant *wpa_s = ctx;
+ int resched;
+@@ -5964,6 +5958,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ event_to_string(event), event);
+ #endif /* CONFIG_NO_STDOUT_DEBUG */
+
++ wpas_ucode_event(wpa_s, event, data);
+ switch (event) {
+ case EVENT_AUTH:
+ #ifdef CONFIG_FST
+@@ -6881,7 +6876,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+
+
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+ {
+ struct wpa_supplicant *wpa_s;
+diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
+index 9229eb51f..ee152c5b9 100644
+--- a/wpa_supplicant/main.c
++++ b/wpa_supplicant/main.c
+@@ -12,6 +12,7 @@
+ #endif /* __linux__ */
+
+ #include "common.h"
++#include "build_features.h"
+ #include "crypto/crypto.h"
+ #include "fst/fst.h"
+ #include "wpa_supplicant_i.h"
+@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
+
+ for (;;) {
+ c = getopt(argc, argv,
+- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
++ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
+ if (c < 0)
+ break;
+ switch (c) {
+@@ -267,6 +268,9 @@ int main(int argc, char *argv[])
+ params.conf_p2p_dev = optarg;
+ break;
+ #endif /* CONFIG_P2P */
++ case 'n':
++ iface_count = 0;
++ break;
+ case 'o':
+ params.override_driver = optarg;
+ break;
+@@ -302,8 +306,12 @@ int main(int argc, char *argv[])
+ break;
+ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+ case 'v':
+- printf("%s\n", wpa_supplicant_version);
+- exitcode = 0;
++ if (optarg) {
++ exitcode = !has_feature(optarg);
++ } else {
++ printf("%s\n", wpa_supplicant_version);
++ exitcode = 0;
++ }
+ goto out;
+ case 'W':
+ params.wait_for_monitor++;
+diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
+index 85c1ea8ba..dabbb0334 100644
+--- a/wpa_supplicant/mesh.c
++++ b/wpa_supplicant/mesh.c
+@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
+ frequency);
+ goto out_free;
+ }
++ if (conf->noscan)
++ ssid->noscan = 1;
+
+ if (ssid->mesh_basic_rates == NULL) {
+ /*
+@@ -630,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+
+ params->meshid = ssid->ssid;
+ params->meshid_len = ssid->ssid_len;
++ params->mcast_rate = ssid->mcast_rate;
+ ibss_mesh_setup_freq(wpa_s, ssid, ¶ms->freq);
+ wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
+ wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
+diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
+index 60f85624f..67352a121 100644
+--- a/wpa_supplicant/wpa_cli.c
++++ b/wpa_supplicant/wpa_cli.c
+@@ -26,6 +26,15 @@
+ #include <cutils/properties.h>
+ #endif /* ANDROID */
+
++#ifndef CONFIG_P2P
++#define CONFIG_P2P
++#endif
++#ifndef CONFIG_AP
++#define CONFIG_AP
++#endif
++#ifndef CONFIG_MESH
++#define CONFIG_MESH
++#endif
+
+ static const char *const wpa_cli_version =
+ "wpa_cli v" VERSION_STR "\n"
+diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
+index 88f3f2a52..92efe5629 100644
+--- a/wpa_supplicant/wpa_priv.c
++++ b/wpa_supplicant/wpa_priv.c
+@@ -1042,8 +1042,8 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
+ }
+
+
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data)
++static void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data)
+ {
+ struct wpa_priv_interface *iface = ctx;
+
+@@ -1106,7 +1106,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+
+
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+ {
+ struct wpa_priv_global *global = ctx;
+@@ -1220,6 +1220,8 @@ int main(int argc, char *argv[])
+ if (os_program_init())
+ return -1;
+
++ wpa_supplicant_event = supplicant_event;
++ wpa_supplicant_event_global = supplicant_event_global;
+ wpa_priv_fd_workaround();
+
+ os_memset(&global, 0, sizeof(global));
+diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
+index ab71e2f27..fea84fe49 100644
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -1060,6 +1060,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+ sme_sched_obss_scan(wpa_s, 0);
+ }
+ wpa_s->wpa_state = state;
++ wpas_ucode_update_state(wpa_s);
+
+ #ifdef CONFIG_BGSCAN
+ if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
+@@ -2698,7 +2699,7 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+ }
+
+
+-static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
++static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
+ {
+ int i;
+
+@@ -2707,7 +2708,10 @@ static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan ||
+- chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++ chan->flag & HOSTAPD_CHAN_DISABLED)
++ return false;
++
++ if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
+ return false;
+ }
+
+@@ -2767,7 +2771,7 @@ static bool ibss_mesh_can_use_vht(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_hw_modes *mode)
+ {
+- if (mode->mode != HOSTAPD_MODE_IEEE80211A)
++ if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
+ return false;
+
+ if (!drv_supports_vht(wpa_s, ssid))
+@@ -2834,12 +2838,13 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_hw_modes *mode,
+ struct hostapd_freq_params *freq,
+- int obss_scan) {
++ int obss_scan, bool dfs_enabled) {
+ int chan_idx;
+ struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+ int i, res;
+ unsigned int j;
+ static const int ht40plus[] = {
++ 1, 2, 3, 4, 5, 6, 7,
+ 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+ 149, 157, 165, 173, 184, 192
+ };
+@@ -2858,8 +2863,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ return;
+
+ /* Check primary channel flags */
+- if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++ if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
++ if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++ if (!dfs_enabled)
++ return;
+
+ #ifdef CONFIG_HT_OVERRIDES
+ if (ssid->disable_ht40)
+@@ -2885,8 +2893,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ return;
+
+ /* Check secondary channel flags */
+- if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++ if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
++ if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++ if (!dfs_enabled)
++ return;
+
+ if (ht40 == -1) {
+ if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+@@ -2941,7 +2952,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_hw_modes *mode,
+ struct hostapd_freq_params *freq,
+- int ieee80211_mode, bool is_6ghz) {
++ int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
+ static const int bw80[] = {
+ 5180, 5260, 5500, 5580, 5660, 5745, 5825,
+ 5955, 6035, 6115, 6195, 6275, 6355, 6435,
+@@ -2986,7 +2997,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ goto skip_80mhz;
+
+ /* Use 40 MHz if channel not usable */
+- if (!ibss_mesh_is_80mhz_avail(channel, mode))
++ if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
+ goto skip_80mhz;
+
+ chwidth = CONF_OPER_CHWIDTH_80MHZ;
+@@ -3000,7 +3011,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ if ((mode->he_capab[ieee80211_mode].phy_cap[
+ HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
+- ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
++ ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
+ for (j = 0; j < ARRAY_SIZE(bw160); j++) {
+ if (freq->freq == bw160[j]) {
+ chwidth = CONF_OPER_CHWIDTH_160MHZ;
+@@ -3028,10 +3039,12 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ if (!chan)
+ continue;
+
+- if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+- HOSTAPD_CHAN_NO_IR |
+- HOSTAPD_CHAN_RADAR))
++ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
++ if (chan->flag & (HOSTAPD_CHAN_RADAR |
++ HOSTAPD_CHAN_NO_IR))
++ if (!dfs_enabled)
++ continue;
+
+ /* Found a suitable second segment for 80+80 */
+ chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+@@ -3083,12 +3096,17 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
+ enum hostapd_hw_mode hw_mode;
+ struct hostapd_hw_modes *mode = NULL;
+- int i, obss_scan = 1;
++ int i, obss_scan = !(ssid->noscan);
+ u8 channel;
+ bool is_6ghz, is_24ghz;
++ bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
+ freq->freq = ssid->frequency;
+
++ if (ssid->fixed_freq) {
++ obss_scan = 0;
++ }
++
+ if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
+ struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
+
+@@ -3132,11 +3150,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
+ ieee80211_mode);
+ freq->channel = channel;
++ if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
++ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ /* Setup higher BW only for 5 GHz */
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
+- ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
++ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
+- ieee80211_mode, is_6ghz))
++ ieee80211_mode, is_6ghz, dfs_enabled))
+ freq->he_enabled = freq->vht_enabled = false;
+ }
+
+@@ -4240,6 +4260,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+ params.beacon_int = ssid->beacon_int;
+ else
+ params.beacon_int = wpa_s->conf->beacon_int;
++ int i = 0;
++ while (i < WLAN_SUPP_RATES_MAX) {
++ params.rates[i] = ssid->rates[i];
++ i++;
++ }
++ params.mcast_rate = ssid->mcast_rate;
+ }
+
+ if (bss && ssid->enable_edmg)
+@@ -5861,7 +5887,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
+ if (wpa_s == NULL)
+ return NULL;
+ wpa_s->scan_req = INITIAL_SCAN_REQ;
+- wpa_s->scan_interval = 5;
++ wpa_s->scan_interval = 1;
+ wpa_s->new_connection = 1;
+ wpa_s->parent = parent ? parent : wpa_s;
+ wpa_s->p2pdev = wpa_s->parent;
+@@ -7576,7 +7602,6 @@ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+ return NULL;
+ }
+
+-
+ /**
+ * wpa_supplicant_match_existing - Match existing interfaces
+ * @global: Pointer to global data from wpa_supplicant_init()
+@@ -7611,6 +7636,11 @@ static int wpa_supplicant_match_existing(struct wpa_global *global)
+
+ #endif /* CONFIG_MATCH_IFACE */
+
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ /**
+ * wpa_supplicant_add_iface - Add a new network interface
+@@ -7693,6 +7723,9 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+ }
+ #endif /* CONFIG_P2P */
+
++ wpas_ubus_add_bss(wpa_s);
++ wpas_ucode_add_bss(wpa_s);
++
+ return wpa_s;
+ }
+
+@@ -7719,6 +7752,9 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
+ struct wpa_supplicant *parent = wpa_s->parent;
+ #endif /* CONFIG_MESH */
+
++ wpas_ucode_free_bss(wpa_s);
++ wpas_ubus_free_bss(wpa_s);
++
+ /* Remove interface from the global list of interfaces */
+ prev = global->ifaces;
+ if (prev == wpa_s) {
+@@ -7867,6 +7903,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+ #ifndef CONFIG_NO_WPA_MSG
+ wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+ #endif /* CONFIG_NO_WPA_MSG */
++ wpa_supplicant_event = supplicant_event;
++ wpa_supplicant_event_global = supplicant_event_global;
+
+ if (params->wpa_debug_file_path)
+ wpa_debug_open_file(params->wpa_debug_file_path);
+@@ -8025,6 +8063,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
++ wpas_ucode_init(global);
+
+ return global;
+ }
+@@ -8097,6 +8136,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
+
+ wpas_notify_supplicant_deinitialized(global);
+
++ wpas_ucode_free();
++
+ eap_peer_unregister_methods();
+ #ifdef CONFIG_AP
+ eap_server_unregister_methods();
+diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
+index 426d077d2..e0c0e5b0c 100644
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -21,6 +21,8 @@
+ #include "config_ssid.h"
+ #include "wmm_ac.h"
+ #include "pasn/pasn_common.h"
++#include "ubus.h"
++#include "ucode.h"
+
+ extern const char *const wpa_supplicant_version;
+ extern const char *const wpa_supplicant_license;
+@@ -319,6 +321,8 @@ struct wpa_global {
+ #endif /* CONFIG_WIFI_DISPLAY */
+
+ struct psk_list_entry *add_psk; /* From group formation */
++
++ struct ubus_object ubus_global;
+ };
+
+
+@@ -693,6 +697,8 @@ struct wpa_supplicant {
+ unsigned char own_addr[ETH_ALEN];
+ unsigned char perm_addr[ETH_ALEN];
+ char ifname[100];
++ struct wpas_ubus_bss ubus;
++ struct wpas_ucode_bss ucode;
+ #ifdef CONFIG_MATCH_IFACE
+ int matched;
+ #endif /* CONFIG_MATCH_IFACE */
+diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
+index 8cd355f6b..136b06583 100644
+--- a/wpa_supplicant/wps_supplicant.c
++++ b/wpa_supplicant/wps_supplicant.c
+@@ -33,6 +33,7 @@
+ #include "p2p/p2p.h"
+ #include "p2p_supplicant.h"
+ #include "wps_supplicant.h"
++#include "ubus.h"
+
+
+ #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
+@@ -401,6 +402,8 @@ static int wpa_supplicant_wps_cred(void *ctx,
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+ cred->cred_attr, cred->cred_attr_len);
+
++ wpas_ubus_notify(wpa_s, cred);
++
+ if (wpa_s->conf->wps_cred_processing == 1)
+ return 0;
+
+diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
+index aae3f7cb5..30b4e9105 100644
+--- a/wpa_supplicant/wps_supplicant.h
++++ b/wpa_supplicant/wps_supplicant.h
+@@ -9,6 +9,7 @@
+ #ifndef WPS_SUPPLICANT_H
+ #define WPS_SUPPLICANT_H
+
++struct wpa_bss;
+ struct wpa_scan_results;
+
+ #ifdef CONFIG_WPS
+@@ -16,8 +17,6 @@ struct wpa_scan_results;
+ #include "wps/wps.h"
+ #include "wps/wps_defs.h"
+
+-struct wpa_bss;
+-
+ struct wps_new_ap_settings {
+ const char *ssid_hex;
+ const char *auth;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
new file mode 100644
index 0000000..2d4b438
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
@@ -0,0 +1,441 @@
+From 63eb73fe7fe0cb9e088b95cffe4b123885bf9ede 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 029/104] mtk: hostapd: Add neighbor report and BSS Termination
+ for MBO certification
+
+1. 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.
+
+2. Support including neighbor report elements in ANQP response
+3. Support including neignbor report elements in BTM response
+4. Support configuring BSS Termination TSF by using hostapd_cli command
+5. Disable interface if BSS Termination TSF is set
+6. Support including neighbor report elements in BTM request
+7. Add hostapd_neighbor_set_own_report_pref()
+8. Add hostapd_neighbor_set_pref_by_non_pref_chan()
+
+Revert set_send_disassoc_frame_timer
+---
+ hostapd/ctrl_iface.c | 5 ++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ctrl_iface_ap.c | 18 ++++++-
+ src/ap/gas_serv.c | 29 ++++++++++
+ src/ap/gas_serv.h | 2 +
+ src/ap/neighbor_db.c | 118 +++++++++++++++++++++++++++++++++++++++++
+ src/ap/neighbor_db.h | 9 ++++
+ src/ap/wnm_ap.c | 43 +++++++++++++--
+ 9 files changed, 221 insertions(+), 5 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index f76226cf4..4240319b7 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1300,6 +1300,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 ca67aeb41..6c8b10291 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -175,6 +175,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+ bss->pasn_comeback_after = 10;
+ bss->pasn_noauth = 1;
+ #endif /* CONFIG_PASN */
++ bss->bss_termination_tsf = 0;
+ }
+
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d10b00be9..379dc22cf 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -560,6 +560,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;
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index cd7db4fc6..a2f89260c 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1377,6 +1377,10 @@ 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));
+ }
+@@ -1403,16 +1407,26 @@ 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"))
++ 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);
++ }
+ if (os_strstr(cmd, " link_removal_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
+
+ #ifdef CONFIG_MBO
++ hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
++
+ pos = os_strstr(cmd, "mbo=");
+ if (pos) {
+ unsigned int mbo_reason, cell_pref, reassoc_delay;
+diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
+index 4642e4927..cce6df41c 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 7646a98a4..ce492b53f 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.
+diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
+index f7a7d83d4..d9216a5ae 100644
+--- a/src/ap/neighbor_db.c
++++ b/src/ap/neighbor_db.c
+@@ -89,6 +89,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);
+@@ -364,3 +396,89 @@ int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
+
+ return 0;
+ }
++
++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;
++ }
++ }
++ }
++ }
++}
++
++#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 53f714203..cf1400256 100644
+--- a/src/ap/neighbor_db.h
++++ b/src/ap/neighbor_db.h
+@@ -25,4 +25,13 @@ 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);
++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 */
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index d259200c9..4ac96b1be 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
+
+@@ -390,13 +391,24 @@ 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;
++ }
+
+ sta = ap_get_sta(hapd, addr);
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+-
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
+@@ -406,10 +418,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 "
+@@ -915,6 +935,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)
+@@ -1006,6 +1042,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.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
new file mode 100644
index 0000000..56988b8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
@@ -0,0 +1,36 @@
+From d87f115b26430a4edd465d21be00ec61599c332e Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 20 Sep 2022 19:33:45 +0800
+Subject: [PATCH 030/104] mtk: hostapd: print sae groups by hostapd ctrl
+
+---
+ hostapd/ctrl_iface.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 4240319b7..1f950bc46 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1376,6 +1376,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
++ } else if (os_strcmp(cmd, "sae_group_capability") == 0) {
++#ifdef CONFIG_SAE
++ /* see sae_set_group() */
++ res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
++ dh_groups_get(15) ? "15 ": "",
++ dh_groups_get(16) ? "16 ": "",
++ dh_groups_get(17) ? "17 ": "",
++ dh_groups_get(18) ? "18 ": "");
++
++ if (os_snprintf_error(buflen, res))
++ return -1;
++ return res;
++#endif /* CONFIG_SAE */
+ }
+
+ return -1;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
new file mode 100644
index 0000000..55286b8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
@@ -0,0 +1,199 @@
+From 5453bd56aa134865ecaab20bde482b6c389831cb Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 31 May 2022 21:15:54 +0800
+Subject: [PATCH 031/104] mtk: hostapd: add support for runtime set in-band
+ discovery
+
+Usage:
+hostapd_cli unsolic_probe_resp [tx_type] [interval]
+
+0: disable all in-band discovery
+1: enable unsolicited probe response
+2: enable FILS discovery
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+
+The mac80211 layer already has a new variable "update",
+so the redundant variable "disable" has been removed.
+
+CR-ID: WCNCR00240597
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 66 ++++++++++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c | 20 +++++++++++
+ src/ap/beacon.c | 5 ++-
+ src/drivers/driver_nl80211.c | 6 ++--
+ 4 files changed, 94 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 1f950bc46..fb9f09bb1 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -772,6 +772,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
+
+ #endif /* CONFIG_INTERWORKING */
+
++static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
++ const char *cmd)
++{
++ struct hostapd_bss_config *conf = hapd->conf;
++ const char *pos = cmd;
++ int tx_type, interval, ret;
++
++ tx_type = atoi(pos);
++ if (tx_type < 0 || tx_type > 2) {
++ wpa_printf(MSG_ERROR, "Invalid tx type\n");
++ return -1;
++ }
++
++ pos = os_strchr(pos, ' ');
++ if(!pos)
++ return -1;
++ pos++;
++ interval = atoi(pos);
++ if (interval < 0 || interval > 20) {
++ wpa_printf(MSG_ERROR, "Invalid interval value\n");
++ return -1;
++ }
++
++ wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
++ tx_type, interval);
++
++#define DISABLE_INBAND_DISC 0
++#define UNSOL_PROBE_RESP 1
++#define FILS_DISCOVERY 2
++
++#ifdef CONFIG_FILS
++ conf->fils_discovery_max_int = 0;
++ conf->fils_discovery_min_int = 0;
++#endif /* CONFIG_FILS */
++ conf->unsol_bcast_probe_resp_interval = 0;
++
++ switch (tx_type) {
++ case DISABLE_INBAND_DISC:
++ default:
++ /* Disable both Unsolicited probe response and FILS discovery*/
++ break;
++ case UNSOL_PROBE_RESP:
++ /* Enable Unsolicited probe response */
++ conf->unsol_bcast_probe_resp_interval = interval;
++ break;
++#ifdef CONFIG_FILS
++ case FILS_DISCOVERY:
++ /* Enable FILS discovery */
++ conf->fils_discovery_min_int = interval;
++ conf->fils_discovery_max_int = interval;
++ break;
++#endif /* CONFIG_FILS */
++ }
++
++ ret = ieee802_11_update_beacons(hapd->iface);
++ if(ret) {
++ wpa_printf(MSG_DEBUG,
++ "Failed to update with inband discovery parameters\n");
++ return -1;
++ }
++
++ return 0;
++}
+
+ #ifdef CONFIG_WNM_AP
+
+@@ -4045,6 +4108,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
+ reply_len = -1;
+ #endif /* CONFIG_WNM_AP */
++ } else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
++ if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
++ reply_len = -1;
+ } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
+ reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
+ reply_size);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index a469b1f4d..1fb6d999e 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -655,6 +655,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ return wpa_ctrl_command(ctrl, buf);
+ }
+
++static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ char buf[300];
++ int res;
++
++ if (argc < 2) {
++ printf("Invalid 'inband_discovery' command - two arguments"
++ "tx_type interval\n");
++ return -1;
++ }
++
++ res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
++ argv[0], argv[1]);
++ if (os_snprintf_error(sizeof(buf), res))
++ return -1;
++ return wpa_ctrl_command(ctrl, buf);
++}
+
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+@@ -1851,6 +1869,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ { "driver", hostapd_cli_cmd_driver, NULL,
+ "<driver sub command> [<hex formatted data>] = send driver command data" },
+ #endif /* ANDROID */
++ { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
++ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 26453cb2c..a5c46b067 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2055,6 +2055,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+ {
+ params->fd_max_int = hapd->conf->fils_discovery_max_int;
++ params->unsol_bcast_probe_resp_interval =
++ hapd->conf->unsol_bcast_probe_resp_interval;
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
+ params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
+@@ -2063,7 +2065,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ if (params->fd_min_int > params->fd_max_int)
+ params->fd_min_int = params->fd_max_int;
+
+- if (params->fd_max_int)
++ if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
++ !params->unsol_bcast_probe_resp_interval))
+ return hostapd_gen_fils_discovery(hapd,
+ ¶ms->fd_frame_tmpl_len);
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 6778ad369..501d0e42e 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4780,7 +4780,6 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
+ nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
+ params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
+ return -1;
+-
+ nla_nest_end(msg, attr);
+ return 0;
+ }
+@@ -5412,7 +5411,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
+ #endif /* CONFIG_SAE */
+
+ #ifdef CONFIG_FILS
+- if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
++ if ((params->fd_max_int ||
++ ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
++ !(params->unsol_bcast_probe_resp_interval))) &&
++ nl80211_fils_discovery(bss, msg, params) < 0)
+ goto fail;
+ #endif /* CONFIG_FILS */
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
new file mode 100644
index 0000000..e93390d
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
@@ -0,0 +1,216 @@
+From 5be34a5e9682f4448e41322dc4f96d5d9a58240e Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 15:04:57 +0800
+Subject: [PATCH 032/104] mtk: hostapd: Add mtk_vendor.h
+
+---
+ src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 197 insertions(+)
+ create mode 100644 src/common/mtk_vendor.h
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+new file mode 100644
+index 000000000..4a19d2fc9
+--- /dev/null
++++ b/src/common/mtk_vendor.h
+@@ -0,0 +1,197 @@
++// SPDX-License-Identifier: ISC
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++#ifndef MTK_VENDOR_H
++#define MTK_VENDOR_H
++
++#define OUI_MTK 0x000ce7
++
++enum mtk_nl80211_vendor_subcmds {
++ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
++ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++ MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++ MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++ EDCCA_CTRL_SET_EN = 0,
++ EDCCA_CTRL_SET_THERS,
++ EDCCA_CTRL_GET_EN,
++ EDCCA_CTRL_GET_THERS,
++ EDCCA_CTRL_NUM,
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++enum mtk_vendor_attr_csi_ctrl {
++ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++ MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++ MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++ MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++ MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++ MTK_VENDOR_ATTR_CSI_DATA_VER,
++ MTK_VENDOR_ATTR_CSI_DATA_TS,
++ MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++ MTK_VENDOR_ATTR_CSI_DATA_SNR,
++ MTK_VENDOR_ATTR_CSI_DATA_BW,
++ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++ MTK_VENDOR_ATTR_CSI_DATA_TA,
++ MTK_VENDOR_ATTR_CSI_DATA_I,
++ MTK_VENDOR_ATTR_CSI_DATA_Q,
++ MTK_VENDOR_ATTR_CSI_DATA_INFO,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++ MTK_VENDOR_ATTR_CSI_DATA_MODE,
++ MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++ MTK_VENDOR_ATTR_CSI_DATA_MAX =
++ NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
++};
++
++enum mtk_vendor_attr_mnt_ctrl {
++ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_CTRL_SET,
++ MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
++ MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_set {
++ MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_SET_INDEX,
++ MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_SET,
++ MTK_VENDOR_ATTR_AMNT_SET_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
++};
++
++enum mtk_vendor_attr_mnt_dump {
++ MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
++ MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
++ MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++ MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++#define CSI_MAX_COUNT 256
++#define ETH_ALEN 6
++
++struct csi_data {
++ s16 data_i[CSI_MAX_COUNT];
++ s16 data_q[CSI_MAX_COUNT];
++ s8 rssi;
++ u8 snr;
++ u32 ts;
++ u8 data_bw;
++ u8 pri_ch_idx;
++ u8 ta[ETH_ALEN];
++ u32 info;
++ u8 rx_mode;
++ u32 h_idx;
++ u16 tx_idx;
++ u16 rx_idx;
++};
++
++struct amnt_data {
++ u8 idx;
++ u8 addr[ETH_ALEN];
++ s8 rssi[4];
++ u32 last_seen;
++};
++#endif /* MTK_VENDOR_H */
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
new file mode 100644
index 0000000..d7544bc
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
@@ -0,0 +1,630 @@
+From 7b735e95647fe46cc5dcf7d859c8f9abbed5ae0d Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 16:31:34 +0800
+Subject: [PATCH 033/104] mtk: hostapd: Support EDCCA hostapd configuration
+
+edcca_enable and edcca_compensation and implement edcca related handlers.
+---
+ hostapd/config_file.c | 34 ++++++
+ hostapd/ctrl_iface.c | 124 +++++++++++++++++++++
+ src/ap/ap_config.c | 4 +
+ src/ap/ap_config.h | 30 ++++++
+ src/ap/ap_drv_ops.c | 24 +++++
+ src/ap/ap_drv_ops.h | 4 +
+ src/ap/hostapd.c | 7 ++
+ src/common/mtk_vendor.h | 20 ++--
+ src/drivers/driver.h | 4 +
+ src/drivers/driver_nl80211.c | 174 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 7 ++
+ 12 files changed, 427 insertions(+), 6 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 0094db279..f8c1eec0a 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5348,6 +5348,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->mld_indicate_disabled = atoi(pos);
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++ } else if (os_strcmp(buf, "edcca_threshold") == 0) {
++ if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
++ conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
++ conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
++ conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
++ conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
++ line);
++ return 1;
++ }
++ } else if (os_strcmp(buf, "edcca_enable") == 0) {
++ int mode = atoi(pos);
++ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
++ " allowed value 0 (Force Disable) or 1(Auto) ",
++ line, mode);
++ return 1;
++ }
++ conf->edcca_enable = (u8) mode;
++ } else if (os_strcmp(buf, "edcca_compensation") == 0) {
++ int val = atoi(pos);
++ if (val < EDCCA_MIN_COMPENSATION ||
++ val > EDCCA_MAX_COMPENSATION) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
++ " value %d; allowed value %d ~ %d.",
++ line, val, EDCCA_MIN_COMPENSATION,
++ EDCCA_MAX_COMPENSATION);
++ return 1;
++ }
++ conf->edcca_compensation = (s8) val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index fb9f09bb1..78a3380f2 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -544,6 +544,19 @@ static const char * pbc_status_str(enum pbc_status status)
+ }
+
+
++static const char *edcca_mode_str(enum edcca_mode status)
++{
++ switch (status) {
++ case EDCCA_MODE_FORCE_DISABLE:
++ return "Force Disable";
++ case EDCCA_MODE_AUTO:
++ return "Auto";
++ default:
++ return "Unknown";
++ }
++}
++
++
+ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+ {
+@@ -3644,6 +3657,111 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+
++static int
++hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *config, *value;
++ config = cmd;
++ pos = os_strchr(config, ' ');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if (pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strcmp(config, "enable") == 0) {
++ int mode = atoi(value);
++ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++ wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
++ return -1;
++ }
++ hapd->iconf->edcca_enable = (u8) mode;
++ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++ return -1;
++ } else if (os_strcmp(config, "compensation") == 0) {
++ int compensation = atoi(value);
++ if (compensation < EDCCA_MIN_COMPENSATION ||
++ compensation > EDCCA_MAX_COMPENSATION) {
++ wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
++ return -1;
++ }
++ hapd->iconf->edcca_compensation = (s8) compensation;
++ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++ return -1;
++ } else if (os_strcmp(config, "threshold") == 0) {
++ char *thres_value;
++ thres_value = os_strchr(value, ':');
++ if (thres_value == NULL)
++ return -1;
++ *thres_value++ = '\0';
++
++ if (thres_value == NULL)
++ return -1;
++ int bw_idx = atoi(value);
++ int threshold = atoi(thres_value);
++
++ if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
++ wpa_printf(MSG_ERROR,
++ "Unsupported Bandwidth idx %d for SET_EDCCA",
++ bw_idx);
++ return -1;
++ }
++ if (threshold < EDCCA_MIN_CONFIG_THRES ||
++ threshold > EDCCA_MAX_CONFIG_THRES) {
++ wpa_printf(MSG_ERROR,
++ "Unsupported threshold %d for SET_EDCCA",
++ threshold);
++ return -1;
++ }
++
++ int threshold_arr[EDCCA_MAX_BW_NUM];
++ /* 0x7f means keep the origival value in firmware */
++ os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
++ threshold_arr[bw_idx] = threshold;
++
++ if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
++ return -1;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for SET_EDCCA", config);
++ return -1;
++ }
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++static int
++hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
++ size_t buflen)
++{
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++ u8 value[EDCCA_MAX_BW_NUM] = {0};
++
++ if (os_strcmp(cmd, "enable") == 0) {
++ return os_snprintf(pos, end - pos, "Enable: %s\n",
++ edcca_mode_str(hapd->iconf->edcca_enable));
++ } else if (os_strcmp(cmd, "compensation") == 0) {
++ return os_snprintf(pos, end - pos, "Compensation: %d\n",
++ hapd->iconf->edcca_compensation);
++ } else if (os_strcmp(cmd, "threshold") == 0) {
++ if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
++ return -1;
++ return os_snprintf(pos, end - pos,
++ "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
++ value[0], value[1], value[2], value[3]);
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for GET_EDCCA", cmd);
++ return -1;
++ }
++}
++
+
+ #ifdef CONFIG_NAN_USD
+
+@@ -4531,6 +4649,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++ } else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
++ reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
++ reply_size);
++ } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
++ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
++ reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 6c8b10291..965600577 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -303,6 +303,9 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+ #endif /* CONFIG_AIRTIME_POLICY */
+
++ conf->edcca_enable = EDCCA_MODE_AUTO;
++ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+ return conf;
+@@ -1034,6 +1037,7 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #ifdef CONFIG_ACS
+ os_free(conf->acs_chan_bias);
+ #endif /* CONFIG_ACS */
++ os_free(conf->edcca_threshold);
+ wpabuf_free(conf->lci);
+ wpabuf_free(conf->civic);
+ #ifdef CONFIG_AFC
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 379dc22cf..09718fada 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1281,8 +1281,38 @@ struct hostapd_config {
+ int min_power;
+ } afc;
+ #endif /* CONFIG_AFC */
++
++ u8 edcca_enable;
++ s8 edcca_compensation;
++ int *edcca_threshold;
++};
++
++enum edcca_mode {
++ EDCCA_MODE_FORCE_DISABLE = 0,
++ EDCCA_MODE_AUTO = 1,
++};
++
++enum edcca_bw_id {
++ EDCCA_BW_20 = 0,
++ EDCCA_BW_40,
++ EDCCA_BW_80,
++ EDCCA_BW_160,
++ EDCCA_MAX_BW_NUM,
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++ EDCCA_CTRL_SET_EN = 0,
++ EDCCA_CTRL_SET_THRES,
++ EDCCA_CTRL_GET_EN,
++ EDCCA_CTRL_GET_THRES,
++ EDCCA_CTRL_NUM,
+ };
+
++#define EDCCA_DEFAULT_COMPENSATION -6
++#define EDCCA_MIN_COMPENSATION -126
++#define EDCCA_MAX_COMPENSATION 126
++#define EDCCA_MIN_CONFIG_THRES -126
++#define EDCCA_MAX_CONFIG_THRES 0
+
+ static inline enum oper_chan_width
+ hostapd_get_oper_chwidth(struct hostapd_config *conf)
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 527b2c984..a6caf6a73 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1245,3 +1245,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms);
+ }
+ #endif /* CONFIG_PASN */
++
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->configure_edcca_enable)
++ return 0;
++ return hapd->driver->configure_edcca_enable(hapd->drv_priv,
++ hapd->iconf->edcca_enable,
++ hapd->iconf->edcca_compensation);
++}
++
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++ const int *threshold)
++{
++ if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++ return 0;
++ return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
++}
++
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
++{
++ if (!hapd->driver || !hapd->driver->get_edcca)
++ return 0;
++ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f8a8725be..98836153f 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -149,6 +149,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ u8 ltf_keyseed_len,
+ const u8 *ltf_keyseed, u32 action);
+
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++ const int *threshold);
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7959859b0..6af31179e 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2693,6 +2693,13 @@ dfs_offload:
+ }
+ #endif /* CONFIG_MESH */
+
++ if (hostapd_drv_configure_edcca_enable(hapd) < 0)
++ goto fail;
++
++ if (hostapd_drv_configure_edcca_threshold(hapd,
++ hapd->iconf->edcca_threshold) < 0)
++ goto fail;
++
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+ if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 4a19d2fc9..6121857dd 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
+ };
+
+-enum mtk_vendor_attr_edcca_ctrl_mode {
+- EDCCA_CTRL_SET_EN = 0,
+- EDCCA_CTRL_SET_THERS,
+- EDCCA_CTRL_GET_EN,
+- EDCCA_CTRL_GET_THERS,
+- EDCCA_CTRL_NUM,
++enum mtk_vendor_attr_edcca_dump {
++ MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
++
++ MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+
++
+ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 3e3e309f4..ed5f5c013 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5220,6 +5220,10 @@ struct wpa_driver_ops {
+ const u8 *match, size_t match_len,
+ bool multicast);
+ #endif /* CONFIG_TESTING_OPTIONS */
++ int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
++ const s8 edcca_compensation);
++ int (*configure_edcca_threshold)(void *priv, const int *threshold);
++ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 501d0e42e..d59efe8b6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -42,6 +42,8 @@
+ #include "radiotap_iter.h"
+ #include "rfkill.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
++#include "ap/ap_config.h"
+
+
+ #ifndef NETLINK_CAP_ACK
+@@ -14079,6 +14081,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+
+ #endif /* CONFIG_TESTING_OPTIONS */
+
++static int nl80211_configure_edcca_enable(void *priv,
++ const u8 edcca_enable,
++ const s8 edcca_compensation)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_edcca_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting EDCCA enable");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++ edcca_compensation)) {
++ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
++static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_edcca_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting EDCCA threshold");
++ return 0;
++ }
++
++ if (!threshold) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Input EDCCA threshold is empty!");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
++ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
++
++static int edcca_info_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *info = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
++ return NL_SKIP;
++ }
++
++ *info++ = nla_get_u8(attr);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
++ return NL_SKIP;
++ }
++
++ *info++ = nla_get_u8(attr);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
++ return NL_SKIP;
++ }
++
++ *info++ = nla_get_u8(attr);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
++ return NL_SKIP;
++ }
++
++ *info = nla_get_u8(attr);
++ return NL_SKIP;
++}
++
++
++static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_edcca_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting EDCCA threshold");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
++ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_resp(drv, msg, edcca_info_handler, value);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
+
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+@@ -14240,4 +14410,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .register_frame = testing_nl80211_register_frame,
+ .radio_disable = testing_nl80211_radio_disable,
+ #endif /* CONFIG_TESTING_OPTIONS */
++/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
++ .configure_edcca_enable = nl80211_configure_edcca_enable,
++ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
++ .get_edcca = nl80211_get_edcca,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 618746e67..62c47efbd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -200,6 +200,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
+ unsigned int puncturing:1;
+ unsigned int qca_ap_allowed_freqs:1;
++ unsigned int mtk_edcca_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index d6a887cef..cd4d799a1 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -18,6 +18,7 @@
+ #include "common/qca-vendor-attr.h"
+ #include "common/brcm_vendor.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+
+
+ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+@@ -1138,6 +1139,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ break;
+ }
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++ } else if (vinfo->vendor_id == OUI_MTK) {
++ switch (vinfo->subcmd) {
++ case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
++ drv->mtk_edcca_vendor_cmd_avail = 1;
++ break;
++ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
new file mode 100644
index 0000000..3849fa4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
@@ -0,0 +1,454 @@
+From fa905ce61f3cfecf94012fc2a11680e2615fc05d Mon Sep 17 00:00:00 2001
+From: TomLiu <tomml.liu@mediatek.com>
+Date: Tue, 9 Aug 2022 10:23:44 -0700
+Subject: [PATCH 034/104] mtk: hostapd: Add hostapd MU SET/GET control
+
+---
+ hostapd/config_file.c | 9 +++
+ hostapd/ctrl_iface.c | 66 ++++++++++++++++++
+ hostapd/hostapd_cli.c | 18 +++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 14 ++++
+ src/ap/ap_drv_ops.h | 2 +
+ src/ap/hostapd.c | 2 +
+ src/common/mtk_vendor.h | 15 ++++
+ src/drivers/driver.h | 13 ++++
+ src/drivers/driver_nl80211.c | 110 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 13 files changed, 255 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index f8c1eec0a..637c2df9f 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4159,6 +4159,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->mbssid = mbssid;
++ } else if (os_strcmp(buf, "mu_onoff") == 0) {
++ int val = atoi(pos);
++ if (val < 0 || val > 15) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid mu_onoff value",
++ line);
++ return 1;
++ }
++ conf->mu_onoff = val;
+ #endif /* CONFIG_IEEE80211AX */
+ } else if (os_strcmp(buf, "max_listen_interval") == 0) {
+ bss->max_listen_interval = atoi(pos);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 78a3380f2..3a79a1284 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4049,6 +4049,67 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+
+
++static int
++hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *config, *value;
++ config = cmd;
++ pos = os_strchr(config, ' ');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if(pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strcmp(config, "onoff") == 0) {
++ int mu = atoi(value);
++ if (mu < 0 || mu > 15) {
++ wpa_printf(MSG_ERROR, "Invalid value for mu");
++ return -1;
++ }
++ hapd->iconf->mu_onoff = (u8) mu;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for SET_MU", config);
++ return -1;
++ }
++
++ if(hostapd_drv_mu_ctrl(hapd) == 0) {
++ return os_snprintf(buf, buflen, "OK\n");
++ } else {
++ return -1;
++ }
++}
++
++
++static int
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ u8 mu_onoff;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hapd->iface->state != HAPD_IFACE_ENABLED)
++ return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
++ hostapd_state_text(hapd->iface->state));
++
++ if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
++ hapd->iconf->mu_onoff = mu_onoff;
++ return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++ !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++ } else {
++ wpa_printf(MSG_INFO, "ctrl iface failed to call");
++ return -1;
++ }
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4655,6 +4716,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
+ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ reply_size);
++ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
++ reply_size);
++ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
++ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 1fb6d999e..da9dabd6f 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1442,6 +1442,20 @@ static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
++static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++}
++
++
+ #ifdef CONFIG_DPP
+
+ static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+@@ -1801,6 +1815,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = show supported driver flags"},
+ { "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL,
+ " = show supported driver flags2"},
++ { "set_mu", hostapd_cli_cmd_set_mu, NULL,
++ "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
++ { "get_mu", hostapd_cli_cmd_get_mu, NULL,
++ " = show mu onoff value in 0-15 bitmap"},
+ #ifdef CONFIG_DPP
+ { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
+ "report a scanned DPP URI from a QR Code" },
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 965600577..9b3ef0b5b 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -289,6 +289,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->reg_def_cli_eirp_psd = -1;
+ conf->reg_sub_cli_eirp_psd = -1;
+ conf->reg_def_cli_eirp = -1;
++ conf->mu_onoff = 15;
+ #endif /* CONFIG_IEEE80211AX */
+
+ /* The third octet of the country string uses an ASCII space character
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 09718fada..f7dbbbec3 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1185,6 +1185,7 @@ struct hostapd_config {
+ int reg_def_cli_eirp;
+
+ bool require_he;
++ u8 mu_onoff;
+ #endif /* CONFIG_IEEE80211AX */
+
+ /* VHT enable/disable config from CHAN_SWITCH */
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index a6caf6a73..897ed6af8 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1269,3 +1269,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ return 0;
+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
++
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->mu_ctrl)
++ return 0;
++ return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++}
++
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
++{
++ if (!hapd->driver || !hapd->driver->mu_dump)
++ return 0;
++ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 98836153f..5ab20cc41 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -153,6 +153,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 6af31179e..d29b51fc5 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2699,6 +2699,8 @@ dfs_offload:
+ if (hostapd_drv_configure_edcca_threshold(hapd,
+ hapd->iconf->edcca_threshold) < 0)
+ goto fail;
++ if (hostapd_drv_mu_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6121857dd..60bc4cd4c 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
+ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
+ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ };
+
+@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
+ };
+
++enum mtk_vendor_attr_mu_ctrl {
++ MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
++ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
++ MTK_VENDOR_ATTR_MU_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
++};
++
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ed5f5c013..df7ce5ab9 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -176,6 +176,11 @@ struct hostapd_channel_data {
+ * punct_bitmap - RU puncturing bitmap
+ */
+ u16 punct_bitmap;
++
++ /**
++ * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
++ */
++ u8 mu_onoff;
+ };
+
+ #define HE_MAC_CAPAB_0 0
+@@ -5224,6 +5229,14 @@ struct wpa_driver_ops {
+ const s8 edcca_compensation);
+ int (*configure_edcca_threshold)(void *priv, const int *threshold);
+ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
++
++ /**
++ * mu_ctrl - ctrl on off for UL/DL MURU
++ * @priv: Private driver interface data
++ *
++ */
++ int (*mu_ctrl)(void *priv, u8 mu_onoff);
++ int (*mu_dump)(void *priv, u8 *mu_onoff);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index d59efe8b6..c234eb029 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13917,6 +13917,114 @@ fail:
+ }
+
+
++#ifdef CONFIG_IEEE80211AX
++static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_mu_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting mu control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if(ret){
++ wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++ }
++ return ret;
++}
++
++
++static int mu_dump_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *mu_onoff = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ static const struct nla_policy
++ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
++ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++ };
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
++ return NL_SKIP;
++ }
++
++ *mu_onoff = nla_get_u8(attr);
++ wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
++
++ return 0;
++}
++
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *attr;
++ int ret;
++
++ if (!drv->mtk_mu_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting mu control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++
++ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!attr) {
++ nlmsg_free(msg);
++ return -1;
++ }
++
++ nla_nest_end(msg, attr);
++
++ ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
++
++ if(ret){
++ wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++}
++#endif /* CONFIG_IEEE80211AX */
++
++
+ #ifdef CONFIG_DPP
+ static int nl80211_dpp_listen(void *priv, bool enable)
+ {
+@@ -14396,6 +14504,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .update_connect_params = nl80211_update_connection_params,
+ .send_external_auth_status = nl80211_send_external_auth_status,
+ .set_4addr_mode = nl80211_set_4addr_mode,
++ .mu_ctrl = nl80211_mu_onoff,
++ .mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ .dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 62c47efbd..f99bba9e1 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -201,6 +201,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int puncturing:1;
+ unsigned int qca_ap_allowed_freqs:1;
+ unsigned int mtk_edcca_vendor_cmd_avail:1;
++ unsigned int mtk_mu_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index cd4d799a1..9c0a47971 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1144,6 +1144,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
+ drv->mtk_edcca_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
++ drv->mtk_mu_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
new file mode 100644
index 0000000..596fdd3
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
@@ -0,0 +1,247 @@
+From 588292b2fac44452523a27ece07b85fbd0f41c5d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 2 Sep 2022 01:03:23 +0800
+Subject: [PATCH 035/104] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
+ command
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c | 4 ++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 13 ++++++++++++
+ src/ap/ap_drv_ops.c | 11 +++++++++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/ap/hostapd.c | 2 ++
+ src/common/mtk_vendor.h | 16 +++++++++++++++
+ src/drivers/driver.h | 8 ++++++++
+ src/drivers/driver_nl80211.c | 33 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +++
+ 11 files changed, 93 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 637c2df9f..3d9923692 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5391,6 +5391,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->edcca_compensation = (s8) val;
++ } else if (os_strcmp(buf, "three_wire_enable") == 0) {
++ u8 en = atoi(pos);
++
++ conf->three_wire_enable = en;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 9b3ef0b5b..79fd3a24b 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -306,6 +306,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+
+ conf->edcca_enable = EDCCA_MODE_AUTO;
+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index f7dbbbec3..d1bbd238c 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1286,6 +1286,19 @@ struct hostapd_config {
+ u8 edcca_enable;
+ s8 edcca_compensation;
+ int *edcca_threshold;
++ u8 three_wire_enable;
++};
++
++enum three_wire_mode {
++ THREE_WIRE_MODE_DISABLE,
++ THREE_WIRE_MODE_EXT0_ENABLE,
++ THREE_WIRE_MODE_EXT1_ENABLE,
++ THREE_WIRE_MODE_ALL_ENABLE,
++
++ /* keep last */
++ NUM_THREE_WIRE_MODE,
++ THREE_WIRE_MODE_MAX =
++ NUM_THREE_WIRE_MODE - 1
+ };
+
+ enum edcca_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 897ed6af8..587b8f37f 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1283,3 +1283,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ return 0;
+ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
+ }
++
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->three_wire_ctrl)
++ return 0;
++ if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
++ wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
++ return 0;
++ }
++ return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 5ab20cc41..7448e7954 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -155,6 +155,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index d29b51fc5..5ceb49962 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2701,6 +2701,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_mu_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 60bc4cd4c..99ecbaf71 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+ };
+
++enum mtk_vendor_attr_3wire_ctrl {
++ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
++ MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_csi_ctrl {
+ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index df7ce5ab9..dec4336a4 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5237,6 +5237,14 @@ struct wpa_driver_ops {
+ */
+ int (*mu_ctrl)(void *priv, u8 mu_onoff);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
++
++ /**
++ * three_wire_ctrl - set three_wire_ctrl mode
++ * @priv: Private driver interface data
++ * @three_wire_enable: three_wire_ctrl mode
++ *
++ */
++ int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c234eb029..c9899e492 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14357,6 +14357,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
+ return ret;
+ }
+
++static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ /* Prepare nl80211 cmd */
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_3wire_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting three wire control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
+
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+@@ -14524,4 +14556,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .configure_edcca_enable = nl80211_configure_edcca_enable,
+ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ .get_edcca = nl80211_get_edcca,
++ .three_wire_ctrl = nl80211_enable_three_wire,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index f99bba9e1..5de6ca6f0 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int qca_ap_allowed_freqs:1;
+ unsigned int mtk_edcca_vendor_cmd_avail:1;
+ unsigned int mtk_mu_vendor_cmd_avail:1;
++ unsigned int mtk_3wire_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 9c0a47971..fddcf8349 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1147,6 +1147,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
+ drv->mtk_mu_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
++ drv->mtk_3wire_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
new file mode 100644
index 0000000..56149a4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
@@ -0,0 +1,431 @@
+From 235a6041bde6ce30da6e631b901260dec5fadcda Mon Sep 17 00:00:00 2001
+From: mtk27835 <shurong.wen@mediatek.com>
+Date: Wed, 7 Sep 2022 14:41:51 -0700
+Subject: [PATCH 036/104] mtk: hostapd: Add hostapd iBF control
+
+Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
+---
+ hostapd/config_file.c | 3 +
+ hostapd/ctrl_iface.c | 26 +++++++
+ hostapd/hostapd_cli.c | 9 +++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 2 +
+ src/ap/ap_drv_ops.c | 14 ++++
+ src/ap/ap_drv_ops.h | 2 +
+ src/ap/hostapd.c | 2 +
+ src/common/mtk_vendor.h | 35 +++++++++-
+ src/drivers/driver.h | 19 ++++++
+ src/drivers/driver_nl80211.c | 108 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 13 files changed, 224 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 3d9923692..247d68811 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5395,6 +5395,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ u8 en = atoi(pos);
+
+ conf->three_wire_enable = en;
++ } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
++ int val = atoi(pos);
++ conf->ibf_enable = !!val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3a79a1284..10bbce341 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4110,6 +4110,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ }
+
+
++static int
++hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ u8 ibf_enable;
++ int ret;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
++ hapd->iconf->ibf_enable = ibf_enable;
++ ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
++ ibf_enable);
++ }
++
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++
++ return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4721,6 +4745,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_size);
+ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index da9dabd6f..276ca578c 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1666,6 +1666,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ #endif /* ANDROID */
+
+
++static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1889,6 +1896,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ #endif /* ANDROID */
+ { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
+ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
++ { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
++ " = show iBF state (enabled/disabled)"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 79fd3a24b..04e263167 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -307,6 +307,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->edcca_enable = EDCCA_MODE_AUTO;
+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
++ conf->ibf_enable = IBF_DEFAULT_ENABLE;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d1bbd238c..5f084796d 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1287,6 +1287,7 @@ struct hostapd_config {
+ s8 edcca_compensation;
+ int *edcca_threshold;
+ u8 three_wire_enable;
++ u8 ibf_enable;
+ };
+
+ enum three_wire_mode {
+@@ -1450,6 +1451,7 @@ hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
+ #endif /* CONFIG_IEEE80211BE */
+ }
+
++#define IBF_DEFAULT_ENABLE 0
+
+ int hostapd_mac_comp(const void *a, const void *b);
+ struct hostapd_config * hostapd_config_defaults(void);
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 587b8f37f..3cace58e5 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1294,3 +1294,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+ }
+ return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
+ }
++
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->ibf_ctrl)
++ return 0;
++ return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
++}
++
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
++{
++ if (!hapd->driver || !hapd->driver->ibf_dump)
++ return 0;
++ return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 7448e7954..0886acb2d 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,6 +156,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 5ceb49962..1d941683f 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2703,6 +2703,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_ibf_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 99ecbaf71..9811f266e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
++ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
++ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+
++enum mtk_vendor_attr_ibf_ctrl {
++ MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
++ MTK_VENDOR_ATTR_IBF_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_dump {
++ MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
++ MTK_VENDOR_ATTR_IBF_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
++};
++
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
+
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dec4336a4..f5cff646e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -181,6 +181,11 @@ struct hostapd_channel_data {
+ * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
+ */
+ u8 mu_onoff;
++
++ /**
++ * ibf_enable=<val>
++ */
++ u8 ibf_enable;
+ };
+
+ #define HE_MAC_CAPAB_0 0
+@@ -5245,6 +5250,20 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
++
++ /**
++ * ibf_ctrl - ctrl disable/enable for ibf
++ * @priv: Private driver interface data
++ *
++ */
++ int (*ibf_ctrl)(void *priv, u8 ibf_enable);
++
++ /**
++ * ibf_dump - dump ibf
++ * @priv: Private driver interface data
++ *
++ */
++ int (*ibf_dump)(void *priv, u8 *ibf_enable);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c9899e492..c17052f22 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14390,6 +14390,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
+ return ret;
+ }
+
++static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_ibf_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ibf control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int ibf_dump_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *ibf_enable = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
++ return NL_SKIP;
++ }
++
++ *ibf_enable = nla_get_u8(attr);
++
++ return NL_SKIP;
++}
++
++static int
++nl80211_ibf_dump(void *priv, u8 *ibf_enable)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, ibf_dump_handler, ibf_enable);
++
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -14557,4 +14663,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ .get_edcca = nl80211_get_edcca,
+ .three_wire_ctrl = nl80211_enable_three_wire,
++ .ibf_ctrl = nl80211_ibf_enable,
++ .ibf_dump = nl80211_ibf_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5de6ca6f0..1432eeda8 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_edcca_vendor_cmd_avail:1;
+ unsigned int mtk_mu_vendor_cmd_avail:1;
+ unsigned int mtk_3wire_vendor_cmd_avail:1;
++ unsigned int mtk_ibf_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index fddcf8349..615af2eb2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1150,6 +1150,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
+ drv->mtk_3wire_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
++ drv->mtk_ibf_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
new file mode 100644
index 0000000..ade8550
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
@@ -0,0 +1,34 @@
+From dce3106e1c43076ab410af89f817e15cee7959a3 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 22 Sep 2022 16:08:09 +0800
+Subject: [PATCH 037/104] mtk: hostapd: Do not include HE capab IE if
+ associated sta's HE capab IE is invalid
+
+The parameter 'sta' passed to send_assoc_resp() might be NULL, so an
+NULL check is necessary before access the 'sta'.
+Only one such check was missed in this function, and this patch fixs it.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I028b7779acfbce5292a60c9f800a83c57c999943
+---
+ src/ap/ieee802_11.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index bda61b998..d972a25f1 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4931,7 +4931,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ #endif /* CONFIG_IEEE80211AC */
+
+ #ifdef CONFIG_IEEE80211AX
+- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
++ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && sta &&
++ sta->flags & WLAN_STA_HE) {
+ p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_he_operation(hapd, p);
+ p = hostapd_eid_cca(hapd, p);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
new file mode 100644
index 0000000..c540b5e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
@@ -0,0 +1,136 @@
+From 9d180d46527a6a53824227c2b7c6e4e37bf3acfb Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:55:49 +0800
+Subject: [PATCH 038/104] mtk: hostapd: Add DFS detection mode
+
+Add DFS detection mode for testing radar detection rate.
+If DFS detection mode is on, AP will not switch channels when receiving
+a radar signal.
+This detection mode also supports background chain.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c | 4 ++++
+ hostapd/ctrl_iface.c | 23 +++++++++++++++++++++++
+ src/ap/ap_config.h | 13 +++++++++++++
+ src/ap/dfs.c | 10 ++++++++++
+ 4 files changed, 50 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 247d68811..40ade89c0 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5398,6 +5398,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
+ int val = atoi(pos);
+ conf->ibf_enable = !!val;
++ } else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
++ u8 en = strtol(pos, NULL, 10);
++
++ conf->dfs_detect_mode = en;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 10bbce341..71a87459c 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4134,6 +4134,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
+ }
+
+
++static int
++hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
++ char *buf, size_t buflen)
++{
++ u8 dfs_detect_mode;
++
++ if (!value)
++ return -1;
++
++ dfs_detect_mode = strtol(value, NULL, 10);
++ if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
++ wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
++ return -1;
++ }
++ hapd->iconf->dfs_detect_mode = dfs_detect_mode;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4747,6 +4767,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
++ reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 5f084796d..7607c63e1 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1288,6 +1288,7 @@ struct hostapd_config {
+ int *edcca_threshold;
+ u8 three_wire_enable;
+ u8 ibf_enable;
++ u8 dfs_detect_mode;
+ };
+
+ enum three_wire_mode {
+@@ -1302,6 +1303,18 @@ enum three_wire_mode {
+ NUM_THREE_WIRE_MODE - 1
+ };
+
++enum dfs_mode {
++ DFS_DETECT_MODE_DISABLE,
++ DFS_DETECT_MODE_AP_ENABLE,
++ DFS_DETECT_MODE_BACKGROUND_ENABLE,
++ DFS_DETECT_MODE_ALL_ENABLE,
++
++ /* keep last */
++ NUM_DFS_DETECT_MODE,
++ DFS_DETECT_MODE_MAX =
++ NUM_DFS_DETECT_MODE - 1
++};
++
+ enum edcca_mode {
+ EDCCA_MODE_FORCE_DISABLE = 0,
+ EDCCA_MODE_AUTO = 1,
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index d14fad136..1df4de6b8 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1336,6 +1336,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ __func__, iface->radar_background.cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
++ /* Skip channel switch when background dfs detect mode is on */
++ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
++ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++ return 0;
++
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+@@ -1384,6 +1389,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ __func__, iface->cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
++ /* Skip channel switch when dfs detect mode is on */
++ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
++ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++ return 0;
++
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
new file mode 100644
index 0000000..a37e541
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
@@ -0,0 +1,192 @@
+From 7f0f977e36718a7a827f7d78e6ab578d53aa630b Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:56:55 +0800
+Subject: [PATCH 039/104] mtk: hostapd: Add DFS offchan channel switch
+
+Add DFS background chain channel switch command for testing purpose.
+This feature is implemented via hostapd_cli command.
+Command format:
+hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
+ src/ap/dfs.c | 25 ++++++---------
+ src/ap/dfs.h | 15 +++++++++
+ 3 files changed, 96 insertions(+), 16 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 71a87459c..68dcc7982 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4154,6 +4154,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
+ }
+
+
++static int
++hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ struct hostapd_iface *iface = hapd->iface;
++ char *pos, *param;
++ enum hostapd_hw_mode hw_mode;
++ bool chan_found = false;
++ int i, num_available_chandefs, channel, chan_width, sec = 0;
++ int sec_chan_idx_80p80 = -1;
++ u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
++ struct hostapd_channel_data *chan;
++ enum dfs_channel_type type = DFS_NO_CAC_YET;
++
++ param = os_strchr(cmd, ' ');
++ if (!param)
++ return -1;
++ *param++ = '\0';
++
++ pos = os_strstr(param, "chan=");
++ if (pos)
++ channel = strtol(pos + 5, NULL, 10);
++ else
++ return -1;
++
++ num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
++ for (i = 0; i < num_available_chandefs; i++) {
++ dfs_find_channel(iface, &chan, i, type);
++ if (chan->chan == channel) {
++ chan_found = true;
++ break;
++ }
++ }
++
++ if (!chan_found)
++ return -1;
++
++ if (iface->conf->secondary_channel)
++ sec = 1;
++
++ dfs_adjust_center_freq(iface, chan,
++ sec,
++ sec_chan_idx_80p80,
++ &oper_centr_freq_seg0_idx,
++ &oper_centr_freq_seg1_idx);
++
++ if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++ chan->freq, chan->chan,
++ iface->conf->ieee80211n,
++ iface->conf->ieee80211ac,
++ iface->conf->ieee80211ax,
++ iface->conf->ieee80211be,
++ sec, hostapd_get_oper_chwidth(iface->conf),
++ oper_centr_freq_seg0_idx,
++ oper_centr_freq_seg1_idx, true)) {
++ wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
++ iface->radar_background.channel = -1;
++ return -1;
++ }
++
++ iface->radar_background.channel = chan->chan;
++ iface->radar_background.freq = chan->freq;
++ iface->radar_background.secondary_channel = sec;
++ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
++ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4770,6 +4840,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+ reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
++ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 1df4de6b8..ece27d070 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -19,13 +19,6 @@
+ #include "dfs.h"
+ #include "crypto/crypto.h"
+
+-
+-enum dfs_channel_type {
+- DFS_ANY_CHANNEL,
+- DFS_AVAILABLE, /* non-radar or radar-available */
+- DFS_NO_CAC_YET, /* radar-not-yet-available */
+-};
+-
+ static struct hostapd_channel_data *
+ dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+ u8 *oper_centr_freq_seg0_idx,
+@@ -238,9 +231,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+ * - hapd->vht/he_oper_centr_freq_seg0_idx
+ * - hapd->vht/he_oper_centr_freq_seg1_idx
+ */
+-static int dfs_find_channel(struct hostapd_iface *iface,
+- struct hostapd_channel_data **ret_chan,
+- int idx, enum dfs_channel_type type)
++int dfs_find_channel(struct hostapd_iface *iface,
++ struct hostapd_channel_data **ret_chan,
++ int idx, enum dfs_channel_type type)
+ {
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+@@ -300,12 +293,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
+ }
+
+
+-static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+- struct hostapd_channel_data *chan,
+- int secondary_channel,
+- int sec_chan_idx_80p80,
+- u8 *oper_centr_freq_seg0_idx,
+- u8 *oper_centr_freq_seg1_idx)
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++ struct hostapd_channel_data *chan,
++ int secondary_channel,
++ int sec_chan_idx_80p80,
++ u8 *oper_centr_freq_seg0_idx,
++ u8 *oper_centr_freq_seg1_idx)
+ {
+ if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+ return;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 606c1b393..c2556d2d9 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -9,6 +9,12 @@
+ #ifndef DFS_H
+ #define DFS_H
+
++enum dfs_channel_type {
++ DFS_ANY_CHANNEL,
++ DFS_AVAILABLE, /* non-radar or radar-available */
++ DFS_NO_CAC_YET, /* radar-not-yet-available */
++};
++
+ int hostapd_handle_dfs(struct hostapd_iface *iface);
+
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ int center_freq);
++int dfs_find_channel(struct hostapd_iface *iface,
++ struct hostapd_channel_data **ret_chan,
++ int idx, enum dfs_channel_type type);
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++ struct hostapd_channel_data *chan,
++ int secondary_channel,
++ int sec_chan_idx_80p80,
++ u8 *oper_centr_freq_seg0_idx,
++ u8 *oper_centr_freq_seg1_idx);
+
+ #endif /* DFS_H */
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
new file mode 100644
index 0000000..ead86d5
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
@@ -0,0 +1,400 @@
+From 8291551130127914cbe3b5346fbd2edc89437df8 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 16 Dec 2022 03:57:11 +0800
+Subject: [PATCH 040/104] mtk: hostapd: Add amsdu set get ctrl
+
+---
+ hostapd/config_file.c | 9 +++
+ hostapd/ctrl_iface.c | 26 +++++++
+ hostapd/hostapd_cli.c | 9 +++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 14 ++++
+ src/ap/ap_drv_ops.h | 2 +
+ src/ap/hostapd.c | 2 +
+ src/common/mtk_vendor.h | 17 ++++-
+ src/drivers/driver.h | 9 +++
+ src/drivers/driver_nl80211.c | 114 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 13 files changed, 207 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 40ade89c0..7695ab196 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5402,6 +5402,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ u8 en = strtol(pos, NULL, 10);
+
+ conf->dfs_detect_mode = en;
++ } else if (os_strcmp(buf, "amsdu") == 0) {
++ int val = atoi(pos);
++ if (val < 0 || val > 1) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid amsdu value",
++ line);
++ return 1;
++ }
++ conf->amsdu = val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 68dcc7982..5f6278ecd 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4224,6 +4224,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
+ }
+
+
++static int
++hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ u8 amsdu;
++ int ret;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
++ hapd->iconf->amsdu = amsdu;
++ ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
++ hapd->iconf->amsdu);
++ }
++
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++
++ return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4842,6 +4866,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply, reply_size);
+ } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
+ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
++ reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 276ca578c..847f867ab 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1673,6 +1673,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
++static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1898,6 +1905,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
+ " = show iBF state (enabled/disabled)"},
++ { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
++ " = show AMSDU state"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 04e263167..2420a251e 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -308,6 +308,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ conf->ibf_enable = IBF_DEFAULT_ENABLE;
++ conf->amsdu = 1;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 7607c63e1..123e12c8f 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1289,6 +1289,7 @@ struct hostapd_config {
+ u8 three_wire_enable;
+ u8 ibf_enable;
+ u8 dfs_detect_mode;
++ u8 amsdu;
+ };
+
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 3cace58e5..23228a8d2 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1307,4 +1307,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
+ if (!hapd->driver || !hapd->driver->ibf_dump)
+ return 0;
+ return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
++
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->amsdu_ctrl)
++ return 0;
++ return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
++}
++
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
++{
++ if (!hapd->driver || !hapd->driver->amsdu_dump)
++ return 0;
++ return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+ }
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 0886acb2d..f3a044557 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -158,6 +158,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 1d941683f..a5b683676 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2705,6 +2705,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_ibf_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 9811f266e..7b4d7c11a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
+
+@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
+ };
+
++enum mtk_vendor_attr_wireless_dump {
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_rfeature_ctrl {
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index f5cff646e..6eeb9c22e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5264,6 +5264,15 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*ibf_dump)(void *priv, u8 *ibf_enable);
++
++ /**
++ * amsdu_ctrl - enable/disable amsdu
++ * amsdu_dump - get current amsdu status
++ * @priv: Private driver interface data
++ *
++ */
++ int (*amsdu_ctrl)(void *priv, u8 amsdu);
++ int (*amsdu_dump)(void *priv, u8 *amsdu);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c17052f22..eca2ff077 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14496,6 +14496,118 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int nl80211_enable_amsdu(void *priv, u8 amsdu)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_wireless_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap wireless control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *amsdu = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr_amsdu;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
++ if (!attr_amsdu ){
++ wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
++ return NL_SKIP;
++ }
++
++ *amsdu = nla_get_u8(attr_amsdu);
++
++ return NL_SKIP;
++}
++
++static int
++nl80211_dump_amsdu(void *priv, u8 *amsdu)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_wireless_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support ap_wireless control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, dump_amsdu_handler, amsdu);
++
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -14665,4 +14777,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .three_wire_ctrl = nl80211_enable_three_wire,
+ .ibf_ctrl = nl80211_ibf_enable,
+ .ibf_dump = nl80211_ibf_dump,
++ .amsdu_ctrl = nl80211_enable_amsdu,
++ .amsdu_dump = nl80211_dump_amsdu,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 1432eeda8..5aa813e26 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_mu_vendor_cmd_avail:1;
+ unsigned int mtk_3wire_vendor_cmd_avail:1;
+ unsigned int mtk_ibf_vendor_cmd_avail:1;
++ unsigned int mtk_wireless_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 615af2eb2..474d4e273 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1153,6 +1153,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
+ drv->mtk_ibf_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
++ drv->mtk_wireless_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
new file mode 100644
index 0000000..5dac957
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
@@ -0,0 +1,102 @@
+From 0fa801d52f2e29c87aef757efc690aa5b2474f1b Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Thu, 12 Jan 2023 15:18:19 +0800
+Subject: [PATCH 041/104] mtk: hostapd: Add he_ldpc configuration
+
+---
+ hostapd/config_file.c | 2 ++
+ hostapd/hostapd.conf | 5 +++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ieee802_11_he.c | 7 +++++++
+ src/common/ieee802_11_defs.h | 3 +++
+ 6 files changed, 19 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 7695ab196..dadc8f108 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -3908,6 +3908,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ conf->he_phy_capab.he_su_beamformee = atoi(pos);
+ } else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
+ conf->he_phy_capab.he_mu_beamformer = atoi(pos);
++ } else if (os_strcmp(buf, "he_ldpc") == 0) {
++ conf->he_phy_capab.he_ldpc = atoi(pos);
+ } else if (os_strcmp(buf, "he_bss_color") == 0) {
+ conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ conf->he_op.he_bss_color_disabled = 0;
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 0d10998af..f988b17b2 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -833,6 +833,11 @@ wmm_ac_vo_acm=0
+ # 1 = supported
+ #he_mu_beamformer=1
+
++#he_ldpc: HE LDPC support
++# 0 = not supported
++# 1 = supported (default)
++#he_ldpc=1
++
+ # he_bss_color: BSS color (1-63)
+ #he_bss_color=1
+
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 2420a251e..ba1b2a7a3 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -273,6 +273,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ #endif /* CONFIG_ACS */
+
+ #ifdef CONFIG_IEEE80211AX
++ conf->he_phy_capab.he_ldpc = 1;
+ conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+ HE_OPERATION_RTS_THRESHOLD_OFFSET;
+ /* Set default basic MCS/NSS set to single stream MCS 0-7 */
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 123e12c8f..d995b8d9c 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -985,6 +985,7 @@ struct hostapd_bss_config {
+ * struct he_phy_capabilities_info - HE PHY capabilities
+ */
+ struct he_phy_capabilities_info {
++ bool he_ldpc;
+ bool he_su_beamformer;
+ bool he_su_beamformee;
+ bool he_mu_beamformer;
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index a2deda6c4..3c6ee72fe 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -139,6 +139,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ os_memcpy(&cap->optional[mcs_nss_size],
+ mode->he_capab[opmode].ppet, ppet_size);
+
++ if (hapd->iface->conf->he_phy_capab.he_ldpc)
++ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
++ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++ else
++ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
++ ~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index 7a1da3252..a289c2d87 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -2452,6 +2452,9 @@ struct ieee80211_spatial_reuse {
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G ((u8) BIT(3))
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G ((u8) BIT(4))
+
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX 1
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD ((u8) BIT(5))
++
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7))
+ #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
new file mode 100644
index 0000000..8676424
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
@@ -0,0 +1,26 @@
+From d342772023b344ce09a22eaeff4307e5bfe52d7c Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Tue, 24 Jan 2023 19:06:44 +0800
+Subject: [PATCH 042/104] mtk: hostapd: Add vendor command attribute for RTS BW
+ signaling.
+
+Signed-off-by: himanshu.goyal <himanshu.goyal@mediatek.com>
+---
+ src/common/mtk_vendor.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 7b4d7c11a..ace993bc8 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -172,6 +172,7 @@ enum mtk_vendor_attr_wireless_ctrl {
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
new file mode 100644
index 0000000..304e444
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
@@ -0,0 +1,24 @@
+From 931c93d5d19249a0b4e4efbc5957f537578dfd81 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 13 Feb 2023 11:03:53 +0800
+Subject: [PATCH 043/104] mtk: hostapd: 6G band does not require DFS
+
+---
+ src/ap/dfs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index ece27d070..44f3a2cb1 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1525,6 +1525,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ !iface->conf->ieee80211h) ||
+ !iface->current_mode ||
++ is_6ghz_freq(iface->freq) ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
new file mode 100644
index 0000000..9a8d8ae
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
@@ -0,0 +1,46 @@
+From 79d4323faf5c14f9a4a8e0cf3219582210463206 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 11:01:18 +0800
+Subject: [PATCH 044/104] mtk: hostapd: Fix sending wrong VHT operation IE in
+ CSA while using ZWDFS
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 44f3a2cb1..c703d2fb8 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1129,6 +1129,14 @@ static int
+ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ {
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++ int ret;
++
++ ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
++ iface->radar_background.freq,
++ iface->radar_background.secondary_channel,
++ current_vht_oper_chwidth,
++ iface->radar_background.centr_freq_seg0_idx,
++ iface->radar_background.centr_freq_seg1_idx);
+
+ iface->conf->channel = iface->radar_background.channel;
+ iface->freq = iface->radar_background.freq;
+@@ -1141,11 +1149,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+
+ hostapd_dfs_update_background_chain(iface);
+
+- return hostapd_dfs_request_channel_switch(
+- iface, iface->conf->channel, iface->freq,
+- iface->conf->secondary_channel, current_vht_oper_chwidth,
+- hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+- hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
++ return ret;
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
new file mode 100644
index 0000000..5ef55c0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
@@ -0,0 +1,189 @@
+From cc4ace547c0b0ebef084e9634f729267b6c89f08 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 10:51:47 +0800
+Subject: [PATCH 045/104] mtk: hostapd: Add sta-assisted DFS state update
+ mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 20 ++++++++++++++++++++
+ src/ap/dfs.h | 3 +++
+ src/ap/drv_callbacks.c | 28 ++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.h | 1 +
+ src/drivers/driver.h | 14 ++++++++++++++
+ src/drivers/driver_nl80211_event.c | 6 ++++++
+ src/drivers/nl80211_copy.h | 6 ++++++
+ 7 files changed, 78 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index c703d2fb8..3e036441b 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1522,6 +1522,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ }
+
+
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, u32 state)
++{
++ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
++ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
++ freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
++ (state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
++
++ /* Proceed only if DFS is not offloaded to the driver */
++ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
++ return 0;
++
++ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
++ cf1, cf2, state);
++
++ return 0;
++}
++
++
+ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ {
+ int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index c2556d2d9..25ba29ca1 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, u32 state);
+ int hostapd_is_dfs_required(struct hostapd_iface *iface);
+ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index e8796f709..caa171474 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2226,6 +2226,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ radar->cf1, radar->cf2);
+ }
+
++static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
++ struct dfs_event *radar)
++{
++ wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
++ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++ radar->chan_offset, radar->chan_width,
++ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
++}
++
++static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
++ struct dfs_event *radar)
++{
++ wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
++ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++ radar->chan_offset, radar->chan_width,
++ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
++}
++
+ #endif /* NEED_AP_MLME */
+
+
+@@ -2592,6 +2610,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ break;
++ case EVENT_DFS_STA_CAC_SKIPPED:
++ if (!data)
++ break;
++ hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
++ break;
++ case EVENT_DFS_STA_CAC_EXPIRED:
++ if (!data)
++ break;
++ hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
++ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ /* channel list changed (regulatory?), update channel list */
+ /* TODO: check this. hostapd_get_hw_features() initializes
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index c5bb9abd7..88ad54d6f 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -383,6 +383,7 @@ extern "C" {
+ #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+ #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+ #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
++#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
+
+ #define AP_CSA_FINISHED "AP-CSA-FINISHED "
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 6eeb9c22e..dbd0137ac 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5900,6 +5900,20 @@ enum wpa_event_type {
+ * EVENT_LINK_RECONFIG - Notification that AP links removed
+ */
+ EVENT_LINK_RECONFIG,
++
++ /**
++ * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
++ *
++ * The channel in the notification is now marked as available.
++ */
++ EVENT_DFS_STA_CAC_SKIPPED,
++
++ /**
++ * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
++ *
++ * The channel in the notification is now marked as usable.
++ */
++ EVENT_DFS_STA_CAC_EXPIRED,
+ };
+
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 4a12d749c..7889930a0 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ case NL80211_RADAR_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
++ case NL80211_RADAR_STA_CAC_SKIPPED:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
++ break;
++ case NL80211_RADAR_STA_CAC_EXPIRED:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
++ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+ "received", event_type);
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index dced2c49d..8917d565b 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ * when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ * when STA is disconnected or leaving the channel
+ */
+ enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+@@ -6707,6 +6711,8 @@ enum nl80211_radar_event {
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
++ NL80211_RADAR_STA_CAC_SKIPPED,
++ NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+
+ /**
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
new file mode 100644
index 0000000..7d905c5
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
@@ -0,0 +1,60 @@
+From 21476005ab6b517c7765fec7c3d6c3383c5f44f4 Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Fri, 3 Mar 2023 12:45:42 +0800
+Subject: [PATCH 046/104] mtk: hostapd: Mark DFS channel as available for CSA.
+
+---
+ hostapd/ctrl_iface.c | 10 ++++++++++
+ hostapd/hostapd_cli.c | 2 +-
+ src/ap/ctrl_iface_ap.c | 1 +
+ 3 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 5f6278ecd..052588da4 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2776,6 +2776,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ break;
+ }
+
++ if (settings.freq_params.radar_background) {
++ hostapd_dfs_sta_update_state(iface,
++ settings.freq_params.freq,
++ settings.freq_params.ht_enabled,
++ settings.freq_params.sec_channel_offset,
++ bandwidth, settings.freq_params.center_freq1,
++ settings.freq_params.center_freq2,
++ HOSTAPD_CHAN_DFS_AVAILABLE);
++ }
++
+ if (settings.freq_params.center_freq1)
+ dfs_range += hostapd_is_dfs_overlap(
+ iface, bandwidth, settings.freq_params.center_freq1);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 847f867ab..da9c0f931 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1775,7 +1775,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<addr> = send QoS Map Configure frame" },
+ { "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
+ "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
+- " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
++ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
+ " = initiate channel switch announcement" },
+ { "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
+ "<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index a2f89260c..b92311e32 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1117,6 +1117,7 @@ int hostapd_parse_csa_settings(const char *pos,
+ settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+ settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
++ settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
+ settings->block_tx = !!os_strstr(pos, " blocktx");
+ #undef SET_CSA_SETTING
+ #undef SET_CSA_SETTING_EXT
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
new file mode 100644
index 0000000..9f3c55b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
@@ -0,0 +1,476 @@
+From 50ceb0f2eb9b542ab115ed79fd2d68d46e9e03a0 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Thu, 26 Jan 2023 09:16:00 +0800
+Subject: [PATCH 047/104] mtk: hostapd: Add available color bitmap
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 74 +++++++++++
+ hostapd/hostapd_cli.c | 18 +++
+ src/ap/ap_drv_ops.c | 10 +-
+ src/ap/ap_drv_ops.h | 2 +
+ src/common/mtk_vendor.h | 11 ++
+ src/drivers/driver.h | 8 ++
+ src/drivers/driver_nl80211.c | 198 +++++++++++++++++++++++++++++-
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 9 files changed, 323 insertions(+), 2 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 052588da4..7b83bdd4f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4257,6 +4257,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
+ return ret;
+ }
+
++static int
++hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ int ret;
++ char *pos, *end;
++ int i;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hapd->iface->conf->he_op.he_bss_color_disabled)
++ ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
++ else
++ ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
++ hapd->iface->conf->he_op.he_bss_color);
++
++ pos += ret;
++
++ return pos - buf;
++}
++
++
++static int
++hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ int ret;
++ char *pos, *end;
++ int i;
++ u64 aval_color_bmp = 0;
++
++ hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
++ hapd->color_collision_bitmap = ~aval_color_bmp;
++
++ pos = buf;
++ end = buf + buflen;
++
++ ret = os_snprintf(buf, buflen,
++ "available color bitmap=0x%llx\n",
++ aval_color_bmp);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
++ int bit = !!((aval_color_bmp >> i) & 1LLU);
++
++ if (i % 8 == 0) {
++ ret = os_snprintf(pos, end - pos, "%2d: ", i);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "%d ", bit);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ if (i % 8 == 7) {
++ ret = os_snprintf(pos, end - pos, "\n");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++ }
++ return pos - buf;
++}
++
+
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+@@ -4878,6 +4948,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
++ reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
++ reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index da9c0f931..865c11432 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1658,6 +1658,20 @@ static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc,
+ #endif /* CONFIG_IEEE80211R_AP */
+
+
++static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
++}
++
++
++static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
++}
++
++
+ #ifdef ANDROID
+ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+@@ -1897,6 +1911,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ { "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL,
+ "= get R0KHs and R1KHs" },
+ #endif /* CONFIG_IEEE80211R_AP */
++ { "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
++ "= get current BSS color" },
++ { "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
++ "= get available BSS color bitmap" },
+ #ifdef ANDROID
+ { "driver", hostapd_cli_cmd_driver, NULL,
+ "<driver sub command> [<hex formatted data>] = send driver command data" },
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 23228a8d2..cabcd47af 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1321,4 +1321,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
+ if (!hapd->driver || !hapd->driver->amsdu_dump)
+ return 0;
+ return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+-}
+\ No newline at end of file
++}
++
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
++{
++ if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
++ hapd->iface->conf->he_op.he_bss_color_disabled)
++ return 0;
++ return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f3a044557..9da2b0049 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -160,6 +160,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
++ u64 *aval_color_bmp);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index ace993bc8..e27fe69b3 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
++ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -256,6 +257,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+ };
+
++enum mtk_vendor_attr_bss_color_ctrl {
++ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
++ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
++};
+
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dbd0137ac..6b6317bfa 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5273,6 +5273,14 @@ struct wpa_driver_ops {
+ */
+ int (*amsdu_ctrl)(void *priv, u8 amsdu);
+ int (*amsdu_dump)(void *priv, u8 *amsdu);
++
++ /**
++ * get_aval_color_bmp - get available BSS color bitmap
++ * @priv: Private driver interface data
++ * @aval_color_bmp: available bss color bitmap
++ *
++ */
++ int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index eca2ff077..4c98e8ab3 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13153,7 +13153,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+ num, MAC2STR(candidate->bssid), buf);
+ }
+
+-
+ static int
+ nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -14608,6 +14607,202 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
++{
++ u64 *aval_color_bmp = arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ static const struct nla_policy
++ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
++ [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
++ };
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ *aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
++
++ return 0;
++}
++
++static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *attr;
++ int ret;
++
++ if (!drv->mtk_bss_color_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support BSS COLOR vendor cmd");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
++ return -ENOBUFS;
++
++ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!attr) {
++ nlmsg_free(msg);
++ return -1;
++ }
++
++ nla_nest_end(msg, attr);
++
++ ret = send_and_recv_resp(drv, msg, nl80211_get_aval_color_bmp_handler, aval_color_bmp);
++
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
++static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_wireless_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap wireless control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
++ nla_put_u16(msg, sub_vendor_id, (u16) value);
++ else
++ nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap rfeatures control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data, *data2;
++ int ret;
++
++ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap rfeatures control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
++ if (!data2)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
++ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
++
++ nla_nest_end(msg, data2);
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -14779,4 +14974,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .ibf_dump = nl80211_ibf_dump,
+ .amsdu_ctrl = nl80211_enable_amsdu,
+ .amsdu_dump = nl80211_dump_amsdu,
++ .get_aval_color_bmp = nl80211_get_aval_color_bmp,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5aa813e26..5b4d45567 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_3wire_vendor_cmd_avail:1;
+ unsigned int mtk_ibf_vendor_cmd_avail:1;
+ unsigned int mtk_wireless_vendor_cmd_avail:1;
++ unsigned int mtk_bss_color_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 474d4e273..a7df2d172 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1156,6 +1156,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
+ drv->mtk_wireless_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
++ drv->mtk_bss_color_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
new file mode 100644
index 0000000..e6f30ed
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
@@ -0,0 +1,210 @@
+From 1b8fc72bfd653ce3ef422e86617b1821948f4805 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Mar 2023 16:08:30 +0800
+Subject: [PATCH 048/104] mtk: hostapd: Fix ZWDFS issue in BW 160
+
+When background radar is enabled and bandwidth is set to 160, AP will
+fail to startup due to the lack of non-DFS channel.
+Under this circumstance, AP should perform CAC itself, and the background
+chain could also perform CAC simultaneously.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 79 insertions(+), 19 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 3e036441b..f5794753e 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -69,15 +69,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ enum dfs_channel_type type)
+ {
++ int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
++
++ if (chan->flag & HOSTAPD_CHAN_DISABLED)
++ return -1;
++
+ if (type == DFS_NO_CAC_YET) {
+ /* 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)
++ if (!(chan->flag & HOSTAPD_CHAN_RADAR))
++ return 0;
++
++ if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
+ return 1;
+- return 0;
++
++ return -1;
+ }
+
+ /*
+@@ -86,16 +93,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ * channel for CSA, unless they are available for immediate use.
+ */
+ if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+- HOSTAPD_CHAN_DFS_AVAILABLE))
+- return 0;
++ (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
++ return -1;
+
+- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+- return 0;
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+- HOSTAPD_CHAN_DFS_UNAVAILABLE))
+- return 0;
++ ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
++ (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
++ return -1;
++
+ return 1;
+ }
+
+@@ -167,7 +172,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ enum dfs_channel_type type)
+ {
+ struct hostapd_channel_data *first_chan, *chan;
+- int i;
++ int i, available = 0, ret = 0;
+ u32 bw = num_chan_to_bw(num_chans);
+
+ if (first_chan_idx + num_chans > mode->num_channels) {
+@@ -203,14 +208,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ return 0;
+ }
+
+- if (!dfs_channel_available(chan, type)) {
++ ret = dfs_channel_available(chan, type);
++ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
+ first_chan->freq + i * 20);
+ return 0;
+ }
++
++ available |= ret;
+ }
+
+- return 1;
++ return available;
+ }
+
+
+@@ -838,8 +846,12 @@ 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 sec = 0, skip_radar = 0;
++ u8 cf1 = 0, cf2 = 0;
++ bool use_radar_background = dfs_use_radar_background(iface);
++ enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+
+ if (is_6ghz_freq(iface->freq))
+ return 1;
+@@ -902,7 +914,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ /* Finally start CAC */
+ hostapd_set_state(iface, HAPD_IFACE_DFS);
+ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+- dfs_use_radar_background(iface) ? " (background)" : "");
++ use_radar_background ? " (background)" : "");
+ 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,
+@@ -912,6 +924,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+ iface->dfs_cac_ms / 1000);
+
++ if (use_radar_background) {
++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
++ /*
++ * AP cannot get any random available channel.
++ * Let AP and dedicated radar chain both perform CAC.
++ */
++ if (!channel)
++ use_radar_background = false;
++ }
++
+ res = hostapd_start_dfs_cac(
+ iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+ iface->conf->ieee80211n, iface->conf->ieee80211ac,
+@@ -920,14 +942,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ hostapd_get_oper_chwidth(iface->conf),
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+- dfs_use_radar_background(iface));
++ use_radar_background);
+
+ if (res) {
+ wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+ return -1;
+ }
+
+- if (dfs_use_radar_background(iface)) {
++ if (use_radar_background) {
+ /* Cache background radar parameters. */
+ iface->radar_background.channel = iface->conf->channel;
+ iface->radar_background.secondary_channel =
+@@ -948,6 +970,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+
+ iface->radar_background.temp_ch = 1;
+ return 1;
++ } else if (dfs_use_radar_background(iface)) {
++ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
++ channel_type = DFS_ANY_CHANNEL;
++
++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
++
++ if (!channel ||
++ (channel->chan == iface->conf->channel &&
++ cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
++ cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
++ wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
++ iface->radar_background.channel = -1;
++ return 0;
++ }
++
++ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++ channel->freq, channel->chan,
++ iface->conf->ieee80211n,
++ iface->conf->ieee80211ac,
++ iface->conf->ieee80211ax,
++ iface->conf->ieee80211be,
++ sec, hostapd_get_oper_chwidth(iface->conf),
++ cf1, cf2, true);
++
++ iface->radar_background.channel = channel->chan;
++ iface->radar_background.freq = channel->freq;
++ iface->radar_background.secondary_channel = sec;
++ iface->radar_background.centr_freq_seg0_idx = cf1;
++ iface->radar_background.centr_freq_seg1_idx = cf2;
+ }
+
+ return 0;
+@@ -1204,6 +1255,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ hostapd_setup_interface_complete(iface, 0);
+ iface->cac_started = 0;
+ }
++
++ /*
++ * When background radar is enabled but the CAC completion
++ * is not received from the background chain.
++ * Then, reset radar background chain.
++ */
++ if (dfs_use_radar_background(iface) &&
++ iface->radar_background.channel == -1)
++ hostapd_dfs_update_background_chain(iface);
+ }
+ } else if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
new file mode 100644
index 0000000..8b21535
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
@@ -0,0 +1,396 @@
+From b9b137827e9c0584682606bd5fe1cd9f50635819 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 17 Mar 2023 16:17:14 +0800
+Subject: [PATCH 049/104] mtk: hostapd: Add vendor for CAPI certification
+ commands
+
+---
+ hostapd/ctrl_iface.c | 99 +++++++++++++++++++++++++++++++
+ src/ap/ap_drv_ops.c | 21 +++++++
+ src/ap/ap_drv_ops.h | 3 +
+ src/common/mtk_vendor.h | 33 +----------
+ src/drivers/driver.h | 22 +++++++
+ src/drivers/driver_nl80211.c | 55 +++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 8 files changed, 206 insertions(+), 31 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 7b83bdd4f..1154a2394 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -71,6 +71,7 @@
+ #include "config_file.h"
+ #include "ctrl_iface.h"
+
++#include "common/mtk_vendor.h"
+
+ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+
+@@ -4327,6 +4328,100 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
+ return pos - buf;
+ }
+
++static int
++hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *value, *config = cmd;
++ enum mtk_vendor_attr_wireless_ctrl sub_cmd;
++
++ pos = os_strchr(config, '=');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if(pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strncmp(config, "fixed_mcs", 9) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
++ else if (os_strncmp(config, "ofdma", 5) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
++ else if (os_strncmp(config, "ppdu_type", 9) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
++ else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
++ else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
++ else if (os_strncmp(config, "mimo", 4) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
++ else if (os_strncmp(config, "cert", 4) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
++ else if (os_strncmp(config, "amsdu", 5) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
++ else if (os_strncmp(config, "rts_sigta", 9) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
++ else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for ap_wireless", config);
++ return -1;
++ }
++
++ if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
++ return -1;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *value, *type, *config = cmd;
++ enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
++
++ pos = os_strchr(config, '=');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if(pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strncmp(config, "he_gi", 5) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
++ else if (os_strncmp(config, "he_ltf", 6) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
++ else if (os_strncmp(config, "trig_type", 9) == 0) {
++ pos = os_strchr(value, ',');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++ if(pos == NULL)
++ return -1;
++ type = pos;
++ goto trigtype;
++ } else if (os_strcmp(config, "ack_policy") == 0)
++ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
++ else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for ap_rfeatures", config);
++ return -1;
++ }
++
++ if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
++ return -1;
++ goto exit;
++
++trigtype:
++ if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
++ return -1;
++
++exit:
++ return os_snprintf(buf, buflen, "OK\n");
++}
+
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+@@ -4952,6 +5047,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
+ reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
++ reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
++ } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
++ reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index cabcd47af..06d71f309 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1330,3 +1330,24 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ return 0;
+ return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
+ }
++
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++ if (!hapd->driver || !hapd->driver->ap_wireless)
++ return 0;
++ return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++ if (!hapd->driver || !hapd->driver->ap_rfeatures)
++ return 0;
++ return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
++{
++ if (!hapd->driver || !hapd->driver->ap_trigtype)
++ return 0;
++ return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 9da2b0049..c58930217 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -162,6 +162,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
+ u64 *aval_color_bmp);
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index e27fe69b3..0b23c76ad 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+
+-
+-static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_3wire_ctrl {
+ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
+
+@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
+ };
+
+-static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
+- [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_csi_ctrl {
+ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+
+@@ -172,7 +157,7 @@ enum mtk_vendor_attr_wireless_ctrl {
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+
+ /* keep last */
+@@ -192,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
+ };
+
+-static const struct nla_policy
+-wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
+- [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_rfeature_ctrl {
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+
+@@ -206,6 +186,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+@@ -247,16 +228,6 @@ enum mtk_vendor_attr_ibf_dump {
+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
+ };
+
+-static struct nla_policy
+-ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+- [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
+-};
+-
+-static struct nla_policy
+-ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+- [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_bss_color_ctrl {
+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 6b6317bfa..a25601c91 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5281,6 +5281,28 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
++
++ /**
++ * ap_wireless - set wireless command
++ * @priv: Private driver interface data
++ * @value: value
++ */
++ int (*ap_wireless)(void *priv, u8 mode, int value);
++
++ /**
++ * ap_rfeatures - set ap rf features command
++ * @priv: Private driver interface data
++ * @value: value
++ */
++ int (*ap_rfeatures)(void *priv, u8 mode, int value);
++
++ /**
++ * ap_trigtype - set trigger type
++ * @priv: Private driver interface data
++ * @enable: enable or disable
++ * @type: trigger type
++ */
++ int (*ap_trigtype)(void *priv, u8 enable, u8 type);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 4c98e8ab3..86e5844cd 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -91,6 +91,58 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
+ wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
+ }
+
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -14975,4 +15027,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .amsdu_ctrl = nl80211_enable_amsdu,
+ .amsdu_dump = nl80211_dump_amsdu,
+ .get_aval_color_bmp = nl80211_get_aval_color_bmp,
++ .ap_wireless = nl80211_ap_wireless,
++ .ap_rfeatures = nl80211_ap_rfeatures,
++ .ap_trigtype = nl80211_ap_trigtype,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5b4d45567..046991a3d 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_ibf_vendor_cmd_avail:1;
+ unsigned int mtk_wireless_vendor_cmd_avail:1;
+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
++ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index a7df2d172..6498eba6d 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1159,6 +1159,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ drv->mtk_bss_color_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
++ drv->mtk_rfeatures_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
new file mode 100644
index 0000000..aecb14a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
@@ -0,0 +1,506 @@
+From 994774c363a07fa90a7a21974b7b4a371b235673 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:18:48 +0800
+Subject: [PATCH 050/104] mtk: hostapd: Air Monitor support in hostapd by
+ vendor
+
+Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 113 +++++++++++++++++++
+ hostapd/hostapd_cli.c | 15 +++
+ src/ap/ap_drv_ops.c | 14 +++
+ src/ap/ap_drv_ops.h | 3 +
+ src/common/mtk_vendor.h | 8 ++
+ src/drivers/driver.h | 16 +++
+ src/drivers/driver_nl80211.c | 179 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 2 +
+ 9 files changed, 351 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 1154a2394..56722384b 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4370,6 +4370,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
+
+ if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
+ return -1;
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp, sta_mac[ETH_ALEN] = {0};
++ int amnt_idx = 0;
++
++ tmp = strtok_r(cmd, " ", &cmd);
++
++ if (!tmp) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ amnt_idx = strtol(tmp, &tmp, 10);
++
++ if (amnt_idx < 0 || amnt_idx > 15) {
++ wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
++ return -1;
++ }
++
++ if (!cmd) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ if (hwaddr_aton(cmd, sta_mac) < 0) {
++ wpa_printf(MSG_ERROR, "station mac is not right.\n");
++ return -1;
++ }
++
++ if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
++ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++ return -1;
++ }
+
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+@@ -4423,6 +4461,75 @@ exit:
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
++static int
++hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp;
++ int amnt_idx = 0, ret = 0;
++ struct amnt_resp_data *resp_buf;
++ char *pos, *end;
++ struct amnt_data *res;
++
++ pos = buf;
++ end = buf + buflen;
++
++ tmp = strtok_r(cmd, " ", &cmd);
++
++ if (!tmp) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ amnt_idx = strtoul(tmp, &tmp, 0);
++
++ if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
++ wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
++ return -1;
++ }
++
++ if (amnt_idx == 0xff)
++ resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
++ * sizeof(struct amnt_data) + 1);
++ else
++ resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
++
++ if (resp_buf == NULL) {
++ wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++ return -1;
++ }
++
++ if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
++ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++ os_free(resp_buf);
++ return -1;
++ }
++
++ for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
++ res = &resp_buf->resp_data[i];
++ ret = os_snprintf(pos, end - pos,
++ "[hostapd_cli] amnt_idx: %d, addr="MACSTR
++ ", rssi=%d/%d/%d/%d, last_seen=%u\n",
++ res->idx,
++ MAC2STR(res->addr), res->rssi[0],
++ res->rssi[1], res->rssi[2],
++ res->rssi[3], res->last_seen);
++ if (os_snprintf_error(end - pos, ret)) {
++ os_free(resp_buf);
++ return 0;
++ }
++ pos = pos + ret;
++ }
++
++ os_free(resp_buf);
++
++ if (pos == buf)
++ return os_snprintf(buf, buflen, "Index %d is not monitored\n",
++ amnt_idx);
++ else
++ return pos - buf;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5051,6 +5158,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
+ } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
+ reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
++ } else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
++ reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
++ reply, reply_size);
++ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
++ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 865c11432..12c580455 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1693,6 +1693,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
+ return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
+ }
+
++static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
++}
+
+ struct hostapd_cli_cmd {
+ const char *cmd;
+@@ -1925,6 +1936,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = show iBF state (enabled/disabled)"},
+ { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
+ " = show AMSDU state"},
++ { "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
++ " = Set Station index and mac to monitor"},
++ { "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
++ " = Dump RSSI of monitoring Station"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 06d71f309..df652b12f 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1351,3 +1351,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
+ return 0;
+ return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
+ }
++
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++ if (!hapd->driver || !hapd->driver->amnt_set)
++ return 0;
++ return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
++}
++
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
++{
++ if (!hapd->driver || !hapd->driver->amnt_dump)
++ return 0;
++ return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index c58930217..4805a2e84 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -166,6 +166,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++
+ #include "drivers/driver.h"
+
+ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 0b23c76ad..dd1ca2164 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -258,10 +258,18 @@ struct csi_data {
+ u16 rx_idx;
+ };
+
++#define AIR_MONITOR_MAX_ENTRY 16
++
+ struct amnt_data {
+ u8 idx;
+ u8 addr[ETH_ALEN];
+ s8 rssi[4];
+ u32 last_seen;
+ };
++
++struct amnt_resp_data {
++ u8 sta_num;
++ struct amnt_data resp_data[0];
++};
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index a25601c91..dd9c33201 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5303,6 +5303,22 @@ struct wpa_driver_ops {
+ * @type: trigger type
+ */
+ int (*ap_trigtype)(void *priv, u8 enable, u8 type);
++
++ /**
++ * amnt_set - add/delete station from monitoring
++ * @priv: Private driver interface data
++ * @amnt_idx: Monitor Index
++ * @amnt_sta_mac: station mac address
++ */
++ int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
++
++ /**
++ * amnt_dump - Dump particular/ all station
++ * @priv: Private driver interface data
++ * @amnt_idx: Monitor Index
++ * @amnt_dump_buf: Buffer to print
++ */
++ int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 86e5844cd..a2a6807f4 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -143,6 +143,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
+ };
+
++static struct nla_policy
++amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
++ [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy
++amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
++ [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -14855,6 +14868,170 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int
++nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ void *tb1;
++ int ret;
++
++ if (!drv->mtk_amnt_vendor_cmd_avail) {
++ wpa_printf(MSG_ERROR,
++ "nl80211: Driver does not support air monitor");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
++ if (!tb1)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
++
++ nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
++
++ nla_nest_end(msg, tb1);
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
++ ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++
++}
++
++static int
++mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
++{
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
++ struct nlattr *attr, *cur, *data;
++ struct amnt_data *res;
++ int len = 0, rem;
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ attr = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!attr)
++ return NL_SKIP;
++
++ nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++ attr, amnt_ctrl_policy);
++
++ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
++ return NL_SKIP;
++
++ nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
++
++ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
++ return NL_SKIP;
++
++ len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
++ if (!len)
++ return 0;
++
++ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
++ return NL_SKIP;
++
++ data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
++
++ nla_for_each_nested(cur, data, rem) {
++ if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
++ return NL_SKIP;
++ res = (struct amnt_data *) nla_data(cur);
++ wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
++ "addr="MACSTR", "
++ "rssi=%d/%d/%d/%d, last_seen=%u\n",
++ res->idx,
++ MAC2STR(res->addr),
++ res->rssi[0], res->rssi[1], res->rssi[2],
++ res->rssi[3], res->last_seen);
++ os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
++ sizeof(struct amnt_data));
++ amnt_dump->sta_num++;
++ }
++ return 0;
++}
++
++static int
++nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ void *tb1;
++ int ret;
++
++ if (!drv->mtk_amnt_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support air monitor");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
++ | NLA_F_NESTED);
++ if (!tb1)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
++
++ nla_nest_end(msg, tb1);
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, mt76_amnt_dump_cb, dump_buf);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
++ , ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15030,4 +15207,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .ap_wireless = nl80211_ap_wireless,
+ .ap_rfeatures = nl80211_ap_rfeatures,
+ .ap_trigtype = nl80211_ap_trigtype,
++ .amnt_set = nl80211_amnt_set,
++ .amnt_dump = nl80211_amnt_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 046991a3d..adc1b9bf7 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_wireless_vendor_cmd_avail:1;
+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
++ unsigned int mtk_amnt_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 6498eba6d..38e83e42b 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1158,6 +1158,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ break;
+ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ drv->mtk_bss_color_vendor_cmd_avail = 1;
++ case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
++ drv->mtk_amnt_vendor_cmd_avail = 1;
+ break;
+ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ drv->mtk_rfeatures_vendor_cmd_avail = 1;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
new file mode 100644
index 0000000..85e1f7a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
@@ -0,0 +1,228 @@
+From ccef6202191f2a17f84f021e6e2ade206b8c9cc1 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:24:19 +0800
+Subject: [PATCH 051/104] mtk: hostapd: Add muru user number debug command
+
+---
+ hostapd/ctrl_iface.c | 13 ++++++++++++-
+ src/ap/ap_drv_ops.c | 4 ++--
+ src/ap/ap_drv_ops.h | 2 +-
+ src/ap/hostapd.c | 3 ++-
+ src/common/mtk_vendor.h | 7 +++++++
+ src/drivers/driver.h | 4 ++--
+ src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
+ 7 files changed, 55 insertions(+), 15 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 56722384b..88475b321 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3673,6 +3673,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+ {
+ char *pos, *config, *value;
++ u8 mode;
++
+ config = cmd;
+ pos = os_strchr(config, ' ');
+ if (pos == NULL)
+@@ -4065,6 +4067,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+ {
+ char *pos, *config, *value;
++ u8 mode;
++
+ config = cmd;
+ pos = os_strchr(config, ' ');
+ if (pos == NULL)
+@@ -4082,13 +4086,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ return -1;
+ }
+ hapd->iconf->mu_onoff = (u8) mu;
++ mode = MU_CTRL_ONOFF;
++ } else if (os_strcmp(config, "ul_user_cnt") == 0) {
++ mode = MU_CTRL_UL_USER_CNT;
++ wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
++ } else if (os_strcmp(config, "dl_user_cnt") == 0) {
++ mode = MU_CTRL_DL_USER_CNT;
++ wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Unsupported parameter %s for SET_MU", config);
+ return -1;
+ }
+
+- if(hostapd_drv_mu_ctrl(hapd) == 0) {
++ if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+ return os_snprintf(buf, buflen, "OK\n");
+ } else {
+ return -1;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index df652b12f..8878db380 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
+ {
+ if (!hapd->driver || !hapd->driver->mu_ctrl)
+ return 0;
+- return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
+ }
+
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 4805a2e84..f77d07da0 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index a5b683676..5fd46d53d 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -58,6 +58,7 @@
+ #include "wpa_auth_kay.h"
+ #include "hw_features.h"
+
++#include "common/mtk_vendor.h"
+
+ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
+ #ifdef CONFIG_WEP
+@@ -2699,7 +2700,7 @@ dfs_offload:
+ if (hostapd_drv_configure_edcca_threshold(hapd,
+ hapd->iconf->edcca_threshold) < 0)
+ goto fail;
+- if (hostapd_drv_mu_ctrl(hapd) < 0)
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
+ goto fail;
+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index dd1ca2164..99371bf73 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -199,6 +199,8 @@ enum mtk_vendor_attr_mu_ctrl {
+
+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
++ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -272,4 +274,9 @@ struct amnt_resp_data {
+ struct amnt_data resp_data[0];
+ };
+
++enum {
++ MU_CTRL_ONOFF,
++ MU_CTRL_DL_USER_CNT,
++ MU_CTRL_UL_USER_CNT,
++};
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dd9c33201..3be4562e7 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5236,11 +5236,11 @@ struct wpa_driver_ops {
+ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+
+ /**
+- * mu_ctrl - ctrl on off for UL/DL MURU
++ * mu_ctrl - ctrl for UL/DL MURU
+ * @priv: Private driver interface data
+ *
+ */
+- int (*mu_ctrl)(void *priv, u8 mu_onoff);
++ int (*mu_ctrl)(void *priv, u8 mode, u8 val);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index a2a6807f4..035a477e2 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13982,13 +13982,13 @@ fail:
+
+
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
+- int ret;
++ int ret = -ENOBUFS;
+
+ if (!drv->mtk_mu_vendor_cmd_avail) {
+ wpa_printf(MSG_INFO,
+@@ -13999,17 +13999,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
+- !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
+- nlmsg_free(msg);
+- return -ENOBUFS;
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
++ goto fail;
++
++ switch (mode) {
++ case MU_CTRL_ONOFF:
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
++ goto fail;
++ break;
++ case MU_CTRL_UL_USER_CNT:
++ case MU_CTRL_DL_USER_CNT:
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
++ goto fail;
++ break;
++ default:
++ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++ ret = -EINVAL;
++ goto fail;
+ }
++
+ nla_nest_end(msg, data);
++
+ ret = send_and_recv_cmd(drv, msg);
+ if(ret){
+- wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++ wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+ }
+ return ret;
++
++fail:
++ nl80211_nlmsg_clear(msg);
++ nlmsg_free(msg);
++ return ret;
+ }
+
+
+@@ -15178,7 +15199,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .update_connect_params = nl80211_update_connection_params,
+ .send_external_auth_status = nl80211_send_external_auth_status,
+ .set_4addr_mode = nl80211_set_4addr_mode,
+- .mu_ctrl = nl80211_mu_onoff,
++ .mu_ctrl = nl80211_mu_ctrl,
+ .mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ .dpp_listen = nl80211_dpp_listen,
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
new file mode 100644
index 0000000..757c28b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
@@ -0,0 +1,647 @@
+From e64468ed944634f41f7f305e7460b6a696b00127 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Sat, 3 Jun 2023 17:12:15 +0800
+Subject: [PATCH 052/104] mtk: hostapd: add connac3 PHY MURU manual mode config
+ support
+
+This commit supports read the following two formats to set MU/RU manual
+mode:
+1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
+2. hostapd_cli -i <intf> set_mu <field> <value>
+
+For the <field>, we support the following field:
+1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
+2. ul_comm_bw/dl_comm_bw: set the bandwith
+3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
+allocate idx
+4. ul_user_mcs/dl_user_mcs: set the mcs for each user
+5. ul_user_ssAlloc_raru: set the number of ss for each user
+6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
+7. dl_comm_toneplan: fix ru toneplan allocation
+8. dl_comm_ack_policy: fix station ack policy
+9. update : trigger driver to send mcu command to set muru manual mode.
+
+For the value of each field, please check wiki to learn the details:
+https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
+
+For the fields that mt76 support to use, we will update in this wiki:
+https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
+
+Please noted that this commit is only for connac 3 gen chips. If this
+feature is to be used in other generations, the following actions must
+be taken:
+1. Different data structue needs to be defined for different
+generations, e.g. connac4_muru_comm, connac4_muru_dl.
+2. hostapd_ctrl_iface_set_mu() shall be modified.
+3. A new code level configuration shall be defined to differentiate the
+code flow that different generations will go through.
+---
+ hostapd/ctrl_iface.c | 237 +++++++++++++++++++++++++++++++----
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 4 +-
+ src/ap/ap_drv_ops.h | 2 +-
+ src/ap/hostapd.c | 2 +-
+ src/common/mtk_vendor.h | 166 +++++++++++++++++++++++-
+ src/drivers/driver.h | 2 +-
+ src/drivers/driver_nl80211.c | 21 ++--
+ 8 files changed, 391 insertions(+), 44 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 88475b321..ed383df7d 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3775,7 +3775,6 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
+ }
+ }
+
+-
+ #ifdef CONFIG_NAN_USD
+
+ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
+@@ -4062,21 +4061,61 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+
+
++static int
++hostapd_parse_argument_helper(char *value, u16 **ptr_input)
++{
++#define MAX_MU_CTRL_NUM 17
++ u16 *input;
++ char *endptr;
++ int cnt = 0;
++
++ input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
++ if (input == NULL) {
++ wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
++ return -1;
++ }
++ while (value) {
++ u8 val = strtol(value, &endptr, 10);
++
++ if (value != endptr) {
++ input[cnt++] = val;
++ value = os_strchr(endptr, ':');
++ if (value)
++ value++;
++ } else {
++ break;
++ }
++ }
++
++ *ptr_input = input;
++ return cnt;
++}
++
++#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do { \
++ if ((le_to_host32(_val) & (_mask)) != _mask) { \
++ wpa_printf(MSG_ERROR, "Set %s first\n", #_mask); \
++ goto fail; \
++ } \
++ } while(0)
++
+ static int
+ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+- char *buf, size_t buflen)
++ char *buf, size_t buflen)
+ {
+ char *pos, *config, *value;
+- u8 mode;
++ u8 i;
++ int cnt = 0, ret;
++ u16 *val;
++ struct connac3_muru *muru;
++ struct connac3_muru_dl *dl;
++ struct connac3_muru_ul *ul;
++ struct connac3_muru_comm *comm;
+
+ config = cmd;
+ pos = os_strchr(config, ' ');
+- if (pos == NULL)
+- return -1;
+- *pos++ = '\0';
++ if (pos != NULL)
++ *pos++ = '\0';
+
+- if(pos == NULL)
+- return -1;
+ value = pos;
+
+ if (os_strcmp(config, "onoff") == 0) {
+@@ -4086,24 +4125,167 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ return -1;
+ }
+ hapd->iconf->mu_onoff = (u8) mu;
+- mode = MU_CTRL_ONOFF;
+- } else if (os_strcmp(config, "ul_user_cnt") == 0) {
+- mode = MU_CTRL_UL_USER_CNT;
+- wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
+- } else if (os_strcmp(config, "dl_user_cnt") == 0) {
+- mode = MU_CTRL_DL_USER_CNT;
+- wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+- } else {
+- wpa_printf(MSG_ERROR,
+- "Unsupported parameter %s for SET_MU", config);
+- return -1;
++
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
++ return os_snprintf(buf, buflen, "OK\n");
++ else
++ goto fail;
+ }
+
+- if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+- return os_snprintf(buf, buflen, "OK\n");
++ if (hapd->iconf->muru_config == NULL)
++ hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
++
++ muru = hapd->iconf->muru_config;
++ dl = &muru->dl;
++ ul = &muru->ul;
++ comm = &muru->comm;
++
++ if (os_strncmp(config, "update", 6) == 0) {
++ ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
++
++ os_free(hapd->iconf->muru_config);
++ hapd->iconf->muru_config = NULL;
++
++ if (ret)
++ goto fail;
++ } else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
++ ul->user_num = (u8)atoi(value);
++ comm->ppdu_format |= MURU_PPDU_HE_TRIG;
++ comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
++ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
++ } else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
++ dl->user_num = (u8)atoi(value);
++ comm->ppdu_format |= MURU_PPDU_HE_MU;
++ comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
++ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
++ } else if (os_strcmp(config, "dl_comm_bw") == 0) {
++ dl->bw = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
++ } else if (os_strcmp(config, "ul_comm_bw") == 0) {
++ ul->bw = (u8)atoi(value);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
++ } else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != (dl->user_num * 2))
++ goto para_fail;
++ for (i = 0; i < dl->user_num; i++) {
++ dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++ dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++ dl->usr[i].ru_idx = val[(2 * i) + 1];
++ }
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
++ } else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != (ul->user_num * 2))
++ goto para_fail;
++ for (i = 0; i < ul->user_num; i++) {
++ ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++ ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++ ul->usr[i].ru_idx = val[(2 * i) + 1];
++ }
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
++ } else if (os_strcmp(config, "dl_user_mcs") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != dl->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ dl->usr[i].mcs = (u8) val[i];
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
++ } else if (os_strcmp(config, "ul_user_mcs") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != ul->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ ul->usr[i].mcs = (u8) val[i];
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
++ } else if (os_strcmp(config, "dl_user_cod") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != dl->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ dl->usr[i].ldpc = (u8) val[i];
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
++ } else if (os_strcmp(config, "ul_user_cod") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != ul->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ ul->usr[i].ldpc = (u8) val[i];
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
++ } else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != ul->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ ul->usr[i].nss = (u8) val[i];
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
++ } else if (os_strcmp(config, "dl_comm_gi") == 0) {
++ dl->gi = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
++ } else if (os_strcmp(config, "dl_comm_ltf") == 0) {
++ dl->ltf = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
++ } else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
++ ul->gi_ltf = (u8)atoi(value);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
++ } else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
++ dl->ack_policy = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
++ } else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ i = pow(2, dl->bw);
++ if (cnt != i)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ dl->ru[i] = host_to_le16(val[i]);
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
+ } else {
+- return -1;
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for SET_MU", config);
++ goto fail;
+ }
++
++ return os_snprintf(buf, buflen, "OK\n");
++
++para_fail:
++ os_free(val);
++ wpa_printf(MSG_ERROR, "Incorrect input number\n");
++fail:
++ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+
+@@ -5148,8 +5330,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+- reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
+- reply_size);
++ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+@@ -5175,6 +5356,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
++ // Replace first ':' with a single space ' '
++ char *pos = buf + 23;
++
++ pos = os_strchr(pos, ':');
++ if (pos)
++ *pos = ' ';
++ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d995b8d9c..3827a8fc8 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1291,6 +1291,7 @@ struct hostapd_config {
+ u8 ibf_enable;
+ u8 dfs_detect_mode;
+ u8 amsdu;
++ void *muru_config;
+ };
+
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 8878db380..116bc4ceb 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
+ {
+ if (!hapd->driver || !hapd->driver->mu_ctrl)
+ return 0;
+- return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
++ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
+ }
+
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f77d07da0..84b41881a 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 5fd46d53d..d1ee0764b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2700,7 +2700,7 @@ dfs_offload:
+ if (hostapd_drv_configure_edcca_threshold(hapd,
+ hapd->iconf->edcca_threshold) < 0)
+ goto fail;
+- if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
+ goto fail;
+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 99371bf73..e140de60b 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -199,8 +199,11 @@ enum mtk_vendor_attr_mu_ctrl {
+
+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
+- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
+- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
++ /**
++ * The above attrs are also used by connac 2. It is best not to modify the
++ * above data structure.
++ */
++ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -275,8 +278,163 @@ struct amnt_resp_data {
+ };
+
+ enum {
++ MU_CTRL_UPDATE,
+ MU_CTRL_ONOFF,
+- MU_CTRL_DL_USER_CNT,
+- MU_CTRL_UL_USER_CNT,
+ };
++
++struct connac3_muru_comm {
++ u8 pda_pol;
++ u8 band;
++ u8 spe_idx;
++ u8 proc_type;
++
++ le16 mlo_ctrl;
++ u8 sch_type;
++ u8 ppdu_format;
++ u8 ac;
++ u8 _rsv[3];
++};
++
++struct connac3_muru_dl {
++ u8 user_num;
++ u8 tx_mode;
++ u8 bw;
++ u8 gi;
++
++ u8 ltf;
++ u8 mcs;
++ u8 dcm;
++ u8 cmprs;
++
++ le16 ru[16];
++
++ u8 c26[2];
++ u8 ack_policy;
++ u8 tx_power;
++
++ le16 mu_ppdu_duration;
++ u8 agc_disp_order;
++ u8 _rsv1;
++
++ u8 agc_disp_pol;
++ u8 agc_disp_ratio;
++ le16 agc_disp_linkMFG;
++
++ le16 prmbl_punc_bmp;
++ u8 _rsv2[2];
++
++ struct {
++ le16 wlan_idx;
++ u8 ru_alloc_seg;
++ u8 ru_idx;
++ u8 ldpc;
++ u8 nss;
++ u8 mcs;
++ u8 mu_group_idx;
++ u8 vht_groud_id;
++ u8 vht_up;
++ u8 he_start_stream;
++ u8 he_mu_spatial;
++ le16 tx_power_alpha;
++ u8 ack_policy;
++ u8 ru_allo_ps160;
++ } usr[16];
++};
++
++struct connac3_muru_ul {
++ u8 user_num;
++ u8 tx_mode;
++
++ u8 ba_type;
++ u8 _rsv;
++
++ u8 bw;
++ u8 gi_ltf;
++ le16 ul_len;
++
++ le16 trig_cnt;
++ u8 pad;
++ u8 trig_type;
++
++ le16 trig_intv;
++ u8 trig_ta[ETH_ALEN];
++ le16 ul_ru[16];
++
++ u8 c26[2];
++ le16 agc_disp_linkMFG;
++
++ u8 agc_disp_mu_len;
++ u8 agc_disp_pol;
++ u8 agc_disp_ratio;
++ u8 agc_disp_pu_idx;
++
++ struct {
++ le16 wlan_idx;
++ u8 ru_alloc_seg;
++ u8 ru_idx;
++ u8 ldpc;
++ u8 nss;
++ u8 mcs;
++ u8 target_rssi;
++ le32 trig_pkt_size;
++ u8 ru_allo_ps160;
++ u8 _rsv2[3];
++ } usr[16];
++};
++
++struct connac3_muru_dbg {
++ /* HE TB RX Debug */
++ le32 rx_hetb_nonsf_en_bitmap;
++ le32 rx_hetb_cfg[2];
++};
++
++struct connac3_muru {
++ le32 cfg_comm;
++ le32 cfg_dl;
++ le32 cfg_ul;
++ le32 cfg_dbg;
++
++ struct connac3_muru_comm comm;
++ struct connac3_muru_dl dl;
++ struct connac3_muru_ul ul;
++ struct connac3_muru_dbg dbg;
++};
++
++#define MURU_OFDMA_SCH_TYPE_DL BIT(0)
++#define MURU_OFDMA_SCH_TYPE_UL BIT(1)
++#define MURU_PPDU_HE_TRIG BIT(2)
++#define MURU_PPDU_HE_MU BIT(3)
++
++/* Common Config */
++#define MURU_COMM_PPDU_FMT BIT(0)
++#define MURU_COMM_BAND BIT(2)
++#define MURU_COMM_WMM BIT(3)
++#define MURU_COMM_SPE_IDX BIT(4)
++#define MURU_COMM_SET (MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
++ MURU_COMM_WMM | MURU_COMM_SPE_IDX)
++
++/* DL Common config */
++#define MURU_FIXED_DL_BW BIT(0)
++#define MURU_FIXED_DL_GI BIT(1)
++#define MURU_FIXED_DL_TONE_PLAN BIT(3)
++#define MURU_FIXED_DL_TOTAL_USER_CNT BIT(4)
++#define MURU_FIXED_DL_LTF BIT(5)
++#define MURU_FIXED_DL_ACK_PLY BIT(9)
++
++/* DL Per User Config */
++#define MURU_FIXED_USER_DL_COD BIT(17)
++#define MURU_FIXED_USER_DL_MCS BIT(18)
++#define MURU_FIXED_USER_DL_RU_ALLOC BIT(20)
++
++/* UL Common Config */
++#define MURU_FIXED_UL_TOTAL_USER_CNT BIT(4)
++#define MURU_FIXED_UL_BW BIT(5)
++#define MURU_FIXED_UL_GILTF BIT(6)
++
++/* UL Per User Config */
++#define MURU_FIXED_USER_UL_COD BIT(18)
++#define MURU_FIXED_USER_UL_MCS BIT(19)
++#define MURU_FIXED_USER_UL_NSS BIT(20)
++#define MURU_FIXED_USER_UL_RU_ALLOC BIT(21)
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 3be4562e7..1c0c38e24 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5240,7 +5240,7 @@ struct wpa_driver_ops {
+ * @priv: Private driver interface data
+ *
+ */
+- int (*mu_ctrl)(void *priv, u8 mode, u8 val);
++ int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 035a477e2..aeb755b11 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13982,12 +13982,13 @@ fail:
+
+
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
++static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
++ struct hostapd_config *cfg = config;
+ int ret = -ENOBUFS;
+
+ if (!drv->mtk_mu_vendor_cmd_avail) {
+@@ -14004,17 +14005,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+
+ switch (mode) {
+ case MU_CTRL_ONOFF:
+- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
+- goto fail;
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++ goto fail;
+ break;
+- case MU_CTRL_UL_USER_CNT:
+- case MU_CTRL_DL_USER_CNT:
+- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
+- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
+- goto fail;
++ case MU_CTRL_UPDATE:
++ if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++ sizeof(struct connac3_muru), cfg->muru_config))
++ goto fail;
+ break;
+ default:
+- wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
+ ret = -EINVAL;
+ goto fail;
+ }
+@@ -14022,9 +14022,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ nla_nest_end(msg, data);
+
+ ret = send_and_recv_cmd(drv, msg);
+- if(ret){
++ if (ret)
+ wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+- }
+ return ret;
+
+ fail:
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
new file mode 100644
index 0000000..c9fc8b7
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
@@ -0,0 +1,52 @@
+From 2dd00ec0a8231bd8c6893f9517875ad94022f9b2 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 9 Jun 2023 09:03:05 +0800
+Subject: [PATCH 053/104] mtk: hostapd: Add HE capabilities check
+
+Add HE capabilities check.
+Since "HE capabilities" check has been removed by driver,
+add the support for "HE capabilities" check in hostapd.
+---
+ src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 672e43a10..a35c5974a 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -709,6 +709,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+ #ifdef CONFIG_IEEE80211AX
+ static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+ {
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
++ struct hostapd_config *conf = iface->conf;
++
++#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap) \
++ do { \
++ if (cfg_cap && !(hw_cap[phy_idx] & field)) { \
++ wpa_printf(MSG_ERROR, "Driver does not support configured" \
++ " HE capability [%s]", #field); \
++ return 0; \
++ } \
++ } while (0)
++
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
++ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
++ conf->he_phy_capab.he_ldpc);
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
++ HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
++ conf->he_phy_capab.he_su_beamformer);
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
++ HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
++ conf->he_phy_capab.he_su_beamformee);
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
++ HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
++ conf->he_phy_capab.he_mu_beamformer);
++
+ return 1;
+ }
+ #endif /* CONFIG_IEEE80211AX */
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
new file mode 100644
index 0000000..e14b996
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
@@ -0,0 +1,64 @@
+From cc4db7ad22853f72f43128f96e5d4edcb7c245a1 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:44:15 +0800
+Subject: [PATCH 054/104] mtk: hostapd: Fix background channel overlapping
+ operating channel issue
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index f5794753e..8be953287 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -814,6 +814,20 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+ }
+
+
++static void dfs_check_background_overlapped(struct hostapd_iface *iface)
++{
++ int width = hostapd_get_oper_chwidth(iface->conf);
++
++ if (!dfs_use_radar_background(iface))
++ return;
++
++ if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
++ width, iface->radar_background.centr_freq_seg0_idx,
++ iface->radar_background.centr_freq_seg1_idx))
++ iface->radar_background.channel = -1;
++}
++
++
+ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+ {
+@@ -1141,6 +1155,8 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
+ &oper_centr_freq_seg1_idx,
+ &channel_type);
+ if (!channel ||
++ channel->chan == iface->conf->channel ||
++ channel->chan == iface->radar_background.channel ||
+ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ channel->freq, channel->chan,
+ iface->conf->ieee80211n,
+@@ -1375,6 +1391,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+ oper_centr_freq_seg1_idx);
+ err = 0;
++ dfs_check_background_overlapped(iface);
+
+ hostapd_setup_interface_complete(iface, err);
+ return err;
+@@ -1502,6 +1519,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ hostapd_set_oper_centr_freq_seg1_idx(
+ iface->conf, oper_centr_freq_seg1_idx);
+
++ dfs_check_background_overlapped(iface);
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
new file mode 100644
index 0000000..4f42bfa
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
@@ -0,0 +1,31 @@
+From 0232fabaf99094f319d03ab818cb0c847b6727c2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:47:20 +0800
+Subject: [PATCH 055/104] mtk: hostapd: Fix hostapd_dfs_start_cac log
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 8be953287..7adaf81ac 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1664,9 +1664,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ /* TODO: How to check CAC time for ETSI weather channels? */
+ iface->dfs_cac_ms = 60000;
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+- "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
++ "freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
+ "seg1=%d cac_time=%ds%s",
+- freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
++ freq, (freq - 5000) / 5, chan_offset,
++ channel_width_to_string(chan_width),
++ (cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
+ iface->dfs_cac_ms / 1000,
+ hostapd_dfs_is_background_event(iface, freq) ?
+ " (background)" : "");
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
new file mode 100644
index 0000000..6943e83
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
@@ -0,0 +1,62 @@
+From 942808028d207776f1a4dbe678166282fb272b37 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 13 Jul 2023 13:14:26 +0800
+Subject: [PATCH 056/104] mtk: hostapd: Check the bridge after ioctl
+ SIOCBRADDIF failed
+
+If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
+already be bridged by others, and linux_br_add_if should not indicate an
+error in the case.
+
+This patch checks whether the interface is correctly brigded when ioctl
+returns EBUSY.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/drivers/linux_ioctl.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
+index 29abc0c59..73d27825d 100644
+--- a/src/drivers/linux_ioctl.c
++++ b/src/drivers/linux_ioctl.c
+@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
+ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ {
+ struct ifreq ifr;
+- int ifindex;
++ int ifindex, ret;
++ char in_br[IFNAMSIZ];
+
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0)
+@@ -165,6 +166,17 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+
+ wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+ "%s: %s", ifname, brname, strerror(errno));
++
++ /* If ioctl returns -EBUSY when adding interface into bridge,
++ * the interface might already be added by netifd, so here we
++ * check whether the interface is currently on the right
++ * bridge. */
++ if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
++ os_strcmp(in_br, brname) == 0)
++ ret = 0;
++ else
++ ret = -1;
++
+ errno = saved_errno;
+
+ /* If ioctl() returns EBUSY when adding an interface into the
+@@ -175,6 +187,8 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
+ os_strcmp(in_br, brname) != 0)
+ return -1;
++
++ return ret;
+ }
+
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
new file mode 100644
index 0000000..9a8f33f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
@@ -0,0 +1,29 @@
+From cd4001cf3751979177cefa215f438888397f5bcb Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 14 Jul 2023 17:19:13 +0800
+Subject: [PATCH 057/104] mtk: hostapd: Update parameter_set_count in MU EDCA
+ IE
+
+without this patch, MU EDCA Parameter update count not equal to
+WMM Parameter set count.
+---
+ src/ap/ieee802_11_he.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index 3c6ee72fe..3b6b2041c 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -317,6 +317,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+ edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+ os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+
++ if (hapd->conf->wmm_enabled)
++ edca->he_qos_info = hapd->parameter_set_count % 0xf;
++
+ wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+ pos, sizeof(*edca));
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
new file mode 100644
index 0000000..07e0656
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
@@ -0,0 +1,36 @@
+From 43840874fe5c56d76612c953d0e31b771818c0d3 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Mon, 24 Jul 2023 11:30:27 +0800
+Subject: [PATCH 058/104] mtk: hostapd: add extension IE list for non-inherit
+ IE in mbssid
+
+Certain clients do not scan all non tx profiles due to absence of
+element ID extension list which is mandatory field in non inheritance
+IE. Non inheritance Element ID is followed by extension element ID.
+Length is expected to be mentioned. Currently we do not support any
+extension element and hence filling length as 0.
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100644 => 100755 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100644
+new mode 100755
+index d972a25f1..e42d4e1cc
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7999,7 +7999,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+ else if (hapd->conf->xrates_supported)
+ ie_count++;
+ if (ie_count)
+- nontx_profile_len += 4 + ie_count;
++ nontx_profile_len += 5 + ie_count;
+
+ if (len + nontx_profile_len > 255)
+ break;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..690dcc0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From ab91679c1eeee2c48b871335756601df995e1a19 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 8 Aug 2023 19:21:41 +0800
+Subject: [PATCH 059/104] mtk: hostapd: add back ht vht cap missing field
+ before dfs channel fallback
+
+hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
+on the bandwidth of switched channel.
+For example, vht bw 160 support field would be cleared if we switch to
+non bw 160 channel.
+This design works fine with NON-DFS channel switch.
+However, for those DFS channels who require CAC, channel switch command
+calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
+This is simply restarting the interface not CHANNEL SWITCHING, so
+hostapd will not receive any ch_switch event from kernel.
+Therefore, the cleared field in vht_capab will not be set back to 1,
+even if we channel switch to dfs channel bw 160.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index d1ee0764b..db451387b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4569,6 +4569,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ break;
+ }
+
++ if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
++ freq_params->bandwidth > 20)
++ iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
++ if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
++ freq_params->bandwidth == 160)
++ iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
++
+ iface->freq = freq_params->freq;
+ iface->conf->channel = freq_params->channel;
+ iface->conf->secondary_channel = freq_params->sec_channel_offset;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
new file mode 100644
index 0000000..ccbe766
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
@@ -0,0 +1,45 @@
+From 00c2dff4bf8d15c1c84321cef1892009aa32e9ed Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 26 May 2023 14:52:35 +0800
+Subject: [PATCH 060/104] mtk: hostapd: Add support for gtk rekeying in hostapd
+ cli
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ hostapd/hostapd_cli.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 12c580455..e0b175386 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1291,6 +1291,15 @@ static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
++#ifdef CONFIG_TESTING_OPTIONS
++static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "REKEY_GTK");
++}
++#endif
++
++
+ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ char cmd[256];
+@@ -1831,6 +1840,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "= update Beacon frame contents\n"},
+ { "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
+ "= stop AP\n"},
++#ifdef CONFIG_TESTING_OPTIONS
++ { "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
++ "= rekey gtk\n"},
++#endif
+ { "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+ "= drop all ERP keys"},
+ { "log_level", hostapd_cli_cmd_log_level, NULL,
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
new file mode 100644
index 0000000..65ba1f0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
@@ -0,0 +1,48 @@
+From 2dc6e435eef405ae0cfb69a89c1c8ec7d2852635 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 11 Jul 2023 14:17:43 +0800
+Subject: [PATCH 061/104] mtk: hostapd: Set WMM and TX queue parameters for
+ wpa_supplicant
+
+Since most of the time, wpa_supplicant will be used to setup an STA
+interface, it's default WMM and TX queue parameters should be set for
+STA.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/config.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index c3943355d..7bb57e2ab 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -4720,19 +4720,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+ const struct hostapd_wmm_ac_params ac_bk =
+ { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ const struct hostapd_wmm_ac_params ac_be =
+- { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
++ { aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
+ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+- { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
++ { aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
+ const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+- { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
++ { aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
+ const struct hostapd_tx_queue_params txq_bk =
+ { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_be =
+- { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
++ { 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_vi =
+- { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
++ { 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
+ const struct hostapd_tx_queue_params txq_vo =
+- { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
++ { 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
+
+ #undef ecw2cw
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
new file mode 100644
index 0000000..502da4c
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
@@ -0,0 +1,78 @@
+From eaf45cd7a14dac2d5d601653792d2bc101118585 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 7 Jul 2023 17:16:11 +0800
+Subject: [PATCH 062/104] mtk: hostapd: Set STA TX queue parameters
+ configuration after association
+
+This patch adds the way for wpa_supplicant to set driver's TX queue
+parameters.
+Since STA parses and apply TX queue parameters from AP beacon's WMM IE
+during association, wpa_supplicant set driver's TX queue parameters
+after the association.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/driver_i.h | 12 ++++++++++++
+ wpa_supplicant/events.c | 16 ++++++++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index d01b52bb1..663e16053 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+ return 0;
+ }
+
++static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
++ int q, int aifs, int cw_min,
++ int cw_max, int burst_time)
++{
++ int link_id = -1;
++ if (wpa_s->driver->set_tx_queue_params)
++ return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
++ aifs, cw_min, cw_max,
++ burst_time, link_id);
++ return 0;
++}
++
+ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
+ const u8 *data, size_t data_len, int noack,
+ unsigned int freq, unsigned int wait)
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 2a9342318..8fd2f2049 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -4070,6 +4070,20 @@ out:
+ return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
+ }
+
++static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
++ struct hostapd_tx_queue_params *p;
++
++ for (int i = 0; i < NUM_TX_QUEUES; i++){
++ p = &wpa_s->conf->tx_queue[i];
++ if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
++ p->cwmin, p->cwmax,
++ p->burst)) {
++ wpa_printf(MSG_DEBUG, "Failed to set TX queue "
++ "parameters for queue %d.", i);
++ /* Continue anyway */
++ }
++ }
++}
+
+ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+@@ -4399,6 +4413,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
+ wpa_supplicant_set_4addr_mode(wpa_s);
++
++ wpa_supplicant_tx_queue_params(wpa_s);
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
new file mode 100644
index 0000000..e1384d9
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
@@ -0,0 +1,27 @@
+From 7449e88f54fb5e16296399c43c9f758535123cde Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Sep 2023 15:31:24 +0800
+Subject: [PATCH 063/104] mtk: hostapd: avoid color switch when beacon is not
+ set
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index db451387b..0d4b79b48 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4707,7 +4707,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+ {
+ struct os_reltime now;
+
+- if (hapd->cca_in_progress)
++ if (hapd->cca_in_progress || !hapd->beacon_set_done)
+ return;
+
+ if (os_get_reltime(&now))
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
new file mode 100644
index 0000000..7d6be8a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
@@ -0,0 +1,30 @@
+From 8e01f276c2d7be41f3521026c92d8f1bd833865f Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Wed, 13 Sep 2023 19:29:51 +0800
+Subject: [PATCH 064/104] mtk: hostapd: 6g bss connect do not consider ht
+ operation
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100755 => 100644 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100755
+new mode 100644
+index e42d4e1cc..923cbebcc
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5591,7 +5591,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ set_beacon = true;
+ }
+
+- if (update_ht_state(hapd, sta) > 0)
++ if (!is_6ghz_op_class(hapd->iconf->op_class) && update_ht_state(hapd, sta) > 0)
+ set_beacon = true;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
new file mode 100644
index 0000000..d87fef0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
@@ -0,0 +1,96 @@
+From 9639b495a347cdd2aadbe2bc2d336b4398518d29 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Sun, 8 Oct 2023 11:50:06 +0800
+Subject: [PATCH 065/104] mtk: hostapd: Add ACS chanlist info in get_config
+
+This patch is used to add ACS chanlist info displaying
+for upper layer application obtaining.
+
+Command format:
+hostapd_cli -i phy0-ap0 get_config
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 59 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index ed383df7d..581acc260 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1058,6 +1058,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ {
+ int ret;
+ char *pos, *end;
++ int i;
+
+ pos = buf;
+ end = buf + buflen;
+@@ -1237,6 +1238,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ pos += ret;
+ }
+
++ /* dump chanlist */
++ if (hapd->iface->conf->acs_ch_list.num > 0) {
++ ret = os_snprintf(pos, end - pos, "chanlist=");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
++ if (i > 0) {
++ ret = os_snprintf(pos, end - pos, ", ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "%d-%d",
++ hapd->iface->conf->acs_ch_list.range[i].min,
++ hapd->iface->conf->acs_ch_list.range[i].max);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "\n");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ /* dump freqlist */
++ if (hapd->iface->conf->acs_freq_list.num > 0) {
++ ret = os_snprintf(pos, end - pos, "freqlist=");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
++ if (i > 0) {
++ ret = os_snprintf(pos, end - pos, ", ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "%d-%d",
++ hapd->iface->conf->acs_freq_list.range[i].min,
++ hapd->iface->conf->acs_freq_list.range[i].max);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "\n");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
+ return pos - buf;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
new file mode 100644
index 0000000..3f59e13
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
@@ -0,0 +1,43 @@
+From b463e82d0eec8674e430a7e837c569be4c9fe2c2 Mon Sep 17 00:00:00 2001
+From: mtk25255 <rohit.kamat@mediatek.com>
+Date: Thu, 12 Oct 2023 14:29:23 +0800
+Subject: [PATCH 066/104] mtk: hostapd: Fix RSNXE Interop issue with STA
+
+---
+ src/ap/ieee802_11.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 923cbebcc..ce3874901 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5294,6 +5294,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ int omit_rsnxe = 0;
+ bool set_beacon = false;
+ bool mld_addrs_not_translated = false;
++ bool sae_pk = false;
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ sizeof(mgmt->u.assoc_req))) {
+@@ -5539,7 +5540,17 @@ static void handle_assoc(struct hostapd_data *hapd,
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+ omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+-
++#ifdef CONFIG_SAE_PK
++ sae_pk = hostapd_sae_pk_in_use(hapd->conf);
++#endif /* CONFIG_SAE_PK */
++ if (omit_rsnxe) {
++ if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
++ (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
++ hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
++ wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
++ omit_rsnxe = 0;
++ }
++ }
+ if (hostapd_get_aid(hapd, sta) < 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
new file mode 100644
index 0000000..8764aa7
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
@@ -0,0 +1,29 @@
+From a6db9becf71712107500adf239b89f4f8523d3f3 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 10 May 2023 13:11:34 +0800
+Subject: [PATCH 067/104] mtk: hostapd: update eht operation element
+
+---
+ src/ap/ieee802_11_eht.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 353a4116e..e13662a59 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -237,9 +237,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+
+ /* TODO: Fill in appropriate EHT-MCS max Nss information */
+ oper->basic_eht_mcs_nss_set[0] = 0x11;
+- oper->basic_eht_mcs_nss_set[1] = 0x00;
+- oper->basic_eht_mcs_nss_set[2] = 0x00;
+- oper->basic_eht_mcs_nss_set[3] = 0x00;
++ oper->basic_eht_mcs_nss_set[1] = 0x11;
++ oper->basic_eht_mcs_nss_set[2] = 0x11;
++ oper->basic_eht_mcs_nss_set[3] = 0x11;
+
+ if (!eht_oper_info_present)
+ return pos + elen;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
new file mode 100644
index 0000000..f383d62
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
@@ -0,0 +1,28 @@
+From f5256a36cd00b54955decd53961ece85dd5f11f9 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 30 Aug 2023 04:23:37 +0800
+Subject: [PATCH 068/104] mtk: hostapd: ucode: add support for ucode to parse
+ BW320MHz info
+
+---
+ src/utils/ucode.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 29c753c32..4b6ed3a94 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ case 2:
+ chanwidth = CONF_OPER_CHWIDTH_160MHZ;
+ break;
++ case 9:
++ width = 3;
++ chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++ break;
+ default:
+ return NULL;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
new file mode 100644
index 0000000..4ca6761
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
@@ -0,0 +1,279 @@
+From d7a803942f27759fe0e27c4550d70e44fb83c897 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 11 Sep 2023 10:16:35 +0800
+Subject: [PATCH 069/104] mtk: hostapd: synchronize bandwidth in AP/STA support
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c | 41 +++++++++++++++++++--
+ src/utils/ucode.c | 12 +++++--
+ wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
+ 3 files changed, 117 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 16d1b5153..98b2a3bf2 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -489,6 +489,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ int i;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++ iface->phy, hostapd_state_text(iface->state));
++
+ if (!iface)
+ return NULL;
+
+@@ -515,6 +518,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ uint64_t intval;
+ int i;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
++ iface->phy, hostapd_state_text(iface->state));
++
+ if (!iface)
+ return NULL;
+
+@@ -537,7 +543,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ UPDATE_VAL(op_class, "op_class");
+ UPDATE_VAL(hw_mode, "hw_mode");
+ UPDATE_VAL(channel, "channel");
+- UPDATE_VAL(secondary_channel, "sec_channel");
++
++ intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
++ if (!errno) {
++ conf->secondary_channel = intval;
++ changed = true;
++ }
++
+ if (!changed &&
+ (iface->bss[0]->beacon_set_done ||
+ iface->state == HAPD_IFACE_DFS))
+@@ -583,6 +595,18 @@ out:
+ return ucv_boolean_new(true);
+ }
+
++ wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
++ wpa_printf(MSG_INFO, " * channel: %d\n", conf->channel);
++ wpa_printf(MSG_INFO, " * op_class: %d\n", conf->op_class);
++ wpa_printf(MSG_INFO, " * secondary channel: %d\n",
++ conf->secondary_channel);
++ wpa_printf(MSG_INFO, " * seg0: %d\n",
++ hostapd_get_oper_centr_freq_seg0_idx(conf));
++ wpa_printf(MSG_INFO, " * seg1: %d\n",
++ hostapd_get_oper_centr_freq_seg0_idx(conf));
++ wpa_printf(MSG_INFO, " * oper_chwidth: %d\n",
++ hostapd_get_oper_chwidth(conf));
++
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+ int ret;
+@@ -617,6 +641,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ uint64_t intval;
+ int i, ret = 0;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
+ if (!iface || ucv_type(info) != UC_OBJECT)
+ return NULL;
+
+@@ -636,7 +661,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ if (errno)
+ intval = hostapd_get_oper_chwidth(conf);
+ if (intval)
+- csa.freq_params.bandwidth = 40 << intval;
++ csa.freq_params.bandwidth = 40 <<
++ (intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
+ else
+ csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
+
+@@ -647,6 +673,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ csa.freq_params.center_freq2 = intval;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
++ wpa_printf(MSG_INFO, " * freq is %d\n", csa.freq_params.freq);
++ wpa_printf(MSG_INFO, " * bandwidth is %d\n",
++ csa.freq_params.bandwidth);
++ wpa_printf(MSG_INFO, " * sec_chan_offset is %d\n",
++ csa.freq_params.sec_channel_offset);
++ wpa_printf(MSG_INFO, " * center_freq1 is %d\n",
++ csa.freq_params.center_freq1);
++ wpa_printf(MSG_INFO, " * center_freq2 is %d\n",
++ csa.freq_params.center_freq2);
++
+ for (i = 0; i < iface->num_bss; i++)
+ ret = hostapd_switch_channel(iface->bss[i], &csa);
+
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 4b6ed3a94..6f82382f3 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ uc_value_t *freq = uc_fn_arg(0);
+ uc_value_t *sec = uc_fn_arg(1);
+ int width = ucv_uint64_get(uc_fn_arg(2));
++ int bw320_offset = 1;
+ int freq_val, center_idx, center_ofs;
+ enum oper_chan_width chanwidth;
+ enum hostapd_hw_mode hw_mode;
+@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ case 9:
+ width = 3;
+ chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++
++ /* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
++ bw320_offset = ucv_uint64_get(uc_fn_arg(3));
+ break;
+ default:
+ return NULL;
+@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
+ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++ ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+
+- if (!sec_channel)
++ if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
++ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
++ ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
+ return ret;
++ }
+
+ if (freq_val >= 5900)
+- center_ofs = 0;
++ center_ofs = 32 * (1 - bw320_offset);
+ else if (freq_val >= 5745)
+ center_ofs = 20;
+ else
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 397f85bde..542ca25c9 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -7,6 +7,8 @@
+ #include "wps_supplicant.h"
+ #include "bss.h"
+ #include "ucode.h"
++#include "driver_i.h"
++#include "common/ieee802_11_common.h"
+
+ static struct wpa_global *wpa_global;
+ static uc_resource_type_t *global_type, *iface_type;
+@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ {
+ const char *state;
+ uc_value_t *val;
++ enum oper_chan_width ch_width;
++ int center_freq1, bw320_offset = 1;
+
+ if (event != EVENT_CH_SWITCH_STARTED)
+ return;
+@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ uc_value_push(ucv_get(val));
+
+ if (event == EVENT_CH_SWITCH_STARTED) {
++ center_freq1 = data->ch_switch.cf1;
++
++ switch (data->ch_switch.ch_width) {
++ case CHAN_WIDTH_80:
++ ch_width = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case CHAN_WIDTH_80P80:
++ ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
++ break;
++ case CHAN_WIDTH_160:
++ ch_width = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ case CHAN_WIDTH_320:
++ ch_width = CONF_OPER_CHWIDTH_320MHZ;
++ break;
++ case CHAN_WIDTH_20_NOHT:
++ case CHAN_WIDTH_20:
++ case CHAN_WIDTH_40:
++ default:
++ ch_width = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ }
++
++ /* Check bandwidth 320 MHz-2 */
++ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++ (center_freq1 == 6265) || center_freq1 == 6585 ||
++ center_freq1 == 6905)
++ bw320_offset = 2;
++
+ ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
+ ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
+ ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+- ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++ ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
+ ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++ ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
++ ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
+ }
+
+ ucv_put(wpa_ucode_call(4));
+@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ struct wpa_bss *bss;
+ uc_value_t *ret, *val;
++ struct wpa_channel_info ci;
++ u8 op_class, channel;
++ enum oper_chan_width ch_width;
++ int center_freq1, bw320_offset = 1, is_24ghz;
++ enum hostapd_hw_mode hw_mode;
+
+ if (!wpa_s)
+ return NULL;
+@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ bss = wpa_s->current_bss;
+ if (bss) {
+ int sec_chan = 0;
+- const u8 *ie;
+-
+- ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+- if (ie && ie[1] >= 2) {
+- const struct ieee80211_ht_operation *ht_oper;
+- int sec;
+-
+- ht_oper = (const void *) (ie + 2);
+- sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+- if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+- sec_chan = 1;
+- else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+- sec_chan = -1;
++
++ hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
++ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++ hw_mode == HOSTAPD_MODE_IEEE80211B;
++
++ wpa_drv_channel_info(wpa_s, &ci);
++ center_freq1 = ci.center_frq1;
++
++ if (bss->freq != center_freq1) {
++ if (is_24ghz)
++ sec_chan = (bss->freq < center_freq1) ? 1 : -1;
++ else
++ sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
++ }
++
++ if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++ sec_chan, &op_class, &channel))
++ return NULL;
++
++ ch_width = op_class_to_ch_width(op_class);
++ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++ (center_freq1 == 6265) || center_freq1 == 6585 ||
++ center_freq1 == 6905) {
++ /* Bandwidth 320 MHz-2 */
++ bw320_offset = 2;
+ }
+
+ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++ ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
++ ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ }
+
+ #ifdef CONFIG_MESH
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
new file mode 100644
index 0000000..d1800fe
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
@@ -0,0 +1,339 @@
+From 0455150c89b046a3ebd81134527ff4cae5025f3d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:25:01 +0800
+Subject: [PATCH 070/104] mtk: hostapd: Add support for updating background
+ channel by driver
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 107 ++++++++++++++++++++++++++++-
+ src/ap/dfs.h | 3 +
+ src/ap/drv_callbacks.c | 22 ++++++
+ src/ap/hostapd.h | 5 ++
+ src/drivers/driver.h | 12 ++++
+ src/drivers/driver_nl80211_event.c | 6 ++
+ src/drivers/nl80211_copy.h | 6 ++
+ 7 files changed, 160 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 7adaf81ac..e39f3c180 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -816,11 +816,14 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+
+ static void dfs_check_background_overlapped(struct hostapd_iface *iface)
+ {
+- int width = hostapd_get_oper_chwidth(iface->conf);
++ int width = iface->radar_background.new_chwidth;
+
+ if (!dfs_use_radar_background(iface))
+ return;
+
++ if (!width)
++ width = hostapd_get_oper_chwidth(iface->conf);
++
+ if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
+ width, iface->radar_background.centr_freq_seg0_idx,
+ iface->radar_background.centr_freq_seg1_idx))
+@@ -985,6 +988,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ iface->radar_background.temp_ch = 1;
+ return 1;
+ } else if (dfs_use_radar_background(iface)) {
++ /*
++ * AP is going to perform CAC, so reset temp_ch to 0,
++ * when dedicated rx has already started CAC.
++ */
++ if (iface->radar_background.cac_started) {
++ iface->radar_background.temp_ch = 0;
++ return 0;
++ }
++
+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ channel_type = DFS_ANY_CHANNEL;
+
+@@ -1125,6 +1137,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ * ch_switch_notify event is received */
+ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+
++ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
++
+ return 0;
+ }
+
+@@ -1176,6 +1190,9 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
+ iface->radar_background.secondary_channel = sec;
+ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++ /* if main channel do not require dfs, then set temp_ch = 1 */
++ if (!hostapd_is_dfs_required(iface))
++ iface->radar_background.temp_ch = 1;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: setting background chain to chan %d (%d MHz)",
+@@ -1198,6 +1215,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ int ret;
+
++ if (iface->radar_background.new_chwidth) {
++ hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
++ iface->radar_background.new_chwidth = 0;
++ }
+ ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
+ iface->radar_background.freq,
+ iface->radar_background.secondary_channel,
+@@ -1220,6 +1241,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ }
+
+
++static void
++hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
++{
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ struct hostapd_channel_data *chan;
++ int i, channel, width = channel_width_to_int(chan_width);
++
++ if (iface->conf->channel - iface->radar_background.channel == width / 5)
++ channel = iface->radar_background.channel;
++ else if (iface->radar_background.channel - iface->conf->channel == width / 5)
++ channel = iface->conf->channel;
++ else
++ return;
++
++ for (i = 0; i < mode->num_channels; i++) {
++ chan = &mode->channels[i];
++ if (chan->chan == channel)
++ break;
++ }
++
++ if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
++ return;
++
++ switch (chan_width) {
++ case CHAN_WIDTH_20_NOHT:
++ case CHAN_WIDTH_20:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ case CHAN_WIDTH_40:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case CHAN_WIDTH_80:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ default:
++ return;
++ }
++
++ iface->radar_background.freq = channel * 5 + 5000;
++ iface->radar_background.channel = channel;
++ iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
++ iface->radar_background.secondary_channel = 1;
++ iface->radar_background.expand_ch = 0;
++}
++
++
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+@@ -1253,6 +1320,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ return 0;
+
+ iface->radar_background.temp_ch = 0;
++
++ if (iface->radar_background.expand_ch)
++ hostapd_dfs_background_expand(iface, chan_width);
++
+ return hostapd_dfs_start_channel_switch_background(iface);
+ }
+
+@@ -1283,6 +1354,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ }
+ } else if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
++ iface->radar_background.temp_ch = 0;
++ iface->radar_background.expand_ch = 0;
+ hostapd_dfs_update_background_chain(iface);
+ }
+
+@@ -1415,6 +1488,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
+ return 0;
+
++ iface->radar_background.temp_ch = 0;
++ iface->radar_background.expand_ch = 0;
++
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+@@ -1649,6 +1725,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ }
+
+
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, bool expand)
++{
++ switch (chan_width) {
++ case CHAN_WIDTH_80:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case CHAN_WIDTH_160:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ default:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ };
++
++ iface->radar_background.freq = freq;
++ iface->radar_background.channel = (freq - 5000) / 5;
++ iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
++ iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
++ if (expand) {
++ iface->radar_background.temp_ch = 1;
++ iface->radar_background.expand_ch = 1;
++ }
++
++ return 0;
++}
++
++
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 25ba29ca1..a1a2be5ec 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, bool expand);
+ int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2, u32 state);
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index caa171474..2d946afd6 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2226,6 +2226,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ radar->cf1, radar->cf2);
+ }
+
++
++static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
++ struct dfs_event *radar, bool expand)
++{
++ wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
++ expand ? "expand" : "update", radar->freq);
++ hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
++ radar->chan_offset, radar->chan_width,
++ radar->cf1, radar->cf2, expand);
++}
++
++
+ static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+ {
+@@ -2610,6 +2622,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ break;
++ case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
++ if (!data)
++ break;
++ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
++ break;
++ case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
++ if (!data)
++ break;
++ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
++ break;
+ case EVENT_DFS_STA_CAC_SKIPPED:
+ if (!data)
+ break;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 1e4113459..5b37be87b 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -640,6 +640,11 @@ struct hostapd_iface {
+ unsigned int temp_ch:1;
+ /* CAC started on radar offchain */
+ unsigned int cac_started:1;
++ /* Main chain should expand its width according to the
++ * current offchain channel after CAC detection on radar offchain.
++ */
++ unsigned int expand_ch:1;
++ int new_chwidth;
+ } radar_background;
+
+ u16 hw_flags;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 1c0c38e24..4e3dc9bdb 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5960,6 +5960,18 @@ enum wpa_event_type {
+ * The channel in the notification is now marked as usable.
+ */
+ EVENT_DFS_STA_CAC_EXPIRED,
++
++ /**
++ * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
++ * channel has been updated.
++ */
++ EVENT_DFS_BACKGROUND_CHAN_UPDATE,
++
++ /**
++ * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
++ * channel has been updated and operating channel should expand its width.
++ */
++ EVENT_DFS_BACKGROUND_CHAN_EXPAND,
+ };
+
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 7889930a0..6631285bf 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ case NL80211_RADAR_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
++ case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
++ break;
++ case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
++ break;
+ case NL80211_RADAR_STA_CAC_SKIPPED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
+ break;
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index 8917d565b..c56954306 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
++ * driver.
++ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
++ * driver and required to expand main operating channel.
+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
+ * when receiving CSA/assoc resp
+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
+@@ -6711,6 +6715,8 @@ enum nl80211_radar_event {
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
++ NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
++ NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
+ NL80211_RADAR_STA_CAC_SKIPPED,
+ NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
new file mode 100644
index 0000000..c2760d9
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
@@ -0,0 +1,291 @@
+From 5b2e33617bfafa8c6776e80b13c8747f0021a804 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 2 Aug 2023 19:00:34 +0800
+Subject: [PATCH 071/104] mtk: hostapd: add zwdfs mode ctrl for eagle efem
+ hwits
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ hostapd/ctrl_iface.c | 30 +++++++++++++++++++++++++++
+ src/ap/ap_config.h | 6 ++++++
+ src/ap/ap_drv_ops.c | 14 +++++++++++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/ap/dfs.c | 6 ++++++
+ src/common/mtk_vendor.h | 12 +++++++++++
+ src/drivers/driver.h | 7 +++++++
+ src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +++
+ 11 files changed, 116 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index dadc8f108..9467a1128 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -3576,6 +3576,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ conf->acs_exclude_6ghz_non_psc = atoi(pos);
+ } else if (os_strcmp(buf, "enable_background_radar") == 0) {
+ conf->enable_background_radar = atoi(pos);
++ } else if (os_strcmp(buf, "background_radar_mode") == 0) {
++ conf->background_radar_mode = atoi(pos);
+ } else if (os_strcmp(buf, "min_tx_power") == 0) {
+ int val = atoi(pos);
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 581acc260..9b072d1b2 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4782,6 +4782,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
+ return pos - buf;
+ }
+
++static int
++hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ struct hostapd_iface *iface = hapd->iface;
++ char *pos, *param;
++
++ param = os_strchr(cmd, ' ');
++ if (!param)
++ return -1;
++ *param++ = '\0';
++
++ pos = os_strstr(param, "mode=");
++ if (!pos)
++ return -1;
++
++ if (os_strncmp(pos + 5, "cert", 4) == 0)
++ iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
++ else if (os_strncmp(pos + 5, "normal", 6) == 0)
++ iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
++
++ if (hostapd_drv_background_radar_mode(hapd) < 0)
++ return -1;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5423,6 +5450,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ if (pos)
+ *pos = ' ';
+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
++ } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
++ reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3827a8fc8..0b07be516 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1051,6 +1051,7 @@ struct hostapd_config {
+ bool hw_mode_set;
+ int acs_exclude_6ghz_non_psc;
+ int enable_background_radar;
++ int background_radar_mode;
+ enum {
+ LONG_PREAMBLE = 0,
+ SHORT_PREAMBLE = 1
+@@ -1306,6 +1307,11 @@ enum three_wire_mode {
+ NUM_THREE_WIRE_MODE - 1
+ };
+
++enum background_radar_mode {
++ BACKGROUND_RADAR_NORMAL_MODE,
++ BACKGROUND_RADAR_CERT_MODE,
++};
++
+ enum dfs_mode {
+ DFS_DETECT_MODE_DISABLE,
+ DFS_DETECT_MODE_AP_ENABLE,
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 116bc4ceb..2028e70fb 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1365,3 +1365,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ return 0;
+ return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
+ }
++
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->background_radar_mode ||
++ !(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) ||
++ !hapd->iface->conf->enable_background_radar)
++ return 0;
++ if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
++ wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
++ return 0;
++ }
++ return hapd->driver->background_radar_mode(hapd->drv_priv,
++ hapd->iconf->background_radar_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 84b41881a..f0e618bcc 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -168,6 +168,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index e39f3c180..b12290556 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -985,6 +985,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ if (res < 0)
+ return res;
+
++ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++ return -1;
++
+ iface->radar_background.temp_ch = 1;
+ return 1;
+ } else if (dfs_use_radar_background(iface)) {
+@@ -1025,6 +1028,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ iface->radar_background.secondary_channel = sec;
+ iface->radar_background.centr_freq_seg0_idx = cf1;
+ iface->radar_background.centr_freq_seg1_idx = cf2;
++
++ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++ return -1;
+ }
+
+ return 0;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index e140de60b..5bc1e0444 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -244,6 +245,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
+ };
+
++enum mtk_vendor_attr_background_radar_ctrl {
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 4e3dc9bdb..863748d4f 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5319,6 +5319,13 @@ struct wpa_driver_ops {
+ * @amnt_dump_buf: Buffer to print
+ */
+ int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
++
++ /**
++ * background_radar_mode - set background radar mode
++ * @priv: Private driver interface data
++ * @background_radar_mode: background radar mode
++ */
++ int (*background_radar_mode)(void *priv, u8 background_radar_mode);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index aeb755b11..e3f00b6d6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15052,6 +15052,39 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ /* Prepare nl80211 cmd */
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_background_radar_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting background radar mode");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15229,4 +15262,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .ap_trigtype = nl80211_ap_trigtype,
+ .amnt_set = nl80211_amnt_set,
+ .amnt_dump = nl80211_amnt_dump,
++ .background_radar_mode = nl80211_background_radar_mode,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index adc1b9bf7..e9aae8d14 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ unsigned int mtk_amnt_vendor_cmd_avail:1;
++ unsigned int mtk_background_radar_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 38e83e42b..9bc98aae7 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1164,6 +1164,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ drv->mtk_rfeatures_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
++ drv->mtk_background_radar_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
new file mode 100644
index 0000000..4959fb4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
@@ -0,0 +1,375 @@
+From 51377e7c81b4164be42de4a5c4c48ba53a638afe Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 21 Sep 2023 10:29:46 +0800
+Subject: [PATCH 072/104] mtk: hostapd: add support enable/disable preamble
+ puncture from mtk vendor command
+
+This commit supports two ways to enable/disable preamble puncture
+feature.
+
+1. Add new hostapd configuration "pp_mode". The possible value could be
+1 to 3. When the value is 0, it means that the firmware will turn off
+the pp algorithm. When the value is 1, it means that the firmware will
+enable the pp algorithm, allowing the algorithm to determine whether pp
+could be applied on each txcmd. When the value is 2, it means that pp
+feature is manually configured by the user. Please noted that for
+current implementation, the default configuration is 0.
+
+2. $ hostapd_cli -i <intf_name> raw set_pp mode val
+The argument "val" could be 0 for PP feature disabled or 1 to configure
+PP feature as auto mode.
+
+This commit also let user check whether pp feature is enabled by
+hostapd_cli command. The usage shows as below:
+$ hostapd_cli -i <intf_name> raw get_pp mode
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/config_file.c | 12 +++++++
+ hostapd/ctrl_iface.c | 59 +++++++++++++++++++++++++++++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 7 ++++
+ src/ap/ap_drv_ops.c | 9 +++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/ap/hostapd.c | 2 ++
+ src/common/mtk_vendor.h | 12 +++++++
+ src/drivers/driver.h | 6 ++++
+ src/drivers/driver_nl80211.c | 49 +++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 ++
+ 12 files changed, 162 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 9467a1128..050ef290e 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5333,6 +5333,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "punct_bitmap") == 0) {
+ if (get_u16(pos, line, &conf->punct_bitmap))
+ return 1;
++ conf->punct_bitmap = atoi(pos);
++ conf->pp_mode = PP_MANUAL_MODE;
+ } else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ int val = atoi(pos);
+
+@@ -5415,6 +5417,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->amsdu = val;
++ } else if (os_strcmp(buf, "pp_mode") == 0) {
++ int val = atoi(pos);
++
++ if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
++ val < PP_DISABLE || val > PP_MANUAL_MODE) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
++ line);
++ return 1;
++ }
++ conf->pp_mode = (u8) val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 9b072d1b2..c9b53c64e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4809,6 +4809,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
++static int
++hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++ size_t buflen)
++{
++ char *pos, *config, *value;
++
++ config = cmd;
++ pos = os_strchr(config, ' ');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if (pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strcmp(config, "mode") == 0) {
++ int val = atoi(value);
++
++ if (val < PP_DISABLE || val > PP_AUTO_MODE) {
++ wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++ return -1;
++ }
++ hapd->iconf->pp_mode = (u8) val;
++ if (hostapd_drv_pp_mode_set(hapd) != 0)
++ return -1;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for set_pp", config);
++ return -1;
++ }
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++ size_t buflen)
++{
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (os_strcmp(cmd, "mode") == 0) {
++ return os_snprintf(pos, end - pos, "pp_mode: %d\n",
++ hapd->iconf->pp_mode);
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for get_pp", cmd);
++ return -1;
++ }
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5442,6 +5495,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "set_pp", 6) == 0) {
++ reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
++ reply_size);
++ } else if (os_strncmp(buf, "get_pp", 6) == 0) {
++ reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
++ reply_size);
+ } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
+ // Replace first ':' with a single space ' '
+ char *pos = buf + 23;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index ba1b2a7a3..056c38f73 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -310,6 +310,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ conf->amsdu = 1;
++ conf->pp_mode = PP_DISABLE;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 0b07be516..40edcdaa7 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1293,6 +1293,7 @@ struct hostapd_config {
+ u8 dfs_detect_mode;
+ u8 amsdu;
+ void *muru_config;
++ u8 pp_mode;
+ };
+
+ enum three_wire_mode {
+@@ -1345,6 +1346,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+ EDCCA_CTRL_NUM,
+ };
+
++enum pp_mode {
++ PP_DISABLE = 0,
++ PP_AUTO_MODE,
++ PP_MANUAL_MODE,
++};
++
+ #define EDCCA_DEFAULT_COMPENSATION -6
+ #define EDCCA_MIN_COMPENSATION -126
+ #define EDCCA_MAX_COMPENSATION 126
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2028e70fb..c71cfe1bd 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1379,3 +1379,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ return hapd->driver->background_radar_mode(hapd->drv_priv,
+ hapd->iconf->background_radar_mode);
+ }
++
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->pp_mode_set ||
++ hapd->iconf->pp_mode > PP_AUTO_MODE)
++ return 0;
++ return hapd->driver->pp_mode_set(hapd->drv_priv,
++ hapd->iconf->pp_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f0e618bcc..ef61001e5 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -169,6 +169,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 0d4b79b48..cdbf81e38 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2708,6 +2708,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_pp_mode_set(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 5bc1e0444..6275c141d 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
++ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -256,6 +257,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
+ };
+
++enum mtk_vendor_attr_pp_ctrl {
++ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_PP_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
++ MTK_VENDOR_ATTR_PP_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 863748d4f..be0e89ba3 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5326,6 +5326,12 @@ struct wpa_driver_ops {
+ * @background_radar_mode: background radar mode
+ */
+ int (*background_radar_mode)(void *priv, u8 background_radar_mode);
++ /**
++ * pp_mode_set - Set preamble puncture operation mode
++ * @priv: Private driver interface data
++ * @pp_mode: Value is defined in enum pp_mode
++ */
++ int (*pp_mode_set)(void *priv, const u8 pp_mode);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index e3f00b6d6..b47ab07ea 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -156,6 +156,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
+ };
+
++static struct nla_policy
++pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
++ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -15085,6 +15090,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ return ret;
+ }
+
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_pp_vendor_cmd_avail) {
++ wpa_printf(MSG_DEBUG,
++ "nl80211: Driver does not support setting preamble puncture");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
++ ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15263,4 +15311,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .amnt_set = nl80211_amnt_set,
+ .amnt_dump = nl80211_amnt_dump,
+ .background_radar_mode = nl80211_background_radar_mode,
++ .pp_mode_set = nl80211_pp_mode_set,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index e9aae8d14..707bb7fe4 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ unsigned int mtk_amnt_vendor_cmd_avail:1;
+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
++ unsigned int mtk_pp_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 9bc98aae7..ba3c0817b 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1167,6 +1167,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
+ drv->mtk_background_radar_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
++ drv->mtk_pp_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
new file mode 100644
index 0000000..31d1951
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
@@ -0,0 +1,247 @@
+From a7adff7d782e329e9f8b1063f78616757f944d51 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 22 Nov 2023 21:41:34 +0800
+Subject: [PATCH 073/104] mtk: hostapd: add no_beacon vendor command for cert
+
+Add the vendor command to disable/enable beacon
+
+[Usage]
+hostapd_cli -i <interface> no_beacon <value>
+ <value>
+ 0: enable beacon
+ 1: disable beacon
+
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 21 +++++++++++++++++++
+ hostapd/hostapd_cli.c | 7 +++++++
+ src/ap/ap_drv_ops.c | 8 ++++++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/common/mtk_vendor.h | 12 +++++++++++
+ src/drivers/driver.h | 7 +++++++
+ src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +++
+ 9 files changed, 94 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index c9b53c64e..0fded7ed4 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4862,6 +4862,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ }
+ }
+
++static int
++hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
++ char *buf, size_t buflen)
++{
++ int disable_beacon = atoi(value);
++
++ if (disable_beacon < 0) {
++ wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
++ return -1;
++ }
++
++ if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
++ return os_snprintf(buf, buflen, "OK\n");
++ else
++ return -1;
++
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5512,6 +5530,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
+ reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
++ reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
++ reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index e0b175386..7e4485cb8 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1464,6 +1464,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
+ }
+
++static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
++}
+
+ #ifdef CONFIG_DPP
+
+@@ -1871,6 +1876,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
+ { "get_mu", hostapd_cli_cmd_get_mu, NULL,
+ " = show mu onoff value in 0-15 bitmap"},
++ { "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
++ "<value> 0: Enable beacon, 1: Disable beacon"},
+ #ifdef CONFIG_DPP
+ { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
+ "report a scanned DPP URI from a QR Code" },
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c71cfe1bd..d6bd157d8 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1388,3 +1388,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ return hapd->driver->pp_mode_set(hapd->drv_priv,
+ hapd->iconf->pp_mode);
+ }
++
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
++{
++ if (!hapd->driver || !hapd->driver->beacon_ctrl)
++ return 0;
++ return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
++}
++
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index ef61001e5..78e5c8d5a 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -170,6 +170,7 @@ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_ma
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6275c141d..5531802b8 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++ MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -268,6 +269,17 @@ enum mtk_vendor_attr_pp_ctrl {
+ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
+ };
+
++enum mtk_vendor_attr_beacon_ctrl {
++ MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
++ MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index be0e89ba3..332a51c55 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5243,6 +5243,13 @@ struct wpa_driver_ops {
+ int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
+
++ /**
++ * beacon_ctrl - ctrl on off for beacon
++ * @priv: Private driver interface data
++ *
++ */
++ int (*beacon_ctrl)(void *priv, u8 beacon_mode);
++
+ /**
+ * three_wire_ctrl - set three_wire_ctrl mode
+ * @priv: Private driver interface data
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index b47ab07ea..e588e7538 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14111,6 +14111,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+
+ return ret;
+ }
++static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
++ wpa_printf(MSG_ERROR,
++ "nl80211: Driver does not support setting beacon control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++}
++
+ #endif /* CONFIG_IEEE80211AX */
+
+
+@@ -15281,6 +15314,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .set_4addr_mode = nl80211_set_4addr_mode,
+ .mu_ctrl = nl80211_mu_ctrl,
+ .mu_dump = nl80211_mu_dump,
++ .beacon_ctrl = nl80211_beacon_ctrl,
+ #ifdef CONFIG_DPP
+ .dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 707bb7fe4..9866c221c 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_amnt_vendor_cmd_avail:1;
+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ unsigned int mtk_pp_vendor_cmd_avail:1;
++ unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index ba3c0817b..f3e3d52e2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1170,6 +1170,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
+ drv->mtk_pp_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
++ drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
new file mode 100644
index 0000000..06ee95c
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
@@ -0,0 +1,64 @@
+From 817f2a256b166e07d7b0abcee789976b47224429 Mon Sep 17 00:00:00 2001
+From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
+Date: Wed, 13 Dec 2023 18:13:01 +0530
+Subject: [PATCH 074/104] mtk: hostapd: WPS added change to configure AP PIN
+ lock timout
+
+added config paramter ap_pin_lockout_time to configure
+AP PIN timeout from hosatpd.conf
+
+Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h | 1 +
+ src/ap/wps_hostapd.c | 9 ++++++---
+ 3 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 050ef290e..7bc19479d 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4191,6 +4191,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->wps_independent = atoi(pos);
+ } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+ bss->ap_setup_locked = atoi(pos);
++ } else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
++ bss->ap_pin_lockout_time = atoi(pos);
+ } else if (os_strcmp(buf, "uuid") == 0) {
+ if (uuid_str2bin(pos, bss->uuid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 40edcdaa7..7f48c71f5 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -493,6 +493,7 @@ struct hostapd_bss_config {
+ #ifdef CONFIG_WPS
+ int wps_independent;
+ int ap_setup_locked;
++ unsigned int ap_pin_lockout_time;
+ u8 uuid[16];
+ char *wps_pin_requests;
+ char *device_name;
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index dfc5c3ecb..8a6fc42b2 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+ } else if (!hapd->conf->ap_setup_locked) {
+- if (hapd->ap_pin_lockout_time == 0)
+- hapd->ap_pin_lockout_time = 60;
+- else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
++ if (hapd->ap_pin_lockout_time == 0) {
++ if (hapd->conf->ap_pin_lockout_time)
++ hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
++ else
++ hapd->ap_pin_lockout_time = 60;
++ } else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+ (hapd->ap_pin_failures % 3) == 0)
+ hapd->ap_pin_lockout_time *= 2;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
new file mode 100644
index 0000000..cfb3f3d
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
@@ -0,0 +1,45 @@
+From 76d8a7f29b56075df3ad756f65374b2b238c2120 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 23 Jan 2024 10:52:57 +0800
+Subject: [PATCH 075/104] hostapd: mtk: ACS: remove chan/freq list check when
+ scan request and factor calculation
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/acs.c | 12 ------------
+ 1 file changed, 12 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 4c4c750ab..cb4db7147 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -595,12 +595,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;
+
+@@ -1358,12 +1352,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;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
new file mode 100644
index 0000000..d4e52f8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
@@ -0,0 +1,44 @@
+From 29b7ebf83cad69c48012eb5a03eb01a2a9fbfcab Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Wed, 1 Nov 2023 19:58:05 +0800
+Subject: [PATCH 076/104] mtk: hostapd: 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.
+
+Solution:
+In the hostapd_switch_channel_fallback, need to disable acs if channel
+is valid.
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ src/ap/hostapd.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index cdbf81e38..636655ea1 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4580,6 +4580,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.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
new file mode 100644
index 0000000..16f32cf
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
@@ -0,0 +1,398 @@
+From 5a471a9025d9bf2a871339f5306e5c9050357703 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 29 Jan 2024 10:26:53 +0800
+Subject: [PATCH 077/104] Revert ACS upstream changes
+
+- 348c047af ACS: More consistent checking of the best channel pointer
+- 98f3bd26d ACS: Extend the 320 MHz support
+- 733de8568 ACS: Fix not selecting the best channel in the segment
+- dc57ede01 tests: Full validation of ACS selecting HT40- channel
+- 4881accbb ACS: Add HT40- support in the 2.4 GHz band
+- 29f38ebcf ACS: Check whether iface->current_mode is NULL before use
+- 6f014c0d0 ACS: Add 320 MHz support for EHT
+
+Note that "e6f2494c3 hostapd: Add eht_bw320_offset configuration option"
+is not reverted due to conflict.
+---
+ src/ap/acs.c | 160 +++++++++----------------------------
+ tests/hwsim/test_ap_acs.py | 19 ++---
+ 2 files changed, 41 insertions(+), 138 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index cb4db7147..cfa4a7d27 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -245,8 +245,6 @@ enum bw_type {
+ ACS_BW40,
+ ACS_BW80,
+ ACS_BW160,
+- ACS_BW320_1,
+- ACS_BW320_2,
+ };
+
+ struct bw_item {
+@@ -288,20 +286,10 @@ static const struct bw_item bw_160[] = {
+ { 6435, 6575, 111 }, { 6595, 6735, 143 },
+ { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+ };
+-static const struct bw_item bw_320_1[] = {
+- { 5955, 6255, 31 }, { 6275, 6575, 95 }, { 6595, 6895, 159 },
+- { -1, -1, -1 }
+-};
+-static const struct bw_item bw_320_2[] = {
+- { 6115, 6415, 63 }, { 6435, 6735, 127 }, { 6755, 7055, 191 },
+- { -1, -1, -1 }
+-};
+ static const struct bw_item *bw_desc[] = {
+ [ACS_BW40] = bw_40,
+ [ACS_BW80] = bw_80,
+ [ACS_BW160] = bw_160,
+- [ACS_BW320_1] = bw_320_1,
+- [ACS_BW320_2] = bw_320_2,
+ };
+
+
+@@ -773,42 +761,6 @@ static void acs_update_puncturing_bitmap(struct hostapd_iface *iface,
+ #endif /* CONFIG_IEEE80211BE */
+
+
+-static bool
+-acs_usable_bw320_chan(struct hostapd_iface *iface,
+- struct hostapd_channel_data *chan, int *bw320_offset)
+-{
+- const char *bw320_str[] = { "320 MHz", "320 MHz-1", "320 MHz-2" };
+- int conf_bw320_offset = hostapd_get_bw320_offset(iface->conf);
+-
+- *bw320_offset = 0;
+- switch (conf_bw320_offset) {
+- case 1:
+- if (acs_usable_bw_chan(chan, ACS_BW320_1))
+- *bw320_offset = 1;
+- break;
+- case 2:
+- if (acs_usable_bw_chan(chan, ACS_BW320_2))
+- *bw320_offset = 2;
+- break;
+- case 0:
+- default:
+- conf_bw320_offset = 0;
+- if (acs_usable_bw_chan(chan, ACS_BW320_1))
+- *bw320_offset = 1;
+- else if (acs_usable_bw_chan(chan, ACS_BW320_2))
+- *bw320_offset = 2;
+- break;
+- }
+-
+- if (!*bw320_offset)
+- wpa_printf(MSG_DEBUG,
+- "ACS: Channel %d: not allowed as primary channel for %s bandwidth",
+- chan->chan, bw320_str[conf_bw320_offset]);
+-
+- return *bw320_offset != 0;
+-}
+-
+-
+ static void
+ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode,
+@@ -820,18 +772,14 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
+ long double factor;
+ int i, j;
+- int bw320_offset = 0, ideal_bw320_offset = 0;
+ unsigned int k;
+- int secondary_channel = 1, freq_offset;
+-
+- if (is_24ghz_mode(mode->mode))
+- secondary_channel = iface->conf->secondary_channel;
+
+ for (i = 0; i < mode->num_channels; i++) {
+- double total_weight = 0;
++ 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
+@@ -863,7 +811,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ iface->conf->country[2] == 0x4f)
+ continue;
+
+- if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
++ if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: BW %u is not supported",
+ chan->chan, bw);
+@@ -884,8 +832,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+- iface->conf->ieee80211be)) {
++ (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_80MHZ &&
+ !acs_usable_bw_chan(chan, ACS_BW80)) {
+@@ -905,25 +852,13 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+ }
+
+- if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- iface->conf->ieee80211be) {
+- if (hostapd_get_oper_chwidth(iface->conf) ==
+- CONF_OPER_CHWIDTH_320MHZ &&
+- !acs_usable_bw320_chan(iface, chan, &bw320_offset))
+- continue;
+- }
+-
+ factor = 0;
+- best = NULL;
+- if (acs_usable_chan(chan)) {
++ if (acs_usable_chan(chan))
+ factor = chan->interference_factor;
+- total_weight = 1;
+- best = chan;
+- }
++ total_weight = 1;
+
+ for (j = 1; j < n_chans; j++) {
+- adj_chan = acs_find_chan(iface, chan->freq +
+- j * secondary_channel * 20);
++ adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ if (!adj_chan)
+ break;
+
+@@ -934,14 +869,16 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ break;
+ }
+
+- if (!acs_usable_chan(adj_chan))
+- continue;
+-
+- factor += adj_chan->interference_factor;
+- total_weight += 1;
++ 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 (!best || adj_chan->interference_factor <
++ if (update_best &&
++ adj_chan->interference_factor <
+ best->interference_factor)
+ best = adj_chan;
+ }
+@@ -954,9 +891,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+
+ /* 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 &&
+- iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- best && chan != best) {
++ 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,
+@@ -969,9 +905,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ * channel interference factor. */
+ if (is_24ghz_mode(mode->mode)) {
+ for (j = 0; j < n_chans; j++) {
+- freq_offset = j * 20 * secondary_channel;
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset - 5);
++ (j * 20) - 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -979,7 +914,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset - 10);
++ (j * 20) - 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -987,7 +922,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset + 5);
++ (j * 20) + 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -995,7 +930,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset + 10);
++ (j * 20) + 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -1004,9 +939,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+ }
+
+- if (total_weight == 0)
+- continue;
+-
+ factor /= total_weight;
+
+ bias = NULL;
+@@ -1044,7 +976,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+
+ *ideal_factor = factor;
+ *ideal_chan = chan;
+- ideal_bw320_offset = bw320_offset;
+
+ #ifdef CONFIG_IEEE80211BE
+ if (iface->conf->ieee80211be)
+@@ -1055,13 +986,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ /* This channel would at least be usable */
+- if (!(*rand_chan)) {
++ if (!(*rand_chan))
+ *rand_chan = chan;
+- ideal_bw320_offset = bw320_offset;
+- }
+ }
+-
+- hostapd_set_and_check_bw320_offset(iface->conf, ideal_bw320_offset);
+ }
+
+
+@@ -1088,12 +1015,19 @@ 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;
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+- iface->conf->ieee80211be) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_80MHZ:
+ n_chans = 4;
+@@ -1101,9 +1035,6 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
+ case CONF_OPER_CHWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+- case CONF_OPER_CHWIDTH_320MHZ:
+- n_chans = 16;
+- break;
+ default:
+ break;
+ }
+@@ -1153,8 +1084,7 @@ static void acs_adjust_secondary(struct hostapd_iface *iface)
+ acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+ return;
+
+- wpa_printf(MSG_DEBUG,
+- "ACS: Adjusting HT/VHT/HE/EHT secondary frequency");
++ 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)
+@@ -1169,7 +1099,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ {
+ int center;
+
+- wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
++ wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+@@ -1187,29 +1117,12 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
+- break;
+- case CONF_OPER_CHWIDTH_320MHZ:
+- switch (hostapd_get_bw320_offset(iface->conf)) {
+- case 1:
+- center = acs_get_bw_center_chan(iface->freq,
+- ACS_BW320_1);
+- break;
+- case 2:
+- center = acs_get_bw_center_chan(iface->freq,
+- ACS_BW320_2);
+- break;
+- default:
+- wpa_printf(MSG_INFO,
+- "ACS: BW320 offset is not selected");
+- return;
+- }
+-
+ break;
+ default:
+ /* TODO: How can this be calculated? Adjust
+ * acs_find_ideal_chan() */
+ wpa_printf(MSG_INFO,
+- "ACS: Only VHT20/40/80/160/320 is supported now");
++ "ACS: Only VHT20/40/80/160 is supported now");
+ return;
+ }
+
+@@ -1272,8 +1185,7 @@ static void acs_study(struct hostapd_iface *iface)
+ iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
+ #endif /* CONFIG_IEEE80211BE */
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+- iface->conf->ieee80211be) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ acs_adjust_secondary(iface);
+ acs_adjust_center_freq(iface);
+ }
+diff --git a/tests/hwsim/test_ap_acs.py b/tests/hwsim/test_ap_acs.py
+index 001a5d4fd..e1359b6eb 100644
+--- a/tests/hwsim/test_ap_acs.py
++++ b/tests/hwsim/test_ap_acs.py
+@@ -205,20 +205,11 @@ def test_ap_acs_40mhz_minus(dev, apdev):
+ params['acs_num_scans'] = '1'
+ params['chanlist'] = '1 11'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+- wait_acs(hapd)
+-
+- freq = hapd.get_status_field("freq")
+- if int(freq) < 2400:
+- raise Exception("Unexpected frequency")
+- sec = hapd.get_status_field("secondary_channel")
+- if int(sec) != -1:
+- raise Exception("Unexpected secondary_channel: " + sec)
+-
+- dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+- sig = dev[0].request("SIGNAL_POLL").splitlines()
+- logger.info("SIGNAL_POLL: " + str(sig))
+- if "WIDTH=40 MHz" not in sig:
+- raise Exception("Station did not report 40 MHz bandwidth")
++ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
++ if not ev:
++ raise Exception("ACS start timed out")
++ # HT40- is not currently supported in hostapd ACS, so do not try to connect
++ # or verify that this operation succeeded.
+
+ def test_ap_acs_5ghz(dev, apdev):
+ """Automatic channel selection on 5 GHz"""
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
new file mode 100644
index 0000000..3f267b0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
@@ -0,0 +1,379 @@
+From 12fe53a7919abb791658bcc7a74b8131f0362300 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Thu, 28 Sep 2023 18:03:08 +0800
+Subject: [PATCH 078/104] mtk: hostapd: ACS: Add EHT320 and HT40- support, fix
+ issue
+
+1. Add 6G EHT320 support;
+2. Add 2.4G HT40- support;
+3. Fix issue: selected best channel is out of channels;
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ src/ap/acs.c | 191 +++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 124 insertions(+), 67 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index cfa4a7d27..1fa8b8e64 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -245,6 +245,7 @@ enum bw_type {
+ ACS_BW40,
+ ACS_BW80,
+ ACS_BW160,
++ ACS_BW320,
+ };
+
+ struct bw_item {
+@@ -286,10 +287,16 @@ static const struct bw_item bw_160[] = {
+ { 6435, 6575, 111 }, { 6595, 6735, 143 },
+ { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+ };
++static const struct bw_item bw_320[] = {
++ { 5955, 6255, 31 }, { 6115, 6415, 63 }, { 6275, 6575, 95 },
++ { 6435, 6735, 127 }, { 6595, 6895, 159 }, { 6755, 7055, 191 },
++ { -1, -1, -1 }
++};
+ static const struct bw_item *bw_desc[] = {
+ [ACS_BW40] = bw_40,
+ [ACS_BW80] = bw_80,
+ [ACS_BW160] = bw_160,
++ [ACS_BW320] = bw_320,
+ };
+
+
+@@ -769,10 +776,19 @@ 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;
+@@ -780,6 +796,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ bool update_best = 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
+@@ -811,7 +830,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 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);
+@@ -832,7 +851,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
++ (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
++ iface->conf->ieee80211be)) {
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_80MHZ &&
+ !acs_usable_bw_chan(chan, ACS_BW80)) {
+@@ -850,63 +870,86 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ chan->chan);
+ continue;
+ }
+- }
+
+- factor = 0;
+- if (acs_usable_chan(chan))
+- factor = chan->interference_factor;
+- total_weight = 1;
+-
+- 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)) {
++ if (iface->conf->ieee80211be &&
++ hostapd_get_oper_chwidth(iface->conf) ==
++ CONF_OPER_CHWIDTH_320MHZ &&
++ !acs_usable_bw_chan(chan, ACS_BW320)) {
+ wpa_printf(MSG_DEBUG,
+- "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
+- chan->chan, adj_chan->chan, bw);
+- break;
++ "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth",
++ chan->chan);
++ continue;
+ }
++ }
+
+- if (acs_usable_chan(adj_chan)) {
+- factor += adj_chan->interference_factor;
++ factor = 0;
++ total_weight = 0;
++
++ 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 (!is_in_chanlist(iface, adj_chan) || !is_in_freqlist(iface, adj_chan))
++ 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;
+- }
++ 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;
++ }
++
++ update_best = true;
++ if (acs_usable_chan(adj_chan)) {
++ factor += adj_chan->interference_factor;
++ total_weight += 1;
++ } else {
++ update_best = false;
++ }
+
+- /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+- * channel interference factor. */
+- if (is_24ghz_mode(mode->mode)) {
++ /* find the best channel in this segment */
++ if (update_best &&
++ adj_chan->interference_factor < best->interference_factor)
++ best = adj_chan;
++ }
++
++ if (j != n_chans) {
++ 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 * sec_ch_factor * 20));
++ 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 * sec_ch_factor * 20) - 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -914,7 +957,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- (j * 20) - 10);
++ (j * sec_ch_factor * 20) - 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -922,7 +965,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- (j * 20) + 5);
++ (j * sec_ch_factor * 20) + 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -930,7 +973,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- (j * 20) + 10);
++ (j * sec_ch_factor * 20) + 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -939,7 +982,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) {
+@@ -958,11 +1002,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);
+ }
+@@ -1015,19 +1059,12 @@ 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;
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
++ iface->conf->ieee80211be) {
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_80MHZ:
+ n_chans = 4;
+@@ -1037,6 +1074,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
+ break;
+ default:
+ break;
++ /* 320 is supported only in 6GHz 11be mode */
+ }
+ }
+
+@@ -1057,7 +1095,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);
+
+ #ifdef CONFIG_IEEE80211BE
+@@ -1072,6 +1110,21 @@ bw_selected:
+ return rand_chan;
+ }
+
++static int acs_get_center_freq_320mhz(int channel)
++{
++ if (channel >= 1 && channel <= 45)
++ return 31;
++ else if (channel >= 49 && channel <= 77)
++ return 63;
++ else if (channel >= 81 && channel <= 109)
++ return 95;
++ else if (channel >= 113 && channel <= 141)
++ return 127;
++ else if (channel >= 145 && channel <= 173)
++ return 159;
++ else
++ return 191;
++}
+
+ static void acs_adjust_secondary(struct hostapd_iface *iface)
+ {
+@@ -1099,7 +1152,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ {
+ int center;
+
+- wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
++ wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
+
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+@@ -1115,6 +1168,9 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ case CONF_OPER_CHWIDTH_80MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
+ break;
++ case CONF_OPER_CHWIDTH_320MHZ:
++ center = acs_get_center_freq_320mhz(iface->conf->channel);
++ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
+ break;
+@@ -1122,7 +1178,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ /* TODO: How can this be calculated? Adjust
+ * acs_find_ideal_chan() */
+ wpa_printf(MSG_INFO,
+- "ACS: Only VHT20/40/80/160 is supported now");
++ "ACS: Only VHT20/40/80/160 EHT320 is supported now");
+ return;
+ }
+
+@@ -1185,7 +1241,8 @@ static void acs_study(struct hostapd_iface *iface)
+ iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
+ #endif /* CONFIG_IEEE80211BE */
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
++ iface->conf->ieee80211be) {
+ acs_adjust_secondary(iface);
+ acs_adjust_center_freq(iface);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
new file mode 100644
index 0000000..a28db10
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
@@ -0,0 +1,122 @@
+From 26c23f7dc1fe47e22ceab581b7abed089148c68f Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 13:38:11 +0800
+Subject: [PATCH 079/104] mtk: hostapd: initialize i802_bss's flink->freq with
+ iface freq.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 6 +++---
+ src/ap/ap_drv_ops.h | 2 +-
+ src/ap/hostapd.c | 2 +-
+ src/drivers/driver.h | 2 +-
+ src/drivers/driver_nl80211.c | 4 ++--
+ wpa_supplicant/driver_i.h | 2 +-
+ 6 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index d6bd157d8..b7896c110 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -368,7 +368,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+ return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+- NULL, NULL, force_ifname, if_addr, NULL, 0);
++ NULL, NULL, force_ifname, if_addr, NULL, 0, hapd->iface->freq);
+ }
+
+
+@@ -560,13 +560,13 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+- const char *bridge, int use_existing)
++ const char *bridge, int use_existing, int freq)
+ {
+ if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+ return -1;
+ return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+ bss_ctx, drv_priv, force_ifname, if_addr,
+- bridge, use_existing, 1);
++ bridge, use_existing, 1, freq);
+ }
+
+
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 78e5c8d5a..5830705a3 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -58,7 +58,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+- const char *bridge, int use_existing);
++ const char *bridge, int use_existing, int freq);
+ int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname);
+ int hostapd_if_link_remove(struct hostapd_data *hapd,
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 636655ea1..e4fc1f85a 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1433,7 +1433,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ conf->iface, addr, hapd,
+ &hapd->drv_priv, force_ifname, if_addr,
+ conf->bridge[0] ? conf->bridge : NULL,
+- first == -1)) {
++ first == -1, hapd->iface->freq)) {
+ wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+ MACSTR ")", MAC2STR(hapd->own_addr));
+ hapd->interface_added = 0;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 332a51c55..2940650df 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3866,7 +3866,7 @@ struct wpa_driver_ops {
+ int (*if_add)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+- const char *bridge, int use_existing, int setup_ap);
++ const char *bridge, int use_existing, int setup_ap, int freq);
+
+ /**
+ * if_remove - Remove a virtual interface
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index e588e7538..3d69c9c49 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8872,7 +8872,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ void *bss_ctx, void **drv_priv,
+ char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing,
+- int setup_ap)
++ int setup_ap, int freq)
+ {
+ enum nl80211_iftype nlmode;
+ struct i802_bss *bss = priv;
+@@ -8992,7 +8992,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ new_bss->valid_links = 0;
+ os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
+
+- new_bss->flink->freq = drv->first_bss->flink->freq;
++ new_bss->flink->freq = (freq == -1) ? drv->first_bss->flink->freq : freq;
+ new_bss->ctx = bss_ctx;
+ new_bss->added_if = added;
+ drv->first_bss->next = new_bss;
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index 663e16053..624192ebd 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -446,7 +446,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
+ if (wpa_s->driver->if_add)
+ return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
+ addr, bss_ctx, NULL, force_ifname,
+- if_addr, bridge, 0, 0);
++ if_addr, bridge, 0, 0, -1);
+ return -1;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
new file mode 100644
index 0000000..42c06e4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
@@ -0,0 +1,35 @@
+From d06dd0d45977ce098df718a29ffbc765896a2758 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Jan 2024 11:24:28 +0800
+Subject: [PATCH 080/104] mtk: hostapd: fix mld_assoc_link_id
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/hostapd.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index e4fc1f85a..f8b05de45 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4034,11 +4034,13 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+
+ #ifdef CONFIG_IEEE80211BE
+- if (ap_sta_is_mld(hapd, sta) &&
+- sta->mld_assoc_link_id != hapd->mld_link_id)
+- return;
++ if (ap_sta_is_mld(hapd, sta)) {
++ if (sta->mld_assoc_link_id != hapd->mld_link_id)
++ return;
++ mld_assoc_link_id = sta->mld_assoc_link_id;
++ }
+ #endif /* CONFIG_IEEE80211BE */
+- if (mld_assoc_link_id != -2)
++ if (mld_assoc_link_id != -2)
+ hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
+
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
new file mode 100644
index 0000000..7bdb4fe
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
@@ -0,0 +1,25 @@
+From 3dbd0105364c15225a18098eeaae58119490918d Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:48:11 +0800
+Subject: [PATCH 081/104] mtk: wpa_s: correctly get assoc frequency
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 6631285bf..90084356d 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -328,6 +328,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+ wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ }
+
++ drv->assoc_freq = nl80211_get_assoc_freq(drv);
+ event.assoc_info.freq = drv->assoc_freq;
+ drv->first_bss->flink->freq = drv->assoc_freq;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
new file mode 100644
index 0000000..8ddecc9
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
@@ -0,0 +1,29 @@
+From 50e36560d14dbb6bf38b46dcfc58f9414e56b283 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:51:55 +0800
+Subject: [PATCH 082/104] mtk: wpa_s: force MLD STA to use SAE H2E during
+ authentication
+
+Otherwise the MLD STA setup will fail with hostapd MLD AP.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index f08184f98..e1183722f 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -199,7 +199,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
+ if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ use_pt = 1;
+- if (bss && is_6ghz_freq(bss->freq) &&
++ if (bss && (is_6ghz_freq(bss->freq) || !is_zero_ether_addr(bss->mld_addr)) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ use_pt = 1;
+ #ifdef CONFIG_SAE_PK
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
new file mode 100644
index 0000000..b3144cb
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
@@ -0,0 +1,71 @@
+From fb3820ff9fff1b15c13d4a799fbef8932fda7a1b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 11 Dec 2023 17:02:05 +0800
+Subject: [PATCH 083/104] mtk: hostapd: extend ap_get_sta() to find the correct
+ sta
+
+There're still some mld address tranlation issues that need to be dealt
+with on driver side (e.g. RX eapol frames). So add the code that find
+station also with link address and across hapds at the moment.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 1 +
+ src/ap/sta_info.c | 16 ++++++++++++++++
+ src/ap/sta_info.h | 1 +
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index ce3874901..0f357d786 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3116,6 +3116,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ mgmt->sa, ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].local_addr,
+ hapd->own_addr, ETH_ALEN);
++ os_memcpy(sta->setup_link_addr, mgmt->sa, ETH_ALEN);
+ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index ee6e20538..e9fa0ed6e 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -73,6 +73,22 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ s = hapd->sta_hash[STA_HASH(sta)];
+ while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ s = s->hnext;
++
++ if (hapd->conf->mld_ap && !s) {
++ u8 link_id;
++
++ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++ struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
++
++ if (!h)
++ continue;
++
++ for (s = h->sta_list; s; s = s->next)
++ if (!os_memcmp(s->setup_link_addr, sta, 6))
++ return s;
++ }
++ }
++
+ return s;
+ }
+
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 38b80903d..cd89db6c8 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -102,6 +102,7 @@ struct sta_info {
+ struct sta_info *next; /* next entry in sta list */
+ struct sta_info *hnext; /* next entry in hash table list */
+ u8 addr[6];
++ u8 setup_link_addr[6];
+ be32 ipaddr;
+ struct dl_list ip6addr; /* list head for struct ip6addr */
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
new file mode 100644
index 0000000..650da0e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
@@ -0,0 +1,39 @@
+From 665bc7cb59b4383aab615fff82fa601c468a4634 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 18 Dec 2023 18:53:35 +0800
+Subject: [PATCH 084/104] mtk: hostapd: update cookie only when noack is unset
+
+This can prevent cookie unmatched problems during setup.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 3d69c9c49..6d300c0c8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4472,7 +4472,7 @@ send_frame_cmd:
+ res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
+ use_cookie, no_cck, noack, offchanok,
+ csa_offs, csa_offs_len, link_id);
+- if (!res)
++ if (!res && !noack)
+ drv->send_frame_link_id = link_id;
+
+ return res;
+@@ -9205,8 +9205,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
+ "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+ (long long unsigned int) cookie);
+
+- if (save_cookie)
+- drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
++ if (save_cookie && !no_ack)
++ drv->send_frame_cookie = cookie;
+
+ if (!wait) {
+ /* There is no need to store this cookie since there
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
new file mode 100644
index 0000000..820e085
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
@@ -0,0 +1,34 @@
+From e2e07813d1e05a72aa649614ad942036b387aaf1 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 29 Dec 2023 15:04:27 +0800
+Subject: [PATCH 085/104] mtk: wpa_s: fix bss selection when setting
+ mld_connect_band_pref
+
+Without this patch, when setting mld_connect_band_pref as 5g, wrong bss
+will be selected.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index e1183722f..5b69812b5 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -437,8 +437,11 @@ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
+ }
+
+ for_each_link(wpa_s->valid_links, i) {
+- if (wpa_s->mlo_assoc_link_id == i)
++ if (wpa_s->mlo_assoc_link_id == i) {
++ if (bss->freq >= low && bss->freq <= high)
++ return bss;
+ continue;
++ }
+
+ if (wpa_s->links[i].freq >= low && wpa_s->links[i].freq <= high)
+ goto found;
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
new file mode 100644
index 0000000..3cf864d
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
@@ -0,0 +1,42 @@
+From bcb603194f7df4fd3060ed6a13a2e4da2715d959 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 26 Dec 2023 08:05:41 +0800
+Subject: [PATCH 086/104] mtk: hostapd: add mld_primary option
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h | 3 +++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 7bc19479d..e9caa45f3 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5349,6 +5349,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ conf->punct_acs_threshold = val;
+ } else if (os_strcmp(buf, "mld_ap") == 0) {
+ bss->mld_ap = !!atoi(pos);
++ } else if (os_strcmp(buf, "mld_primary") == 0) {
++ bss->mld_primary = !!atoi(pos);
+ } else if (os_strcmp(buf, "mld_addr") == 0) {
+ if (hwaddr_aton(pos, bss->mld_addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 7f48c71f5..1f686550e 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -966,6 +966,9 @@ struct hostapd_bss_config {
+ /* The AP is part of an AP MLD */
+ u8 mld_ap;
+
++ /* The AP is the primary AP of an AP MLD */
++ u8 mld_primary;
++
+ /* The MLD ID to which the AP MLD is affiliated with */
+ u8 mld_id;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
new file mode 100644
index 0000000..9634a0c
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
@@ -0,0 +1,104 @@
+From d6e854c62cd825756cc1b46c8b006855cf9e057e Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 6 Mar 2024 15:01:33 +0800
+Subject: [PATCH 087/104] mtk: wpa_supplicant: add 'mld_allowed_phy'
+ configuration option for MLD STA
+
+A new configuration option named 'mld_allowed_phy' is added for MLD STA.
+This option indicates the bitmap of allowed phy for MLO connection.
+Note that setting 'mld_allowed_phy' to 0 makes no phy allowed for MLO.
+In other word, the STA becomes a legacy STA.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I5ad15d3748c6fef476aa067cc4901157a96f8804
+---
+ wpa_supplicant/config.c | 1 +
+ wpa_supplicant/config.h | 1 +
+ wpa_supplicant/config_file.c | 2 ++
+ wpa_supplicant/sme.c | 18 ++++++++++++++++++
+ 4 files changed, 22 insertions(+)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index 7bb57e2ab..d3c75ee94 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -5680,6 +5680,7 @@ static const struct global_parse_data global_fields[] = {
+ #endif /* CONFIG_PASN */
+ #ifdef CONFIG_TESTING_OPTIONS
+ { INT_RANGE(mld_force_single_link, 0, 1), 0 },
++ { INT_RANGE(mld_allowed_phy, 0, 7), 0 },
+ { INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 },
+ { FUNC(mld_connect_bssid_pref), 0 },
+ #endif /* CONFIG_TESTING_OPTIONS */
+diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
+index 8981305c2..c0164fa76 100644
+--- a/wpa_supplicant/config.h
++++ b/wpa_supplicant/config.h
+@@ -1800,6 +1800,7 @@ struct wpa_config {
+ u8 mld_connect_bssid_pref[ETH_ALEN];
+
+ int mld_force_single_link;
++ u8 mld_allowed_phy; /* bitmap of allowed phy for MLO connection */
+ #endif /* CONFIG_TESTING_OPTIONS */
+ };
+
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index 7a3ed6373..875d00bb4 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -1622,6 +1622,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
+ #ifdef CONFIG_TESTING_OPTIONS
+ if (config->mld_force_single_link)
+ fprintf(f, "mld_force_single_link=1\n");
++ if (config->mld_allowed_phy)
++ fprintf(f, "mld_allowed_phy=%u\n", config->mld_allowed_phy);
+ if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO)
+ fprintf(f, "mld_connect_band_pref=%d\n",
+ config->mld_connect_band_pref);
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index 5b69812b5..ef258fadc 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -517,6 +517,16 @@ out:
+ }
+
+
++#ifdef CONFIG_TESTING_OPTIONS
++static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
++{
++ return ((wpa_s->conf->mld_allowed_phy & BIT(0)) && IS_2P4GHZ(freq)) ||
++ ((wpa_s->conf->mld_allowed_phy & BIT(1)) && IS_5GHZ(freq)) ||
++ ((wpa_s->conf->mld_allowed_phy & BIT(2)) && is_6ghz_freq(freq));
++}
++#endif /* CONFIG_TESTING_OPTIONS */
++
++
+ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+ {
+@@ -528,6 +538,11 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ for_each_link(bss->valid_links, i) {
+ const u8 *bssid = bss->mld_links[i].bssid;
+
++#ifdef CONFIG_TESTING_OPTIONS
++ if (!check_mld_allowed_phy(wpa_s, bss->mld_links[i].freq))
++ continue;
++#endif /* CONFIG_TESTING_OPTIONS */
++
+ wpa_s->valid_links |= BIT(i);
+ os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
+ wpa_s->links[i].freq = bss->mld_links[i].freq;
+@@ -577,6 +592,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
+ !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
+ NULL, ssid, NULL) &&
++#ifdef CONFIG_TESTING_OPTIONS
++ wpa_s->conf->mld_allowed_phy &&
++#endif /* CONFIG_TESTING_OPTIONS */
+ bss->valid_links) {
+ wpa_printf(MSG_DEBUG, "MLD: In authentication");
+ wpas_sme_set_mlo_links(wpa_s, bss);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
new file mode 100644
index 0000000..a04abae
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
@@ -0,0 +1,286 @@
+From a5b5d12eac0a1a4c53de67adc5793dbee87cb769 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 29 Feb 2024 19:55:34 +0800
+Subject: [PATCH 088/104] mtk: hostapd: support band_idx option for
+ set_mu/get_mu vendor command
+
+Support band_idx for set_mu and get_mu vendor command. The usage shows
+as below:
+1. get_mu: $ hostapd_cli -i <intf> get_mu <band_idx>
+2. set_mu: $ hostapd_cli -i <intf> set_mu <mu_onff>:<band_idx>
+
+CR-Id: WCNCR00240772
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Change-Id: I58635e588225a8876a77346c417ec281f9b76e4c
+---
+ hostapd/config_file.c | 9 +++++
+ hostapd/ctrl_iface.c | 78 ++++++++++++++++++++++++++++--------
+ hostapd/hostapd_cli.c | 2 +-
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 2 +-
+ src/common/mtk_vendor.h | 1 +
+ src/drivers/driver.h | 2 +-
+ src/drivers/driver_nl80211.c | 15 +++----
+ 8 files changed, 82 insertions(+), 28 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index e9caa45f3..ef9bafb28 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5431,6 +5431,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->pp_mode = (u8) val;
++ } else if (os_strcmp(buf, "band_idx") == 0) {
++ int val = atoi(pos);
++
++ if (val < 0) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid band_idx value",
++ line);
++ return 1;
++ }
++ conf->band_idx = (u8) val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 0fded7ed4..c5540f5fd 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4178,17 +4178,42 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ value = pos;
+
+ if (os_strcmp(config, "onoff") == 0) {
+- int mu = atoi(value);
+- if (mu < 0 || mu > 15) {
+- wpa_printf(MSG_ERROR, "Invalid value for mu");
+- return -1;
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt < 1 || val[0] > 15)
++ goto para_fail;
++
++ if (hostapd_is_mld_ap(hapd)) {
++ u8 band_idx;
++
++ if (cnt != 2)
++ goto para_fail;
++
++ band_idx = val[1];
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ struct hostapd_iface *iface;
++
++ iface = hapd->iface->interfaces->iface[i];
++ if (!iface || !iface->conf)
++ continue;
++
++ if (iface->conf->band_idx == band_idx) {
++ hapd = iface->bss[0];
++ break;
++ }
++ }
++ if (hapd->iface->conf->band_idx != band_idx)
++ goto para_fail;
+ }
+- hapd->iconf->mu_onoff = (u8) mu;
+
+- if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
+- return os_snprintf(buf, buflen, "OK\n");
+- else
++ hapd->iconf->mu_onoff = val[0];
++ os_free(val);
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) != 0)
+ goto fail;
++
++ return os_snprintf(buf, buflen, "OK\n");
+ }
+
+ if (hapd->iconf->muru_config == NULL)
+@@ -4200,6 +4225,7 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ comm = &muru->comm;
+
+ if (os_strncmp(config, "update", 6) == 0) {
++ // [ToDo] "update" needs to support band_idx argument
+ ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
+
+ os_free(hapd->iconf->muru_config);
+@@ -4342,15 +4368,14 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+
+ para_fail:
+ os_free(val);
+- wpa_printf(MSG_ERROR, "Incorrect input number\n");
++ wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
+ fail:
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+-
+ static int
+-hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+- size_t buflen)
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *input, char *buf,
++ size_t buflen)
+ {
+ u8 mu_onoff;
+ char *pos, *end;
+@@ -4358,14 +4383,35 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ pos = buf;
+ end = buf + buflen;
+
++ if (hostapd_is_mld_ap(hapd)) {
++ u8 band_idx, i;
++
++ band_idx = (u8)atoi(input);
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ struct hostapd_iface *iface;
++
++ iface = hapd->iface->interfaces->iface[i];
++ if (!iface || !iface->conf)
++ continue;
++
++ if (iface->conf->band_idx == band_idx) {
++ hapd = iface->bss[0];
++ break;
++ }
++ }
++ if (hapd->iface->conf->band_idx != band_idx)
++ return os_snprintf(pos, end - pos, "Invalid band idx to get_mu\n");
++ }
++
+ if (hapd->iface->state != HAPD_IFACE_ENABLED)
+ return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
+ hostapd_state_text(hapd->iface->state));
+
+ if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
+ hapd->iconf->mu_onoff = mu_onoff;
+- return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
+- !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++ return os_snprintf(pos, end - pos, "Band idx %u: UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++ hapd->iconf->band_idx, !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
+ } else {
+ wpa_printf(MSG_INFO, "ctrl iface failed to call");
+ return -1;
+@@ -5488,8 +5534,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_size);
+ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+- } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_MU ", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_get_mu(hapd, buf + 7, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 7e4485cb8..100896c34 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1461,7 +1461,7 @@ static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
+ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+- return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++ return hostapd_cli_cmd(ctrl, "GET_MU", 0, argc, argv);
+ }
+
+ static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 1f686550e..3bd8df9ce 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1298,6 +1298,7 @@ struct hostapd_config {
+ u8 amsdu;
+ void *muru_config;
+ u8 pp_mode;
++ u8 band_idx;
+ };
+
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index b7896c110..ac7ef00cd 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1281,7 +1281,7 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ {
+ if (!hapd->driver || !hapd->driver->mu_dump)
+ return 0;
+- return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff, hapd->iconf->band_idx);
+ }
+
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 5531802b8..1e4c3670e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -207,6 +207,7 @@ enum mtk_vendor_attr_mu_ctrl {
+ * above data structure.
+ */
+ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++ MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 2940650df..10ae48729 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5241,7 +5241,7 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*mu_ctrl)(void *priv, u8 mode, void *config);
+- int (*mu_dump)(void *priv, u8 *mu_onoff);
++ int (*mu_dump)(void *priv, u8 *mu_onoff, u8 band_idx);
+
+ /**
+ * beacon_ctrl - ctrl on off for beacon
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 6d300c0c8..17aaa16a8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14010,7 +14010,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+
+ switch (mode) {
+ case MU_CTRL_ONOFF:
+- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, cfg->band_idx))
+ goto fail;
+ break;
+ case MU_CTRL_UPDATE:
+@@ -14074,7 +14075,7 @@ static int mu_dump_handler(struct nl_msg *msg, void *arg)
+ return 0;
+ }
+
+-static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff, u8 band_idx)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14090,17 +14091,13 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+
+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++ !(attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, band_idx)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+- attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+- if (!attr) {
+- nlmsg_free(msg);
+- return -1;
+- }
+-
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
new file mode 100644
index 0000000..fab4ef8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
@@ -0,0 +1,134 @@
+From a0cbcd04400458bf7f4f4086beecbf8db6800c36 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:11:05 +0800
+Subject: [PATCH 089/104] mtk: hostapd: Handle DFS radar detection in MLO
+
+To handle DFS CAC in MLO, we add the following changes:
+1. Add link id info to radar detect cmd for the kernel to use the correct link.
+2. Block RNR IE for disabled iface. (the EID len would be wrong without it)
+3. Only flush the old stations for the first BSS; otherwise, after DFS CAC
+stations would be flushed again.
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Idbeba92aeadd8b34f61202e4c3df52bae19d9b73
+
+Add background radar handling
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 9 +++++++++
+ src/ap/hostapd.c | 3 +++
+ src/ap/ieee802_11.c | 3 +++
+ src/drivers/driver_nl80211.c | 18 ++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_event.c | 3 ++-
+ 6 files changed, 36 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index ac7ef00cd..9357ce7b6 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1012,6 +1012,15 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ return -1;
+ }
+ data.radar_background = radar_background;
++ data.link_id = -1;
++
++#ifdef CONFIG_IEEE80211BE
++ if (hapd->conf->mld_ap) {
++ data.link_id = hapd->mld_link_id;
++ wpa_printf(MSG_DEBUG,
++ "hostapd_start_dfs_cac: link_id=%d", data.link_id);
++ }
++#endif /* CONFIG_IEEE80211BE */
+
+ res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ if (!res) {
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f8b05de45..8e3f0b281 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1371,6 +1371,9 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ if (!hostapd_mld_is_first_bss(hapd))
+ wpa_printf(MSG_DEBUG,
+ "MLD: %s: Setting non-first BSS", __func__);
++ else
++ /* Only flush old stations when setting up first BSS for MLD. */
++ flush_old_stations = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+ __func__, hapd, conf->iface, first);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 0f357d786..fe0a5bce4 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7852,6 +7852,9 @@ u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
++ if (!iface->bss[0]->started)
++ continue;
++
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ current_len, NULL, false);
+ }
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 17aaa16a8..ad73b4ac1 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -10589,6 +10589,24 @@ static int nl80211_start_radar_detection(void *priv,
+ return -1;
+ }
+
++ if (freq->link_id != NL80211_DRV_LINK_ID_NA) {
++ wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for radar detect",
++ freq->link_id);
++
++ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++
++ if (freq->radar_background) {
++ struct i802_link *link = nl80211_get_link(bss, freq->link_id);
++
++ link->background_freq = freq->freq;
++ } else {
++ nl80211_link_set_freq(bss, freq->link_id, freq->freq);
++ }
++ }
++
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret == 0)
+ return 0;
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 9866c221c..a0a62e536 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -56,6 +56,7 @@ struct i802_link {
+ unsigned int beacon_set:1;
+
+ int freq;
++ int background_freq;
+ int bandwidth;
+ u8 addr[ETH_ALEN];
+ void *ctx;
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 90084356d..03bad4bb3 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1631,7 +1631,8 @@ nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
+ unsigned int i;
+
+ for_each_link(bss->valid_links, i) {
+- if ((unsigned int) bss->links[i].freq == freq)
++ if ((unsigned int) bss->links[i].freq == freq ||
++ (unsigned int) bss->links[i].background_freq == freq)
+ return i;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
new file mode 100644
index 0000000..8f3096b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
@@ -0,0 +1,60 @@
+From d71c29484010bcb0bda82eb529689d0748bd653e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 31 Jan 2024 10:39:08 +0800
+Subject: [PATCH 090/104] mtk: hostapd: add link id to hostapd cli chan switch
+
+temporary workaround for mlo channel switch
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 3 +--
+ hostapd/hostapd_cli.c | 2 +-
+ src/ap/ctrl_iface_ap.c | 2 ++
+ 3 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index c5540f5fd..f0c990314 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2801,10 +2801,9 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ if (ret)
+ return ret;
+
+- settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ if (iface->num_bss && iface->bss[0]->conf->mld_ap)
+- settings.link_id = iface->bss[0]->mld_link_id;
++ iface = iface->interfaces->iface[settings.link_id];
+ #endif /* CONFIG_IEEE80211BE */
+
+ ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 100896c34..acfa3b1d1 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1195,7 +1195,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
+ printf("Invalid chan_switch command: needs at least two "
+ "arguments (count and freq)\n"
+ "usage: <cs_count> <freq> [sec_channel_offset=] "
+- "[center_freq1=] [center_freq2=] [bandwidth=] "
++ "[center_freq1=] [center_freq2=] [bandwidth=] [link_id=] "
+ "[blocktx] [ht|vht|he|eht]\n");
+ return -1;
+ }
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index b92311e32..ca4e3e7a4 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1108,6 +1108,8 @@ int hostapd_parse_csa_settings(const char *pos,
+ } \
+ } while (0)
+
++ SET_CSA_SETTING(link_id);
++ settings->link_id = settings->freq_params.link_id;
+ SET_CSA_SETTING(center_freq1);
+ SET_CSA_SETTING(center_freq2);
+ SET_CSA_SETTING(bandwidth);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
new file mode 100644
index 0000000..1ce57ff
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
@@ -0,0 +1,160 @@
+From 11e9653a0df41e119ff5acad3ffcbd2825690413 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 16 Jan 2024 16:22:17 +0800
+Subject: [PATCH 091/104] mtk: wifi: hostapd: add wds mlo support
+
+1. Add mld_assoc_sta to get the primary sta_info.
+2. Find hapd according to mld address.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: Ic2d67c0623299dfc00021925296c54e25fe86959
+---
+ src/ap/drv_callbacks.c | 3 ++-
+ src/ap/ieee802_11.c | 8 ++++++++
+ src/ap/sta_info.c | 6 +++++-
+ src/ap/sta_info.h | 1 +
+ src/drivers/driver.h | 3 +++
+ src/drivers/driver_nl80211.c | 14 ++++++++++++++
+ 6 files changed, 33 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 2d946afd6..27c7555a1 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1762,6 +1762,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
+ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ const u8 *bssid, int link_id)
+ {
++ struct hostapd_data *ret = NULL;
+ size_t i;
+
+ if (bssid == NULL)
+@@ -1797,7 +1798,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ #endif /*CONFIG_IEEE80211BE */
+ }
+
+- return NULL;
++ return ret;
+ }
+
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fe0a5bce4..688393422 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3105,6 +3105,7 @@ static void handle_auth(struct hostapd_data *hapd,
+
+ ap_sta_set_mld(sta, true);
+ sta->mld_assoc_link_id = link_id;
++ sta->mld_assoc_sta = sta;
+
+ /*
+ * Set the MLD address as the station address and the
+@@ -4479,6 +4480,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+
+ sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
++ sta->mld_assoc_sta = origin_sta;
+
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ if (status != WLAN_STATUS_SUCCESS) {
+@@ -6957,6 +6959,12 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, src);
++
++#ifdef CONFIG_IEEE80211BE
++ if (sta && sta->mld_info.mld_sta)
++ sta = sta->mld_assoc_sta;
++#endif
++
+ if (sta &&
+ ((sta->flags & WLAN_STA_ASSOC) ||
+ ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index e9fa0ed6e..2b8307a27 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -74,6 +74,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ s = s->hnext;
+
++#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && !s) {
+ u8 link_id;
+
+@@ -84,10 +85,13 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ continue;
+
+ for (s = h->sta_list; s; s = s->next)
+- if (!os_memcmp(s->setup_link_addr, sta, 6))
++ if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
++ !os_memcmp(s->addr, sta, 6)) &&
++ s->flags & WLAN_STA_ASSOC)
+ return s;
+ }
+ }
++#endif
+
+ return s;
+ }
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index cd89db6c8..8e500ec9a 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -334,6 +334,7 @@ struct sta_info {
+ #ifdef CONFIG_IEEE80211BE
+ struct mld_info mld_info;
+ u8 mld_assoc_link_id;
++ struct sta_info *mld_assoc_sta;
+ #endif /* CONFIG_IEEE80211BE */
+ };
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 10ae48729..ba61f5842 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5339,6 +5339,9 @@ struct wpa_driver_ops {
+ * @pp_mode: Value is defined in enum pp_mode
+ */
+ int (*pp_mode_set)(void *priv, const u8 pp_mode);
++#ifdef CONFIG_IEEE80211BE
++ int (*get_mld_addr)(void *priv, u8 *addr);
++#endif
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index ad73b4ac1..df4a7ec41 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15181,6 +15181,17 @@ fail:
+ return -ENOBUFS;
+ }
+
++#ifdef CONFIG_IEEE80211BE
++static int nl80211_get_mld_addr(void *priv, u8 *addr)
++{
++ struct i802_bss *bss = priv;
++
++ os_memcpy(addr, bss->addr, ETH_ALEN);
++
++ return 0;
++}
++#endif
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15361,4 +15372,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .amnt_dump = nl80211_amnt_dump,
+ .background_radar_mode = nl80211_background_radar_mode,
+ .pp_mode_set = nl80211_pp_mode_set,
++#ifdef CONFIG_IEEE80211BE
++ .get_mld_addr = nl80211_get_mld_addr,
++#endif
+ };
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
new file mode 100644
index 0000000..607e260
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
@@ -0,0 +1,29 @@
+From ce0ccc758fc8a5076ce3476f627b00019cf90ab1 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Mar 2024 16:59:53 +0800
+Subject: [PATCH 093/104] mtk: hostapd: prevent getting non-MLD STA for other
+ links
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/sta_info.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 2b8307a27..9a8510980 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -87,7 +87,8 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ for (s = h->sta_list; s; s = s->next)
+ if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
+ !os_memcmp(s->addr, sta, 6)) &&
+- s->flags & WLAN_STA_ASSOC)
++ s->flags & WLAN_STA_ASSOC &&
++ s->mld_info.mld_sta)
+ return s;
+ }
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
new file mode 100644
index 0000000..a5e6812
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
@@ -0,0 +1,40 @@
+From 8c4eb9b740a9f2ae57e048edbbc4aad1d62734f2 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 27 Feb 2024 15:04:35 +0800
+Subject: [PATCH 094/104] mtk: hostapd: AP MLD: specify link id for unicast
+ DEAUTH
+
+When deauthenticating the STA, hostapd should specifies the setup link
+of the target STA so that the TX status of the DEAUTH can be forwarded
+to the correct link (BSS).
+
+(The original gerrit somehow disappears, so I commit it again)
+(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/hostapd/+/8715613)
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I938f0fb80862074b95fc33b3c4f566f92ae21de1
+---
+ src/ap/ap_drv_ops.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 9357ce7b6..2c535f24a 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -867,7 +867,11 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+- link_id = hapd->mld_link_id;
++ if (sta)
++ link_id = sta->mld_assoc_link_id;
++ else
++ link_id = hapd->mld_link_id;
++
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld->mld_addr;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
new file mode 100644
index 0000000..90da0fd
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
@@ -0,0 +1,38 @@
+From 161c066295847f1c1828f641e789565fe4bacd11 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 14 Mar 2024 14:31:28 +0800
+Subject: [PATCH 095/104] mtk: hostapd: using MLD addr as SA/BSSID for
+ broadcast DEAUTH
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I4ba7763a0f5c5a16174cf80393b5f1588a9ce42c
+---
+ src/ap/ap_drv_ops.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2c535f24a..3c1609656 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -872,7 +872,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ else
+ link_id = hapd->mld_link_id;
+
+- if (ap_sta_is_mld(hapd, sta))
++ if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ own_addr = hapd->mld->mld_addr;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+@@ -893,7 +893,7 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+- if (ap_sta_is_mld(hapd, sta))
++ if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ own_addr = hapd->mld->mld_addr;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
new file mode 100644
index 0000000..6fd7d62
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
@@ -0,0 +1,32 @@
+From eb9916b7e0226793f14616dc98fda76c4d8337bf Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 27 Feb 2024 15:32:06 +0800
+Subject: [PATCH 096/104] mtk: hostapd: support vht bfee sts can be up to 0x4
+
+Without this commit, the maximum vht bfee sts can only be 0x3. This commit
+support to read BF-ANTENNA-5 to set vht bfee sts capability as 4.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Change-Id: I3ec1dc28149961bbb01c31a12cd4acd0fd77c2f4
+---
+ hostapd/config_file.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index ef9bafb28..b9a062193 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1190,6 +1190,9 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ if (os_strstr(capab, "[BF-ANTENNA-4]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
++ if (os_strstr(capab, "[BF-ANTENNA-5]") &&
++ (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
++ conf->vht_capab |= (4 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
new file mode 100644
index 0000000..1afcf3e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
@@ -0,0 +1,54 @@
+From c1e7c5e556cd372594a789091d2ab65a9d8dd56f Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 14:08:50 +0800
+Subject: [PATCH 097/104] mtk: hostapd: fix issue that tx status handle with
+ unmatch hostapd_data.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ieee802_11.c | 11 ++++++++++-
+ src/drivers/driver_nl80211_event.c | 2 +-
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 688393422..2dd763e63 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6536,11 +6536,20 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta) &&
+ hapd->mld_link_id != sta->mld_assoc_link_id) {
++ struct hostapd_data *temp_hapd = hapd;
++
+ /* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
+ wpa_printf(MSG_DEBUG,
+ "%s: MLD: ignore on link station (%d != %d)",
+ __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
+- return;
++
++ if (temp_hapd->conf->mld_ap && sta->mld_assoc_link_id >= 0) {
++ struct hostapd_data *link_bss;
++
++ link_bss = hostapd_mld_get_link_bss(temp_hapd, sta->mld_assoc_link_id);
++ if (link_bss)
++ hapd = link_bss;
++ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 03bad4bb3..885fc4c9e 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1374,7 +1374,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ event.rx_mgmt.ctx = bss->ctx;
+ event.rx_mgmt.link_id = link_id;
+
+- wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
++ wpa_supplicant_event(bss->ctx, EVENT_RX_MGMT, &event);
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
new file mode 100644
index 0000000..061f038
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
@@ -0,0 +1,692 @@
+From c125d2615df25bd9a5a4801abfbcc91f21482e02 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Tue, 6 Feb 2024 15:46:05 +0800
+Subject: [PATCH 098/104] mtk: hostapd: add connac3 csi control interface
+
+1. add hostapd_cli interface
+2. add csi set/dump flow
+3. add csi raw data to json
+
+CR-Id: WCNCR00364748
+Change-Id: If16d4b39ebb7942e47ed4bf4459589d6191983ac
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 193 ++++++++++++++++++++++++
+ hostapd/hostapd_cli.c | 16 ++
+ src/ap/ap_drv_ops.c | 13 ++
+ src/ap/ap_drv_ops.h | 2 +
+ src/common/mtk_vendor.h | 31 +++-
+ src/drivers/driver.h | 16 ++
+ src/drivers/driver_nl80211.c | 235 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 9 files changed, 503 insertions(+), 7 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index f0c990314..b5f6431bf 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4925,6 +4925,193 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
+
+ }
+
++static int
++hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp;
++ u8 sta_mac[ETH_ALEN] = {0};
++ u32 csi_para[4] = {0};
++ char mac_str[18] = {0};
++ u8 csi_para_cnt = 0;
++
++ tmp = strtok_r(cmd, ",", &cmd);
++
++ while (tmp) {
++ csi_para_cnt++;
++
++ if (csi_para_cnt <= 4)
++ csi_para[csi_para_cnt - 1] = strtol(tmp, &tmp, 10);
++ else if (csi_para_cnt == 5) {
++ memcpy(mac_str, tmp, sizeof(mac_str) - 1);
++ break;
++ }
++
++ tmp = strtok_r(NULL, ",", &cmd);
++ }
++
++ if (strlen(mac_str)) { /* user input mac string */
++ if (hwaddr_aton(mac_str, sta_mac) < 0) {
++ wpa_printf(MSG_ERROR, "station mac is not right.\n");
++ return -1;
++ }
++
++ if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], sta_mac)) {
++ wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d,%s\n",
++ csi_para[0], csi_para[1], csi_para[2], csi_para[3], mac_str);
++ return -1;
++ }
++ } else {
++ if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], NULL)) {
++ wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d\n",
++ csi_para[0], csi_para[1], csi_para[2], csi_para[3]);
++ return -1;
++ }
++ }
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int mt76_csi_to_json(char *fname, struct csi_resp_data *resp_buf)
++{
++#define MAX_BUF_SIZE 10000
++ FILE *f;
++ int i;
++
++ if (!fname) {
++ wpa_printf(MSG_ERROR, "csi dump file name is null!\n");
++ return -1;
++ }
++
++ f = fopen(fname, "a+");
++ if (!f) {
++ wpa_printf(MSG_ERROR, "open csi dump file %s failed\n", fname);
++ return -1;
++ }
++
++ if (fwrite("[", 1, 1, f) != 1) {
++ fclose(f);
++ return -1;
++ }
++
++ for (i = 0; i < resp_buf->buf_cnt; i++) {
++ struct csi_data *c = &resp_buf->csi_buf[i];
++ char *pos, *buf;
++ int j;
++
++ buf = malloc(MAX_BUF_SIZE);
++ if (!buf) {
++ fclose(f);
++ return -1;
++ }
++
++ pos = buf;
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ts);
++ pos += snprintf(pos, MAX_BUF_SIZE, "\"%02x%02x%02x%02x%02x%02x\",", c->ta[0], c->ta[1], c->ta[2], c->ta[3], c->ta[4], c->ta[5]);
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->chain_info);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ext_info);
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++ for (j = 0; j < c->data_num; j++) {
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]);
++ if (j != (c->data_num - 1))
++ pos += snprintf(pos, MAX_BUF_SIZE, ",");
++ }
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']');
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++ for (j = 0; j < c->data_num; j++) {
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]);
++ if (j != (c->data_num - 1))
++ pos += snprintf(pos, MAX_BUF_SIZE, ",");
++ }
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++ if (i != resp_buf->buf_cnt - 1)
++ pos += snprintf(pos, MAX_BUF_SIZE, ",");
++
++ if (fwrite(buf, 1, pos - buf, f) != (pos - buf)) {
++ perror("fwrite");
++ free(buf);
++ fclose(f);
++ return -1;
++ }
++
++ free(buf);
++ }
++
++ if (fwrite("]", 1, 1, f) != 1) {
++ fclose(f);
++ return -1;
++ }
++
++ fclose(f);
++
++ return 0;
++}
++
++static int
++hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp, *fname;
++ int data_cnt = 0, ret = 0;
++ struct csi_resp_data resp_buf;
++
++ tmp = strtok_r(cmd, ",", &cmd);
++
++ if (!tmp) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ data_cnt = strtoul(tmp, &tmp, 0);
++
++ if (data_cnt > 3000) {
++ wpa_printf(MSG_ERROR, "Wrong input csi data cnt\n");
++ return -1;
++ }
++
++ fname = strtok_r(NULL, ",", &cmd);
++
++ if (!fname) {
++ wpa_printf(MSG_ERROR, "Error in command format, csi_filename.\n");
++ return -1;
++ }
++
++ resp_buf.csi_buf = (struct csi_data *)os_zalloc(sizeof(struct csi_data) * data_cnt);
++
++ if (resp_buf.csi_buf == NULL) {
++ wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++ return -1;
++ }
++
++ resp_buf.usr_need_cnt = data_cnt;
++ resp_buf.buf_cnt = 0;
++
++ if (hostapd_drv_csi_dump(hapd, (void *)&resp_buf)) {
++ wpa_printf(MSG_ERROR, "Not able to set csi dump\n");
++ os_free(resp_buf.csi_buf);
++ return -1;
++ }
++
++ mt76_csi_to_json(fname, &resp_buf);
++
++ os_free(resp_buf.csi_buf);
++ return 0;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5578,6 +5765,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
+ reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
+ reply_size);
++ } else if (os_strncmp(buf, "SET_CSI ", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_set_csi(hapd, buf + 8,
++ reply, reply_size);
++ } else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
++ reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index acfa3b1d1..81b74d6de 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1719,6 +1719,18 @@ static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
+ return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
+ }
+
++static int hostapd_cli_cmd_set_csi(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "SET_CSI", 1, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1960,6 +1972,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = Set Station index and mac to monitor"},
+ { "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
+ " = Dump RSSI of monitoring Station"},
++ { "set_csi", hostapd_cli_cmd_set_csi, NULL,
++ " = Set csi configuaration"},
++ { "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
++ " = Dump csi data to a json file"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 3c1609656..cb782fa31 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1409,3 +1409,16 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+ return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
+ }
+
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++ if (!hapd->driver || !hapd->driver->csi_set)
++ return 0;
++ return hapd->driver->csi_set(hapd->drv_priv, mode, cfg, v1, v2, mac);
++}
++
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
++{
++ if (!hapd->driver || !hapd->driver->csi_dump)
++ return 0;
++ return hapd->driver->csi_dump(hapd->drv_priv, dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 5830705a3..7abc58377 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -171,6 +171,8 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 1e4c3670e..c290e72a7 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -73,7 +73,6 @@ enum mtk_vendor_attr_csi_ctrl {
+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
+ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
+- MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
+
+ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
+
+@@ -96,6 +95,7 @@ enum mtk_vendor_attr_csi_data {
+ MTK_VENDOR_ATTR_CSI_DATA_BW,
+ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
+ MTK_VENDOR_ATTR_CSI_DATA_TA,
++ MTK_VENDOR_ATTR_CSI_DATA_NUM,
+ MTK_VENDOR_ATTR_CSI_DATA_I,
+ MTK_VENDOR_ATTR_CSI_DATA_Q,
+ MTK_VENDOR_ATTR_CSI_DATA_INFO,
+@@ -106,7 +106,7 @@ enum mtk_vendor_attr_csi_data {
+ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
+ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
+ MTK_VENDOR_ATTR_CSI_DATA_MODE,
+- MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++ MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
+@@ -281,23 +281,40 @@ enum mtk_vendor_attr_beacon_ctrl {
+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+
+-#define CSI_MAX_COUNT 256
++#define CSI_BW20_DATA_COUNT 64
++#define CSI_BW40_DATA_COUNT 128
++#define CSI_BW80_DATA_COUNT 256
++#define CSI_BW160_DATA_COUNT 512
++#define CSI_BW320_DATA_COUNT 1024
+ #define ETH_ALEN 6
+
+ struct csi_data {
+- s16 data_i[CSI_MAX_COUNT];
+- s16 data_q[CSI_MAX_COUNT];
++ u8 ch_bw;
++ u16 data_num;
++ s16 data_i[CSI_BW320_DATA_COUNT];
++ s16 data_q[CSI_BW320_DATA_COUNT];
++ u8 band;
+ s8 rssi;
+ u8 snr;
+ u32 ts;
+ u8 data_bw;
+ u8 pri_ch_idx;
+ u8 ta[ETH_ALEN];
+- u32 info;
++ u32 ext_info;
+ u8 rx_mode;
+- u32 h_idx;
++ u32 chain_info;
+ u16 tx_idx;
+ u16 rx_idx;
++ u32 segment_num;
++ u8 remain_last;
++ u16 pkt_sn;
++ u8 tr_stream;
++};
++
++struct csi_resp_data {
++ u16 usr_need_cnt;
++ u16 buf_cnt;
++ struct csi_data *csi_buf;
+ };
+
+ #define AIR_MONITOR_MAX_ENTRY 16
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ba61f5842..7efb5e342 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5342,6 +5342,22 @@ struct wpa_driver_ops {
+ #ifdef CONFIG_IEEE80211BE
+ int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
++ /**
++ * csi_set - Set csi related mode and parameter
++ * @priv: Private driver interface data
++ * @mode: Csi mode parameter
++ * @cfg: Csi config parameter
++ * @v1: Value1
++ * @v2: Value2
++ * @mac: Station mac for station filter
++ */
++ int (*csi_set)(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++ /**
++ * csi_dump - Dump csi data to json file
++ * @priv: Private driver interface data
++ * @dump_buf: Dump_struct that store csi data and related info
++ */
++ int (*csi_dump)(void *priv, void *dump_buf);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index df4a7ec41..39b45ef4b 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -161,6 +161,35 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+ };
+
++static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
++ [MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_DATA_NUM] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -15192,6 +15221,210 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
+ }
+ #endif
+
++static int
++nl80211_csi_set(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ void *tb1, *tb2;
++ int ret, i;
++
++ if (!drv->mtk_csi_vendor_cmd_avail) {
++ wpa_printf(MSG_ERROR,
++ "nl80211: Driver does not support csi");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED);
++ if (!tb1)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE, mode);
++ nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE, cfg);
++ nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, v1);
++ nla_put_u32(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, v2);
++
++ nla_nest_end(msg, tb1);
++
++ if (mac) {
++ tb2 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED);
++ if (!tb2)
++ goto fail;
++
++ for (i = 0; i < ETH_ALEN; i++)
++ nla_put_u8(msg, i, mac[i]);
++
++ nla_nest_end(msg, tb2);
++ }
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set csi. ret=%d (%s)",
++ ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++
++}
++
++static int
++mt76_csi_dump_cb(struct nl_msg *msg, void *arg)
++{
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
++ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_CSI_DATA];
++ struct nlattr *attr, *cur, *data;
++ int len = 0, rem, idx;
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct csi_resp_data *csi_resp = (struct csi_resp_data *)arg;
++ struct csi_data *c = csi_resp->csi_buf;
++
++ c += csi_resp->buf_cnt;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ attr = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!attr)
++ return NL_SKIP;
++
++ nla_parse_nested(tb1, MTK_VENDOR_ATTR_CSI_CTRL_MAX,
++ attr, csi_ctrl_policy);
++
++ if (!tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA])
++ return NL_SKIP;
++
++ nla_parse_nested(tb2, MTK_VENDOR_ATTR_CSI_DATA_MAX,
++ tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy);
++
++ if (!(tb2[MTK_VENDOR_ATTR_CSI_DATA_VER] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_TS] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_BW] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_TA] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_I] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_Q] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO])) {
++ fprintf(stderr, "Attributes error for CSI data\n");
++ return NL_SKIP;
++ }
++
++ c->rssi = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI]);
++ c->snr = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR]);
++ c->data_bw = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_BW]);
++ c->pri_ch_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]);
++ c->rx_mode = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE]);
++
++ c->tx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]);
++ c->rx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]);
++
++ c->ext_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO]);
++ c->chain_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO]);
++
++ c->ts = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_TS]);
++
++ c->data_num = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_NUM]);
++
++ idx = 0;
++ nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) {
++ if (idx < ETH_ALEN)
++ c->ta[idx++] = nla_get_u8(cur);
++ }
++
++ idx = 0;
++ nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_I], rem) {
++ if (idx < c->data_num)
++ c->data_i[idx++] = nla_get_u16(cur);
++ }
++
++ idx = 0;
++ nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) {
++ if (idx < c->data_num)
++ c->data_q[idx++] = nla_get_u16(cur);
++ }
++
++ csi_resp->buf_cnt++;
++
++ return NL_SKIP;
++}
++
++static int
++nl80211_csi_dump(void *priv, void *dump_buf)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++ struct csi_resp_data *csi_resp;
++ u16 pkt_num, i;
++
++ if (!drv->mtk_csi_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support csi");
++ return 0;
++ }
++
++ csi_resp = (struct csi_resp_data *)dump_buf;
++ pkt_num = csi_resp->usr_need_cnt;
++
++ if (pkt_num > 3000)
++ return -EINVAL;
++
++#define CSI_DUMP_PER_NUM 3
++ for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) {
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM);
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, mt76_csi_dump_cb, dump_buf);
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15375,4 +15608,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ #ifdef CONFIG_IEEE80211BE
+ .get_mld_addr = nl80211_get_mld_addr,
+ #endif
++ .csi_set = nl80211_csi_set,
++ .csi_dump = nl80211_csi_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index a0a62e536..b710b50cd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ unsigned int mtk_pp_vendor_cmd_avail:1;
+ unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
++ unsigned int mtk_csi_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index f3e3d52e2..8688b849f 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1173,6 +1173,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
+ drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
++ drv->mtk_csi_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
new file mode 100644
index 0000000..a63f81a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
@@ -0,0 +1,46 @@
+From a5c0e5c09398a247236d73078a4f86a960a97e34 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 8 Apr 2024 16:27:51 +0800
+Subject: [PATCH 099/104] fixup! mtk: wifi: hostapd: add wds mlo support
+
+The latest get_hapd_bssid return hapd only if link id is matched.
+However,the hostapd_rx_from_unknown_sta does not have link
+information so it cannot get hapd.
+
+Modify get_hapd_bssid to ignore link id when link id is -1.
+
+Without this patch, wds mode cannot work and the AP would not be
+aware that station is using 4 address.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I9661226ed0847abb1de0d8c808daf3ad8cd5dc99
+---
+ src/ap/drv_callbacks.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 27c7555a1..82973c5e4 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1779,7 +1779,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ if (ether_addr_equal(bssid, hapd->own_addr) ||
+ (hapd->conf->mld_ap &&
+ ether_addr_equal(bssid, hapd->mld->mld_addr) &&
+- link_id == hapd->mld_link_id)) {
++ (link_id == hapd->mld_link_id || link_id == -1))) {
+ return hapd;
+ } else if (hapd->conf->mld_ap) {
+ for_each_mld_link(p_hapd, hapd) {
+@@ -1788,7 +1788,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+
+ if (ether_addr_equal(bssid, p_hapd->own_addr) ||
+ (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
+- link_id == p_hapd->mld_link_id))
++ (link_id == p_hapd->mld_link_id || link_id == -1)))
+ return p_hapd;
+ }
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch b/recipes-wifi/hostapd/files/patches-2.10.3/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch
deleted file mode 100644
index 0a51c84..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch
+++ /dev/null
@@ -1,135 +0,0 @@
-From 8de8cd8380af0c43d4fde67a668d79ef73b26b26 Mon Sep 17 00:00:00 2001
-From: Peter Oh <peter.oh@bowerswilkins.com>
-Date: Tue, 30 Jun 2020 14:18:58 +0200
-Subject: [PATCH 10/19] mesh: Allow DFS channels to be selected if dfs is
- enabled
-
-Note: DFS is assumed to be usable if a country code has been set
-
-Signed-off-by: Benjamin Berg <benjamin@sipsolutions.net>
-Signed-off-by: Peter Oh <peter.oh@bowerswilkins.com>
----
- wpa_supplicant/wpa_supplicant.c | 25 +++++++++++++++++++------
- 1 file changed, 19 insertions(+), 6 deletions(-)
-
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -2638,7 +2638,7 @@ static int drv_supports_vht(struct wpa_s
- }
-
-
--static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
-+static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
- {
- int i;
-
-@@ -2647,7 +2647,10 @@ static bool ibss_mesh_is_80mhz_avail(int
-
- chan = hw_get_channel_chan(mode, i, NULL);
- if (!chan ||
-- chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+ chan->flag & HOSTAPD_CHAN_DISABLED)
-+ return false;
-+
-+ if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
- return false;
- }
-
-@@ -2774,7 +2777,7 @@ static void ibss_mesh_select_40mhz(struc
- const struct wpa_ssid *ssid,
- struct hostapd_hw_modes *mode,
- struct hostapd_freq_params *freq,
-- int obss_scan) {
-+ int obss_scan, bool dfs_enabled) {
- int chan_idx;
- struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
- int i, res;
-@@ -2798,8 +2801,11 @@ static void ibss_mesh_select_40mhz(struc
- return;
-
- /* Check primary channel flags */
-- if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+ if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
- return;
-+ if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+ if (!dfs_enabled)
-+ return;
-
- #ifdef CONFIG_HT_OVERRIDES
- if (ssid->disable_ht40)
-@@ -2825,8 +2831,11 @@ static void ibss_mesh_select_40mhz(struc
- return;
-
- /* Check secondary channel flags */
-- if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+ if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
- return;
-+ if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+ if (!dfs_enabled)
-+ return;
-
- if (ht40 == -1) {
- if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
-@@ -2880,7 +2889,7 @@ static bool ibss_mesh_select_80_160mhz(s
- const struct wpa_ssid *ssid,
- struct hostapd_hw_modes *mode,
- struct hostapd_freq_params *freq,
-- int ieee80211_mode, bool is_6ghz) {
-+ int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
- static const int bw80[] = {
- 5180, 5260, 5500, 5580, 5660, 5745, 5825,
- 5955, 6035, 6115, 6195, 6275, 6355, 6435,
-@@ -2925,7 +2934,7 @@ static bool ibss_mesh_select_80_160mhz(s
- goto skip_80mhz;
-
- /* Use 40 MHz if channel not usable */
-- if (!ibss_mesh_is_80mhz_avail(channel, mode))
-+ if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
- goto skip_80mhz;
-
- chwidth = CONF_OPER_CHWIDTH_80MHZ;
-@@ -2939,7 +2948,7 @@ static bool ibss_mesh_select_80_160mhz(s
- if ((mode->he_capab[ieee80211_mode].phy_cap[
- HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
- HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
-- ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
-+ ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
- for (j = 0; j < ARRAY_SIZE(bw160); j++) {
- if (freq->freq == bw160[j]) {
- chwidth = CONF_OPER_CHWIDTH_160MHZ;
-@@ -2967,10 +2976,12 @@ static bool ibss_mesh_select_80_160mhz(s
- if (!chan)
- continue;
-
-- if (chan->flag & (HOSTAPD_CHAN_DISABLED |
-- HOSTAPD_CHAN_NO_IR |
-- HOSTAPD_CHAN_RADAR))
-+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
- continue;
-+ if (chan->flag & (HOSTAPD_CHAN_RADAR |
-+ HOSTAPD_CHAN_NO_IR))
-+ if (!dfs_enabled)
-+ continue;
-
- /* Found a suitable second segment for 80+80 */
- chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
-@@ -3025,6 +3036,7 @@ void ibss_mesh_setup_freq(struct wpa_sup
- int i, obss_scan = 1;
- u8 channel;
- bool is_6ghz;
-+ bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
-
- freq->freq = ssid->frequency;
-
-@@ -3070,9 +3082,9 @@ void ibss_mesh_setup_freq(struct wpa_sup
- freq->channel = channel;
- /* Setup higher BW only for 5 GHz */
- if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
-- ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
-+ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
-- ieee80211_mode, is_6ghz))
-+ ieee80211_mode, is_6ghz, dfs_enabled))
- freq->he_enabled = freq->vht_enabled = false;
- }
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
new file mode 100644
index 0000000..e688a56
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
@@ -0,0 +1,90 @@
+From 30cfdaf10a5b3bb2a173a096b2c708c4a18d55bc Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 8 Apr 2024 14:34:36 +0800
+Subject: [PATCH 100/104] mtk: hostapd: MLD: find partner links by BSSID and
+ SSID
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/bss.c | 8 ++++++--
+ wpa_supplicant/sme.c | 14 ++++++++------
+ 2 files changed, 14 insertions(+), 8 deletions(-)
+
+diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
+index 289035310..ae0e61bc3 100644
+--- a/wpa_supplicant/bss.c
++++ b/wpa_supplicant/bss.c
+@@ -1529,8 +1529,12 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
+ wpa_printf(MSG_DEBUG,
+ "MLD: Reported link not part of MLD");
+ } else if (!(BIT(link_id) & *seen)) {
+- struct wpa_bss *neigh_bss =
+- wpa_bss_get_bssid(wpa_s, pos + 1);
++ struct wpa_bss *neigh_bss;
++
++ if (ssid)
++ neigh_bss = wpa_bss_get(wpa_s, pos + 1, ssid->ssid, ssid->ssid_len);
++ else
++ neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1);
+
+ *seen |= BIT(link_id);
+ wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index ef258fadc..0b4b8e3ce 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -390,7 +390,8 @@ static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s,
+
+ #ifdef CONFIG_TESTING_OPTIONS
+ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
+- struct wpa_bss *bss)
++ struct wpa_bss *bss,
++ struct wpa_ssid *ssid)
+ {
+ unsigned int low, high, i;
+
+@@ -459,7 +460,7 @@ found:
+ MAC2STR(wpa_s->links[i].bssid));
+
+ /* Get the BSS entry and do the switch */
+- bss = wpa_bss_get_bssid(wpa_s, wpa_s->links[i].bssid);
++ bss = wpa_bss_get(wpa_s, wpa_s->links[i].bssid, ssid->ssid, ssid->ssid_len);
+ wpa_s->mlo_assoc_link_id = i;
+
+ return bss;
+@@ -528,7 +529,7 @@ static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
+
+
+ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+- struct wpa_bss *bss)
++ struct wpa_bss *bss, struct wpa_ssid *ssid)
+ {
+ u8 i;
+
+@@ -551,7 +552,8 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ if (bss->mld_link_id == i)
+ wpa_s->links[i].bss = bss;
+ else
+- wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
++ wpa_s->links[i].bss = wpa_bss_get(wpa_s, bssid, ssid->ssid,
++ ssid->ssid_len);
+ }
+ }
+
+@@ -597,10 +599,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ #endif /* CONFIG_TESTING_OPTIONS */
+ bss->valid_links) {
+ wpa_printf(MSG_DEBUG, "MLD: In authentication");
+- wpas_sme_set_mlo_links(wpa_s, bss);
++ wpas_sme_set_mlo_links(wpa_s, bss, ssid);
+
+ #ifdef CONFIG_TESTING_OPTIONS
+- bss = wpas_ml_connect_pref(wpa_s, bss);
++ bss = wpas_ml_connect_pref(wpa_s, bss, ssid);
+
+ if (wpa_s->conf->mld_force_single_link) {
+ wpa_printf(MSG_DEBUG, "MLD: Force single link");
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
new file mode 100644
index 0000000..27f986a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
@@ -0,0 +1,248 @@
+From 0cc6f925c0a97da331f41462a07c4ae4b6698409 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 2 Apr 2024 15:36:29 +0800
+Subject: [PATCH 101/104] mtk: hostapd: MLD: hostapd: add support for basic MLD
+ Extender
+
+Add basic MLD Extender support, including
+1. Extender STA stops all Extender AP's links before scaning.
+2. After finishing the connection with root AP, Extender STA
+ synchronizes control channel with each link on the Extender
+ AP.
+
+Advanced support includes BW cynchronization and channel switch.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c | 52 ++++++++++++++++++++++++++++++++++++++----
+ src/utils/ucode.c | 4 +++-
+ wpa_supplicant/ucode.c | 45 +++++++++++++++++++++++++++++-------
+ 3 files changed, 88 insertions(+), 13 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 98b2a3bf2..2642e87c7 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -487,14 +487,16 @@ static uc_value_t *
+ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ {
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ struct hostapd_data *first_hapd;
++ struct hostapd_bss_config *conf;
+ int i;
+
+- wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
+- iface->phy, hostapd_state_text(iface->state));
+-
+ if (!iface)
+ return NULL;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++ iface->phy, hostapd_state_text(iface->state));
++
+ if (iface->state != HAPD_IFACE_ENABLED)
+ uc_hostapd_disable_iface(iface);
+
+@@ -505,6 +507,32 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ hapd->beacon_set_done = 0;
+ }
+
++#ifdef CONFIG_IEEE80211BE
++ first_hapd = iface->bss[0];
++ conf = first_hapd->conf;
++ for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
++ struct hostapd_iface *h = iface->interfaces->iface[i];
++ struct hostapd_data *h_hapd = h->bss[0];
++ struct hostapd_bss_config *hconf = h_hapd->conf;
++
++ if (h == iface) {
++ wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
++ continue;
++ }
++
++ if (!hconf->mld_ap) {
++ wpa_printf(MSG_DEBUG,
++ "MLD: Skip non MLD");
++ continue;
++ }
++
++ if (hostapd_is_ml_partner(first_hapd, h_hapd)) {
++ hostapd_drv_stop_ap(h_hapd);
++ h_hapd->beacon_set_done = 0;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ return NULL;
+ }
+
+@@ -512,11 +540,12 @@ static uc_value_t *
+ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ {
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ struct hostapd_data *tmp_hapd;
+ uc_value_t *info = uc_fn_arg(0);
+ struct hostapd_config *conf;
+ bool changed = false;
+ uint64_t intval;
+- int i;
++ int i, band_idx;
+
+ wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
+ iface->phy, hostapd_state_text(iface->state));
+@@ -532,6 +561,21 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ if (ucv_type(info) != UC_OBJECT)
+ return NULL;
+
++ intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
++ if (!errno)
++ band_idx = intval;
++
++#ifdef CONFIG_IEEE80211BE
++ if (hostapd_is_mld_ap(iface->bss[0])) {
++ for_each_mld_link(tmp_hapd, iface->bss[0]) {
++ if (band_idx == tmp_hapd->iconf->band_idx) {
++ iface = tmp_hapd->iface;
++ break;
++ }
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ #define UPDATE_VAL(field, name) \
+ if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) && \
+ !errno && intval != conf->field) do { \
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 6f82382f3..81d472f6b 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,7 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ uc_value_t *freq = uc_fn_arg(0);
+ uc_value_t *sec = uc_fn_arg(1);
+ int width = ucv_uint64_get(uc_fn_arg(2));
+- int bw320_offset = 1;
++ int bw320_offset = 1, band_idx;
+ int freq_val, center_idx, center_ofs;
+ enum oper_chan_width chanwidth;
+ enum hostapd_hw_mode hw_mode;
+@@ -123,6 +123,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ return NULL;
+
+ freq_val = ucv_int64_get(freq);
++ band_idx = ucv_int64_get(uc_fn_arg(4));
+ if (ucv_type(sec) == UC_INTEGER)
+ sec_channel = ucv_int64_get(sec);
+ else if (sec)
+@@ -183,6 +184,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
+ ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
++ ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+
+ if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
+ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 542ca25c9..ac0639a90 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -246,12 +246,13 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ {
+ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ struct wpa_bss *bss;
+- uc_value_t *ret, *val;
++ uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ struct wpa_channel_info ci;
+ u8 op_class, channel;
+- enum oper_chan_width ch_width;
+- int center_freq1, bw320_offset = 1, is_24ghz;
++ enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
++ int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ enum hostapd_hw_mode hw_mode;
++ int link_id = ucv_int64_get(link_obj);
+
+ if (!wpa_s)
+ return NULL;
+@@ -261,13 +262,25 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
+ ucv_object_add(ret, "state", ucv_get(val));
+
+- bss = wpa_s->current_bss;
++ bss = link_id == -1 ? wpa_s->current_bss : wpa_s->links[link_id].bss;
+ if (bss) {
+ int sec_chan = 0;
+
+ hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
+ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ hw_mode == HOSTAPD_MODE_IEEE80211B;
++ /*
++ * Assume that the mapping between band and band_idx is
++ * 2 GHz band: band_idx 0
++ * 5 GHz band: band_idx 1
++ * 6 GHz band: band_idx 2
++ * */
++ if (is_24ghz)
++ band_idx = 0;
++ else if (IS_5GHZ(bss->freq))
++ band_idx = 1;
++ else if (is_6ghz_freq(bss->freq))
++ band_idx = 2;
+
+ wpa_drv_channel_info(wpa_s, &ci);
+ center_freq1 = ci.center_frq1;
+@@ -279,11 +292,10 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
+ }
+
+- if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+- sec_chan, &op_class, &channel))
+- return NULL;
++ if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++ sec_chan, &op_class, &channel))
++ ch_width = op_class_to_ch_width(op_class);
+
+- ch_width = op_class_to_ch_width(op_class);
+ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
+ (center_freq1 == 6265) || center_freq1 == 6585 ||
+ center_freq1 == 6905) {
+@@ -295,6 +307,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+ ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
+ ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
++ ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ }
+
+ #ifdef CONFIG_MESH
+@@ -309,6 +322,21 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ return ret;
+ }
+
++static uc_value_t *
++uc_wpas_iface_get_valid_links(uc_vm_t *vm, size_t nargs)
++{
++ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++ uc_value_t *ret;
++
++ if (!wpa_s)
++ return NULL;
++
++ ret = ucv_object_new(vm);
++ ucv_object_add(ret, "valid_links", ucv_uint64_new(wpa_s->valid_links));
++
++ return ret;
++}
++
+ int wpas_ucode_init(struct wpa_global *gl)
+ {
+ static const uc_function_list_t global_fns[] = {
+@@ -320,6 +348,7 @@ int wpas_ucode_init(struct wpa_global *gl)
+ };
+ static const uc_function_list_t iface_fns[] = {
+ { "status", uc_wpas_iface_status },
++ { "get_valid_links", uc_wpas_iface_get_valid_links },
+ };
+ uc_value_t *data, *proto;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
new file mode 100644
index 0000000..8881b72
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
@@ -0,0 +1,338 @@
+From 80b2cbbd80700d1fa04d97aae5b8a083a4b4f2ba Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 2 Apr 2024 16:51:07 +0800
+Subject: [PATCH 102/104] mtk: hostapd: Refactor static PP and mld support
+
+Add band_idx attribute in pp cmd for vendor cmd under mld setting.
+
+CR-Id: WCNCR00259302
+Change-Id: I3c4c7e9dff5eaa2ff48537dc0c5398006a305713
+---
+ hostapd/config_file.c | 6 ++--
+ hostapd/ctrl_iface.c | 69 +++++++++++++++++++++++++-----------
+ hostapd/ctrl_iface.h | 3 +-
+ hostapd/hostapd_cli.c | 15 ++++++++
+ src/ap/ap_config.h | 4 +--
+ src/ap/ap_drv_ops.c | 7 ++--
+ src/ap/dfs.c | 3 ++
+ src/ap/hostapd.c | 4 +--
+ src/common/mtk_vendor.h | 1 +
+ src/drivers/driver.h | 3 +-
+ src/drivers/driver_nl80211.c | 4 ++-
+ 11 files changed, 87 insertions(+), 32 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index b9a062193..2add62ca9 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5339,7 +5339,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ if (get_u16(pos, line, &conf->punct_bitmap))
+ return 1;
+ conf->punct_bitmap = atoi(pos);
+- conf->pp_mode = PP_MANUAL_MODE;
++ conf->pp_mode = PP_USR_MODE;
+ } else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ int val = atoi(pos);
+
+@@ -5427,8 +5427,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "pp_mode") == 0) {
+ int val = atoi(pos);
+
+- if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
+- val < PP_DISABLE || val > PP_MANUAL_MODE) {
++ if ((val != PP_USR_MODE && conf->punct_bitmap) ||
++ val < PP_DISABLE || val > PP_USR_MODE) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
+ line);
+ return 1;
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b5f6431bf..6d4ce8acb 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4854,27 +4854,57 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx)
++{
++ struct hostapd_data *link;
++
++ if (!hostapd_is_mld_ap(hapd))
++ return hapd;
++
++ for_each_mld_link(link, hapd) {
++ if (link->iconf->band_idx == band_idx)
++ break;
++ }
++
++ if (!link || link->iconf->band_idx != band_idx) {
++ wpa_printf(MSG_ERROR, "Invalid band idx %d\n", band_idx);
++ return NULL;
++ }
++
++ return link;
++}
++
+ static int
+ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ size_t buflen)
+ {
+- char *pos, *config, *value;
++ char *band, *config, *value;
++ u8 band_idx;
+
+ config = cmd;
+- pos = os_strchr(config, ' ');
+- if (pos == NULL)
++
++ value = os_strchr(config, ' ');
++ if (value == NULL)
+ return -1;
+- *pos++ = '\0';
++ *value++ = '\0';
+
+- if (pos == NULL)
++ band = os_strchr(value, ' ');
++ if (band == NULL)
++ return -1;
++ *band++ = '\0';
++ band_idx = strtol(band, NULL, 10);
++
++ hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++ if (!hapd)
+ return -1;
+- value = pos;
+
+ if (os_strcmp(config, "mode") == 0) {
+- int val = atoi(value);
++ int val = strtol(value, NULL, 10);
+
+- if (val < PP_DISABLE || val > PP_AUTO_MODE) {
+- wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++ if (val < PP_DISABLE || val > PP_FW_MODE) {
++ wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
+ return -1;
+ }
+ hapd->iconf->pp_mode = (u8) val;
+@@ -4882,7 +4912,8 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ return -1;
+ } else {
+ wpa_printf(MSG_ERROR,
+- "Unsupported parameter %s for set_pp", config);
++ "Unsupported parameter %s for SET_PP"
++ "Usage: set_pp mode <value> <band_idx>", config);
+ return -1;
+ }
+ return os_snprintf(buf, buflen, "OK\n");
+@@ -4892,19 +4923,17 @@ static int
+ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ size_t buflen)
+ {
+- char *pos, *end;
++ u8 band_idx;
+
+- pos = buf;
+- end = buf + buflen;
++ band_idx = strtol(cmd, NULL, 10);
+
+- if (os_strcmp(cmd, "mode") == 0) {
+- return os_snprintf(pos, end - pos, "pp_mode: %d\n",
+- hapd->iconf->pp_mode);
+- } else {
+- wpa_printf(MSG_ERROR,
+- "Unsupported parameter %s for get_pp", cmd);
++ hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++ if (!hapd)
+ return -1;
+- }
++
++ return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
++ hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
+ }
+
+ static int
+diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
+index 3341a66bd..82a64b880 100644
+--- a/hostapd/ctrl_iface.h
++++ b/hostapd/ctrl_iface.h
+@@ -35,5 +35,6 @@ hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
+ {
+ }
+ #endif /* CONFIG_NO_CTRL_IFACE */
+-
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx);
+ #endif /* CTRL_IFACE_H */
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 81b74d6de..4bf70d183 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1730,6 +1730,17 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
+ {
+ return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
+ }
++static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
++}
++
++static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
++}
+
+ struct hostapd_cli_cmd {
+ const char *cmd;
+@@ -1976,6 +1987,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = Set csi configuaration"},
+ { "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
+ " = Dump csi data to a json file"},
++ { "set_pp", hostapd_cli_cmd_set_pp, NULL,
++ " = Set preamble puncture mode"},
++ { "get_pp", hostapd_cli_cmd_get_pp, NULL,
++ " = Get preamble puncture status"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3bd8df9ce..5192c1f07 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1353,8 +1353,8 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+
+ enum pp_mode {
+ PP_DISABLE = 0,
+- PP_AUTO_MODE,
+- PP_MANUAL_MODE,
++ PP_FW_MODE,
++ PP_USR_MODE,
+ };
+
+ #define EDCCA_DEFAULT_COMPENSATION -6
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index cb782fa31..a76148eba 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1396,10 +1396,13 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ {
+ if (!hapd->driver || !hapd->driver->pp_mode_set ||
+- hapd->iconf->pp_mode > PP_AUTO_MODE)
++ hapd->iconf->pp_mode >= PP_USR_MODE ||
++ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
++
+ return hapd->driver->pp_mode_set(hapd->drv_priv,
+- hapd->iconf->pp_mode);
++ hapd->iconf->pp_mode,
++ hapd->iconf->band_idx);
+ }
+
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index b12290556..9bbeab1d6 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1080,6 +1080,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ os_memset(&csa_settings, 0, sizeof(csa_settings));
+ csa_settings.cs_count = 5;
+ csa_settings.block_tx = 1;
++ csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
+ csa_settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ if (iface->bss[0]->conf->mld_ap)
+@@ -1644,6 +1645,8 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ return 0;
+ }
+
++ iface->bss[0]->iconf->punct_bitmap = 0;
++
+ if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+ /* Radar detected while operating, switch the channel. */
+ return hostapd_dfs_start_channel_switch(iface);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 8e3f0b281..3d3359291 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2697,6 +2697,8 @@ dfs_offload:
+ }
+ #endif /* CONFIG_MESH */
+
++ if (hostapd_drv_pp_mode_set(hapd) < 0)
++ goto fail;
+ if (hostapd_drv_configure_edcca_enable(hapd) < 0)
+ goto fail;
+
+@@ -2711,8 +2713,6 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ goto fail;
+- if (hostapd_drv_pp_mode_set(hapd) < 0)
+- goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index c290e72a7..09805b6fb 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -263,6 +263,7 @@ enum mtk_vendor_attr_pp_ctrl {
+ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_PP_MODE,
++ MTK_VENDOR_ATTR_PP_BAND_IDX,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 7efb5e342..539771729 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5337,8 +5337,9 @@ struct wpa_driver_ops {
+ * pp_mode_set - Set preamble puncture operation mode
+ * @priv: Private driver interface data
+ * @pp_mode: Value is defined in enum pp_mode
++ * @band_idx: chip band index
+ */
+- int (*pp_mode_set)(void *priv, const u8 pp_mode);
++ int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
+ #ifdef CONFIG_IEEE80211BE
+ int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 39b45ef4b..6ec2c32df 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -159,6 +159,7 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ static struct nla_policy
+ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
+ };
+
+ static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+@@ -15167,7 +15168,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ return ret;
+ }
+
+-static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15194,6 +15195,7 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
+ if (!data)
+ goto fail;
+
++ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
+ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
+
+ nla_nest_end(msg, data);
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
new file mode 100644
index 0000000..9f5346e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
@@ -0,0 +1,126 @@
+From ec7d47b2566da59c52d995f5e404a1c00e746fe5 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 11 Apr 2024 18:16:38 +0800
+Subject: [PATCH 103/104] mtk: hostapd: make sure all links are set before
+ enabling beacon
+
+NL80211_CMD_NEW_BEACON will first be set, but we've modified mac80211 to
+disable this beacon. After that, hostapd will block
+NL80211_CMD_SET_BEACON until all links are setting up.
+(use NL80211_CMD_START_AP event to check if all expected links are enabled)
+
+Update: in wpa_driver_nl80211_set_ap(), send_and_recv() is used, implies
+that hostapd should already sync with driver, so don't need to use
+NL80211_CMD_START_AP event.
+
+This can make sure that the first beacon of each link includes the
+correct RNR and per-STA profile.
+
+Note that in NL80211_CMD_NEW_BEACON, we also set beacon interval to 0,
+which helps to bypass some mac80211 beacon active checks, e.g., during ACS.
+
+CR-Id: WCNCR00238098
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I99320f7802041dc7d8e780041bb686ce3700c0b6
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h | 2 ++
+ src/ap/beacon.c | 10 ++++++++++
+ src/ap/hostapd.c | 14 ++++++++++++++
+ src/ap/hostapd.h | 1 +
+ 5 files changed, 29 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 2add62ca9..8abe1bc46 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5354,6 +5354,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->mld_ap = !!atoi(pos);
+ } else if (os_strcmp(buf, "mld_primary") == 0) {
+ bss->mld_primary = !!atoi(pos);
++ } else if (os_strcmp(buf, "mld_allowed_links") == 0) {
++ bss->mld_allowed_links = atoi(pos);
+ } else if (os_strcmp(buf, "mld_addr") == 0) {
+ if (hwaddr_aton(pos, bss->mld_addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 5192c1f07..0ea5a04e2 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -968,6 +968,8 @@ struct hostapd_bss_config {
+
+ /* The AP is the primary AP of an AP MLD */
+ u8 mld_primary;
++ /* Allowed link bitmap of the AP MLD to which the AP is affiliated */
++ u16 mld_allowed_links;
+
+ /* The MLD ID to which the AP MLD is affiliated with */
+ u8 mld_id;
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index a5c46b067..a5c9dd87e 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2164,6 +2164,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+ head->u.beacon.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
++ /* if MLD AP hasn't finished setting up all links, also set beacon interval
++ * to 0. This allows mac80211 to bypass some beacon active checks, for
++ * example, when doing ACS
++ */
++ if (hapd->conf->mld_ap && !hapd->mld->started)
++ head->u.beacon.beacon_int = host_to_le16(0);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ capab_info = hostapd_own_capab_info(hapd);
+@@ -2553,6 +2559,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ int res, ret = -1, i;
+ struct hostapd_hw_modes *mode;
+
++ /* skip setting beacon if other links are not started yet */
++ if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
++ return 0;
++
+ if (!hapd->drv_priv) {
+ wpa_printf(MSG_ERROR, "Interface is disabled");
+ return -1;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 3d3359291..c31e0badd 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1314,6 +1314,20 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ return -1;
+
++ if (hapd->conf->mld_ap && !hapd->mld->started) {
++ struct hostapd_data *p_hapd;
++ u16 valid_links = 0;
++
++ for_each_mld_link(p_hapd, hapd)
++ valid_links |= BIT(p_hapd->mld_link_id);
++
++ if (valid_links == hapd->conf->mld_allowed_links ||
++ !hapd->conf->mld_allowed_links) {
++ hapd->mld->started = 1;
++ ieee802_11_set_beacon(hapd);
++ }
++ }
++
+ if (flush_old_stations && !conf->start_disabled &&
+ conf->broadcast_deauth) {
+ u8 addr[ETH_ALEN];
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 5b37be87b..83636649f 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -537,6 +537,7 @@ struct hostapd_mld {
+ * freed when num_links is 0.
+ */
+ u8 refcount;
++ bool started;
+
+ struct hostapd_data *fbss;
+ struct dl_list links; /* List head of all affiliated links */
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
new file mode 100644
index 0000000..95be18f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
@@ -0,0 +1,54 @@
+From 7b4363892397c667e65fae9e036c83ac039e308d Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 17 Apr 2024 13:17:59 +0800
+Subject: [PATCH 104/104] mtk: hostapd: ucode: add is_mld_finished check
+
+Add is_mld_finished check for ucode need.
+This function returns ture only if all links fromt all MLD APs are
+ready.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 2642e87c7..9f9cc2022 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -734,6 +734,23 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ return ucv_boolean_new(!ret);
+ }
+
++static uc_value_t *
++uc_hostapd_iface_is_mld_finished(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ bool finished = true;
++ int i;
++
++ for (i = 0; i < iface->num_bss; i++) {
++ if (iface->bss[i]->conf->mld_ap && !iface->bss[i]->mld->started) {
++ finished = false;
++ break;
++ }
++ }
++
++ return ucv_boolean_new(finished);
++}
++
+ static uc_value_t *
+ uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
+ {
+@@ -806,6 +823,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+ { "stop", uc_hostapd_iface_stop },
+ { "start", uc_hostapd_iface_start },
+ { "switch_channel", uc_hostapd_iface_switch_channel },
++ { "is_mld_finished", uc_hostapd_iface_is_mld_finished },
+ };
+ uc_value_t *data, *proto;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/011-mesh-use-deterministic-channel-on-channel-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/011-mesh-use-deterministic-channel-on-channel-switch.patch
deleted file mode 100644
index 07b7a59..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/011-mesh-use-deterministic-channel-on-channel-switch.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From fc8ea40f6130ac18d9c66797de2cf1d5af55d496 Mon Sep 17 00:00:00 2001
-From: Markus Theil <markus.theil@tu-ilmenau.de>
-Date: Tue, 30 Jun 2020 14:19:07 +0200
-Subject: [PATCH 19/19] mesh: use deterministic channel on channel switch
-
-This patch uses a deterministic channel on DFS channel switch
-in mesh networks. Otherwise, when switching to a usable but not
-available channel, no CSA can be sent and a random channel is choosen
-without notification of other nodes. It is then quite likely, that
-the mesh network gets disconnected.
-
-Fix this by using a deterministic number, based on the sha256 hash
-of the mesh ID, in order to use at least a different number in each
-mesh network.
-
-Signed-off-by: Markus Theil <markus.theil@tu-ilmenau.de>
----
- src/ap/dfs.c | 20 +++++++++++++++++++-
- src/drivers/driver_nl80211.c | 4 ++++
- 2 files changed, 23 insertions(+), 1 deletion(-)
-
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -17,6 +17,7 @@
- #include "ap_drv_ops.h"
- #include "drivers/driver.h"
- #include "dfs.h"
-+#include "crypto/crypto.h"
-
-
- enum dfs_channel_type {
-@@ -526,9 +527,14 @@ dfs_get_valid_channel(struct hostapd_ifa
- int num_available_chandefs;
- int chan_idx, chan_idx2;
- int sec_chan_idx_80p80 = -1;
-+ bool is_mesh = false;
- int i;
- u32 _rand;
-
-+#ifdef CONFIG_MESH
-+ is_mesh = iface->mconf;
-+#endif
-+
- wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
- *secondary_channel = 0;
- *oper_centr_freq_seg0_idx = 0;
-@@ -548,8 +554,20 @@ dfs_get_valid_channel(struct hostapd_ifa
- if (num_available_chandefs == 0)
- return NULL;
-
-- if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
-+ /* try to use deterministic channel in mesh, so that both sides
-+ * have a chance to switch to the same channel */
-+ if (is_mesh) {
-+#ifdef CONFIG_MESH
-+ u64 hash[4];
-+ const u8 *meshid[1] = { &iface->mconf->meshid[0] };
-+ const size_t meshid_len = iface->mconf->meshid_len;
-+
-+ sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
-+ _rand = hash[0] + hash[1] + hash[2] + hash[3];
-+#endif
-+ } else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
- return NULL;
-+
- chan_idx = _rand % num_available_chandefs;
- dfs_find_channel(iface, &chan, chan_idx, type);
- if (!chan) {
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -11017,6 +11017,10 @@ static int nl80211_switch_channel(void *
- if (ret)
- goto error;
-
-+ if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
-+ nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
-+ }
-+
- /* beacon_csa params */
- beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
- if (!beacon_csa)
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/021-fix-sta-add-after-previous-connection.patch b/recipes-wifi/hostapd/files/patches-2.10.3/021-fix-sta-add-after-previous-connection.patch
deleted file mode 100644
index edf599e..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/021-fix-sta-add-after-previous-connection.patch
+++ /dev/null
@@ -1,26 +0,0 @@
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4621,6 +4621,13 @@ static int add_associated_sta(struct hos
- * drivers to accept the STA parameter configuration. Since this is
- * after a new FT-over-DS exchange, a new TK has been derived, so key
- * reinstallation is not a concern for this case.
-+ *
-+ * If the STA was associated and authorized earlier, but came for a new
-+ * connection (!added_unassoc + !reassoc), remove the existing STA entry
-+ * so that it can be re-added. This case is rarely seen when the AP could
-+ * not receive the deauth/disassoc frame from the STA. And the STA comes
-+ * back with new connection within a short period or before the inactive
-+ * STA entry is removed from the list.
- */
- wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
- " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
-@@ -4634,7 +4641,8 @@ static int add_associated_sta(struct hos
- (!(sta->flags & WLAN_STA_AUTHORIZED) ||
- (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
- (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
-- !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
-+ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
-+ (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
- hostapd_drv_sta_remove(hapd, sta->addr);
- wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
- set = 0;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/022-hostapd-fix-use-of-uninitialized-stack-variables.patch b/recipes-wifi/hostapd/files/patches-2.10.3/022-hostapd-fix-use-of-uninitialized-stack-variables.patch
deleted file mode 100644
index 8dec325..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/022-hostapd-fix-use-of-uninitialized-stack-variables.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 8 Jul 2021 16:33:03 +0200
-Subject: [PATCH] hostapd: fix use of uninitialized stack variables
-
-When a CSA is performed on an 80 MHz channel, hostapd_change_config_freq
-unconditionally calls hostapd_set_oper_centr_freq_seg0/1_idx with seg0/1
-filled by ieee80211_freq_to_chan.
-However, if ieee80211_freq_to_chan fails (because the freq is 0 or invalid),
-seg0/1 remains uninitialized and filled with stack garbage, causing errors
-such as "hostapd: 80 MHz: center segment 1 configured"
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -3764,7 +3764,7 @@ static int hostapd_change_config_freq(st
- struct hostapd_freq_params *old_params)
- {
- int channel;
-- u8 seg0, seg1;
-+ u8 seg0 = 0, seg1 = 0;
- struct hostapd_hw_modes *mode;
-
- if (!params->channel) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch b/recipes-wifi/hostapd/files/patches-2.10.3/023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch
deleted file mode 100644
index 9ff9b23..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 28 Jul 2021 05:43:29 +0200
-Subject: [PATCH] ndisc_snoop: call dl_list_del before freeing ipv6 addresses
-
-Fixes a segmentation fault on sta disconnect
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/ndisc_snoop.c
-+++ b/src/ap/ndisc_snoop.c
-@@ -61,6 +61,7 @@ void sta_ip6addr_del(struct hostapd_data
- dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
- list) {
- hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
-+ dl_list_del(&ip6addr->list);
- os_free(ip6addr);
- }
- }
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch b/recipes-wifi/hostapd/files/patches-2.10.3/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch
deleted file mode 100644
index ef2bb40..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch
+++ /dev/null
@@ -1,275 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 28 Jul 2021 05:49:46 +0200
-Subject: [PATCH] driver_nl80211: rewrite neigh code to not depend on
- libnl3-route
-
-Removes an unnecessary dependency and also makes the code smaller
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -16,9 +16,6 @@
- #include <net/if.h>
- #include <netlink/genl/genl.h>
- #include <netlink/genl/ctrl.h>
--#ifdef CONFIG_LIBNL3_ROUTE
--#include <netlink/route/neighbour.h>
--#endif /* CONFIG_LIBNL3_ROUTE */
- #include <linux/rtnetlink.h>
- #include <netpacket/packet.h>
- #include <linux/errqueue.h>
-@@ -5783,26 +5780,29 @@ fail:
-
- static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- struct wpa_driver_nl80211_data *drv = bss->drv;
-- struct rtnl_neigh *rn;
-- struct nl_addr *nl_addr;
-+ struct ndmsg nhdr = {
-+ .ndm_state = NUD_PERMANENT,
-+ .ndm_ifindex = bss->ifindex,
-+ .ndm_family = AF_BRIDGE,
-+ };
-+ struct nl_msg *msg;
- int err;
-
-- rn = rtnl_neigh_alloc();
-- if (!rn)
-+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+ if (!msg)
- return;
-
-- rtnl_neigh_set_family(rn, AF_BRIDGE);
-- rtnl_neigh_set_ifindex(rn, bss->ifindex);
-- nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
-- if (!nl_addr) {
-- rtnl_neigh_put(rn);
-- return;
-- }
-- rtnl_neigh_set_lladdr(rn, nl_addr);
-+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
-+ goto errout;
-+
-+ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
-+ goto errout;
-+
-+ if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
-+ goto errout;
-
-- err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+ err = nl_wait_for_ack(drv->rtnl_sk);
- if (err < 0) {
- wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
- MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
-@@ -5812,9 +5812,8 @@ static void rtnl_neigh_delete_fdb_entry(
- MACSTR, MAC2STR(addr));
- }
-
-- nl_addr_put(nl_addr);
-- rtnl_neigh_put(rn);
--#endif /* CONFIG_LIBNL3_ROUTE */
-+errout:
-+ nlmsg_free(msg);
- }
-
-
-@@ -8492,7 +8491,6 @@ static void *i802_init(struct hostapd_da
- (params->num_bridge == 0 || !params->bridge[0]))
- add_ifidx(drv, br_ifindex, drv->ifindex);
-
--#ifdef CONFIG_LIBNL3_ROUTE
- if (bss->added_if_into_bridge || bss->already_in_bridge) {
- int err;
-
-@@ -8509,7 +8507,6 @@ static void *i802_init(struct hostapd_da
- goto failed;
- }
- }
--#endif /* CONFIG_LIBNL3_ROUTE */
-
- if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
- wpa_printf(MSG_DEBUG,
-@@ -11883,13 +11880,14 @@ static int wpa_driver_br_add_ip_neigh(vo
- const u8 *ipaddr, int prefixlen,
- const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
-- struct rtnl_neigh *rn;
-- struct nl_addr *nl_ipaddr = NULL;
-- struct nl_addr *nl_lladdr = NULL;
-- int family, addrsize;
-+ struct ndmsg nhdr = {
-+ .ndm_state = NUD_PERMANENT,
-+ .ndm_ifindex = bss->br_ifindex,
-+ };
-+ struct nl_msg *msg;
-+ int addrsize;
- int res;
-
- if (!ipaddr || prefixlen == 0 || !addr)
-@@ -11908,85 +11906,66 @@ static int wpa_driver_br_add_ip_neigh(vo
- }
-
- if (version == 4) {
-- family = AF_INET;
-+ nhdr.ndm_family = AF_INET;
- addrsize = 4;
- } else if (version == 6) {
-- family = AF_INET6;
-+ nhdr.ndm_family = AF_INET6;
- addrsize = 16;
- } else {
- return -EINVAL;
- }
-
-- rn = rtnl_neigh_alloc();
-- if (rn == NULL)
-+ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
-+ if (!msg)
- return -ENOMEM;
-
-- /* set the destination ip address for neigh */
-- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
-- if (nl_ipaddr == NULL) {
-- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
-- res = -ENOMEM;
-+ res = -ENOMEM;
-+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- goto errout;
-- }
-- nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
-- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
-- if (res) {
-- wpa_printf(MSG_DEBUG,
-- "nl80211: neigh set destination addr failed");
-+
-+ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- goto errout;
-- }
-
-- /* set the corresponding lladdr for neigh */
-- nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
-- if (nl_lladdr == NULL) {
-- wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
-- res = -ENOMEM;
-+ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
- goto errout;
-- }
-- rtnl_neigh_set_lladdr(rn, nl_lladdr);
-
-- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-- rtnl_neigh_set_state(rn, NUD_PERMANENT);
-+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+ if (res < 0)
-+ goto errout;
-
-- res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
-+ res = nl_wait_for_ack(drv->rtnl_sk);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Adding bridge ip neigh failed: %s",
- nl_geterror(res));
- }
- errout:
-- if (nl_lladdr)
-- nl_addr_put(nl_lladdr);
-- if (nl_ipaddr)
-- nl_addr_put(nl_ipaddr);
-- if (rn)
-- rtnl_neigh_put(rn);
-+ nlmsg_free(msg);
- return res;
--#else /* CONFIG_LIBNL3_ROUTE */
-- return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
-
-
- static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
- const u8 *ipaddr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
-- struct rtnl_neigh *rn;
-- struct nl_addr *nl_ipaddr;
-- int family, addrsize;
-+ struct ndmsg nhdr = {
-+ .ndm_state = NUD_PERMANENT,
-+ .ndm_ifindex = bss->br_ifindex,
-+ };
-+ struct nl_msg *msg;
-+ int addrsize;
- int res;
-
- if (!ipaddr)
- return -EINVAL;
-
- if (version == 4) {
-- family = AF_INET;
-+ nhdr.ndm_family = AF_INET;
- addrsize = 4;
- } else if (version == 6) {
-- family = AF_INET6;
-+ nhdr.ndm_family = AF_INET6;
- addrsize = 16;
- } else {
- return -EINVAL;
-@@ -12004,41 +11983,30 @@ static int wpa_driver_br_delete_ip_neigh
- return -1;
- }
-
-- rn = rtnl_neigh_alloc();
-- if (rn == NULL)
-+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+ if (!msg)
- return -ENOMEM;
-
-- /* set the destination ip address for neigh */
-- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
-- if (nl_ipaddr == NULL) {
-- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
-- res = -ENOMEM;
-+ res = -ENOMEM;
-+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- goto errout;
-- }
-- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
-- if (res) {
-- wpa_printf(MSG_DEBUG,
-- "nl80211: neigh set destination addr failed");
-+
-+ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- goto errout;
-- }
-
-- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+ if (res < 0)
-+ goto errout;
-
-- res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+ res = nl_wait_for_ack(drv->rtnl_sk);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Deleting bridge ip neigh failed: %s",
- nl_geterror(res));
- }
- errout:
-- if (nl_ipaddr)
-- nl_addr_put(nl_ipaddr);
-- if (rn)
-- rtnl_neigh_put(rn);
-+ nlmsg_free(msg);
- return res;
--#else /* CONFIG_LIBNL3_ROUTE */
-- return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
-
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/040-mesh-allow-processing-authentication-frames-in-block.patch b/recipes-wifi/hostapd/files/patches-2.10.3/040-mesh-allow-processing-authentication-frames-in-block.patch
deleted file mode 100644
index b7bf9e3..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/040-mesh-allow-processing-authentication-frames-in-block.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 18 Feb 2019 12:57:11 +0100
-Subject: [PATCH] mesh: allow processing authentication frames in blocked state
-
-If authentication fails repeatedly e.g. because of a weak signal, the link
-can end up in blocked state. If one of the nodes tries to establish a link
-again before it is unblocked on the other side, it will block the link to
-that other side. The same happens on the other side when it unblocks the
-link. In that scenario, the link never recovers on its own.
-
-To fix this, allow restarting authentication even if the link is in blocked
-state, but don't initiate the attempt until the blocked period is over.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -3020,15 +3020,6 @@ static void handle_auth(struct hostapd_d
- seq_ctrl);
- return;
- }
--#ifdef CONFIG_MESH
-- if ((hapd->conf->mesh & MESH_ENABLED) &&
-- sta->plink_state == PLINK_BLOCKED) {
-- wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
-- " is blocked - drop Authentication frame",
-- MAC2STR(sa));
-- return;
-- }
--#endif /* CONFIG_MESH */
- #ifdef CONFIG_PASN
- if (auth_alg == WLAN_AUTH_PASN &&
- (sta->flags & WLAN_STA_ASSOC)) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/050-build_fix.patch b/recipes-wifi/hostapd/files/patches-2.10.3/050-build_fix.patch
deleted file mode 100644
index 8680b07..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/050-build_fix.patch
+++ /dev/null
@@ -1,20 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -324,6 +324,7 @@ ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -331,6 +331,7 @@ endif
- ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/110-mbedtls-TLS-crypto-option-initial-port.patch b/recipes-wifi/hostapd/files/patches-2.10.3/110-mbedtls-TLS-crypto-option-initial-port.patch
deleted file mode 100644
index 2210794..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/110-mbedtls-TLS-crypto-option-initial-port.patch
+++ /dev/null
@@ -1,8051 +0,0 @@
-From e16f200dc1d2f69efc78c7c55af0d7b410a981f9 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 5 Jul 2022 02:49:50 -0400
-Subject: [PATCH 1/7] mbedtls: TLS/crypto option (initial port)
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- hostapd/Makefile | 91 +
- hostapd/defconfig | 15 +-
- src/crypto/crypto_mbedtls.c | 4043 +++++++++++++++++
- src/crypto/tls_mbedtls.c | 3313 ++++++++++++++
- .../build/build-wpa_supplicant-mbedtls.config | 24 +
- tests/hwsim/example-hostapd.config | 4 +
- tests/hwsim/example-wpa_supplicant.config | 4 +
- wpa_supplicant/Makefile | 74 +
- wpa_supplicant/defconfig | 6 +-
- 9 files changed, 7571 insertions(+), 3 deletions(-)
- create mode 100644 src/crypto/crypto_mbedtls.c
- create mode 100644 src/crypto/tls_mbedtls.c
- create mode 100644 tests/build/build-wpa_supplicant-mbedtls.config
-
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -745,6 +745,40 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
-
-+ifeq ($(CONFIG_TLS), mbedtls)
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls
-+ifndef CONFIG_DPP
-+LIBS += -lmbedx509
-+endif
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifdef NEED_FIPS186_2_PRF
-+OBJS += ../src/crypto/fips_prf_internal.o
-+SHA1OBJS += ../src/crypto/sha1-internal.o
-+endif
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+ifdef CONFIG_DPP
-+LIBS += -lmbedx509
-+LIBS_h += -lmbedx509
-+LIBS_n += -lmbedx509
-+LIBS_s += -lmbedx509
-+endif
-+LIBS += -lmbedcrypto
-+LIBS_h += -lmbedcrypto
-+LIBS_n += -lmbedcrypto
-+LIBS_s += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
-@@ -924,9 +958,11 @@ endif
-
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -936,38 +972,48 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_UNWRAP
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_AES_DEC=y
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_DEC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_DEC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-dec.o
-@@ -982,12 +1028,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -996,16 +1046,22 @@ endif
- endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
-
- ifdef NEED_SHA1
- OBJS += $(SHA1OBJS)
-@@ -1015,11 +1071,13 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
-
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
-@@ -1058,56 +1116,81 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- OBJS += ../src/crypto/sha256-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- ifdef NEED_SHA384
- CFLAGS += -DCONFIG_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- CFLAGS += -DCONFIG_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
-
- ifdef CONFIG_INTERNAL_SHA384
- CFLAGS += -DCONFIG_INTERNAL_SHA384
-@@ -1152,11 +1235,13 @@ HOBJS += $(SHA1OBJS)
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
-
- ifdef CONFIG_RADIUS_SERVER
- CFLAGS += -DRADIUS_SERVER
-@@ -1329,7 +1414,9 @@ NOBJS += ../src/utils/trace.o
- endif
-
- HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/aes-encblock.o
-+endif
- ifdef CONFIG_INTERNAL_AES
- HOBJS += ../src/crypto/aes-internal.o
- HOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1352,13 +1439,17 @@ SOBJS += ../src/common/sae.o
- SOBJS += ../src/common/sae_pk.o
- SOBJS += ../src/common/dragonfly.o
- SOBJS += $(AESOBJS)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-prf.o
- SOBJS += ../src/crypto/sha384-prf.o
- SOBJS += ../src/crypto/sha512-prf.o
-+endif
- SOBJS += ../src/crypto/dh_groups.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-kdf.o
- SOBJS += ../src/crypto/sha384-kdf.o
- SOBJS += ../src/crypto/sha512-kdf.o
-+endif
-
- _OBJS_VAR := NOBJS
- include ../src/objs.mk
---- a/hostapd/defconfig
-+++ b/hostapd/defconfig
-@@ -6,9 +6,21 @@
- # just setting VARIABLE=n is not disabling that variable.
- #
- # This file is included in Makefile, so variables like CFLAGS and LIBS can also
--# be modified from here. In most cass, these lines should use += in order not
-+# be modified from here. In most cases, these lines should use += in order not
- # to override previous values of the variables.
-
-+
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
-+#CFLAGS += -I/usr/local/openssl/include
-+#LIBS += -L/usr/local/openssl/lib
-+
-+# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
-+# the kerberos files are not in the default include path. Following line can be
-+# used to fix build issues on such systems (krb5.h not found).
-+#CFLAGS += -I/usr/include/kerberos
-+
-+
- # Driver interface for Host AP driver
- CONFIG_DRIVER_HOSTAP=y
-
-@@ -278,6 +290,7 @@ CONFIG_IPV6=y
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
---- /dev/null
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -0,0 +1,4043 @@
-+/*
-+ * crypto wrapper functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/entropy.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/asn1.h>
-+#include <mbedtls/asn1write.h>
-+#include <mbedtls/aes.h>
-+#include <mbedtls/md.h>
-+#include <mbedtls/md5.h>
-+#include <mbedtls/sha1.h>
-+#include <mbedtls/sha256.h>
-+#include <mbedtls/sha512.h>
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__ __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__ __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+#include "crypto.h"
-+#include "aes_wrap.h"
-+#include "aes.h"
-+#include "md5.h"
-+#include "sha1.h"
-+#include "sha256.h"
-+#include "sha384.h"
-+#include "sha512.h"
-+
-+
-+/*
-+ * selective code inclusion based on preprocessor defines
-+ *
-+ * future: additional code could be wrapped with preprocessor checks if
-+ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
-+ * setting preprocessor defines for named groups of functionality
-+ */
-+
-+#if defined(CONFIG_FIPS)
-+#undef MBEDTLS_MD4_C /* omit md4_vector() */
-+#undef MBEDTLS_MD5_C /* omit md5_vector() hmac_md5_vector() hmac_md5() */
-+#undef MBEDTLS_DES_C /* omit des_encrypt() */
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#define CRYPTO_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if !defined(CONFIG_FIPS)
-+#if defined(EAP_PWD) \
-+ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
-+ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
-+ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
-+ || defined(EAP_SERVER_MSCHAPV2)
-+#ifndef MBEDTLS_MD4_C /* (MD4 not in mbedtls 3.x) */
-+#include "md4-internal.c"/* pull in hostap local implementation */
-+#endif /* md4_vector() */
-+#else
-+#undef MBEDTLS_MD4_C /* omit md4_vector() */
-+#endif
-+#endif
-+
-+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
-+#ifndef MBEDTLS_ARC4_C /* (RC4 not in mbedtls 3.x) */
-+#include "rc4.c" /* pull in hostap local implementation */
-+#endif /* rc4_skip() */
-+#else
-+#undef MBEDTLS_ARC4_C /* omit rc4_skip() */
-+#endif
-+
-+#if defined(CONFIG_MACSEC) \
-+ || defined(CONFIG_NO_RADIUS) \
-+ || defined(CONFIG_IEEE80211R) \
-+ || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_SERVER_TEAP) \
-+ || !defined(CONFIG_NO_WPA)
-+ /* aes_wrap() aes_unwrap() */
-+#else
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#endif
-+
-+#if !defined(CONFIG_SHA256)
-+#undef MBEDTLS_SHA256_C
-+#endif
-+
-+#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
-+#undef MBEDTLS_SHA512_C
-+#endif
-+
-+#if defined(CONFIG_HMAC_SHA256_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+#endif
-+#if defined(CONFIG_HMAC_SHA384_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+#endif
-+#if defined(CONFIG_HMAC_SHA512_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+#endif
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define CRYPTO_MBEDTLS_SHA1_T_PRF
-+#endif
-+
-+#if defined(CONFIG_DES)
-+#define CRYPTO_MBEDTLS_DES_ENCRYPT
-+#endif /* des_encrypt() */
-+
-+#if !defined(CONFIG_NO_PBKDF2)
-+#define CRYPTO_MBEDTLS_PBKDF2_SHA1
-+#endif /* pbkdf2_sha1() */
-+
-+#if defined(EAP_IKEV2) \
-+ || defined(EAP_IKEV2_DYNAMIC) \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+#endif /* crypto_cipher_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HASH
-+#endif /* crypto_hash_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
-+ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#endif /* crypto_bignum_*() */
-+
-+#if defined(EAP_PWD) /* CONFIG_EAP_PWD=y */ \
-+ || defined(EAP_EKE) /* CONFIG_EAP_EKE=y */ \
-+ || defined(EAP_EKE_DYNAMIC) /* CONFIG_EAP_EKE=y */ \
-+ || defined(EAP_SERVER_EKE) /* CONFIG_EAP_EKE=y */ \
-+ || defined(EAP_IKEV2) /* CONFIG_EAP_IKEV2y */ \
-+ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */ \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */ \
-+ || defined(CONFIG_SAE) /* CONFIG_SAE=y */ \
-+ || defined(CONFIG_WPS) /* CONFIG_WPS=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_DH
-+#if defined(CONFIG_WPS_NFC)
-+#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+#endif /* dh5_init_fixed() */
-+#endif /* crypto_dh_*() */
-+
-+#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
-+#define CRYPTO_MBEDTLS_CRYPTO_ECDH
-+#endif /* crypto_ecdh_*() */
-+
-+#if defined(CONFIG_ECC)
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#define CRYPTO_MBEDTLS_CRYPTO_EC
-+#endif /* crypto_ec_*() crypto_ec_key_*() */
-+
-+#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
-+#define CRYPTO_MBEDTLS_CRYPTO_CSR
-+#endif /* crypto_csr_*() */
-+
-+#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#endif
-+
-+#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+#endif /* crypto_pkcs7_*() */
-+
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
-+ || defined(CONFIG_AP) || defined(HOSTAPD)
-+/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
-+#if defined(CRYPTO_RSA_OAEP_SHA256)
-+#define CRYPTO_MBEDTLS_CRYPTO_RSA
-+#endif
-+#endif /* crypto_rsa_*() */
-+
-+
-+static int ctr_drbg_init_state;
-+static mbedtls_ctr_drbg_context ctr_drbg;
-+static mbedtls_entropy_context entropy;
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#include <mbedtls/bignum.h>
-+static mbedtls_mpi mpi_sw_A;
-+#endif
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
-+{
-+ mbedtls_ctr_drbg_init(&ctr_drbg);
-+ mbedtls_entropy_init(&entropy);
-+ if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
-+ NULL, 0)) {
-+ wpa_printf(MSG_ERROR, "Init of random number generator failed");
-+ /* XXX: abort? */
-+ }
-+ else
-+ ctr_drbg_init_state = 1;
-+
-+ return &ctr_drbg;
-+}
-+
-+__attribute_cold__
-+void crypto_unload(void)
-+{
-+ if (ctr_drbg_init_state) {
-+ mbedtls_ctr_drbg_free(&ctr_drbg);
-+ mbedtls_entropy_free(&entropy);
-+ #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+ mbedtls_mpi_free(&mpi_sw_A);
-+ #endif
-+ ctr_drbg_init_state = 0;
-+ }
-+}
-+
-+/* init ctr_drbg on first use
-+ * crypto_global_init() and crypto_global_deinit() are not available here
-+ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+inline
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
-+{
-+ return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
-+int crypto_get_random(void *buf, size_t len)
-+{
-+ return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
-+}
-+#endif
-+
-+
-+#if 1
-+
-+/* tradeoff: slightly smaller code size here at cost of slight increase
-+ * in instructions and function calls at runtime versus the expanded
-+ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
-+
-+__attribute_noinline__
-+static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-+ u8 *mac, mbedtls_md_type_t md_type)
-+{
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_starts(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_finish(&ctx, mac);
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
-+}
-+#endif
-+
-+#else /* expanded per-message-digest functions */
-+
-+#ifdef MBEDTLS_SHA512_C
-+#include <mbedtls/sha512.h>
-+__attribute_noinline__
-+static int sha384_512_vector(size_t num_elem, const u8 *addr[],
-+ const size_t *len, u8 *mac, int is384)
-+{
-+ struct mbedtls_sha512_context ctx;
-+ mbedtls_sha512_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_sha512_starts(&ctx, is384);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha512_update(&ctx, addr[i], len[i]);
-+ mbedtls_sha512_finish(&ctx, mac);
-+ #else
-+ mbedtls_sha512_starts_ret(&ctx, is384);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_sha512_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_sha512_free(&ctx);
-+ return 0;
-+}
-+
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return sha384_512_vector(num_elem, addr, len, mac, 0);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return sha384_512_vector(num_elem, addr, len, mac, 1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#include <mbedtls/sha256.h>
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_sha256_context ctx;
-+ mbedtls_sha256_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_sha256_starts(&ctx, 0);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha256_update(&ctx, addr[i], len[i]);
-+ mbedtls_sha256_finish(&ctx, mac);
-+ #else
-+ mbedtls_sha256_starts_ret(&ctx, 0);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_sha256_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_sha256_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+#include <mbedtls/sha1.h>
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_sha1_context ctx;
-+ mbedtls_sha1_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_sha1_starts(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha1_update(&ctx, addr[i], len[i]);
-+ mbedtls_sha1_finish(&ctx, mac);
-+ #else
-+ mbedtls_sha1_starts_ret(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_sha1_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_sha1_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+#include <mbedtls/md5.h>
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_md5_context ctx;
-+ mbedtls_md5_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_md5_starts(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md5_update(&ctx, addr[i], len[i]);
-+ mbedtls_md5_finish(&ctx, mac);
-+ #else
-+ mbedtls_md5_starts_ret(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_md5_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_md5_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_md4_context ctx;
-+ mbedtls_md4_init(&ctx);
-+ mbedtls_md4_starts_ret(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_md4_finish_ret(&ctx, mac);
-+ mbedtls_md4_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#endif /* expanded per-message-digest functions */
-+
-+
-+__attribute_noinline__
-+static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac,
-+ mbedtls_md_type_t md_type)
-+{
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_hmac_starts(&ctx, key, key_len);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, mac);
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA384);
-+}
-+
-+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA256);
-+}
-+
-+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA1);
-+}
-+
-+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_MD5);
-+}
-+
-+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+
-+#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
-+
-+#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
-+
-+#include <mbedtls/hkdf.h>
-+
-+/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
-+
-+/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
-+/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
-+/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
-+__attribute_noinline__
-+static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
-+ const char *label, const u8 *info, size_t info_len,
-+ u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
-+{
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+ #ifdef MBEDTLS_HKDF_C
-+ if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */
-+ return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
-+ info_len, okm, okm_len) ? -1 : 0;
-+ #endif
-+
-+ const size_t mac_len = mbedtls_md_get_size(md_info);
-+ /* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
-+ if (okm_len > ((mac_len << 8) - mac_len))
-+ return -1;
-+
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_hmac_starts(&ctx, prk, prk_len);
-+
-+ u8 iter = 1;
-+ const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
-+ size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
-+
-+ for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, okm);
-+ mbedtls_md_hmac_reset(&ctx);
-+ addr[0] = okm;
-+ okm += mac_len;
-+ len[0] = mac_len; /*(include digest in subsequent rounds)*/
-+ }
-+
-+ if (okm_len) {
-+ u8 hash[MBEDTLS_MD_MAX_SIZE];
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, hash);
-+ os_memcpy(okm, hash, okm_len);
-+ forced_memzero(hash, mac_len);
-+ }
-+
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
-+ const char *label, const u8 *seed, size_t seed_len,
-+ u8 *out, size_t outlen)
-+{
-+ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+ out, outlen, MBEDTLS_MD_SHA512);
-+}
-+#endif
-+
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
-+ const char *label, const u8 *seed, size_t seed_len,
-+ u8 *out, size_t outlen)
-+{
-+ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+ out, outlen, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
-+ const char *label, const u8 *seed, size_t seed_len,
-+ u8 *out, size_t outlen)
-+{
-+ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+ out, outlen, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
-+
-+
-+/* sha256-prf.c sha384-prf.c sha512-prf.c */
-+
-+/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
-+__attribute_noinline__
-+static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf,
-+ size_t buf_len_bits, mbedtls_md_type_t md_type)
-+{
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_hmac_starts(&ctx, key, key_len);
-+
-+ u16 ctr, n_le = host_to_le16(buf_len_bits);
-+ const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
-+ const size_t len[] = { 2, os_strlen(label), data_len, 2 };
-+ const size_t mac_len = mbedtls_md_get_size(md_info);
-+ size_t buf_len = (buf_len_bits + 7) / 8;
-+ for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
-+ #if __BYTE_ORDER == __BIG_ENDIAN
-+ ctr = host_to_le16(ctr);
-+ #endif
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, buf);
-+ mbedtls_md_hmac_reset(&ctx);
-+ buf += mac_len;
-+ #if __BYTE_ORDER == __BIG_ENDIAN
-+ ctr = le_to_host16(ctr);
-+ #endif
-+ }
-+
-+ if (buf_len) {
-+ u8 hash[MBEDTLS_MD_MAX_SIZE];
-+ #if __BYTE_ORDER == __BIG_ENDIAN
-+ ctr = host_to_le16(ctr);
-+ #endif
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, hash);
-+ os_memcpy(buf, hash, buf_len);
-+ buf += buf_len;
-+ forced_memzero(hash, mac_len);
-+ }
-+
-+ /* Mask out unused bits in last octet if it does not use all the bits */
-+ if ((buf_len_bits &= 0x7))
-+ buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
-+
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len * 8, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len * 8, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len * 8, MBEDTLS_MD_SHA256);
-+}
-+
-+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf,
-+ size_t buf_len_bits)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len_bits, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
-+
-+
-+#ifdef MBEDTLS_SHA1_C
-+
-+/* sha1-prf.c */
-+
-+/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
-+
-+int sha1_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ /*(note: algorithm differs from hmac_prf_bits() */
-+ /*(note: smaller code size instead of expanding hmac_sha1_vector()
-+ * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+ u8 counter = 0;
-+ const u8 *addr[] = { (u8 *)label, data, &counter };
-+ const size_t len[] = { os_strlen(label)+1, data_len, 1 };
-+
-+ for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
-+ if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
-+ return -1;
-+ buf += SHA1_MAC_LEN;
-+ }
-+
-+ if (buf_len) {
-+ u8 hash[SHA1_MAC_LEN];
-+ if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
-+ return -1;
-+ os_memcpy(buf, hash, buf_len);
-+ forced_memzero(hash, sizeof(hash));
-+ }
-+
-+ return 0;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
-+
-+/* sha1-tprf.c */
-+
-+/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
-+
-+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
-+{
-+ /*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
-+ /*(note: smaller code size instead of expanding hmac_sha1_vector()
-+ * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+ u8 ctr;
-+ u16 olen = host_to_be16(buf_len);
-+ const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
-+ size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
-+
-+ for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
-+ if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
-+ return -1;
-+ addr[0] = buf;
-+ buf += SHA1_MAC_LEN;
-+ len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
-+ }
-+
-+ if (buf_len) {
-+ u8 hash[SHA1_MAC_LEN];
-+ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
-+ return -1;
-+ os_memcpy(buf, hash, buf_len);
-+ forced_memzero(hash, sizeof(hash));
-+ }
-+
-+ return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
-+
-+#endif /* MBEDTLS_SHA1_C */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
-+#ifdef MBEDTLS_DES_C
-+#include <mbedtls/des.h>
-+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-+{
-+ u8 pkey[8], next, tmp;
-+ int i;
-+
-+ /* Add parity bits to the key */
-+ next = 0;
-+ for (i = 0; i < 7; i++) {
-+ tmp = key[i];
-+ pkey[i] = (tmp >> i) | next | 1;
-+ next = tmp << (7 - i);
-+ }
-+ pkey[i] = next | 1;
-+
-+ mbedtls_des_context des;
-+ mbedtls_des_init(&des);
-+ int ret = mbedtls_des_setkey_enc(&des, pkey)
-+ || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
-+ mbedtls_des_free(&des);
-+ return ret;
-+}
-+#else
-+#include "des-internal.c"/* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
-+/* sha1-pbkdf2.c */
-+#include <mbedtls/pkcs5.h>
-+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
-+ int iterations, u8 *buf, size_t buflen)
-+{
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
-+ return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
-+ (const u8 *)passphrase, os_strlen(passphrase),
-+ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+ #else
-+ const mbedtls_md_info_t *md_info;
-+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
-+ if (md_info == NULL)
-+ return -1;
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ int ret = mbedtls_md_setup(&ctx, md_info, 1)
-+ || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
-+ (const u8 *)passphrase, os_strlen(passphrase),
-+ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+ mbedtls_md_free(&ctx);
-+ return ret;
-+ #endif
-+}
-+#endif
-+
-+
-+/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
-+
-+static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
-+{
-+ mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
-+ if (!aes)
-+ return NULL;
-+
-+ mbedtls_aes_init(aes);
-+ if ((mode == MBEDTLS_AES_ENCRYPT
-+ ? mbedtls_aes_setkey_enc(aes, key, len * 8)
-+ : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
-+ return aes;
-+
-+ mbedtls_aes_free(aes);
-+ os_free(aes);
-+ return NULL;
-+}
-+
-+void *aes_encrypt_init(const u8 *key, size_t len)
-+{
-+ return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-+{
-+ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
-+}
-+
-+void aes_encrypt_deinit(void *ctx)
-+{
-+ mbedtls_aes_free(ctx);
-+ os_free(ctx);
-+}
-+
-+void *aes_decrypt_init(const u8 *key, size_t len)
-+{
-+ return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-+{
-+ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
-+}
-+
-+void aes_decrypt_deinit(void *ctx)
-+{
-+ mbedtls_aes_free(ctx);
-+ os_free(ctx);
-+}
-+
-+
-+#include "aes_wrap.h"
-+
-+
-+#ifdef MBEDTLS_NIST_KW_C
-+
-+#include <mbedtls/nist_kw.h>
-+
-+/* aes-wrap.c */
-+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
-+{
-+ mbedtls_nist_kw_context ctx;
-+ mbedtls_nist_kw_init(&ctx);
-+ size_t olen;
-+ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+ kek, kek_len*8, 1)
-+ || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
-+ cipher, &olen, (n+1)*8) ? -1 : 0;
-+ mbedtls_nist_kw_free(&ctx);
-+ return ret;
-+}
-+
-+/* aes-unwrap.c */
-+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
-+{
-+ mbedtls_nist_kw_context ctx;
-+ mbedtls_nist_kw_init(&ctx);
-+ size_t olen;
-+ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+ kek, kek_len*8, 0)
-+ || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
-+ (n+1)*8, plain, &olen, n*8) ? -1 : 0;
-+ mbedtls_nist_kw_free(&ctx);
-+ return ret;
-+}
-+
-+#else
-+
-+#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
-+#include "aes-wrap.c" /* pull in hostap local implementation */
-+#include "aes-unwrap.c" /* pull in hostap local implementation */
-+#endif
-+
-+#endif /* MBEDTLS_NIST_KW_C */
-+
-+
-+#ifdef MBEDTLS_CMAC_C
-+
-+/* aes-omac1.c */
-+
-+#include <mbedtls/cmac.h>
-+
-+int omac1_aes_vector(
-+ const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
-+ const size_t *len, u8 *mac)
-+{
-+ mbedtls_cipher_type_t cipher_type;
-+ switch (key_len) {
-+ case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
-+ case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
-+ case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
-+ default: return -1;
-+ }
-+ const mbedtls_cipher_info_t *cipher_info;
-+ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+ if (cipher_info == NULL)
-+ return -1;
-+
-+ mbedtls_cipher_context_t ctx;
-+ mbedtls_cipher_init(&ctx);
-+ int ret = -1;
-+ if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
-+ && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
-+ ret = 0;
-+ for (size_t i = 0; i < num_elem && ret == 0; ++i)
-+ ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
-+ }
-+ if (ret == 0)
-+ ret = mbedtls_cipher_cmac_finish(&ctx, mac);
-+ mbedtls_cipher_free(&ctx);
-+ return ret ? -1 : 0;
-+}
-+
-+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-+ const u8 *addr[], const size_t *len,
-+ u8 *mac)
-+{
-+ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
-+}
-+
-+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+ return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
-+}
-+
-+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
-+}
-+
-+#else
-+
-+#include "aes-omac1.c" /* pull in hostap local implementation */
-+
-+#ifndef MBEDTLS_AES_BLOCK_SIZE
-+#define MBEDTLS_AES_BLOCK_SIZE 16
-+#endif
-+
-+#endif /* MBEDTLS_CMAC_C */
-+
-+
-+/* These interfaces can be inefficient when used in loops, as the overhead of
-+ * initialization each call is large for each block input (e.g. 16 bytes) */
-+
-+
-+/* aes-encblock.c */
-+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
-+{
-+ mbedtls_aes_context aes;
-+ mbedtls_aes_init(&aes);
-+ int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
-+ || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
-+ ? -1
-+ : 0;
-+ mbedtls_aes_free(&aes);
-+ return ret;
-+}
-+
-+
-+/* aes-ctr.c */
-+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
-+ u8 *data, size_t data_len)
-+{
-+ unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
-+ unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
-+ os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
-+
-+ mbedtls_aes_context ctx;
-+ mbedtls_aes_init(&ctx);
-+ size_t nc_off = 0;
-+ int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
-+ || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
-+ counter, stream_block,
-+ data, data) ? -1 : 0;
-+ forced_memzero(stream_block, sizeof(stream_block));
-+ mbedtls_aes_free(&ctx);
-+ return ret;
-+}
-+
-+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
-+ u8 *data, size_t data_len)
-+{
-+ return aes_ctr_encrypt(key, 16, nonce, data, data_len);
-+}
-+
-+
-+/* aes-cbc.c */
-+static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
-+ u8 *data, size_t data_len, int mode)
-+{
-+ unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
-+ os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
-+
-+ mbedtls_aes_context ctx;
-+ mbedtls_aes_init(&ctx);
-+ int ret = (mode == MBEDTLS_AES_ENCRYPT
-+ ? mbedtls_aes_setkey_enc(&ctx, key, 128)
-+ : mbedtls_aes_setkey_dec(&ctx, key, 128))
-+ || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
-+ mbedtls_aes_free(&ctx);
-+ return ret ? -1 : 0;
-+}
-+
-+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+
-+/*
-+ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
-+ * but such comments are not accurate:
-+ *
-+ * "This function is only used with internal TLSv1 implementation
-+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
-+ * to implement this."
-+ */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+
-+#include <mbedtls/cipher.h>
-+
-+struct crypto_cipher
-+{
-+ mbedtls_cipher_context_t ctx_enc;
-+ mbedtls_cipher_context_t ctx_dec;
-+};
-+
-+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-+ const u8 *iv, const u8 *key,
-+ size_t key_len)
-+{
-+ /* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
-+ * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
-+
-+ mbedtls_cipher_type_t cipher_type;
-+ size_t iv_len;
-+ switch (alg) {
-+ #ifdef MBEDTLS_ARC4_C
-+ #if 0
-+ case CRYPTO_CIPHER_ALG_RC4:
-+ cipher_type = MBEDTLS_CIPHER_ARC4_128;
-+ iv_len = 0;
-+ break;
-+ #endif
-+ #endif
-+ #ifdef MBEDTLS_AES_C
-+ case CRYPTO_CIPHER_ALG_AES:
-+ if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
-+ if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
-+ if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
-+ iv_len = 16;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_DES_C
-+ case CRYPTO_CIPHER_ALG_3DES:
-+ cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
-+ iv_len = 8;
-+ break;
-+ #if 0
-+ case CRYPTO_CIPHER_ALG_DES:
-+ cipher_type = MBEDTLS_CIPHER_DES_CBC;
-+ iv_len = 8;
-+ break;
-+ #endif
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+
-+ const mbedtls_cipher_info_t *cipher_info;
-+ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+ if (cipher_info == NULL)
-+ return NULL;
-+
-+ key_len *= 8; /* key_bitlen */
-+ #if 0 /*(were key_bitlen not already available)*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+ key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
-+ #else
-+ key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
-+ #endif
-+ #endif
-+
-+ #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
-+ iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
-+ #endif
-+
-+ struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
-+ if (!ctx)
-+ return NULL;
-+
-+ mbedtls_cipher_init(&ctx->ctx_enc);
-+ mbedtls_cipher_init(&ctx->ctx_dec);
-+ if ( mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
-+ && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
-+ && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
-+ && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
-+ && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
-+ && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
-+ && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
-+ && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
-+ return ctx;
-+ }
-+
-+ mbedtls_cipher_free(&ctx->ctx_enc);
-+ mbedtls_cipher_free(&ctx->ctx_dec);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+int crypto_cipher_encrypt(struct crypto_cipher *ctx,
-+ const u8 *plain, u8 *crypt, size_t len)
-+{
-+ size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
-+ return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
-+ || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
-+}
-+
-+int crypto_cipher_decrypt(struct crypto_cipher *ctx,
-+ const u8 *crypt, u8 *plain, size_t len)
-+{
-+ size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
-+ return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
-+ || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
-+}
-+
-+void crypto_cipher_deinit(struct crypto_cipher *ctx)
-+{
-+ mbedtls_cipher_free(&ctx->ctx_enc);
-+ mbedtls_cipher_free(&ctx->ctx_dec);
-+ os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
-+
-+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
-+ size_t key_len)
-+{
-+ mbedtls_md_type_t md_type;
-+ int is_hmac = 0;
-+
-+ switch (alg) {
-+ #ifdef MBEDTLS_MD5_C
-+ case CRYPTO_HASH_ALG_MD5:
-+ md_type = MBEDTLS_MD_MD5;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA1_C
-+ case CRYPTO_HASH_ALG_SHA1:
-+ md_type = MBEDTLS_MD_SHA1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_MD5_C
-+ case CRYPTO_HASH_ALG_HMAC_MD5:
-+ md_type = MBEDTLS_MD_MD5;
-+ is_hmac = 1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA1_C
-+ case CRYPTO_HASH_ALG_HMAC_SHA1:
-+ md_type = MBEDTLS_MD_SHA1;
-+ is_hmac = 1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA256_C
-+ case CRYPTO_HASH_ALG_SHA256:
-+ md_type = MBEDTLS_MD_SHA256;
-+ break;
-+ case CRYPTO_HASH_ALG_HMAC_SHA256:
-+ md_type = MBEDTLS_MD_SHA256;
-+ is_hmac = 1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA512_C
-+ case CRYPTO_HASH_ALG_SHA384:
-+ md_type = MBEDTLS_MD_SHA384;
-+ break;
-+ case CRYPTO_HASH_ALG_SHA512:
-+ md_type = MBEDTLS_MD_SHA512;
-+ break;
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+ if (!md_info)
-+ return NULL;
-+
-+ mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
-+ if (mctx == NULL)
-+ return NULL;
-+
-+ mbedtls_md_init(mctx);
-+ if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
-+ os_free(mctx);
-+ return NULL;
-+ }
-+
-+ if (is_hmac)
-+ mbedtls_md_hmac_starts(mctx, key, key_len);
-+ else
-+ mbedtls_md_starts(mctx);
-+ return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
-+}
-+
-+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
-+{
-+ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+ #if 0
-+ /*(mbedtls_md_hmac_update() and mbedtls_md_update()
-+ * make same modifications under the hood in mbedtls)*/
-+ if ((uintptr_t)ctx & 1uL)
-+ mbedtls_md_hmac_update(mctx, data, len);
-+ else
-+ #endif
-+ mbedtls_md_update(mctx, data, len);
-+}
-+
-+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
-+{
-+ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+ if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
-+ #else
-+ const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
-+ #endif
-+ size_t maclen = mbedtls_md_get_size(md_info);
-+ if (*len < maclen) {
-+ *len = maclen;
-+ /*(note: ctx not freed; can call again with larger *len)*/
-+ return -1;
-+ }
-+ *len = maclen;
-+ if ((uintptr_t)ctx & 1uL)
-+ mbedtls_md_hmac_finish(mctx, mac);
-+ else
-+ mbedtls_md_finish(mctx, mac);
-+ }
-+ mbedtls_md_free(mctx);
-+ os_free(mctx);
-+ return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+
-+#include <mbedtls/bignum.h>
-+
-+/* crypto.h bignum interfaces */
-+
-+struct crypto_bignum *crypto_bignum_init(void)
-+{
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn)
-+ mbedtls_mpi_init(bn);
-+ return (struct crypto_bignum *)bn;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
-+{
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn) {
-+ mbedtls_mpi_init(bn);
-+ if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
-+ return (struct crypto_bignum *)bn;
-+ }
-+
-+ os_free(bn);
-+ return NULL;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
-+{
-+ #if 0 /*(hostap use of this interface passes int, not uint)*/
-+ val = host_to_be32(val);
-+ return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
-+ #else
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn) {
-+ mbedtls_mpi_init(bn);
-+ if (mbedtls_mpi_lset(bn, (int)val) == 0)
-+ return (struct crypto_bignum *)bn;
-+ }
-+
-+ os_free(bn);
-+ return NULL;
-+ #endif
-+}
-+
-+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
-+{
-+ mbedtls_mpi_free((mbedtls_mpi *)n);
-+ os_free(n);
-+}
-+
-+int crypto_bignum_to_bin(const struct crypto_bignum *a,
-+ u8 *buf, size_t buflen, size_t padlen)
-+{
-+ size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
-+ if (n < padlen)
-+ n = padlen;
-+ return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
-+ ? -1
-+ : (int)(n);
-+}
-+
-+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
-+{
-+ /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
-+ return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+ #else
-+ /* (needed by EAP_PWD, SAE, DPP) */
-+ wpa_printf(MSG_ERROR,
-+ "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
-+ return -1;
-+ #endif
-+}
-+
-+int crypto_bignum_add(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_exptmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ const struct crypto_bignum *c,
-+ struct crypto_bignum *d)
-+{
-+ /* (check if input params match d; d is the result) */
-+ /* (a == d) is ok in current mbedtls implementation */
-+ if (b == d || c == d) { /*(not ok; store result in intermediate)*/
-+ mbedtls_mpi R;
-+ mbedtls_mpi_init(&R);
-+ int rc = mbedtls_mpi_exp_mod(&R,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b,
-+ (const mbedtls_mpi *)c,
-+ NULL)
-+ || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
-+ mbedtls_mpi_free(&R);
-+ return rc;
-+ }
-+ else {
-+ return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b,
-+ (const mbedtls_mpi *)c,
-+ NULL) ? -1 : 0;
-+ }
-+}
-+
-+int crypto_bignum_inverse(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sub(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_div(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ /*(most current use of this crypto.h interface has a == c (result),
-+ * so store result in an intermediate to avoid overwritten input)*/
-+ mbedtls_mpi R;
-+ mbedtls_mpi_init(&R);
-+ int rc = mbedtls_mpi_div_mpi(&R, NULL,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b)
-+ || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
-+ mbedtls_mpi_free(&R);
-+ return rc;
-+}
-+
-+int crypto_bignum_addmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ const struct crypto_bignum *c,
-+ struct crypto_bignum *d)
-+{
-+ return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b)
-+ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+ (mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mulmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ const struct crypto_bignum *c,
-+ struct crypto_bignum *d)
-+{
-+ return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b)
-+ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+ (mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ #if 1
-+ return crypto_bignum_mulmod(a, a, b, c);
-+ #else
-+ mbedtls_mpi bn;
-+ mbedtls_mpi_init(&bn);
-+ if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
-+ return -1;
-+ int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a, &bn,
-+ (const mbedtls_mpi *)b, NULL) ? -1 : 0;
-+ mbedtls_mpi_free(&bn);
-+ return ret;
-+ #endif
-+}
-+
-+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-+ struct crypto_bignum *r)
-+{
-+ return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
-+ || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
-+}
-+
-+int crypto_bignum_cmp(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b)
-+{
-+ return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
-+}
-+
-+int crypto_bignum_is_zero(const struct crypto_bignum *a)
-+{
-+ /* XXX: src/common/sae.c:sswu() contains comment:
-+ * "TODO: Make sure crypto_bignum_is_zero() is constant time"
-+ * Note: mbedtls_mpi_cmp_int() *is not* constant time */
-+ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
-+}
-+
-+int crypto_bignum_is_one(const struct crypto_bignum *a)
-+{
-+ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
-+}
-+
-+int crypto_bignum_is_odd(const struct crypto_bignum *a)
-+{
-+ return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
-+}
-+
-+#include "utils/const_time.h"
-+int crypto_bignum_legendre(const struct crypto_bignum *a,
-+ const struct crypto_bignum *p)
-+{
-+ /* Security Note:
-+ * mbedtls_mpi_exp_mod() is not documented to run in constant time,
-+ * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
-+ * Compare to crypto_openssl.c:crypto_bignum_legendre()
-+ * which uses openssl BN_mod_exp_mont_consttime()
-+ * mbedtls/library/ecp.c has further countermeasures to timing attacks,
-+ * (but ecp.c funcs are not used here) */
-+
-+ mbedtls_mpi exp, tmp;
-+ mbedtls_mpi_init(&exp);
-+ mbedtls_mpi_init(&tmp);
-+
-+ /* exp = (p-1) / 2 */
-+ int res;
-+ if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
-+ && mbedtls_mpi_shift_r(&exp, 1) == 0
-+ && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
-+ (const mbedtls_mpi *)p, NULL) == 0) {
-+ /*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
-+ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
-+ * to use constant time selection to avoid branches here. */
-+ unsigned int mask;
-+ res = -1;
-+ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
-+ res = const_time_select_int(mask, 1, res);
-+ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
-+ res = const_time_select_int(mask, 0, res);
-+ } else {
-+ res = -2;
-+ }
-+
-+ mbedtls_mpi_free(&tmp);
-+ mbedtls_mpi_free(&exp);
-+ return res;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
-+
-+/* crypto_internal-modexp.c */
-+
-+#include <mbedtls/bignum.h>
-+#include <mbedtls/dhm.h>
-+
-+#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
-+int crypto_mod_exp(const u8 *base, size_t base_len,
-+ const u8 *power, size_t power_len,
-+ const u8 *modulus, size_t modulus_len,
-+ u8 *result, size_t *result_len)
-+{
-+ mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
-+ mbedtls_mpi_init(&bn_base);
-+ mbedtls_mpi_init(&bn_exp);
-+ mbedtls_mpi_init(&bn_modulus);
-+ mbedtls_mpi_init(&bn_result);
-+
-+ size_t len;
-+ int ret = mbedtls_mpi_read_binary(&bn_base, base, base_len)
-+ || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
-+ || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
-+ || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
-+ || (len = mbedtls_mpi_size(&bn_result)) > *result_len
-+ || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
-+ ? -1
-+ : 0;
-+
-+ mbedtls_mpi_free(&bn_base);
-+ mbedtls_mpi_free(&bn_exp);
-+ mbedtls_mpi_free(&bn_modulus);
-+ mbedtls_mpi_free(&bn_result);
-+ return ret;
-+}
-+#endif
-+
-+static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
-+ const u8 *prime, size_t prime_len)
-+{
-+ /*(could set these directly in MBEDTLS_PRIVATE members)*/
-+ mbedtls_mpi P, G;
-+ mbedtls_mpi_init(&P);
-+ mbedtls_mpi_init(&G);
-+ int ret = mbedtls_mpi_lset(&G, generator)
-+ || mbedtls_mpi_read_binary(&P, prime, prime_len)
-+ || mbedtls_dhm_set_group(ctx, &P, &G);
-+ mbedtls_mpi_free(&P);
-+ mbedtls_mpi_free(&G);
-+ return ret;
-+}
-+
-+__attribute_noinline__
-+static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
-+ const u8 *prime, size_t prime_len,
-+ u8 *privkey, u8 *pubkey)
-+{
-+ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
-+ || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()))
-+ return -1;
-+
-+ /*(enable later when upstream mbedtls interface changes require)*/
-+ #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ mbedtls_mpi X;
-+ mbedtls_mpi_init(&X);
-+ int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
-+ || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
-+ mbedtls_mpi_free(&X);
-+ return ret;
-+ #else
-+ return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
-+ privkey, prime_len) ? -1 : 0;
-+ #endif
-+}
-+
-+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
-+ u8 *pubkey)
-+{
-+ #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
-+ size_t pubkey_len, pad;
-+
-+ if (os_get_random(privkey, prime_len) < 0)
-+ return -1;
-+ if (os_memcmp(privkey, prime, prime_len) > 0) {
-+ /* Make sure private value is smaller than prime */
-+ privkey[0] = 0;
-+ }
-+
-+ pubkey_len = prime_len;
-+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
-+ pubkey, &pubkey_len) < 0)
-+ return -1;
-+ if (pubkey_len < prime_len) {
-+ pad = prime_len - pubkey_len;
-+ os_memmove(pubkey + pad, pubkey, pubkey_len);
-+ os_memset(pubkey, 0, pad);
-+ }
-+
-+ return 0;
-+ #else
-+ /* Prefer to use mbedtls to derive our public/private key, as doing so
-+ * leverages mbedtls to properly format output and to perform blinding*/
-+ mbedtls_dhm_context ctx;
-+ mbedtls_dhm_init(&ctx);
-+ int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
-+ prime_len, privkey, pubkey);
-+ mbedtls_dhm_free(&ctx);
-+ return ret;
-+ #endif
-+}
-+
-+/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
-+ * instead of being reimplemented in each crypto_*.c)*/
-+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
-+ const u8 *order, size_t order_len,
-+ const u8 *privkey, size_t privkey_len,
-+ const u8 *pubkey, size_t pubkey_len,
-+ u8 *secret, size_t *len)
-+{
-+ #if 0
-+ if (pubkey_len > prime_len ||
-+ (pubkey_len == prime_len &&
-+ os_memcmp(pubkey, prime, prime_len) >= 0))
-+ return -1;
-+
-+ int res = 0;
-+ mbedtls_mpi pub;
-+ mbedtls_mpi_init(&pub);
-+ if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
-+ || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
-+ res = -1;
-+ } else if (order) {
-+ mbedtls_mpi p, q, tmp;
-+ mbedtls_mpi_init(&p);
-+ mbedtls_mpi_init(&q);
-+ mbedtls_mpi_init(&tmp);
-+
-+ /* verify: pubkey^q == 1 mod p */
-+ res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
-+ || mbedtls_mpi_read_binary(&q, order, order_len)
-+ || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
-+ || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
-+
-+ mbedtls_mpi_free(&p);
-+ mbedtls_mpi_free(&q);
-+ mbedtls_mpi_free(&tmp);
-+ }
-+ mbedtls_mpi_free(&pub);
-+
-+ return (res == 0)
-+ ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-+ prime, prime_len, secret, len)
-+ : -1;
-+ #else
-+ /* Prefer to use mbedtls to derive DH shared secret, as doing so
-+ * leverages mbedtls to validate params and to perform blinding.
-+ *
-+ * Attempt to reconstitute DH context to derive shared secret
-+ * (due to limitations of the interface, which ought to pass context).
-+ * Force provided G (our private key) into context without validation.
-+ * Regenerating GX (our public key) not needed to derive shared secret.
-+ */
-+ /*(older compilers might not support VLAs)*/
-+ /*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
-+ unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
-+ unsigned char *p = buf + 2 + prime_len;
-+ if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
-+ return -1;
-+ WPA_PUT_BE16(buf, prime_len); /*(2-byte big-endian size of prime)*/
-+ p[0] = 0; /*(2-byte big-endian size of generator)*/
-+ p[1] = 1;
-+ p[2] = generator;
-+ WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
-+ os_memcpy(p+5, pubkey, pubkey_len);
-+ os_memcpy(buf+2, prime, prime_len);
-+
-+ mbedtls_dhm_context ctx;
-+ mbedtls_dhm_init(&ctx);
-+ p = buf;
-+ int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
-+ || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
-+ privkey, privkey_len)
-+ || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+ mbedtls_dhm_free(&ctx);
-+ return ret;
-+ #endif
-+}
-+
-+/* dh_group5.c */
-+
-+#include "dh_group5.h"
-+
-+/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
-+
-+static const unsigned char RFC3526_PRIME_1536[] = {
-+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
-+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
-+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
-+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
-+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
-+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
-+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
-+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
-+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
-+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
-+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
-+ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
-+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
-+ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
-+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
-+ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-+};
-+
-+static const unsigned char RFC3526_GENERATOR_1536[] = {
-+ 0x02
-+};
-+
-+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
-+{
-+ const unsigned char * const prime = RFC3526_PRIME_1536;
-+ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+ const u8 generator = *RFC3526_GENERATOR_1536;
-+ struct wpabuf *wpubl = NULL, *wpriv = NULL;
-+
-+ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_dhm_init(ctx);
-+
-+ if ( (wpubl = wpabuf_alloc(prime_len))
-+ && (wpriv = wpabuf_alloc(prime_len))
-+ && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
-+ wpabuf_put(wpriv, prime_len),
-+ wpabuf_put(wpubl, prime_len))==0) {
-+ wpabuf_free(*publ);
-+ wpabuf_clear_free(*priv);
-+ *publ = wpubl;
-+ *priv = wpriv;
-+ return ctx;
-+ }
-+
-+ wpabuf_clear_free(wpriv);
-+ wpabuf_free(wpubl);
-+ mbedtls_dhm_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
-+{
-+ const unsigned char * const prime = RFC3526_PRIME_1536;
-+ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+ const u8 generator = *RFC3526_GENERATOR_1536;
-+
-+ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_dhm_init(ctx);
-+
-+ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
-+ #if 0 /*(ignore; not required to derive shared secret)*/
-+ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
-+ wpabuf_head(publ),wpabuf_len(publ))==0
-+ #endif
-+ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
-+ wpabuf_head(priv),wpabuf_len(priv))==0) {
-+ return ctx;
-+ }
-+
-+ mbedtls_dhm_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+#endif
-+
-+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
-+ const struct wpabuf *own_private)
-+{
-+ /*((mbedtls_dhm_context *)ctx must already contain own_private)*/
-+ /* mbedtls 2.x: prime_len = ctx->len; */
-+ /* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
-+ size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
-+ struct wpabuf *buf = wpabuf_alloc(olen);
-+ if (buf == NULL)
-+ return NULL;
-+ if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
-+ wpabuf_head(peer_public),
-+ wpabuf_len(peer_public)) == 0
-+ && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0) {
-+ wpabuf_put(buf, olen);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+void dh5_free(void *ctx)
-+{
-+ mbedtls_dhm_free(ctx);
-+ os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
-+
-+#include <mbedtls/ecp.h>
-+
-+#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
-+#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
-+#define CRYPTO_EC_P(e) (&((mbedtls_ecp_group *)(e))->P)
-+#define CRYPTO_EC_N(e) (&((mbedtls_ecp_group *)(e))->N)
-+#define CRYPTO_EC_A(e) (&((mbedtls_ecp_group *)(e))->A)
-+#define CRYPTO_EC_B(e) (&((mbedtls_ecp_group *)(e))->B)
-+#define CRYPTO_EC_G(e) (&((mbedtls_ecp_group *)(e))->G)
-+
-+static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
-+{
-+ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+ switch (group) {
-+ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+ case 19: return MBEDTLS_ECP_DP_SECP256R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+ case 20: return MBEDTLS_ECP_DP_SECP384R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+ case 21: return MBEDTLS_ECP_DP_SECP521R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+ case 25: return MBEDTLS_ECP_DP_SECP192R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+ case 26: return MBEDTLS_ECP_DP_SECP224R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+ case 28: return MBEDTLS_ECP_DP_BP256R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+ case 29: return MBEDTLS_ECP_DP_BP384R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+ case 30: return MBEDTLS_ECP_DP_BP512R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+ case 31: return MBEDTLS_ECP_DP_CURVE25519;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+ case 32: return MBEDTLS_ECP_DP_CURVE448;
-+ #endif
-+ default: return MBEDTLS_ECP_DP_NONE;
-+ }
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
-+{
-+ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+ /*(for crypto_ec_key_group())*/
-+ switch (grp_id) {
-+ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP256R1: return 19;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP384R1: return 20;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP521R1: return 21;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP192R1: return 25;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP224R1: return 26;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP256R1: return 28;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP384R1: return 29;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP512R1: return 30;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE25519: return 31;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE448: return 32;
-+ #endif
-+ default: return -1;
-+ }
-+}
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
-+
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return -1;
-+ const mbedtls_pk_info_t *pk_info =
-+ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+ if (pk_info == NULL)
-+ return -1;
-+ return mbedtls_pk_setup(pk, pk_info)
-+ || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
-+
-+#include <mbedtls/ecdh.h>
-+#include <mbedtls/ecdsa.h>
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+/* wrap mbedtls_ecdh_context for more future-proof direct access to components
-+ * (mbedtls_ecdh_context internal implementation may change between releases)
-+ *
-+ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
-+ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
-+ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
-+ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
-+ * wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
-+struct crypto_ecdh {
-+ mbedtls_ecdh_context ctx;
-+ mbedtls_ecp_group grp;
-+ mbedtls_ecp_point Q;
-+};
-+
-+struct crypto_ecdh * crypto_ecdh_init(int group)
-+{
-+ mbedtls_pk_context pk;
-+ mbedtls_pk_init(&pk);
-+ struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
-+ ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
-+ : NULL;
-+ mbedtls_pk_free(&pk);
-+ return ecdh;
-+}
-+
-+struct crypto_ecdh * crypto_ecdh_init2(int group,
-+ struct crypto_ec_key *own_key)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
-+ struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
-+ if (ecdh == NULL)
-+ return NULL;
-+ mbedtls_ecdh_init(&ecdh->ctx);
-+ mbedtls_ecp_group_init(&ecdh->grp);
-+ mbedtls_ecp_point_init(&ecdh->Q);
-+ if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
-+ && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
-+ /* copy grp and Q for later use
-+ * (retrieving this info later is more convoluted
-+ * even if mbedtls_ecdh_make_public() is considered)*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ mbedtls_mpi d;
-+ mbedtls_mpi_init(&d);
-+ if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
-+ mbedtls_mpi_free(&d);
-+ return ecdh;
-+ }
-+ mbedtls_mpi_free(&d);
-+ #else
-+ if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
-+ && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
-+ return ecdh;
-+ #endif
-+ }
-+
-+ mbedtls_ecp_point_free(&ecdh->Q);
-+ mbedtls_ecp_group_free(&ecdh->grp);
-+ mbedtls_ecdh_free(&ecdh->ctx);
-+ os_free(ecdh);
-+ return NULL;
-+}
-+
-+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
-+{
-+ mbedtls_ecp_group *grp = &ecdh->grp;
-+ size_t len = CRYPTO_EC_plen(grp);
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ /* len */
-+ #endif
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-+ len = inc_y ? len*2+1 : len+1;
-+ #endif
-+ struct wpabuf *buf = wpabuf_alloc(len);
-+ if (buf == NULL)
-+ return NULL;
-+ inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
-+ if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &len,
-+ wpabuf_mhead_u8(buf), len) == 0) {
-+ wpabuf_put(buf, len);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
-+ mbedtls_mpi *bn,
-+ int parity_bit)
-+{
-+ /* y^2 = x^3 + ax + b
-+ * sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) */
-+ mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+ crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
-+ (const struct crypto_bignum *)bn); /*x*/
-+ if (cy2 == NULL)
-+ return -1;
-+
-+ /*mbedtls_mpi_free(bn);*/
-+ /*(reuse bn to store result (y))*/
-+
-+ mbedtls_mpi exp;
-+ mbedtls_mpi_init(&exp);
-+ int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
-+ || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
-+ || mbedtls_mpi_add_int(&exp, &grp->P, 1)
-+ || mbedtls_mpi_shift_r(&exp, 2)
-+ || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
-+ || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
-+ && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
-+ mbedtls_mpi_free(&exp);
-+ mbedtls_mpi_free(cy2);
-+ os_free(cy2);
-+ return ret;
-+}
-+#endif
-+
-+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
-+ const u8 *key, size_t len)
-+{
-+ if (len == 0) /*(invalid peer key)*/
-+ return NULL;
-+
-+ mbedtls_ecp_group *grp = &ecdh->grp;
-+
-+ #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ /* add header for mbedtls_ecdh_read_public() */
-+ u8 buf[256];
-+ if (sizeof(buf)-1 < len)
-+ return NULL;
-+ buf[0] = (u8)(len);
-+ os_memcpy(buf+1, key, len);
-+
-+ if (inc_y) {
-+ if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
-+ if (sizeof(buf)-2 < len)
-+ return NULL;
-+ buf[0] = (u8)(1+len);
-+ buf[1] = 0x04;
-+ os_memcpy(buf+2, key, len);
-+ }
-+ len >>= 1; /*(repurpose len to prime_len)*/
-+ }
-+ else if (key[0] == 0x02 || key[0] == 0x03) { /* (inc_y == 0) */
-+ --len; /*(repurpose len to prime_len)*/
-+
-+ /* mbedtls_ecp_point_read_binary() does not currently support
-+ * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
-+ * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
-+
-+ /* derive y, amend buf[] with y for UNCOMPRESSED format */
-+ if (sizeof(buf)-2 < len*2 || len == 0)
-+ return NULL;
-+ buf[0] = (u8)(1+len*2);
-+ buf[1] = 0x04;
-+ mbedtls_mpi bn;
-+ mbedtls_mpi_init(&bn);
-+ int ret = mbedtls_mpi_read_binary(&bn, key+1, len)
-+ || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn,
-+ key[0] & 1)
-+ || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
-+ mbedtls_mpi_free(&bn);
-+ if (ret != 0)
-+ return NULL;
-+ }
-+
-+ if (key[0] == 0) /*(repurpose len to prime_len)*/
-+ len = CRYPTO_EC_plen(grp);
-+
-+ if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
-+ return NULL;
-+ }
-+ #endif
-+ #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
-+ return NULL;
-+ }
-+ #endif
-+
-+ struct wpabuf *buf = wpabuf_alloc(len);
-+ if (buf == NULL)
-+ return NULL;
-+
-+ if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
-+ wpabuf_mhead(buf), len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0) {
-+ wpabuf_put(buf, len);
-+ return buf;
-+ }
-+
-+ wpabuf_clear_free(buf);
-+ return NULL;
-+}
-+
-+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
-+{
-+ if (ecdh == NULL)
-+ return;
-+ mbedtls_ecp_point_free(&ecdh->Q);
-+ mbedtls_ecp_group_free(&ecdh->grp);
-+ mbedtls_ecdh_free(&ecdh->ctx);
-+ os_free(ecdh);
-+}
-+
-+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
-+{
-+ return CRYPTO_EC_plen(&ecdh->grp);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+
-+#include <mbedtls/ecp.h>
-+
-+struct crypto_ec *crypto_ec_init(int group)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ mbedtls_ecp_group *e = os_malloc(sizeof(*e));
-+ if (e == NULL)
-+ return NULL;
-+ mbedtls_ecp_group_init(e);
-+ if (mbedtls_ecp_group_load(e, grp_id) == 0)
-+ return (struct crypto_ec *)e;
-+
-+ mbedtls_ecp_group_free(e);
-+ os_free(e);
-+ return NULL;
-+}
-+
-+void crypto_ec_deinit(struct crypto_ec *e)
-+{
-+ mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
-+ os_free(e);
-+}
-+
-+size_t crypto_ec_prime_len(struct crypto_ec *e)
-+{
-+ return CRYPTO_EC_plen(e);
-+}
-+
-+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
-+{
-+ return CRYPTO_EC_pbits(e);
-+}
-+
-+size_t crypto_ec_order_len(struct crypto_ec *e)
-+{
-+ return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
-+{
-+ return (const struct crypto_bignum *)CRYPTO_EC_P(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
-+{
-+ return (const struct crypto_bignum *)CRYPTO_EC_N(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
-+{
-+ static const uint8_t secp256r1_a[] =
-+ {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
-+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+ static const uint8_t secp384r1_a[] =
-+ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
-+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
-+ static const uint8_t secp521r1_a[] =
-+ {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xfc};
-+ static const uint8_t secp192r1_a[] =
-+ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+ static const uint8_t secp224r1_a[] =
-+ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xfe};
-+
-+ const uint8_t *bin = NULL;
-+ size_t len = 0;
-+
-+ /* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
-+ switch (((mbedtls_ecp_group *)e)->id) {
-+ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP256R1:
-+ bin = secp256r1_a;
-+ len = sizeof(secp256r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP384R1:
-+ bin = secp384r1_a;
-+ len = sizeof(secp384r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP521R1:
-+ bin = secp521r1_a;
-+ len = sizeof(secp521r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP192R1:
-+ bin = secp192r1_a;
-+ len = sizeof(secp192r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP224R1:
-+ bin = secp224r1_a;
-+ len = sizeof(secp224r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP256R1:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP384R1:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP512R1:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE25519:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE448:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+
-+ /*(note: not thread-safe; returns file-scoped static storage)*/
-+ if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
-+ return (const struct crypto_bignum *)&mpi_sw_A;
-+ return NULL;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
-+{
-+ return (const struct crypto_bignum *)CRYPTO_EC_B(e);
-+}
-+
-+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
-+{
-+ return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
-+}
-+
-+struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
-+{
-+ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+ if (p != NULL)
-+ mbedtls_ecp_point_init(p);
-+ return (struct crypto_ec_point *)p;
-+}
-+
-+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
-+{
-+ mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
-+ os_free(p);
-+}
-+
-+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
-+ struct crypto_bignum *x)
-+{
-+ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+ return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
-+ ? -1
-+ : 0;
-+}
-+
-+int crypto_ec_point_to_bin(struct crypto_ec *e,
-+ const struct crypto_ec_point *point, u8 *x, u8 *y)
-+{
-+ /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
-+ size_t len = CRYPTO_EC_plen(e);
-+ if (x) {
-+ mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
-+ if (mbedtls_mpi_write_binary(px, x, len))
-+ return -1;
-+ }
-+ if (y) {
-+ #if 0 /*(should not be necessary; py mpi should be in initial state)*/
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ os_memset(y, 0, len);
-+ return 0;
-+ }
-+ #endif
-+ #endif
-+ mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
-+ if (mbedtls_mpi_write_binary(py, y, len))
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
-+ const u8 *val)
-+{
-+ size_t len = CRYPTO_EC_plen(e);
-+ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+ if (p == NULL)
-+ return NULL;
-+ mbedtls_ecp_point_init(p);
-+
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
-+ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+ mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
-+
-+ if (mbedtls_mpi_read_binary(px, val, len) == 0
-+ && mbedtls_mpi_read_binary(py, val + len, len) == 0
-+ && mbedtls_mpi_lset(pz, 1) == 0)
-+ return (struct crypto_ec_point *)p;
-+ #else
-+ buf[0] = 0x04;
-+ os_memcpy(buf+1, val, len*2);
-+ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+ buf, 1+len*2) == 0)
-+ return (struct crypto_ec_point *)p;
-+ #endif
-+ }
-+ #endif
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ /* crypto.h interface documents crypto_ec_point_from_bin()
-+ * val is length: prime_len * 2 and is big-endian
-+ * (Short Weierstrass is assumed by hostap)
-+ * Reverse to little-endian format for Montgomery */
-+ for (unsigned int i = 0; i < len; ++i)
-+ buf[i] = val[len-1-i];
-+ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+ buf, len) == 0)
-+ return (struct crypto_ec_point *)p;
-+ }
-+ #endif
-+
-+ mbedtls_ecp_point_free(p);
-+ os_free(p);
-+ return NULL;
-+}
-+
-+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
-+ const struct crypto_ec_point *b,
-+ struct crypto_ec_point *c)
-+{
-+ /* mbedtls does not provide an mbedtls_ecp_point add function */
-+ mbedtls_mpi one;
-+ mbedtls_mpi_init(&one);
-+ int ret = mbedtls_mpi_lset(&one, 1)
-+ || mbedtls_ecp_muladd(
-+ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
-+ &one, (const mbedtls_ecp_point *)a,
-+ &one, (const mbedtls_ecp_point *)b) ? -1 : 0;
-+ mbedtls_mpi_free(&one);
-+ return ret;
-+}
-+
-+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
-+ const struct crypto_bignum *b,
-+ struct crypto_ec_point *res)
-+{
-+ return mbedtls_ecp_mul(
-+ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
-+ (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
-+ mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
-+{
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
-+ wpa_printf(MSG_ERROR,
-+ "%s not implemented for Montgomery curves",__func__);
-+ return -1;
-+ }
-+
-+ /* mbedtls does not provide an mbedtls_ecp_point invert function */
-+ /* below works for Short Weierstrass; incorrect for Montgomery curves */
-+ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
-+ || mbedtls_mpi_cmp_int(py, 0) == 0 /*point is its own inverse*/
-+ || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
-+}
-+
-+#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+static int
-+crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+ mbedtls_mpi *y2)
-+{
-+ /* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS y^2 = x^3 + a x + b */
-+
-+ /* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
-+ /* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
-+ * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
-+ * treated as if A = -3. */
-+
-+ #if 0
-+ /* y^2 = x^3 + ax + b */
-+ mbedtls_mpi *A = &e->A;
-+ mbedtls_mpi t, A_neg3;
-+ if (&e->A.p == NULL) {
-+ mbedtls_mpi_init(&A_neg3);
-+ if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
-+ mbedtls_mpi_free(&A_neg3);
-+ return -1;
-+ }
-+ A = &A_neg3;
-+ }
-+ mbedtls_mpi_init(&t);
-+ int ret = /* x^3 */
-+ mbedtls_mpi_lset(&t, 3)
-+ || mbedtls_mpi_exp_mod(y2, x, &t, &e->P, NULL)
-+ /* ax */
-+ || mbedtls_mpi_mul_mpi(y2, y2, A)
-+ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+ /* ax + b */
-+ || mbedtls_mpi_add_mpi(&t, &t, &e->B)
-+ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+ /* x^3 + ax + b */
-+ || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
-+ || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
-+ mbedtls_mpi_free(&t);
-+ if (A == &A_neg3)
-+ mbedtls_mpi_free(&A_neg3);
-+ return ret; /* 0: success, non-zero: failure */
-+ #else
-+ /* y^2 = x^3 + ax + b = (x^2 + a)x + b */
-+ return /* x^2 */
-+ mbedtls_mpi_mul_mpi(y2, x, x)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* x^2 + a */
-+ || (e->A.MBEDTLS_PRIVATE(p)
-+ ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
-+ : mbedtls_mpi_sub_int(y2, y2, 3))
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x^2 + a)x */
-+ || mbedtls_mpi_mul_mpi(y2, y2, x)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x^2 + a)x + b */
-+ || mbedtls_mpi_add_mpi(y2, y2, &e->B)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+ #endif
-+}
-+#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
-+
-+#if 0 /* not used by hostap */
-+#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+static int
-+crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+ mbedtls_mpi *y2)
-+{
-+ /* XXX: !!! must be reviewed and audited for correctness !!! */
-+
-+ /* MBEDTLS_ECP_TYPE_MONTGOMERY y^2 = x^3 + a x^2 + x */
-+
-+ /* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
-+ mbedtls_mpi x2;
-+ mbedtls_mpi_init(&x2);
-+ int ret = /* x^2 */
-+ mbedtls_mpi_mul_mpi(&x2, x, x)
-+ || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
-+ /* x + a */
-+ || mbedtls_mpi_add_mpi(y2, x, &e->A)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x + a)x^2 */
-+ || mbedtls_mpi_mul_mpi(y2, y2, &x2)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x + a)x^2 + x */
-+ || mbedtls_mpi_add_mpi(y2, y2, x)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+ mbedtls_mpi_free(&x2);
-+ return ret; /* 0: success, non-zero: failure */
-+}
-+#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
-+#endif
-+
-+struct crypto_bignum *
-+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
-+ const struct crypto_bignum *x)
-+{
-+ mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
-+ if (y2 == NULL)
-+ return NULL;
-+ mbedtls_mpi_init(y2);
-+
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+ && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
-+ (const mbedtls_mpi *)x,
-+ y2) == 0)
-+ return (struct crypto_bignum *)y2;
-+ #endif
-+ #if 0 /* not used by hostap */
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY
-+ && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
-+ (const mbedtls_mpi *)x,
-+ y2) == 0)
-+ return (struct crypto_bignum *)y2;
-+ #endif
-+ #endif
-+
-+ mbedtls_mpi_free(y2);
-+ os_free(y2);
-+ return NULL;
-+}
-+
-+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
-+ const struct crypto_ec_point *p)
-+{
-+ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
-+}
-+
-+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
-+ const struct crypto_ec_point *p)
-+{
-+ #if 1
-+ return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
-+ (const mbedtls_ecp_point *)p) == 0;
-+ #else
-+ /* compute y^2 mod P and compare to y^2 mod P */
-+ /*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
-+ const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+ mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+ crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
-+ if (cy2 == NULL)
-+ return 0;
-+
-+ mbedtls_mpi y2;
-+ mbedtls_mpi_init(&y2);
-+ const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+ int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
-+ || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
-+ || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
-+
-+ mbedtls_mpi_free(&y2);
-+ mbedtls_mpi_free(cy2);
-+ os_free(cy2);
-+ return is_on_curve;
-+ #endif
-+}
-+
-+int crypto_ec_point_cmp(const struct crypto_ec *e,
-+ const struct crypto_ec_point *a,
-+ const struct crypto_ec_point *b)
-+{
-+ return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
-+ (const mbedtls_ecp_point *)b);
-+}
-+
-+#if !defined(CONFIG_NO_STDOUT_DEBUG)
-+void crypto_ec_point_debug_print(const struct crypto_ec *e,
-+ const struct crypto_ec_point *p,
-+ const char *title)
-+{
-+ u8 x[MBEDTLS_MPI_MAX_SIZE];
-+ u8 y[MBEDTLS_MPI_MAX_SIZE];
-+ size_t len = CRYPTO_EC_plen(e);
-+ /* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
-+ struct crypto_ec *ee;
-+ *(const struct crypto_ec **)&ee = e; /*(cast away const)*/
-+ if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
-+ if (title)
-+ wpa_printf(MSG_DEBUG, "%s", title);
-+ wpa_hexdump(MSG_DEBUG, "x:", x, len);
-+ wpa_hexdump(MSG_DEBUG, "y:", y, len);
-+ }
-+}
-+#endif
-+
-+
-+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
-+{
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
-+ #else
-+ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0)
-+ #endif
-+ return (struct crypto_ec_key *)ctx;
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#ifdef CONFIG_MODULE_TESTS
-+/*(for crypto_module_tests.c)*/
-+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
-+ const u8 *raw, size_t raw_len)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ const mbedtls_pk_info_t *pk_info =
-+ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+ if (pk_info == NULL)
-+ return NULL;
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ if (mbedtls_pk_setup(ctx, pk_info) == 0
-+ && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
-+ return (struct crypto_ec_key *)ctx;
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+#endif
-+#endif
-+
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
-+{
-+ /* The following is modified from:
-+ * mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
-+ * mbedtls/library/pkparse.c:pk_get_pk_alg()
-+ * mbedtls/library/pkparse.c:pk_use_ecparams()
-+ */
-+ mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
-+ const mbedtls_pk_info_t *pk_info;
-+ int ret;
-+ size_t len;
-+ const unsigned char *end = der+der_len;
-+ unsigned char *p;
-+ *(const unsigned char **)&p = der;
-+
-+ if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
-+ {
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
-+ }
-+
-+ end = p + len;
-+
-+ /*
-+ if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
-+ return( ret );
-+ */
-+ mbedtls_asn1_buf alg_oid, params;
-+ memset( ¶ms, 0, sizeof(mbedtls_asn1_buf) );
-+ if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, ¶ms ) ) != 0 )
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
-+ if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
-+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+ if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
-+
-+ if( p + len != end )
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
-+ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
-+
-+ if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
-+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
-+ return( ret );
-+
-+ /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
-+ * has already run with ctx initialized up to pk_get_ecpubkey(),
-+ * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
-+ *
-+ * mbedtls mbedtls_ecp_point_read_binary()
-+ * does not handle point in COMPRESSED format
-+ *
-+ * (validate assumption that algorithm is EC) */
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+ if (ecp_kp == NULL)
-+ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ mbedtls_ecp_group_id grp_id;
-+
-+
-+ /* mbedtls/library/pkparse.c:pk_use_ecparams() */
-+
-+ if( params.tag == MBEDTLS_ASN1_OID )
-+ {
-+ if( mbedtls_oid_get_ec_grp( ¶ms, &grp_id ) != 0 )
-+ return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
-+ }
-+ else
-+ {
-+#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
-+ /*(large code block not copied from mbedtls; unsupported)*/
-+ #if 0
-+ if( ( ret = pk_group_id_from_specified( ¶ms, &grp_id ) ) != 0 )
-+ return( ret );
-+ #endif
-+#endif
-+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+ }
-+
-+ /*
-+ * grp may already be initialized; if so, make sure IDs match
-+ */
-+ if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
-+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+
-+ if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
-+ return( ret );
-+
-+
-+ /* (validate assumption that EC point is in COMPRESSED format) */
-+ len = CRYPTO_EC_plen(ecp_kp_grp);
-+ if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+ || (end - p) != 1+len
-+ || (*p != 0x02 && *p != 0x03) )
-+ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+
-+ /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
-+ * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
-+ mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
-+ mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
-+ mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
-+ ret = mbedtls_mpi_lset(Z, 1);
-+ if (ret != 0)
-+ return( ret );
-+ ret = mbedtls_mpi_read_binary(X, p+1, len);
-+ if (ret != 0)
-+ return( ret );
-+ /* derive Y
-+ * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
-+ ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
-+ || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
-+ if (ret != 0)
-+ return( ret );
-+
-+ return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
-+{
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ /*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
-+ int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
-+ if (rc == 0)
-+ return (struct crypto_ec_key *)ctx;
-+ else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
-+ /* mbedtls mbedtls_ecp_point_read_binary()
-+ * does not handle point in COMPRESSED format; parse internally */
-+ rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
-+ if (rc == 0)
-+ return (struct crypto_ec_key *)ctx;
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+static struct crypto_ec_key *
-+crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
-+ const mbedtls_ecp_point *pub,
-+ const u8 *buf, size_t len)
-+{
-+ const mbedtls_pk_info_t *pk_info =
-+ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+ if (pk_info == NULL)
-+ return NULL;
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ if (mbedtls_pk_setup(ctx, pk_info) == 0) {
-+ /* (Is private key generation necessary for callers?)
-+ * alt: gen key then overwrite Q
-+ * mbedtls_ecp_gen_key(grp_id, ecp_kp,
-+ * mbedtls_ctr_drbg_random,
-+ * crypto_mbedtls_ctr_drbg()) == 0
-+ */
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+ if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
-+ && (pub
-+ ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
-+ : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
-+ buf, len) == 0)
-+ && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0){
-+ return (struct crypto_ec_key *)ctx;
-+ }
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
-+ const u8 *y, size_t len)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ if (len > MBEDTLS_MPI_MAX_SIZE)
-+ return NULL;
-+ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+ buf[0] = 0x04; /* assume x,y for Short Weierstrass */
-+ os_memcpy(buf+1, x, len);
-+ os_memcpy(buf+1+len, y, len);
-+
-+ return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
-+}
-+
-+struct crypto_ec_key *
-+crypto_ec_key_set_pub_point(struct crypto_ec *e,
-+ const struct crypto_ec_point *pub)
-+{
-+ mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
-+ mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
-+ return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
-+}
-+
-+
-+struct crypto_ec_key * crypto_ec_key_gen(int group)
-+{
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
-+ return (struct crypto_ec_key *)ctx;
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+void crypto_ec_key_deinit(struct crypto_ec_key *key)
-+{
-+ mbedtls_pk_free((mbedtls_pk_context *)key);
-+ os_free(key);
-+}
-+
-+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
-+{
-+ /* (similar to crypto_ec_key_get_pubkey_point(),
-+ * but compressed point format and ASN.1 DER wrapping)*/
-+#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+ unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
-+ int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
-+ buf, sizeof(buf));
-+ if (len < 0)
-+ return NULL;
-+ /* Note: data is written at the end of the buffer! Use the
-+ * return value to determine where you should start
-+ * using the buffer */
-+ unsigned char *p = buf+sizeof(buf)-len;
-+
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ /* Note: sae_pk.c expects pubkey point in compressed format,
-+ * but mbedtls_pk_write_pubkey_der() writes uncompressed format.
-+ * Manually translate format and update lengths in DER format */
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ unsigned char *end = buf+sizeof(buf);
-+ size_t n;
-+ /* SubjectPublicKeyInfo SEQUENCE */
-+ mbedtls_asn1_get_tag(&p, end, &n,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ /* algorithm AlgorithmIdentifier */
-+ unsigned char *a = p;
-+ size_t alen;
-+ mbedtls_asn1_get_tag(&p, end, &alen,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ p += alen;
-+ alen = (size_t)(p - a);
-+ /* subjectPublicKey BIT STRING */
-+ mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
-+ /* rewrite into compressed point format and rebuild ASN.1 */
-+ p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
-+ n = 1 + 1 + (n-2)/2;
-+ len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
-+ len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
-+ os_memmove(p-alen, a, alen);
-+ len += alen;
-+ p -= alen;
-+ len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
-+ len += mbedtls_asn1_write_tag(&p, buf,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ }
-+ #endif
-+ return wpabuf_alloc_copy(p, (size_t)len);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
-+ bool include_pub)
-+{
-+#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+ unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
-+ int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
-+ priv, sizeof(priv));
-+ if (privlen < 0)
-+ return NULL;
-+
-+ struct wpabuf *wbuf;
-+
-+ /* Note: data is written at the end of the buffer! Use the
-+ * return value to determine where you should start
-+ * using the buffer */
-+ /* mbedtls_pk_write_key_der() includes publicKey in DER */
-+ if (include_pub)
-+ wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
-+ else {
-+ /* calculate publicKey offset and skip from end of buffer */
-+ unsigned char *p = priv+sizeof(priv)-privlen;
-+ unsigned char *end = priv+sizeof(priv);
-+ size_t len;
-+ /* ECPrivateKey SEQUENCE */
-+ mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ /* version INTEGER */
-+ unsigned char *v = p;
-+ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
-+ p += len;
-+ /* privateKey OCTET STRING */
-+ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
-+ p += len;
-+ /* parameters ECParameters */
-+ mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
-+ p += len;
-+
-+ /* write new SEQUENCE header (we know that it fits in priv[]) */
-+ len = (size_t)(p - v);
-+ p = v;
-+ len += mbedtls_asn1_write_len(&p, priv, len);
-+ len += mbedtls_asn1_write_tag(&p, priv,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ wbuf = wpabuf_alloc_copy(p, len);
-+ }
-+
-+ forced_memzero(priv, sizeof(priv));
-+ return wbuf;
-+}
-+
-+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
-+ int prefix)
-+{
-+ /*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ size_t len = CRYPTO_EC_plen(grp);
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ /* len */
-+ #endif
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-+ len = len*2+1;
-+ #endif
-+ struct wpabuf *buf = wpabuf_alloc(len);
-+ if (buf == NULL)
-+ return NULL;
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
-+ MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
-+ wpabuf_mhead_u8(buf), len) == 0) {
-+ if (!prefix) /* Remove 0x04 prefix if requested */
-+ os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
-+ wpabuf_put(buf, len);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+struct crypto_ec_point *
-+crypto_ec_key_get_public_key(struct crypto_ec_key *key)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+ if (p != NULL) {
-+ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+ mbedtls_ecp_point_init(p);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
-+ mbedtls_ecp_point_free(p);
-+ os_free(p);
-+ p = NULL;
-+ }
-+ }
-+ return (struct crypto_ec_point *)p;
-+}
-+
-+struct crypto_bignum *
-+crypto_ec_key_get_private_key(struct crypto_ec_key *key)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn) {
-+ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+ mbedtls_mpi_init(bn);
-+ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+ if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
-+ mbedtls_mpi_free(bn);
-+ os_free(bn);
-+ bn = NULL;
-+ }
-+ }
-+ return (struct crypto_bignum *)bn;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
-+{
-+ /* get mbedtls_md_type_t from length of hash data to be signed */
-+ switch (len) {
-+ case 64: return MBEDTLS_MD_SHA512;
-+ case 48: return MBEDTLS_MD_SHA384;
-+ case 32: return MBEDTLS_MD_SHA256;
-+ case 20: return MBEDTLS_MD_SHA1;
-+ case 16: return MBEDTLS_MD_MD5;
-+ default: return MBEDTLS_MD_NONE;
-+ }
-+}
-+
-+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
-+ size_t len)
-+{
-+ #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
-+ #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
-+ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
-+ #else
-+ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
-+ #endif
-+ #endif
-+ size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
-+ struct wpabuf *buf = wpabuf_alloc(sig_len);
-+ if (buf == NULL)
-+ return NULL;
-+ if (mbedtls_pk_sign((mbedtls_pk_context *)key,
-+ crypto_ec_key_sign_md(len), data, len,
-+ wpabuf_mhead_u8(buf),
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ sig_len,
-+ #endif
-+ &sig_len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0) {
-+ wpabuf_put(buf, sig_len);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
-+ const u8 *data, size_t len)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+
-+ size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
-+ u8 buf[MBEDTLS_ECDSA_MAX_LEN];
-+ if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
-+ data, len, buf,
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ sig_len,
-+ #endif
-+ &sig_len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg())) {
-+ return NULL;
-+ }
-+
-+ /*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
-+ /* parse ASN.1 to get r and s and lengths */
-+ u8 *p = buf, *r, *s;
-+ u8 *end = p + sig_len;
-+ size_t rlen, slen;
-+ mbedtls_asn1_get_tag(&p, end, &rlen,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
-+ r = p;
-+ p += rlen;
-+ mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
-+ s = p;
-+
-+ /* write raw r and s into out
-+ * (including removal of leading 0 if added for ASN.1 integer)
-+ * note: DPP caller expects raw r, s each padded to prime len */
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
-+ if (rlen > plen) {
-+ r += (rlen - plen);
-+ rlen = plen;
-+ }
-+ if (slen > plen) {
-+ s += (slen - plen);
-+ slen = plen;
-+ }
-+ struct wpabuf *out = wpabuf_alloc(plen*2);
-+ if (out) {
-+ wpabuf_put(out, plen*2);
-+ p = wpabuf_mhead_u8(out);
-+ os_memset(p, 0, plen*2);
-+ os_memcpy(p+plen*1-rlen, r, rlen);
-+ os_memcpy(p+plen*2-slen, s, slen);
-+ }
-+ return out;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
-+ size_t len, const u8 *sig, size_t sig_len)
-+{
-+ switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
-+ crypto_ec_key_sign_md(len), data, len,
-+ sig, sig_len)) {
-+ case 0:
-+ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+ return 1;
-+ case MBEDTLS_ERR_ECP_VERIFY_FAILED:
-+ return 0;
-+ default:
-+ return -1;
-+ }
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
-+ const u8 *data, size_t len,
-+ const u8 *r, size_t r_len,
-+ const u8 *s, size_t s_len)
-+{
-+ /* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
-+ * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return -1;
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+
-+ mbedtls_mpi mpi_r;
-+ mbedtls_mpi mpi_s;
-+ mbedtls_mpi_init(&mpi_r);
-+ mbedtls_mpi_init(&mpi_s);
-+ int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
-+ || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
-+ if (ret == 0) {
-+ ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
-+ ecp_kp_Q, &mpi_r, &mpi_s);
-+ ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
-+ }
-+ mbedtls_mpi_free(&mpi_r);
-+ mbedtls_mpi_free(&mpi_s);
-+ return ret;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_group(struct crypto_ec_key *key)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return -1;
-+ mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
-+{
-+#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+ (const mbedtls_pk_context *)key2) ? -1 : 0;
-+ #else
-+ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+ (const mbedtls_pk_context *)key2,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+ #endif
-+#else
-+ mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
-+ mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
-+ if (ecp_kp1 == NULL || ecp_kp2 == NULL)
-+ return -1;
-+ mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
-+ mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
-+ return ecp_kp1_grp->id != ecp_kp2_grp->id
-+ || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
-+#endif
-+}
-+
-+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
-+ const char *title)
-+{
-+ /* TBD: what info is desirable here and in what human readable format?*/
-+ /*(crypto_openssl.c prints a human-readably public key and attributes)*/
-+ #if 0
-+ struct mbedtls_pk_debug_item debug_item;
-+ if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
-+ return;
-+ /* ... */
-+ #endif
-+ wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
-+
-+#include <mbedtls/x509_csr.h>
-+#include <mbedtls/oid.h>
-+
-+struct crypto_csr * crypto_csr_init(void)
-+{
-+ mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
-+ if (csr != NULL)
-+ mbedtls_x509write_csr_init(csr);
-+ return (struct crypto_csr *)csr;
-+}
-+
-+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
-+{
-+ /* future: look for alternatives to MBEDTLS_PRIVATE() access */
-+
-+ /* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
-+ * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
-+ * so allocate different object (mbedtls_x509_csr *) and special-case
-+ * object when used in crypto_csr_get_attribute() and when free()d in
-+ * crypto_csr_deinit(). */
-+
-+ mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
-+ if (csr == NULL)
-+ return NULL;
-+ mbedtls_x509_csr_init(csr);
-+ const mbedtls_md_info_t *md_info;
-+ unsigned char digest[MBEDTLS_MD_MAX_SIZE];
-+ if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
-+ && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
-+ != NULL
-+ && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
-+ switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
-+ digest, mbedtls_md_get_size(md_info),
-+ csr->MBEDTLS_PRIVATE(sig).p,
-+ csr->MBEDTLS_PRIVATE(sig).len)) {
-+ case 0:
-+ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+ return (struct crypto_csr *)((uintptr_t)csr | 1uL);
-+ default:
-+ break;
-+ }
-+ }
-+
-+ mbedtls_x509_csr_free(csr);
-+ os_free(csr);
-+ return NULL;
-+}
-+
-+void crypto_csr_deinit(struct crypto_csr *csr)
-+{
-+ if ((uintptr_t)csr & 1uL) {
-+ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+ mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
-+ }
-+ else
-+ mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
-+ os_free(csr);
-+}
-+
-+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
-+ struct crypto_ec_key *key)
-+{
-+ mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
-+ (mbedtls_pk_context *)key);
-+ return 0;
-+}
-+
-+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
-+ const char *name)
-+{
-+ /* specialized for src/common/dpp_crypto.c */
-+
-+ /* sole caller src/common/dpp_crypto.c:dpp_build_csr()
-+ * calls this function only once, using type == CSR_NAME_CN
-+ * (If called more than once, this code would need to append
-+ * components to the subject name, which we could do by
-+ * appending to (mbedtls_x509write_csr *) private member
-+ * mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
-+
-+ const char *label;
-+ switch (type) {
-+ case CSR_NAME_CN: label = "CN="; break;
-+ case CSR_NAME_SN: label = "SN="; break;
-+ case CSR_NAME_C: label = "C="; break;
-+ case CSR_NAME_O: label = "O="; break;
-+ case CSR_NAME_OU: label = "OU="; break;
-+ default: return -1;
-+ }
-+
-+ size_t len = strlen(name);
-+ struct wpabuf *buf = wpabuf_alloc(3+len+1);
-+ if (buf == NULL)
-+ return -1;
-+ wpabuf_put_data(buf, label, strlen(label));
-+ wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
-+ /* Note: 'name' provided is set as given and should be backslash-escaped
-+ * by caller when necessary, e.g. literal ',' which are not separating
-+ * components should be backslash-escaped */
-+
-+ int ret =
-+ mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
-+ wpabuf_head(buf)) ? -1 : 0;
-+ wpabuf_free(buf);
-+ return ret;
-+}
-+
-+/* OBJ_pkcs9_challengePassword 1 2 840 113549 1 9 7 */
-+static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
-+
-+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
-+ int attr_type, const u8 *value, size_t len)
-+{
-+ /* specialized for src/common/dpp_crypto.c */
-+ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+ * attr == CSR_ATTR_CHALLENGE_PASSWORD
-+ * attr_type == ASN1_TAG_UTF8STRING */
-+
-+ const char *oid;
-+ size_t oid_len;
-+ switch (attr) {
-+ case CSR_ATTR_CHALLENGE_PASSWORD:
-+ oid = OBJ_pkcs9_challengePassword;
-+ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+ break;
-+ default:
-+ return -1;
-+ }
-+
-+ #if 0 /*(incorrect; sets an extension, not an attribute)*/
-+ return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
-+ oid, oid_len,
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ 0, /*(critical flag)*/
-+ #endif
-+ value, len) ? -1 : 0;
-+ #else
-+ (void)oid;
-+ (void)oid_len;
-+ #endif
-+
-+ /* mbedtls does not currently provide way to set an attribute in a CSR:
-+ * https://github.com/Mbed-TLS/mbedtls/issues/4886 */
-+ wpa_printf(MSG_ERROR,
-+ "mbedtls does not currently support setting challengePassword "
-+ "attribute in CSR");
-+ return -1;
-+}
-+
-+const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
-+ const char *oid, size_t oid_len,
-+ size_t *vlen, int *vtype)
-+{
-+ /* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
-+ * so validation checks are not repeated here
-+ *
-+ * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
-+ * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
-+ * already parsed the rest of CertificationRequestInfo, some of which is
-+ * repeated here to step to Attributes. Since csr->subject_raw.p points
-+ * into csr->cri.p, which points into csr->raw.p, step over version and
-+ * subject of CertificationRequestInfo (SEQUENCE) */
-+ unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
-+ unsigned char *end = csr->cri.p + csr->cri.len, *ext;
-+ size_t len;
-+
-+ /* step over SubjectPublicKeyInfo */
-+ mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ p += len;
-+
-+ /* Attributes
-+ * { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
-+ */
-+ if (mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
-+ return NULL;
-+ }
-+ while (p < end) {
-+ if (mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
-+ return NULL;
-+ }
-+ ext = p;
-+ p += len;
-+
-+ if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
-+ return NULL;
-+ if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
-+ continue;
-+
-+ /* found oid; return value */
-+ *vtype = *ext++; /* tag */
-+ return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
-+ }
-+
-+ return NULL;
-+}
-+
-+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
-+ enum crypto_csr_attr attr,
-+ size_t *len, int *type)
-+{
-+ /* specialized for src/common/dpp_crypto.c */
-+ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+ * attr == CSR_ATTR_CHALLENGE_PASSWORD */
-+
-+ const char *oid;
-+ size_t oid_len;
-+ switch (attr) {
-+ case CSR_ATTR_CHALLENGE_PASSWORD:
-+ oid = OBJ_pkcs9_challengePassword;
-+ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+ break;
-+ default:
-+ return NULL;
-+ }
-+
-+ /* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
-+ if (!((uintptr_t)csr & 1uL))
-+ return NULL;
-+ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+
-+ return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
-+ oid, oid_len, len, type);
-+}
-+
-+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
-+ struct crypto_ec_key *key,
-+ enum crypto_hash_alg algo)
-+{
-+ mbedtls_md_type_t sig_md;
-+ switch (algo) {
-+ #ifdef MBEDTLS_SHA256_C
-+ case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
-+ #endif
-+ #ifdef MBEDTLS_SHA512_C
-+ case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
-+ case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+ mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
-+
-+ #if 0
-+ unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
-+ | MBEDTLS_X509_KU_KEY_CERT_SIGN;
-+ if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
-+ key_usage))
-+ return NULL;
-+ #endif
-+
-+ #if 0
-+ unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
-+ | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
-+ if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
-+ ns_cert_type))
-+ return NULL;
-+ #endif
-+
-+ #if 0
-+ /* mbedtls does not currently provide way to set an attribute in a CSR:
-+ * https://github.com/Mbed-TLS/mbedtls/issues/4886
-+ * XXX: hwsim dpp_enterprise test fails due to this limitation.
-+ *
-+ * Current usage of this function is solely by dpp_build_csr(),
-+ * so as a kludge, might consider custom (struct crypto_csr *)
-+ * containing (mbedtls_x509write_csr *) and a list of attributes
-+ * (i.e. challengePassword). Might have to totally reimplement
-+ * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
-+ * handles signing the CSR. (This is more work that appending an
-+ * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
-+ */
-+ #endif
-+
-+ unsigned char buf[4096]; /* XXX: large enough? too large? */
-+ int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
-+ buf, sizeof(buf),
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg());
-+ if (len < 0)
-+ return NULL;
-+ /* Note: data is written at the end of the buffer! Use the
-+ * return value to determine where you should start
-+ * using the buffer */
-+ return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+
-+#if 0
-+#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
-+#include <mbedtls/pem.h>
-+#endif
-+
-+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
-+{
-+ /* PKCS7 is not currently supported in mbedtls */
-+ return NULL;
-+
-+#if 0
-+ /* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
-+ * (??? potential future contribution to mbedtls ???) */
-+
-+ /* Note: PKCS7 signature *is not* verified by this function.
-+ * The function interface does not provide for passing a certificate */
-+
-+ mbedtls_pkcs7 mpkcs7;
-+ mbedtls_pkcs7_init(&mpkcs7);
-+ int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
-+ wpabuf_len(pkcs7),
-+ &mpkcs7);
-+ wpabuf *buf = NULL;
-+ do {
-+ if (pkcs7_type < 0)
-+ break;
-+
-+ /* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
-+ * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
-+ * (? are adding certificate headers and footers desired ?) */
-+
-+ /* development-pkcs7 branch does not currently provide
-+ * additional interfaces to retrieve the parsed data */
-+
-+ mbedtls_x509_crt *certs =
-+ &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
-+ int ncerts =
-+ mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
-+
-+ /* allocate buffer for PEM (base64-encoded DER)
-+ * plus header, footer, newlines, and some extra */
-+ buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
-+ if (buf == NULL)
-+ break;
-+
-+ #define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
-+ #define PEM_END_CRT "-----END CERTIFICATE-----\n"
-+ size_t olen;
-+ for (int i = 0; i < ncerts; ++i) {
-+ int ret = mbedtls_pem_write_buffer(
-+ PEM_BEGIN_CRT, PEM_END_CRT,
-+ certs[i].raw.p, certs[i].raw.len,
-+ wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
-+ &olen));
-+ if (ret == 0)
-+ wpabuf_put(buf, olen);
-+ } else {
-+ if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
-+ ret = wpabuf_resize(
-+ &buf,olen-wpabuf_tailroom(buf));
-+ if (ret == 0) {
-+ --i;/*(adjust loop iterator for retry)*/
-+ continue;
-+ }
-+ wpabuf_free(buf);
-+ buf = NULL;
-+ break;
-+ }
-+ }
-+ } while (0);
-+
-+ mbedtls_pkcs7_free(&mpkcs7);
-+ return buf;
-+#endif
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
-+
-+
-+#ifdef MBEDTLS_ARC4_C
-+#include <mbedtls/arc4.h>
-+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
-+ u8 *data, size_t data_len)
-+{
-+ mbedtls_arc4_context ctx;
-+ mbedtls_arc4_init(&ctx);
-+ mbedtls_arc4_setup(&ctx, key, keylen);
-+
-+ if (skip) {
-+ /*(prefer [16] on ancient hardware with smaller cache lines)*/
-+ unsigned char skip_buf[64]; /*('skip' is generally small)*/
-+ /*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
-+ size_t len;
-+ do {
-+ len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
-+ mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
-+ } while ((skip -= len));
-+ }
-+
-+ int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
-+ mbedtls_arc4_free(&ctx);
-+ return ret;
-+}
-+#endif
-+
-+
-+/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+ #if 0 /* #ifdef MBEDTLS_FS_IO */
-+ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+ return -1;
-+ }
-+ #else
-+ /*(use os_readfile() so that we can use os_free()
-+ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+ * on buf aborts in tests if buf not allocated via os_malloc())*/
-+ *buf = (u8 *)os_readfile(path, n);
-+ if (!*buf) {
-+ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+ return -1;
-+ }
-+ u8 *buf0 = os_realloc(*buf, *n+1);
-+ if (!buf0) {
-+ bin_clear_free(*buf, *n);
-+ *buf = NULL;
-+ return -1;
-+ }
-+ buf0[(*n)++] = '\0';
-+ *buf = buf0;
-+ #endif
-+ return 0;
-+}
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
-+#ifdef MBEDTLS_RSA_C
-+
-+#include <mbedtls/pk.h>
-+#include <mbedtls/rsa.h>
-+
-+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
-+{
-+ /* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
-+ * require #ifdef MBEDTLS_FS_IO in mbedtls library. Prefer to use
-+ * crypto_mbedtls_readfile(), which wraps os_readfile() */
-+ u8 *data;
-+ size_t len;
-+ if (crypto_mbedtls_readfile(file, &data, &len) != 0)
-+ return NULL;
-+
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL) {
-+ bin_clear_free(data, len);
-+ return NULL;
-+ }
-+ mbedtls_pk_init(ctx);
-+
-+ int rc;
-+ rc = (private_key
-+ ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ ,mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()
-+ #endif
-+ )
-+ : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
-+ && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
-+
-+ bin_clear_free(data, len);
-+
-+ if (rc) {
-+ /* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
-+ /* use MBEDTLS_MD_SHA256 for these hostap interfaces */
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ /*(no return value in mbedtls 2.x)*/
-+ mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+ MBEDTLS_RSA_PKCS_V21,
-+ MBEDTLS_MD_SHA256);
-+ #else
-+ if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+ MBEDTLS_RSA_PKCS_V21,
-+ MBEDTLS_MD_SHA256) == 0)
-+ #endif
-+ return (struct crypto_rsa_key *)ctx;
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
-+ const struct wpabuf *in)
-+{
-+ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+ size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+ struct wpabuf *buf = wpabuf_alloc(olen);
-+ if (buf == NULL)
-+ return NULL;
-+
-+ /* mbedtls_pk_encrypt() takes a few more hops to get to same func */
-+ if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg(),
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ MBEDTLS_RSA_PRIVATE,
-+ #endif
-+ NULL, 0,
-+ wpabuf_len(in), wpabuf_head(in),
-+ wpabuf_put(buf, olen)) == 0) {
-+ return buf;
-+ }
-+
-+ wpabuf_clear_free(buf);
-+ return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
-+ const struct wpabuf *in)
-+{
-+ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+ size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+ struct wpabuf *buf = wpabuf_alloc(olen);
-+ if (buf == NULL)
-+ return NULL;
-+
-+ /* mbedtls_pk_decrypt() takes a few more hops to get to same func */
-+ if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg(),
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ MBEDTLS_RSA_PUBLIC,
-+ #endif
-+ NULL, 0, &olen, wpabuf_head(in),
-+ wpabuf_mhead(buf), olen) == 0) {
-+ wpabuf_put(buf, olen);
-+ return buf;
-+ }
-+
-+ wpabuf_clear_free(buf);
-+ return NULL;
-+}
-+
-+void crypto_rsa_key_free(struct crypto_rsa_key *key)
-+{
-+ mbedtls_pk_free((mbedtls_pk_context *)key);
-+ os_free(key);
-+}
-+
-+#endif /* MBEDTLS_RSA_C */
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+
-+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
-+ enum hpke_kdf_id kdf_id,
-+ enum hpke_aead_id aead_id,
-+ struct crypto_ec_key *peer_pub,
-+ const u8 *info, size_t info_len,
-+ const u8 *aad, size_t aad_len,
-+ const u8 *pt, size_t pt_len)
-+{
-+ /* not yet implemented */
-+ return NULL;
-+}
-+
-+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
-+ enum hpke_kdf_id kdf_id,
-+ enum hpke_aead_id aead_id,
-+ struct crypto_ec_key *own_priv,
-+ const u8 *info, size_t info_len,
-+ const u8 *aad, size_t aad_len,
-+ const u8 *enc_ct, size_t enc_ct_len)
-+{
-+ /* not yet implemented */
-+ return NULL;
-+}
-+
-+#endif
---- /dev/null
-+++ b/src/crypto/tls_mbedtls.c
-@@ -0,0 +1,3313 @@
-+/*
-+ * SSL/TLS interface functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ *
-+ * template: src/crypto/tls_none.c
-+ * reference: src/crypto/tls_*.c
-+ *
-+ * Known Limitations:
-+ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
-+ * - no OCSP (not yet available in mbedtls)
-+ * - mbedtls does not support all certificate encodings used by hwsim tests
-+ * PCKS#5 v1.5
-+ * PCKS#12
-+ * DH DSA
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - mbedtls does not currently provide way to set an attribute in a CSR
-+ * https://github.com/Mbed-TLS/mbedtls/issues/4886
-+ * so tests/hwsim dpp_enterprise tests fail
-+ * - DPP2 not supported
-+ * PKCS#7 parsing is not supported in mbedtls
-+ * See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
-+ * - DPP3 not supported
-+ * hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
-+ *
-+ * Status:
-+ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
-+ * (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
-+ * (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
-+ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
-+ * - passes all tests/ crypto module tests (incomplete coverage)
-+ * ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
-+ * - passes almost all tests/hwsim tests
-+ * (hwsim tests skipped for missing features)
-+ *
-+ * RFE:
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - client/server session resumption, and/or save client session ticket
-+ */
-+
-+#include "includes.h"
-+#include "common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+#include <mbedtls/pem.h>
-+#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/ssl.h>
-+#include <mbedtls/ssl_ticket.h>
-+#include <mbedtls/x509.h>
-+#include <mbedtls/x509_crt.h>
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
-+#include <mbedtls/net_sockets.h>
-+#else
-+#include <mbedtls/net.h>
-+#endif
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
-+ ((ssl)->MBEDTLS_PRIVATE(session) \
-+ ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
-+ : 0)
-+#define mbedtls_ssl_ciphersuite_get_name(info) \
-+ (info)->MBEDTLS_PRIVATE(name)
-+#endif
-+
-+#include "crypto.h" /* sha256_vector() */
-+#include "tls.h"
-+
-+#ifndef SHA256_DIGEST_LENGTH
-+#define SHA256_DIGEST_LENGTH 32
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
-+#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_RAND_LEN
-+#define MBEDTLS_EXPKEY_RAND_LEN 32
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
-+#else /*(not implemented; return error)*/
-+#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
-+typedef mbedtls_tls_prf_types int;
-+#endif
-+
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#ifdef MBEDTLS_SSL_SESSION_TICKETS
-+#ifdef MBEDTLS_SSL_TICKET_C
-+#define TLS_MBEDTLS_SESSION_TICKETS
-+#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#define TLS_MBEDTLS_EAP_TEAP
-+#endif
-+#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define TLS_MBEDTLS_EAP_FAST
-+#endif
-+#endif
-+#endif
-+#endif
-+#endif
-+
-+
-+struct tls_conf {
-+ mbedtls_ssl_config conf;
-+
-+ unsigned int verify_peer:1;
-+ unsigned int verify_depth0_only:1;
-+ unsigned int check_crl:2; /*(needs :2 bits for 0, 1, 2)*/
-+ unsigned int check_crl_strict:1; /*(needs :1 bit for 0, 1)*/
-+ unsigned int ca_cert_probe:1;
-+ unsigned int has_ca_cert:1;
-+ unsigned int has_client_cert:1;
-+ unsigned int has_private_key:1;
-+ unsigned int suiteb128:1;
-+ unsigned int suiteb192:1;
-+ mbedtls_x509_crl *crl;
-+ mbedtls_x509_crt ca_cert;
-+ mbedtls_x509_crt client_cert;
-+ mbedtls_pk_context private_key;
-+
-+ uint32_t refcnt;
-+
-+ unsigned int flags;
-+ char *subject_match;
-+ char *altsubject_match;
-+ char *suffix_match;
-+ char *domain_match;
-+ char *check_cert_subject;
-+ u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
-+
-+ int *ciphersuites; /* list of ciphersuite ids for mbedtls_ssl_config */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+ mbedtls_ecp_group_id *curves;
-+#else
-+ uint16_t *curves; /* list of curve ids for mbedtls_ssl_config */
-+#endif
-+};
-+
-+
-+struct tls_global {
-+ struct tls_conf *tls_conf;
-+ char *ocsp_stapling_response;
-+ mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ mbedtls_ssl_ticket_context ticket_ctx;
-+ #endif
-+ char *ca_cert_file;
-+ struct os_reltime crl_reload_previous;
-+ unsigned int crl_reload_interval;
-+ uint32_t refcnt;
-+ struct tls_config init_conf;
-+};
-+
-+static struct tls_global tls_ctx_global;
-+
-+
-+struct tls_connection {
-+ struct tls_conf *tls_conf;
-+ struct wpabuf *push_buf;
-+ struct wpabuf *pull_buf;
-+ size_t pull_buf_offset;
-+
-+ unsigned int established:1;
-+ unsigned int resumed:1;
-+ unsigned int verify_peer:1;
-+ unsigned int is_server:1;
-+
-+ mbedtls_ssl_context ssl;
-+
-+ mbedtls_tls_prf_types tls_prf_type;
-+ size_t expkey_keyblock_size;
-+ size_t expkey_secret_len;
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
-+ #else
-+ unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
-+ #endif
-+ unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
-+
-+ int read_alerts, write_alerts, failed;
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ tls_session_ticket_cb session_ticket_cb;
-+ void *session_ticket_cb_ctx;
-+ unsigned char *clienthello_session_ticket;
-+ size_t clienthello_session_ticket_len;
-+ #endif
-+ char *peer_subject; /* peer subject info for authenticated peer */
-+ struct wpabuf *success_data;
-+};
-+
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__ __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__ __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsg(int level, const char * const msg)
-+{
-+ wpa_printf(level, "MTLS: %s", msg);
-+}
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsgrc(int level, const char * const msg, int rc)
-+{
-+ #ifdef MBEDTLS_ERROR_C
-+ /* error logging convenience function that decodes mbedtls result codes */
-+ char buf[256];
-+ mbedtls_strerror(rc, buf, sizeof(buf));
-+ wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
-+ #else
-+ wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
-+ #endif
-+}
-+
-+
-+#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
-+#define ilog(rc, msg) emsgrc(MSG_INFO, (msg), (rc))
-+
-+
-+struct tls_conf * tls_conf_init(void *tls_ctx)
-+{
-+ struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
-+ if (tls_conf == NULL)
-+ return NULL;
-+ tls_conf->refcnt = 1;
-+
-+ mbedtls_ssl_config_init(&tls_conf->conf);
-+ mbedtls_ssl_conf_rng(&tls_conf->conf,
-+ mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
-+ mbedtls_x509_crt_init(&tls_conf->ca_cert);
-+ mbedtls_x509_crt_init(&tls_conf->client_cert);
-+ mbedtls_pk_init(&tls_conf->private_key);
-+
-+ return tls_conf;
-+}
-+
-+
-+void tls_conf_deinit(struct tls_conf *tls_conf)
-+{
-+ if (tls_conf == NULL || --tls_conf->refcnt != 0)
-+ return;
-+
-+ mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+ mbedtls_x509_crt_free(&tls_conf->client_cert);
-+ if (tls_conf->crl) {
-+ mbedtls_x509_crl_free(tls_conf->crl);
-+ os_free(tls_conf->crl);
-+ }
-+ mbedtls_pk_free(&tls_conf->private_key);
-+ mbedtls_ssl_config_free(&tls_conf->conf);
-+ os_free(tls_conf->curves);
-+ os_free(tls_conf->ciphersuites);
-+ os_free(tls_conf->subject_match);
-+ os_free(tls_conf->altsubject_match);
-+ os_free(tls_conf->suffix_match);
-+ os_free(tls_conf->domain_match);
-+ os_free(tls_conf->check_cert_subject);
-+ os_free(tls_conf);
-+}
-+
-+
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+
-+__attribute_cold__
-+void * tls_init(const struct tls_config *conf)
-+{
-+ /* RFE: review struct tls_config *conf (different from tls_conf) */
-+
-+ if (++tls_ctx_global.refcnt > 1)
-+ return &tls_ctx_global;
-+
-+ tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
-+ mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
-+ mbedtls_ctr_drbg_random,
-+ tls_ctx_global.ctr_drbg,
-+ MBEDTLS_CIPHER_AES_256_GCM,
-+ 43200); /* ticket timeout: 12 hours */
-+ #endif
-+ /* copy struct for future use */
-+ tls_ctx_global.init_conf = *conf;
-+ if (conf->openssl_ciphers)
-+ tls_ctx_global.init_conf.openssl_ciphers =
-+ os_strdup(conf->openssl_ciphers);
-+
-+ tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
-+ os_get_reltime(&tls_ctx_global.crl_reload_previous);
-+
-+ return &tls_ctx_global;
-+}
-+
-+
-+__attribute_cold__
-+void tls_deinit(void *tls_ctx)
-+{
-+ if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
-+ return;
-+
-+ tls_conf_deinit(tls_ctx_global.tls_conf);
-+ os_free(tls_ctx_global.ca_cert_file);
-+ os_free(tls_ctx_global.ocsp_stapling_response);
-+ char *openssl_ciphers; /*(allocated in tls_init())*/
-+ *(const char **)&openssl_ciphers =
-+ tls_ctx_global.init_conf.openssl_ciphers;
-+ os_free(openssl_ciphers);
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
-+ #endif
-+ os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
-+}
-+
-+
-+int tls_get_errors(void *tls_ctx)
-+{
-+ return 0;
-+}
-+
-+
-+static void tls_connection_deinit_expkey(struct tls_connection *conn)
-+{
-+ conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
-+ conn->expkey_keyblock_size = 0;
-+ conn->expkey_secret_len = 0;
-+ forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
-+ forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
-+{
-+ if (conn->clienthello_session_ticket) {
-+ mbedtls_platform_zeroize(conn->clienthello_session_ticket,
-+ conn->clienthello_session_ticket_len);
-+ mbedtls_free(conn->clienthello_session_ticket);
-+ conn->clienthello_session_ticket = NULL;
-+ conn->clienthello_session_ticket_len = 0;
-+ }
-+}
-+#endif
-+
-+
-+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
-+{
-+ if (conn == NULL)
-+ return;
-+
-+ #if 0 /*(good intention, but never sent since we destroy self below)*/
-+ if (conn->established)
-+ mbedtls_ssl_close_notify(&conn->ssl);
-+ #endif
-+
-+ if (conn->tls_prf_type)
-+ tls_connection_deinit_expkey(conn);
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ if (conn->clienthello_session_ticket)
-+ tls_connection_deinit_clienthello_session_ticket(conn);
-+ #endif
-+
-+ os_free(conn->peer_subject);
-+ wpabuf_free(conn->success_data);
-+ wpabuf_free(conn->push_buf);
-+ wpabuf_free(conn->pull_buf);
-+ mbedtls_ssl_free(&conn->ssl);
-+ tls_conf_deinit(conn->tls_conf);
-+ os_free(conn);
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void);
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
-+
-+struct tls_connection * tls_connection_init(void *tls_ctx)
-+{
-+ struct tls_connection *conn = os_zalloc(sizeof(*conn));
-+ if (conn == NULL)
-+ return NULL;
-+
-+ mbedtls_ssl_init(&conn->ssl);
-+
-+ conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
-+ if (conn->tls_conf) {
-+ ++conn->tls_conf->refcnt;
-+ /* check for CRL refresh if inheriting from global config */
-+ tls_mbedtls_refresh_crl();
-+
-+ conn->verify_peer = conn->tls_conf->verify_peer;
-+ if (tls_mbedtls_ssl_setup(conn) != 0) {
-+ tls_connection_deinit(&tls_ctx_global, conn);
-+ return NULL;
-+ }
-+ }
-+
-+ return conn;
-+}
-+
-+
-+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
-+{
-+ return conn ? conn->established : 0;
-+}
-+
-+
-+__attribute_noinline__
-+char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
-+{
-+ /* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
-+ * colons, so generate the hex serial number here. The func
-+ * wpa_snprintf_hex_uppercase() is similarly inefficient. */
-+ size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
-+ while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
-+ if (i == crt->serial.len) --i;
-+
-+ const unsigned char *s = crt->serial.p + i;
-+ const size_t e = (crt->serial.len - i) * 2;
-+ if (e >= len)
-+ return NULL;
-+ #if 0
-+ wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
-+ #else
-+ for (i = 0; i < e; i+=2, ++s) {
-+ serial_num[i+0] = "0123456789ABCDEF"[(*s >> 4)];
-+ serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
-+ }
-+ serial_num[e] = '\0';
-+ #endif
-+ return serial_num;
-+}
-+
-+
-+char * tls_connection_peer_serial_num(void *tls_ctx,
-+ struct tls_connection *conn)
-+{
-+ const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
-+ if (crt == NULL)
-+ return NULL;
-+ size_t len = crt->serial.len * 2 + 1;
-+ char *serial_num = os_malloc(len);
-+ if (!serial_num)
-+ return NULL;
-+ return tls_mbedtls_peer_serial_num(crt, serial_num, len);
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn);
-+
-+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
-+{
-+ /* Note: this function called from eap_peer_tls_reauth_init()
-+ * for session resumption, not for connection shutdown */
-+
-+ if (conn == NULL)
-+ return -1;
-+
-+ tls_pull_buf_reset(conn);
-+ wpabuf_free(conn->push_buf);
-+ conn->push_buf = NULL;
-+ conn->established = 0;
-+ conn->resumed = 0;
-+ if (conn->tls_prf_type)
-+ tls_connection_deinit_expkey(conn);
-+
-+ /* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
-+
-+ return mbedtls_ssl_session_reset(&conn->ssl);
-+}
-+
-+
-+static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
-+ const unsigned char *data, size_t dlen)
-+{
-+ if (wpabuf_resize(buf, dlen) < 0)
-+ return 0;
-+ wpabuf_put_data(*buf, data, dlen);
-+ return 1;
-+}
-+
-+
-+static int tls_pull_buf_append(struct tls_connection *conn,
-+ const struct wpabuf *in_data)
-+{
-+ /*(interface does not lend itself to move semantics)*/
-+ return tls_wpabuf_resize_put_data(&conn->pull_buf,
-+ wpabuf_head(in_data),
-+ wpabuf_len(in_data));
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn)
-+{
-+ /*(future: might consider reusing conn->pull_buf)*/
-+ wpabuf_free(conn->pull_buf);
-+ conn->pull_buf = NULL;
-+ conn->pull_buf_offset = 0;
-+}
-+
-+
-+__attribute_cold__
-+static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
-+{
-+ size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+ if (discard)
-+ wpa_printf(MSG_DEBUG,
-+ "%s - %zu bytes remaining in pull_buf; discarding",
-+ func, discard);
-+ tls_pull_buf_reset(conn);
-+}
-+
-+
-+static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
-+{
-+ struct tls_connection *conn = (struct tls_connection *) ptr;
-+ if (conn->pull_buf == NULL)
-+ return MBEDTLS_ERR_SSL_WANT_READ;
-+ const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+ if (dlen == 0)
-+ return MBEDTLS_ERR_SSL_WANT_READ;
-+
-+ if (len > dlen)
-+ len = dlen;
-+ os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
-+
-+ if (len == dlen) {
-+ tls_pull_buf_reset(conn);
-+ /*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
-+ }
-+ else {
-+ conn->pull_buf_offset += len;
-+ /*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
-+ __func__, dlen - len);*/
-+ }
-+ return (int)len;
-+}
-+
-+
-+static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
-+{
-+ struct tls_connection *conn = (struct tls_connection *) ptr;
-+ return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
-+ ? (int)len
-+ : MBEDTLS_ERR_SSL_ALLOC_FAILED;
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
-+
-+
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
-+{
-+ #if 0
-+ /* mbedtls_ssl_setup() must be called only once */
-+ /* If this func might be called multiple times (e.g. via set_params),
-+ * then we should set a flag in conn that ssl was initialized */
-+ if (conn->ssl_is_init) {
-+ mbedtls_ssl_free(&conn->ssl);
-+ mbedtls_ssl_init(&conn->ssl);
-+ }
-+ #endif
-+
-+ int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_ssl_setup");
-+ return -1;
-+ }
-+
-+ mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ mbedtls_ssl_set_export_keys_cb(
-+ &conn->ssl, tls_connection_export_keys_cb, conn);
-+ #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+ mbedtls_ssl_conf_export_keys_ext_cb(
-+ &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
-+ #endif
-+ if (conn->verify_peer)
-+ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_data_is_pem(const u8 *data)
-+{
-+ return (NULL != os_strstr((char *)data, "-----"));
-+}
-+
-+
-+static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
-+ mbedtls_ssl_config *conf)
-+{
-+ #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
-+ #endif
-+
-+ /* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
-+ if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
-+ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
-+ }
-+
-+ const unsigned int flags = tls_conf->flags;
-+
-+ /* attempt to map flags to min and max TLS protocol version */
-+
-+ int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_3)
-+ ? 4
-+ : 3
-+ : 2
-+ : 1
-+ : 0;
-+
-+ int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_0)
-+ ? -1
-+ : 0
-+ : 1
-+ : 2
-+ : 3;
-+
-+ if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
-+ if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
-+ if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
-+ if (max < min) {
-+ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+ return;
-+ }
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ /* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
-+ if (min < 2 || max < 2) {
-+ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+ if (min < 2) min = 2;
-+ if (max < 2) max = 2;
-+ }
-+ #endif
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ /* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
-+ /* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
-+ min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+ max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+ mbedtls_ssl_conf_min_tls_version(conf, min);
-+ mbedtls_ssl_conf_max_tls_version(conf, max);
-+ #else
-+ #ifndef MBEDTLS_SSL_MINOR_VERSION_4
-+ if (min == 3) min = 2;
-+ if (max == 3) max = 2;
-+ #endif
-+ /* MBEDTLS_SSL_MINOR_VERSION_0 0 *//*!< SSL v3.0 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_1 1 *//*!< TLS v1.0 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_2 2 *//*!< TLS v1.1 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_3 3 *//*!< TLS v1.2 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_4 4 *//*!< TLS v1.3 */
-+ mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
-+ mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
-+ #endif
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
-+
-+
-+static int
-+tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
-+{
-+ size_t len;
-+ u8 *data;
-+ if (tls_mbedtls_readfile(dh_file, &data, &len))
-+ return 0;
-+
-+ /* parse only if DH parameters if in PEM format */
-+ if (tls_mbedtls_data_is_pem(data)
-+ && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
-+ if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
-+ wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
-+ else
-+ wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
-+ forced_memzero(data, len);
-+ os_free(data);
-+ return 0;
-+ }
-+
-+ /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
-+ mbedtls_dhm_context dhm;
-+ mbedtls_dhm_init(&dhm);
-+ int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
-+ if (0 == rc)
-+ rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
-+ if (0 != rc)
-+ elog(rc, dh_file);
-+ mbedtls_dhm_free(&dhm);
-+
-+ forced_memzero(data, len);
-+ os_free(data);
-+ return (0 == rc);
-+}
-+
-+
-+/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
-+ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+static int
-+tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
-+{
-+ if (1 >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR, "error: too many curves during list expand");
-+ return -1;
-+ }
-+ ids[++nids] = id;
-+ return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+ mbedtls_ecp_group_id ids[512];
-+ int nids = -1;
-+ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+ for (const char *e = curvelist-1; e; ) {
-+ const char * const n = e+1;
-+ e = os_strchr(n, ':');
-+ size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+ mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
-+ switch (len) {
-+ case 5:
-+ if (0 == os_memcmp("P-521", n, 5))
-+ grp_id = MBEDTLS_ECP_DP_SECP521R1;
-+ else if (0 == os_memcmp("P-384", n, 5))
-+ grp_id = MBEDTLS_ECP_DP_SECP384R1;
-+ else if (0 == os_memcmp("P-256", n, 5))
-+ grp_id = MBEDTLS_ECP_DP_SECP256R1;
-+ break;
-+ case 6:
-+ if (0 == os_memcmp("BP-521", n, 6))
-+ grp_id = MBEDTLS_ECP_DP_BP512R1;
-+ else if (0 == os_memcmp("BP-384", n, 6))
-+ grp_id = MBEDTLS_ECP_DP_BP384R1;
-+ else if (0 == os_memcmp("BP-256", n, 6))
-+ grp_id = MBEDTLS_ECP_DP_BP256R1;
-+ break;
-+ default:
-+ break;
-+ }
-+ if (grp_id != MBEDTLS_ECP_DP_NONE) {
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
-+ if (-1 == nids) return 0;
-+ continue;
-+ }
-+ /* similar to mbedtls_ecp_curve_info_from_name() */
-+ const mbedtls_ecp_curve_info *info;
-+ for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
-+ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+ break;
-+ }
-+ if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
-+ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+ return 0;
-+ }
-+
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
-+ if (-1 == nids) return 0;
-+ }
-+
-+ /* mod_openssl configures "prime256v1" if curve list not specified,
-+ * but mbedtls provides a list of supported curves if not explicitly set */
-+ if (-1 == nids) return 1; /* empty list; no-op */
-+
-+ ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
-+ ++nids;
-+
-+ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+ tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
-+ if (tls_conf->curves == NULL)
-+ return 0;
-+ os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
-+
-+ mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
-+ return 1;
-+}
-+#else
-+static int
-+tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
-+{
-+ if (1 >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR, "error: too many curves during list expand");
-+ return -1;
-+ }
-+ ids[++nids] = id;
-+ return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+ /* TLS Supported Groups (renamed from "EC Named Curve Registry")
-+ * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
-+ */
-+ uint16_t ids[512];
-+ int nids = -1;
-+ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+ for (const char *e = curvelist-1; e; ) {
-+ const char * const n = e+1;
-+ e = os_strchr(n, ':');
-+ size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+ uint16_t tls_id = 0;
-+ switch (len) {
-+ case 5:
-+ if (0 == os_memcmp("P-521", n, 5))
-+ tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
-+ else if (0 == os_memcmp("P-384", n, 5))
-+ tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
-+ else if (0 == os_memcmp("P-256", n, 5))
-+ tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
-+ break;
-+ case 6:
-+ if (0 == os_memcmp("BP-521", n, 6))
-+ tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
-+ else if (0 == os_memcmp("BP-384", n, 6))
-+ tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
-+ else if (0 == os_memcmp("BP-256", n, 6))
-+ tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
-+ break;
-+ default:
-+ break;
-+ }
-+ if (tls_id != 0) {
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
-+ if (-1 == nids) return 0;
-+ continue;
-+ }
-+ /* similar to mbedtls_ecp_curve_info_from_name() */
-+ const mbedtls_ecp_curve_info *info;
-+ for (info = curve_info; info->tls_id != 0; ++info) {
-+ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+ break;
-+ }
-+ if (info->tls_id == 0) {
-+ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+ return 0;
-+ }
-+
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
-+ if (-1 == nids) return 0;
-+ }
-+
-+ /* mod_openssl configures "prime256v1" if curve list not specified,
-+ * but mbedtls provides a list of supported curves if not explicitly set */
-+ if (-1 == nids) return 1; /* empty list; no-op */
-+
-+ ids[++nids] = 0; /* terminate list */
-+ ++nids;
-+
-+ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+ tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
-+ if (tls_conf->curves == NULL)
-+ return 0;
-+ os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
-+
-+ mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
-+ return 1;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
-+
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_256_ephemeral[] = {
-+ /* All AES-256 ephemeral suites */
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_128_ephemeral[] = {
-+ /* All AES-128 ephemeral suites */
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+/* HIGH cipher list (mapped from openssl list to mbedtls) */
-+static const int suite_HIGH[] = {
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
-+};
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
-+{
-+ if (xsz >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR, "error: too many ciphers during list expand");
-+ return -1;
-+ }
-+
-+ for (int i = 0; i < xsz; ++i)
-+ ids[++nids] = x[i];
-+
-+ return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
-+{
-+ const mbedtls_ssl_ciphersuite_t *info =
-+ mbedtls_ssl_ciphersuite_from_id(id);
-+ if (info == NULL)
-+ return 0;
-+ const char *name = mbedtls_ssl_ciphersuite_get_name(info);
-+ const size_t len = os_strlen(name);
-+ if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
-+ return 0;
-+ if (len >= buflen)
-+ return 0;
-+ os_strlcpy(buf, name, buflen);
-+
-+ /* attempt to translate mbedtls string to openssl string
-+ * (some heuristics; incomplete) */
-+ size_t i = 0, j = 0;
-+ if (buf[0] == 'T') {
-+ if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
-+ buf[3] = '-';
-+ j = 4; /* remove "1-3" from "TLS1-3-" prefix */
-+ i = 7;
-+ }
-+ else if (os_strncmp(buf, "TLS-", 4) == 0)
-+ i = 4; /* remove "TLS-" prefix */
-+ }
-+ for (; buf[i]; ++i) {
-+ if (buf[i] == '-') {
-+ if (i >= 3) {
-+ if (0 == os_memcmp(buf+i-3, "AES", 3))
-+ continue; /* "AES-" -> "AES" */
-+ }
-+ if (i >= 4) {
-+ if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
-+ j -= 4; /* remove "WITH-" */
-+ continue;
-+ }
-+ }
-+ }
-+ buf[j++] = buf[i];
-+ }
-+ buf[j] = '\0';
-+
-+ return j;
-+}
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
-+{
-+ /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
-+ os_free(tls_conf->ciphersuites);
-+ tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
-+ if (tls_conf->ciphersuites == NULL)
-+ return 0;
-+ os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
-+ mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
-+ return 1;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
-+{
-+ char buf[64];
-+ int ids[512];
-+ int nids = -1;
-+ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+ const char *next;
-+ size_t blen, clen;
-+ do {
-+ next = os_strchr(ciphers, ':');
-+ clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
-+ if (!clen)
-+ continue;
-+
-+ /* special-case a select set of openssl group names for hwsim tests */
-+ /* (review; remove excess code if tests are not run for non-OpenSSL?) */
-+ if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
-+ static int ssl_preset_suiteb192_ciphersuites[] = {
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+ 0
-+ };
-+ return tls_mbedtls_set_ciphersuites(tls_conf,
-+ ssl_preset_suiteb192_ciphersuites,
-+ 2);
-+ }
-+ if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
-+ static int ssl_preset_suiteb128_ciphersuites[] = {
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ 0
-+ };
-+ return tls_mbedtls_set_ciphersuites(tls_conf,
-+ ssl_preset_suiteb128_ciphersuites,
-+ 2);
-+ }
-+ if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
-+ continue;
-+ if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
-+ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+ suite_AES_128_ephemeral,
-+ (int)ARRAY_SIZE(suite_AES_128_ephemeral));
-+ if (nids == -1)
-+ return 0;
-+ continue;
-+ }
-+ if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
-+ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+ suite_AES_256_ephemeral,
-+ (int)ARRAY_SIZE(suite_AES_256_ephemeral));
-+ if (nids == -1)
-+ return 0;
-+ continue;
-+ }
-+ if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
-+ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
-+ (int)ARRAY_SIZE(suite_HIGH));
-+ if (nids == -1)
-+ return 0;
-+ continue;
-+ }
-+ /* ignore anonymous cipher group names (?not supported by mbedtls?) */
-+ if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
-+ continue;
-+ if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
-+ continue;
-+ if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
-+ continue;
-+
-+ /* attempt to match mbedtls cipher names
-+ * nb: does not support openssl group names or list manipulation syntax
-+ * (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
-+ * mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
-+ * note: not efficient to rewrite list for each ciphers entry,
-+ * but this code is expected to run only at startup
-+ */
-+ const int *list = mbedtls_ssl_list_ciphersuites();
-+ for (; *list; ++list) {
-+ blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
-+ if (!blen)
-+ continue;
-+
-+ /* matching heuristics additional to translate_ciphername above */
-+ if (blen == clen+4) {
-+ char *cbc = os_strstr(buf, "CBC-");
-+ if (cbc) {
-+ os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
-+ blen -= 4;
-+ }
-+ }
-+ if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
-+ && (blen == clen
-+ || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
-+ if (1 >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR,
-+ "error: too many ciphers during list expand");
-+ return 0;
-+ }
-+ ids[++nids] = *list;
-+ break;
-+ }
-+ }
-+ if (*list == 0) {
-+ wpa_printf(MSG_ERROR,
-+ "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
-+ return 0;
-+ }
-+ } while ((ciphers = next ? next+1 : NULL));
-+
-+ if (-1 == nids) return 1; /* empty list; no-op */
-+
-+ ids[++nids] = 0; /* terminate list */
-+ ++nids;
-+
-+ return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_set_item(char **config_item, const char *item)
-+{
-+ os_free(*config_item);
-+ *config_item = NULL;
-+ return item ? (*config_item = os_strdup(item)) != NULL : 1;
-+}
-+
-+
-+static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ int rc = 1;
-+ rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
-+ params->subject_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
-+ params->altsubject_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
-+ params->suffix_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
-+ params->domain_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
-+ params->check_cert_subject);
-+ return rc;
-+}
-+
-+
-+/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+ #if 0 /* #ifdef MBEDTLS_FS_IO */
-+ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+ return -1;
-+ }
-+ #else
-+ /*(use os_readfile() so that we can use os_free()
-+ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+ * on buf aborts in tests if buf not allocated via os_malloc())*/
-+ *buf = (u8 *)os_readfile(path, n);
-+ if (!*buf) {
-+ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+ return -1;
-+ }
-+ u8 *buf0 = os_realloc(*buf, *n+1);
-+ if (!buf0) {
-+ bin_clear_free(*buf, *n);
-+ *buf = NULL;
-+ return -1;
-+ }
-+ buf0[(*n)++] = '\0';
-+ *buf = buf0;
-+ #endif
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
-+{
-+ /* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
-+ if (len && data[len-1] == '\0'
-+ && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
-+ && tls_mbedtls_data_is_pem(data))
-+ return 0;
-+
-+ mbedtls_x509_crl crl;
-+ mbedtls_x509_crl_init(&crl);
-+ int rc = mbedtls_x509_crl_parse(&crl, data, len);
-+ if (rc < 0) {
-+ mbedtls_x509_crl_free(&crl);
-+ return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
-+ }
-+
-+ mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
-+ if (crl_new == NULL) {
-+ mbedtls_x509_crl_free(&crl);
-+ return MBEDTLS_ERR_X509_ALLOC_FAILED;
-+ }
-+ os_memcpy(crl_new, &crl, sizeof(crl));
-+
-+ mbedtls_x509_crl *crl_old = tls_conf->crl;
-+ tls_conf->crl = crl_new;
-+ if (crl_old) {
-+ mbedtls_x509_crl_free(crl_old);
-+ os_free(crl_old);
-+ }
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
-+{
-+ /* load crt struct onto stack and then copy into tls_conf in
-+ * order to preserve existing tls_conf value if error occurs
-+ *
-+ * hostapd is not threaded, or else should allocate memory and swap in
-+ * pointer reduce race condition. (If threaded, would also need to
-+ * keep reference count of use to avoid freeing while still in use.) */
-+
-+ mbedtls_x509_crt crt;
-+ mbedtls_x509_crt_init(&crt);
-+ int rc = mbedtls_x509_crt_parse(&crt, data, len);
-+ if (rc < 0) {
-+ mbedtls_x509_crt_free(&crt);
-+ return rc;
-+ }
-+
-+ mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+ os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
-+{
-+ size_t len;
-+ u8 *data;
-+ if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
-+ return -1;
-+
-+ int rc;
-+ if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
-+ && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
-+ || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
-+ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+ &tls_conf->ca_cert,
-+ tls_conf->crl);
-+ }
-+ else {
-+ elog(rc, __func__);
-+ emsg(MSG_ERROR, ca_cert_file);
-+ }
-+
-+ forced_memzero(data, len);
-+ os_free(data);
-+ return rc;
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void)
-+{
-+ /* check for CRL refresh
-+ * continue even if error occurs; continue with previous cert, CRL */
-+ unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
-+ const char *ca_cert_file = tls_ctx_global.ca_cert_file;
-+ if (!crl_reload_interval || !ca_cert_file)
-+ return;
-+
-+ struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
-+ struct os_reltime now;
-+ if (os_get_reltime(&now) != 0
-+ || !os_reltime_expired(&now, previous, crl_reload_interval))
-+ return;
-+
-+ /* Note: modifying global state is not thread-safe
-+ * if in use by existing connections
-+ *
-+ * src/utils/os.h does not provide a portable stat()
-+ * or else it would be a good idea to check mtime and size,
-+ * and avoid reloading if file has not changed */
-+
-+ if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
-+ *previous = now;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ if (params->ca_cert) {
-+ if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
-+ tls_conf->ca_cert_probe = 1;
-+ tls_conf->has_ca_cert = 1;
-+ return 0;
-+ }
-+
-+ if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
-+ const char *pos = params->ca_cert + 7;
-+ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
-+ emsg(MSG_ERROR, "unsupported ca_cert hash value");
-+ return -1;
-+ }
-+ pos += 14;
-+ if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
-+ emsg(MSG_ERROR, "unexpected ca_cert hash length");
-+ return -1;
-+ }
-+ if (hexstr2bin(pos, tls_conf->ca_cert_hash,
-+ SHA256_DIGEST_LENGTH) < 0) {
-+ emsg(MSG_ERROR, "invalid ca_cert hash value");
-+ return -1;
-+ }
-+ emsg(MSG_DEBUG, "checking only server certificate match");
-+ tls_conf->verify_depth0_only = 1;
-+ tls_conf->has_ca_cert = 1;
-+ return 0;
-+ }
-+
-+ if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
-+ return -1;
-+ }
-+ if (params->ca_cert_blob) {
-+ size_t len = params->ca_cert_blob_len;
-+ int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
-+ if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
-+ ++len; /*(include '\0' in len for PEM)*/
-+ int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
-+ params->ca_cert_blob, len);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_x509_crt_parse");
-+ return -1;
-+ }
-+ if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
-+ ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_x509_crl_parse");
-+ return -1;
-+ }
-+ }
-+ }
-+
-+ if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
-+ || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
-+ emsg(MSG_WARNING, "ca_cert expired or not yet valid");
-+ if (params->ca_cert)
-+ emsg(MSG_WARNING, params->ca_cert);
-+ }
-+
-+ tls_conf->has_ca_cert = 1;
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ int ret;
-+
-+ if (params->ca_cert || params->ca_cert_blob) {
-+ if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
-+ return -1;
-+ }
-+ else if (params->ca_path) {
-+ emsg(MSG_INFO, "ca_path support not implemented");
-+ return -1;
-+ }
-+
-+ if (!tls_conf->has_ca_cert)
-+ mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
-+ else {
-+ /* Initial setting: REQUIRED for client, OPTIONAL for server
-+ * (see also tls_connection_set_verify()) */
-+ tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
-+ int authmode = tls_conf->verify_peer
-+ ? MBEDTLS_SSL_VERIFY_REQUIRED
-+ : MBEDTLS_SSL_VERIFY_OPTIONAL;
-+ mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
-+ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+ &tls_conf->ca_cert,
-+ tls_conf->crl);
-+
-+ if (!tls_connection_set_subject_match(tls_conf, params))
-+ return -1;
-+ }
-+
-+ if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
-+ emsg(MSG_INFO, "server_cert2 support not implemented");
-+
-+ if (params->client_cert) {
-+ size_t len;
-+ u8 *data;
-+ if (tls_mbedtls_readfile(params->client_cert, &data, &len))
-+ return -1;
-+ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
-+ forced_memzero(data, len);
-+ os_free(data);
-+ }
-+ if (params->client_cert_blob) {
-+ size_t len = params->client_cert_blob_len;
-+ if (len && params->client_cert_blob[len-1] != '\0'
-+ && tls_mbedtls_data_is_pem(params->client_cert_blob))
-+ ++len; /*(include '\0' in len for PEM)*/
-+ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
-+ params->client_cert_blob, len);
-+ }
-+ if (params->client_cert || params->client_cert_blob) {
-+ if (ret < 0) {
-+ elog(ret, "mbedtls_x509_crt_parse");
-+ if (params->client_cert)
-+ emsg(MSG_ERROR, params->client_cert);
-+ return -1;
-+ }
-+ if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
-+ || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
-+ emsg(MSG_WARNING, "cert expired or not yet valid");
-+ if (params->client_cert)
-+ emsg(MSG_WARNING, params->client_cert);
-+ }
-+ tls_conf->has_client_cert = 1;
-+ }
-+
-+ if (params->private_key || params->private_key_blob) {
-+ size_t len = params->private_key_blob_len;
-+ u8 *data;
-+ *(const u8 **)&data = params->private_key_blob;
-+ if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
-+ ++len; /*(include '\0' in len for PEM)*/
-+ if (params->private_key
-+ && tls_mbedtls_readfile(params->private_key, &data, &len)) {
-+ return -1;
-+ }
-+ const char *pwd = params->private_key_passwd;
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+ data, len,
-+ (const unsigned char *)pwd,
-+ pwd ? os_strlen(pwd) : 0,
-+ mbedtls_ctr_drbg_random,
-+ tls_ctx_global.ctr_drbg);
-+ #else
-+ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+ data, len,
-+ (const unsigned char *)pwd,
-+ pwd ? os_strlen(pwd) : 0);
-+ #endif
-+ if (params->private_key) {
-+ forced_memzero(data, len);
-+ os_free(data);
-+ }
-+ if (ret < 0) {
-+ elog(ret, "mbedtls_pk_parse_key");
-+ return -1;
-+ }
-+ tls_conf->has_private_key = 1;
-+ }
-+
-+ if (tls_conf->has_client_cert && tls_conf->has_private_key) {
-+ ret = mbedtls_ssl_conf_own_cert(
-+ &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
-+ if (ret < 0) {
-+ elog(ret, "mbedtls_ssl_conf_own_cert");
-+ return -1;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+
-+/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
-+{
-+ /* Only SHA-256 and 384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+ /* Only ECDSA */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+ /* Only NIST P-256 and P-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+ 0,
-+#endif
-+ 2048,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
-+{
-+ /* Only SHA-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+ /* Only ECDSA */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+ /* Only NIST P-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+ 0,
-+#endif
-+ 3072,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
-+{
-+ /* Only SHA-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+ 0xFFFFFFF, /* Any PK alg */
-+#if defined(MBEDTLS_ECP_C)
-+ /* Only NIST P-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+ 0,
-+#endif
-+ 3072,
-+};
-+
-+
-+static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ tls_conf->flags = params->flags;
-+
-+ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
-+ emsg(MSG_INFO, "ocsp=3 not supported");
-+ return -1;
-+ }
-+
-+ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
-+ emsg(MSG_INFO, "ocsp not supported");
-+ return -1;
-+ }
-+
-+ int suiteb128 = 0;
-+ int suiteb192 = 0;
-+ if (params->openssl_ciphers) {
-+ if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
-+ suiteb192 = 1;
-+ tls_conf->flags |= TLS_CONN_SUITEB;
-+ }
-+ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
-+ suiteb128 = 1;
-+ tls_conf->flags |= TLS_CONN_SUITEB;
-+ }
-+ }
-+
-+ int ret = mbedtls_ssl_config_defaults(
-+ &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
-+ : MBEDTLS_SSL_IS_CLIENT,
-+ MBEDTLS_SSL_TRANSPORT_STREAM,
-+ (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
-+ : MBEDTLS_SSL_PRESET_DEFAULT);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_ssl_config_defaults");
-+ return -1;
-+ }
-+
-+ if (suiteb128) {
-+ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+ &tls_mbedtls_crt_profile_suiteb128);
-+ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
-+ }
-+ else if (suiteb192) {
-+ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+ &tls_mbedtls_crt_profile_suiteb192);
-+ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+ }
-+ else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ /* treat as suiteb192 while allowing any PK algorithm */
-+ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+ &tls_mbedtls_crt_profile_suiteb192_anypk);
-+ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+ }
-+
-+ tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
-+ ret = tls_mbedtls_set_certs(tls_conf, params);
-+ if (ret != 0)
-+ return -1;
-+
-+ if (params->dh_file
-+ && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
-+ return -1;
-+ }
-+
-+ if (params->openssl_ecdh_curves
-+ && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
-+ return -1;
-+ }
-+
-+ if (params->openssl_ciphers) {
-+ if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
-+ return -1;
-+ }
-+ else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ /* special-case a select set of ciphers for hwsim tests */
-+ if (!tls_mbedtls_set_ciphers(tls_conf,
-+ (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
-+ ? "DHE-RSA-AES256-GCM-SHA384"
-+ : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-+
-+
-+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-+ const struct tls_connection_params *params)
-+{
-+ if (conn == NULL || params == NULL)
-+ return -1;
-+
-+ tls_conf_deinit(conn->tls_conf);
-+ struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
-+ if (tls_conf == NULL)
-+ return -1;
-+
-+ if (tls_ctx_global.tls_conf) {
-+ tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
-+ tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
-+ /*(tls_openssl.c inherits check_cert_subject from global conf)*/
-+ if (tls_ctx_global.tls_conf->check_cert_subject) {
-+ tls_conf->check_cert_subject =
-+ os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
-+ if (tls_conf->check_cert_subject == NULL)
-+ return -1;
-+ }
-+ }
-+
-+ if (tls_mbedtls_set_params(tls_conf, params) != 0)
-+ return -1;
-+ conn->verify_peer = tls_conf->verify_peer;
-+
-+ return tls_mbedtls_ssl_setup(conn);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
-+ const u8 *data, size_t len)
-+{
-+ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+ return -1;
-+ if (conn->clienthello_session_ticket)
-+ tls_connection_deinit_clienthello_session_ticket(conn);
-+ if (len) {
-+ conn->clienthello_session_ticket = mbedtls_calloc(1, len);
-+ if (conn->clienthello_session_ticket == NULL)
-+ return -1;
-+ conn->clienthello_session_ticket_len = len;
-+ os_memcpy(conn->clienthello_session_ticket, data, len);
-+ }
-+ return 0;
-+}
-+
-+
-+static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
-+{
-+ mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
-+ if (sess->MBEDTLS_PRIVATE(ticket)) {
-+ mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
-+ sess->MBEDTLS_PRIVATE(ticket_len));
-+ mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
-+ }
-+ sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
-+ sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
-+ sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
-+
-+ conn->clienthello_session_ticket = NULL;
-+ conn->clienthello_session_ticket_len = 0;
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
-+ const mbedtls_ssl_session *session,
-+ unsigned char *start,
-+ const unsigned char *end,
-+ size_t *tlen,
-+ uint32_t *lifetime)
-+{
-+ struct tls_connection *conn = p_ticket;
-+ if (conn && conn->session_ticket_cb) {
-+ /* see tls_mbedtls_clienthello_session_ticket_prep() */
-+ /* see tls_mbedtls_clienthello_session_ticket_set() */
-+ return 0;
-+ }
-+
-+ return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
-+ session, start, end, tlen, lifetime);
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
-+ mbedtls_ssl_session *session,
-+ unsigned char *buf,
-+ size_t len)
-+{
-+ /* XXX: TODO: not implemented in client;
-+ * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
-+
-+ if (len == 0)
-+ return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-+
-+ struct tls_connection *conn = p_ticket;
-+ if (conn && conn->session_ticket_cb) {
-+ /* XXX: have random and secret been initialized yet?
-+ * or must keys first be exported?
-+ * EAP-FAST uses all args, EAP-TEAP only uses secret */
-+ struct tls_random data;
-+ if (tls_connection_get_random(NULL, conn, &data) != 0)
-+ return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
-+ int ret =
-+ conn->session_ticket_cb(conn->session_ticket_cb_ctx,
-+ buf, len,
-+ data.client_random,
-+ data.server_random,
-+ conn->expkey_secret);
-+ if (ret == 1) {
-+ conn->resumed = 1;
-+ return 0;
-+ }
-+ emsg(MSG_ERROR, "EAP session ticket ext not implemented");
-+ return MBEDTLS_ERR_SSL_INVALID_MAC;
-+ /*(non-zero return used for mbedtls debug logging)*/
-+ }
-+
-+ /* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
-+ int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
-+ session, buf, len);
-+ if (conn)
-+ conn->resumed = (rc == 0);
-+ return rc;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+__attribute_cold__
-+int tls_global_set_params(void *tls_ctx,
-+ const struct tls_connection_params *params)
-+{
-+ /* XXX: why might global_set_params be called more than once? */
-+ if (tls_ctx_global.tls_conf)
-+ tls_conf_deinit(tls_ctx_global.tls_conf);
-+ tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
-+ if (tls_ctx_global.tls_conf == NULL)
-+ return -1;
-+
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ #ifdef MBEDTLS_SSL_TICKET_C
-+ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+ tls_mbedtls_ssl_ticket_write,
-+ tls_mbedtls_ssl_ticket_parse,
-+ NULL);
-+ #else
-+ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+ mbedtls_ssl_ticket_write,
-+ mbedtls_ssl_ticket_parse,
-+ &tls_ctx_global.ticket_ctx);
-+ #endif
-+ #endif
-+ #endif
-+
-+ os_free(tls_ctx_global.ocsp_stapling_response);
-+ tls_ctx_global.ocsp_stapling_response = NULL;
-+ if (params->ocsp_stapling_response)
-+ tls_ctx_global.ocsp_stapling_response =
-+ os_strdup(params->ocsp_stapling_response);
-+
-+ os_free(tls_ctx_global.ca_cert_file);
-+ tls_ctx_global.ca_cert_file = NULL;
-+ if (params->ca_cert)
-+ tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
-+ return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
-+}
-+
-+
-+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
-+{
-+ tls_ctx_global.tls_conf->check_crl = check_crl;
-+ tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
-+ return 0;
-+}
-+
-+
-+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-+ int verify_peer, unsigned int flags,
-+ const u8 *session_ctx, size_t session_ctx_len)
-+{
-+ /*(EAP server-side calls this from eap_server_tls_ssl_init())*/
-+ if (conn == NULL)
-+ return -1;
-+
-+ conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
-+
-+ int authmode;
-+ switch (verify_peer) {
-+ case 2: authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
-+ case 1: authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
-+ default: authmode = MBEDTLS_SSL_VERIFY_NONE; break;
-+ }
-+ mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
-+
-+ if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
-+ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+ else
-+ mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
-+
-+ return 0;
-+}
-+
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static void tls_connection_export_keys_cb(
-+ void *p_expkey, mbedtls_ssl_key_export_type secret_type,
-+ const unsigned char *secret, size_t secret_len,
-+ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ mbedtls_tls_prf_types tls_prf_type)
-+{
-+ struct tls_connection *conn = p_expkey;
-+ conn->tls_prf_type = tls_prf_type;
-+ if (!tls_prf_type)
-+ return;
-+ if (secret_len > sizeof(conn->expkey_secret)) {
-+ emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
-+ conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
-+ return;
-+ }
-+ conn->expkey_secret_len = secret_len;
-+ os_memcpy(conn->expkey_secret, secret, secret_len);
-+ os_memcpy(conn->expkey_randbytes,
-+ client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+ server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+}
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static int tls_connection_export_keys_cb(
-+ void *p_expkey,
-+ const unsigned char *ms,
-+ const unsigned char *kb,
-+ size_t maclen,
-+ size_t keylen,
-+ size_t ivlen,
-+ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ mbedtls_tls_prf_types tls_prf_type )
-+{
-+ struct tls_connection *conn = p_expkey;
-+ conn->tls_prf_type = tls_prf_type;
-+ if (!tls_prf_type)
-+ return -1; /*(return value ignored by mbedtls)*/
-+ conn->expkey_keyblock_size = maclen + keylen + ivlen;
-+ conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
-+ os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
-+ os_memcpy(conn->expkey_randbytes,
-+ client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+ server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+ return 0;
-+}
-+#endif
-+
-+
-+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
-+ struct tls_random *data)
-+{
-+ if (!conn || !conn->tls_prf_type)
-+ return -1;
-+ data->client_random = conn->expkey_randbytes;
-+ data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+ data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
-+ data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+ return 0;
-+}
-+
-+
-+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-+ const char *label, const u8 *context,
-+ size_t context_len, u8 *out, size_t out_len)
-+{
-+ /* (EAP-PEAP EAP-TLS EAP-TTLS) */
-+ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+ return (conn && conn->established && conn->tls_prf_type)
-+ ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+ conn->expkey_secret, conn->expkey_secret_len, label,
-+ conn->expkey_randbytes,
-+ sizeof(conn->expkey_randbytes), out, out_len)
-+ : -1;
-+ #else
-+ /* not implemented here for mbedtls < 2.18.0 */
-+ return -1;
-+ #endif
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+/* keyblock size info is not exposed in mbed TLS 3.0.0 */
-+/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
-+#include <mbedtls/ssl_ciphersuites.h>
-+#include <mbedtls/cipher.h>
-+static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
-+{
-+ #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
-+ #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+ if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3)
-+ return 0; /* (calculation not extracted) */
-+ #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
-+
-+ int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
-+ const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
-+ mbedtls_ssl_ciphersuite_from_id(ciphersuite);
-+ if (ciphersuite_info == NULL)
-+ return 0;
-+
-+ const mbedtls_cipher_info_t *cipher_info =
-+ mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
-+ if (cipher_info == NULL)
-+ return 0;
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+ size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
-+ mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
-+ #else
-+ size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
-+ mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
-+ #endif
-+ #if defined(MBEDTLS_GCM_C) || \
-+ defined(MBEDTLS_CCM_C) || \
-+ defined(MBEDTLS_CHACHAPOLY_C)
-+ if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
-+ return keylen + 4;
-+ else if (mode == MBEDTLS_MODE_CHACHAPOLY)
-+ return keylen + 12;
-+ else
-+ #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
-+ #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
-+ {
-+ const mbedtls_md_info_t *md_info =
-+ mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
-+ if (md_info == NULL)
-+ return 0;
-+ size_t mac_key_len = mbedtls_md_get_size(md_info);
-+ size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
-+ return keylen + mac_key_len + ivlen;
-+ }
-+ #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-+ #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
-+ return 0;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
-+
-+
-+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
-+ u8 *out, size_t out_len)
-+{
-+ /* XXX: has export keys callback been run? */
-+ if (!conn || !conn->tls_prf_type)
-+ return -1;
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
-+ if (conn->expkey_keyblock_size == 0)
-+ return -1;
-+ #endif
-+ size_t skip = conn->expkey_keyblock_size * 2;
-+ unsigned char *tmp_out = os_malloc(skip + out_len);
-+ if (!tmp_out)
-+ return -1;
-+
-+ /* server_random and then client_random */
-+ unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
-+ os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+ MBEDTLS_EXPKEY_RAND_LEN);
-+ os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
-+ MBEDTLS_EXPKEY_RAND_LEN);
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+ int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+ conn->expkey_secret, conn->expkey_secret_len,
-+ "key expansion", seed, sizeof(seed),
-+ tmp_out, skip + out_len);
-+ if (ret == 0)
-+ os_memcpy(out, tmp_out + skip, out_len);
-+ #else
-+ int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
-+ #endif
-+
-+ bin_clear_free(tmp_out, skip + out_len);
-+ forced_memzero(seed, sizeof(seed));
-+ return ret;
-+}
-+
-+#endif /* TLS_MBEDTLS_EAP_FAST */
-+
-+
-+__attribute_cold__
-+static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
-+{
-+ /* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
-+ if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
-+ return;
-+ if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
-+ return;
-+ #if 0
-+ /*(info not available on client;
-+ * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
-+ if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
-+ #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+ mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
-+ #else
-+ mbedtls_ssl_get_ciphersuite_id(
-+ mbedtls_ssl_get_ciphersuite(&conn->ssl))
-+ #endif
-+ && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
-+ < 384 /*(3072/8)*/)
-+ #endif
-+ {
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (init_conf->event_cb) {
-+ union tls_event_data ev;
-+ os_memset(&ev, 0, sizeof(ev));
-+ ev.alert.is_local = 1;
-+ ev.alert.type = "fatal";
-+ /*"internal error" string for tests/hwsim/test_suiteb.py */
-+ ev.alert.description = "internal error: handshake failure";
-+ /*ev.alert.description = "insufficient security";*/
-+ init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
-+ }
-+ }
-+}
-+
-+
-+struct wpabuf * tls_connection_handshake(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data,
-+ struct wpabuf **appl_data)
-+{
-+ if (appl_data)
-+ *appl_data = NULL;
-+
-+ if (in_data && wpabuf_len(in_data)) {
-+ /*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
-+ if (conn->pull_buf && 0) /* disable; appears unwise */
-+ tls_pull_buf_discard(conn, __func__);
-+ if (!tls_pull_buf_append(conn, in_data))
-+ return NULL;
-+ }
-+
-+ if (conn->tls_conf == NULL) {
-+ struct tls_connection_params params;
-+ os_memset(¶ms, 0, sizeof(params));
-+ params.openssl_ciphers =
-+ tls_ctx_global.init_conf.openssl_ciphers;
-+ params.flags = tls_ctx_global.tls_conf->flags;
-+ if (tls_connection_set_params(tls_ctx, conn, ¶ms) != 0)
-+ return NULL;
-+ }
-+
-+ if (conn->verify_peer) /*(call here might be redundant; nbd)*/
-+ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ if (conn->clienthello_session_ticket)
-+ /*(starting handshake for EAP-FAST and EAP-TEAP)*/
-+ tls_mbedtls_clienthello_session_ticket_set(conn);
-+
-+ /* (not thread-safe due to need to set userdata 'conn' for callback) */
-+ /* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
-+ * since ticket write and parse callbacks take (mbedtls_ssl_session *)
-+ * param instead of (mbedtls_ssl_context *) param) */
-+ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+ NULL, NULL, NULL);
-+ else
-+ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+ tls_mbedtls_ssl_ticket_write,
-+ tls_mbedtls_ssl_ticket_parse,
-+ conn);
-+ #endif
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ int ret = mbedtls_ssl_handshake(&conn->ssl);
-+ #else
-+ int ret = 0;
-+ while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
-+ ret = mbedtls_ssl_handshake_step(&conn->ssl);
-+ if (ret != 0)
-+ break;
-+ }
-+ #endif
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+ tls_mbedtls_ssl_ticket_write,
-+ tls_mbedtls_ssl_ticket_parse,
-+ NULL);
-+ #endif
-+
-+ switch (ret) {
-+ case 0:
-+ conn->established = 1;
-+ if (conn->push_buf == NULL)
-+ /* Need to return something to get final TLS ACK. */
-+ conn->push_buf = wpabuf_alloc(0);
-+
-+ if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
-+ *appl_data = NULL; /* RFE: check for application data */
-+ break;
-+ case MBEDTLS_ERR_SSL_WANT_WRITE:
-+ case MBEDTLS_ERR_SSL_WANT_READ:
-+ case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
-+ case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
-+ if (tls_ctx_global.tls_conf /*(is server)*/
-+ && conn->established && conn->push_buf == NULL)
-+ /* Need to return something to trigger completion of EAP-TLS. */
-+ conn->push_buf = wpabuf_alloc(0);
-+ break;
-+ default:
-+ ++conn->failed;
-+ switch (ret) {
-+ case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
-+ case MBEDTLS_ERR_NET_CONN_RESET:
-+ case MBEDTLS_ERR_NET_SEND_FAILED:
-+ ++conn->write_alerts;
-+ break;
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
-+ #else
-+ case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
-+ #endif
-+ tls_mbedtls_suiteb_handshake_alert(conn);
-+ /* fall through */
-+ case MBEDTLS_ERR_NET_RECV_FAILED:
-+ case MBEDTLS_ERR_SSL_CONN_EOF:
-+ case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
-+ case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
-+ ++conn->read_alerts;
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ ilog(ret, "mbedtls_ssl_handshake");
-+ break;
-+ }
-+
-+ struct wpabuf *out_data = conn->push_buf;
-+ conn->push_buf = NULL;
-+ return out_data;
-+}
-+
-+
-+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data,
-+ struct wpabuf **appl_data)
-+{
-+ conn->is_server = 1;
-+ return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
-+}
-+
-+
-+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data)
-+{
-+ int res = mbedtls_ssl_write(&conn->ssl,
-+ wpabuf_head_u8(in_data), wpabuf_len(in_data));
-+ if (res < 0) {
-+ elog(res, "mbedtls_ssl_write");
-+ return NULL;
-+ }
-+
-+ struct wpabuf *buf = conn->push_buf;
-+ conn->push_buf = NULL;
-+ return buf;
-+}
-+
-+
-+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data)
-+{
-+ int res;
-+ struct wpabuf *out;
-+
-+ /*assert(in_data != NULL);*/
-+ if (!tls_pull_buf_append(conn, in_data))
-+ return NULL;
-+
-+ #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
-+ /* Add extra buffer space to handle the possibility of decrypted
-+ * data being longer than input data due to TLS compression. */
-+ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-+ #else /* TLS compression is disabled in mbedtls 3.x */
-+ out = wpabuf_alloc(wpabuf_len(in_data));
-+ #endif
-+ if (out == NULL)
-+ return NULL;
-+
-+ res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
-+ if (res < 0) {
-+ #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
-+ if (res == MBEDTLS_ERR_SSL_WANT_READ)
-+ return out;
-+ #endif
-+ elog(res, "mbedtls_ssl_read");
-+ wpabuf_free(out);
-+ return NULL;
-+ }
-+ wpabuf_put(out, res);
-+
-+ return out;
-+}
-+
-+
-+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
-+{
-+ /* XXX: might need to detect if session resumed from TLS session ticket
-+ * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
-+ /* (?ssl->handshake->resume during session ticket validation?) */
-+ return conn && conn->resumed;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-+ u8 *ciphers)
-+{
-+ /* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
-+ int ids[7];
-+ const int idsz = (int)sizeof(ids);
-+ int nids = -1, id;
-+ for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
-+ switch (*ciphers) {
-+ case TLS_CIPHER_RC4_SHA:
-+ #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
-+ id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
-+ break;
-+ #else
-+ continue; /*(not supported in mbedtls 3.x; ignore)*/
-+ #endif
-+ case TLS_CIPHER_AES128_SHA:
-+ id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
-+ break;
-+ case TLS_CIPHER_RSA_DHE_AES128_SHA:
-+ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-+ break;
-+ case TLS_CIPHER_ANON_DH_AES128_SHA:
-+ continue; /*(not supported in mbedtls; ignore)*/
-+ case TLS_CIPHER_RSA_DHE_AES256_SHA:
-+ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-+ break;
-+ case TLS_CIPHER_AES256_SHA:
-+ id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
-+ break;
-+ default:
-+ return -1; /* should not happen */
-+ }
-+ if (++nids == idsz)
-+ return -1; /* should not happen */
-+ ids[nids] = id;
-+ }
-+ if (nids < 0)
-+ return 0; /* nothing to do */
-+ if (++nids == idsz)
-+ return -1; /* should not happen */
-+ ids[nids] = 0; /* terminate list */
-+ ++nids;
-+
-+ return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
-+}
-+#endif
-+
-+
-+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
-+ char *buf, size_t buflen)
-+{
-+ if (conn == NULL)
-+ return -1;
-+ os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
-+ return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
-+{
-+ if (conn == NULL)
-+ return 0;
-+ return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+}
-+#endif
-+
-+
-+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
-+ char *buf, size_t buflen)
-+{
-+ if (conn == NULL)
-+ return -1;
-+ const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+ return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+int tls_connection_enable_workaround(void *tls_ctx,
-+ struct tls_connection *conn)
-+{
-+ /* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
-+ /* XXX: is there a relevant setting for this in mbed TLS? */
-+ /* (do we even care that much about older CBC ciphers?) */
-+ return 0;
-+}
-+
-+
-+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
-+ int ext_type, const u8 *data,
-+ size_t data_len)
-+{
-+ /* (EAP-FAST and EAP-TEAP) */
-+ if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
-+ return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
-+ data_len);
-+
-+ return -1;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
-+{
-+ return conn ? conn->failed : -1;
-+}
-+
-+
-+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
-+{
-+ return conn ? conn->read_alerts : -1;
-+}
-+
-+
-+int tls_connection_get_write_alerts(void *tls_ctx,
-+ struct tls_connection *conn)
-+{
-+ return conn ? conn->write_alerts : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+int tls_connection_set_session_ticket_cb(
-+ void *tls_ctx, struct tls_connection *conn,
-+ tls_session_ticket_cb cb, void *ctx)
-+{
-+ if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
-+ /* (EAP-FAST and EAP-TEAP) */
-+ conn->session_ticket_cb = cb;
-+ conn->session_ticket_cb_ctx = ctx;
-+ return 0;
-+ }
-+ return -1;
-+}
-+#endif
-+
-+
-+int tls_get_library_version(char *buf, size_t buf_len)
-+{
-+ #ifndef MBEDTLS_VERSION_C
-+ const char * const ver = "n/a";
-+ #else
-+ char ver[9];
-+ mbedtls_version_get_string(ver);
-+ #endif
-+ return os_snprintf(buf, buf_len,
-+ "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
-+}
-+
-+
-+void tls_connection_set_success_data(struct tls_connection *conn,
-+ struct wpabuf *data)
-+{
-+ wpabuf_free(conn->success_data);
-+ conn->success_data = data;
-+}
-+
-+
-+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
-+{
-+}
-+
-+
-+const struct wpabuf *
-+tls_connection_get_success_data(struct tls_connection *conn)
-+{
-+ return conn->success_data;
-+}
-+
-+
-+void tls_connection_remove_session(struct tls_connection *conn)
-+{
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
-+{
-+ #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
-+ /* data from TLS handshake Finished message */
-+ size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
-+ char *verify_data = (conn->is_server ^ conn->resumed)
-+ ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
-+ : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
-+ if (verify_len && verify_len <= max_len) {
-+ os_memcpy(buf, verify_data, verify_len);
-+ return (int)verify_len;
-+ }
-+ #endif
-+ return -1;
-+}
-+#endif
-+
-+
-+__attribute_noinline__
-+static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
-+{
-+ if (conn->peer_subject)
-+ return;
-+ char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+ int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
-+ if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
-+ os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
-+{
-+ if (!conn)
-+ return NULL;
-+ if (!conn->peer_subject) { /*(if not set during cert verify)*/
-+ const mbedtls_x509_crt *peer_cert =
-+ mbedtls_ssl_get_peer_cert(&conn->ssl);
-+ if (peer_cert)
-+ tls_mbedtls_set_peer_subject(conn, peer_cert);
-+ }
-+ return conn->peer_subject;
-+}
-+#endif
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
-+{
-+ /* XXX: availability of cert does not necessary mean that client
-+ * received certificate request from server and then sent cert.
-+ * ? step handshake in tls_connection_handshake() looking for
-+ * MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
-+ const struct tls_conf * const tls_conf = conn->tls_conf;
-+ return (tls_conf->has_client_cert && tls_conf->has_private_key);
-+}
-+#endif
-+
-+
-+#if defined(CONFIG_FIPS)
-+#define TLS_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if defined(CONFIG_SHA256)
-+#define TLS_MBEDTLS_TLS_PRF_SHA256
-+#endif
-+
-+#if defined(CONFIG_SHA384)
-+#define TLS_MBEDTLS_TLS_PRF_SHA384
-+#endif
-+
-+
-+#ifndef TLS_MBEDTLS_CONFIG_FIPS
-+#if defined(CONFIG_MODULE_TESTS)
-+/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
-+ && MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+/* sha1-tlsprf.c */
-+#include "sha1.h"
-+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
-+ secret, secret_len, label,
-+ seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha1-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
-+/* sha256-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha256.h"
-+int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
-+ secret, secret_len, label,
-+ seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha256-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
-+/* sha384-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha384.h"
-+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
-+ secret, secret_len, label,
-+ seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha384-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
-+ ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
-+#endif
-+
-+struct mlist { const char *p; size_t n; };
-+
-+
-+static int
-+tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
-+{
-+ /* RFE: this could be pre-parsed into structured data at config time */
-+ struct mlist list[256]; /*(much larger than expected)*/
-+ int nlist = 0;
-+ if ( os_strncmp(match, "EMAIL:", 6) != 0
-+ && os_strncmp(match, "DNS:", 4) != 0
-+ && os_strncmp(match, "URI:", 4) != 0 ) {
-+ wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
-+ return 0;
-+ }
-+ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+ do { } while ((tok = os_strchr(s, ';'))
-+ && os_strncmp(tok+1, "EMAIL:", 6) != 0
-+ && os_strncmp(tok+1, "DNS:", 4) != 0
-+ && os_strncmp(tok+1, "URI:", 4) != 0);
-+ list[nlist].p = s;
-+ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+ wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
-+ match);
-+ break; /* truncate huge list and continue */
-+ }
-+ }
-+
-+ if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+ return 0;
-+
-+ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+ for (; cur != NULL; cur = cur->next) {
-+ const unsigned char san_type = (unsigned char)cur->buf.tag
-+ & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+ char t;
-+ size_t step = 4;
-+ switch (san_type) { /* "EMAIL:" or "DNS:" or "URI:" */
-+ case MBEDTLS_X509_SAN_RFC822_NAME: step = 6; t = 'E'; break;
-+ case MBEDTLS_X509_SAN_DNS_NAME: t = 'D'; break;
-+ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
-+ default: continue;
-+ }
-+
-+ for (int i = 0; i < nlist; ++i) {
-+ /* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
-+ /* Note: v is not '\0'-terminated, but is a known length vlen,
-+ * so okay to pass to os_strncasecmp() even though not z-string */
-+ if (cur->buf.len == list[i].n - step && t == *list[i].p
-+ && 0 == os_strncasecmp((char *)cur->buf.p,
-+ list[i].p+step, cur->buf.len)) {
-+ return 1; /* match */
-+ }
-+ }
-+ }
-+ return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffix(const char *v, size_t vlen,
-+ const struct mlist *list, int nlist, int full)
-+{
-+ /* Note: v is not '\0'-terminated, but is a known length vlen,
-+ * so okay to pass to os_strncasecmp() even though not z-string */
-+ for (int i = 0; i < nlist; ++i) {
-+ size_t n = list[i].n;
-+ if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
-+ && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
-+ return 1; /* match */
-+ }
-+ return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
-+{
-+ /* RFE: this could be pre-parsed into structured data at config time */
-+ struct mlist list[256]; /*(much larger than expected)*/
-+ int nlist = 0;
-+ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+ tok = os_strchr(s, ';');
-+ list[nlist].p = s;
-+ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+ wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
-+ break; /* truncate huge list and continue */
-+ }
-+ }
-+
-+ /* check subjectAltNames */
-+ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
-+ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+ for (; cur != NULL; cur = cur->next) {
-+ const unsigned char san_type = (unsigned char)cur->buf.tag
-+ & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+ if (san_type == MBEDTLS_X509_SAN_DNS_NAME
-+ && tls_mbedtls_match_suffix((char *)cur->buf.p,
-+ cur->buf.len,
-+ list, nlist, full)) {
-+ return 1; /* match */
-+ }
-+ }
-+ }
-+
-+ /* check subject CN */
-+ const mbedtls_x509_name *name = &crt->subject;
-+ for (; name != NULL; name = name->next) {
-+ if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
-+ break;
-+ }
-+ if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
-+ list, nlist, full)) {
-+ return 1; /* match */
-+ }
-+
-+ return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
-+{
-+ /* RFE: this could be pre-parsed into structured data at config time */
-+ struct mlistoid { const char *p; size_t n;
-+ const char *oid; size_t olen;
-+ int prefix; };
-+ struct mlistoid list[32]; /*(much larger than expected)*/
-+ int nlist = 0;
-+ for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
-+ tok = os_strchr(s, '/');
-+ list[nlist].oid = NULL;
-+ list[nlist].olen = 0;
-+ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+ e = memchr(s, '=', list[nlist].n);
-+ if (e == NULL) {
-+ if (list[nlist].n == 0)
-+ continue; /* skip consecutive, repeated '/' */
-+ if (list[nlist].n == 1 && *s == '*') {
-+ /* special-case "*" to match any OID and value */
-+ s = e = "=*";
-+ list[nlist].n = 2;
-+ list[nlist].oid = "";
-+ }
-+ else {
-+ wpa_printf(MSG_INFO,
-+ "MTLS: invalid check_cert_subject '%s' missing '='",
-+ match);
-+ return 0;
-+ }
-+ }
-+ switch (e - s) {
-+ case 1:
-+ if (*s == 'C') {
-+ list[nlist].oid = MBEDTLS_OID_AT_COUNTRY;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
-+ }
-+ else if (*s == 'L') {
-+ list[nlist].oid = MBEDTLS_OID_AT_LOCALITY;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
-+ }
-+ else if (*s == 'O') {
-+ list[nlist].oid = MBEDTLS_OID_AT_ORGANIZATION;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
-+ }
-+ break;
-+ case 2:
-+ if (s[0] == 'C' && s[1] == 'N') {
-+ list[nlist].oid = MBEDTLS_OID_AT_CN;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
-+ }
-+ else if (s[0] == 'S' && s[1] == 'T') {
-+ list[nlist].oid = MBEDTLS_OID_AT_STATE;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
-+ }
-+ else if (s[0] == 'O' && s[1] == 'U') {
-+ list[nlist].oid = MBEDTLS_OID_AT_ORG_UNIT;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
-+ }
-+ break;
-+ case 12:
-+ if (os_memcmp(s, "emailAddress", 12) == 0) {
-+ list[nlist].oid = MBEDTLS_OID_PKCS9_EMAIL;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
-+ }
-+ break;
-+ default:
-+ break;
-+ }
-+ if (list[nlist].oid == NULL) {
-+ wpa_printf(MSG_INFO,
-+ "MTLS: Unknown field in check_cert_subject '%s'",
-+ match);
-+ return 0;
-+ }
-+ list[nlist].n -= (size_t)(++e - s);
-+ list[nlist].p = e;
-+ if (list[nlist].n && e[list[nlist].n-1] == '*') {
-+ --list[nlist].n;
-+ list[nlist].prefix = 1;
-+ }
-+ /*(could easily add support for suffix matches if value begins with '*',
-+ * but suffix match is not currently supported by other TLS modules)*/
-+
-+ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+ wpa_printf(MSG_INFO,
-+ "MTLS: excessive check_cert_subject match '%s'",
-+ match);
-+ break; /* truncate huge list and continue */
-+ }
-+ }
-+
-+ /* each component in match string must match cert Subject in order listed
-+ * The behavior below preserves ordering but is slightly different than
-+ * the grossly inefficient contortions implemented in tls_openssl.c */
-+ const mbedtls_x509_name *name = &crt->subject;
-+ for (int i = 0; i < nlist; ++i) {
-+ int found = 0;
-+ for (; name != NULL && !found; name = name->next) {
-+ if (!name->oid.p)
-+ continue;
-+ /* special-case "*" to match any OID and value */
-+ if (list[i].olen == 0) {
-+ found = 1;
-+ continue;
-+ }
-+ /* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
-+ if (list[i].olen != name->oid.len
-+ || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
-+ continue;
-+ /* Note: v is not '\0'-terminated, but is a known length vlen,
-+ * so okay to pass to os_strncasecmp() even though not z-string */
-+ if ((list[i].prefix
-+ ? list[i].n <= name->val.len /* prefix match */
-+ : list[i].n == name->val.len) /* full match */
-+ && 0 == os_strncasecmp((char *)name->val.p,
-+ list[i].p, list[i].n)) {
-+ found = 1;
-+ continue;
-+ }
-+ }
-+ if (!found)
-+ return 0; /* no match */
-+ }
-+ return 1; /* match */
-+}
-+
-+
-+__attribute_cold__
-+static void
-+tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
-+ const char *errmsg, enum tls_fail_reason reason)
-+{
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (init_conf->event_cb == NULL)
-+ return;
-+
-+ struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+ subject[0] = '\0';
-+ union tls_event_data ev;
-+ os_memset(&ev, 0, sizeof(ev));
-+ ev.cert_fail.reason = reason;
-+ ev.cert_fail.depth = depth;
-+ ev.cert_fail.subject = subject;
-+ ev.cert_fail.reason_txt = errmsg;
-+ ev.cert_fail.cert = certbuf;
-+
-+ init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
-+
-+ wpabuf_free(certbuf);
-+}
-+
-+
-+__attribute_noinline__
-+static void
-+tls_mbedtls_verify_cert_event (struct tls_connection *conn,
-+ mbedtls_x509_crt *crt, int depth)
-+{
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (init_conf->event_cb == NULL)
-+ return;
-+
-+ struct wpabuf *certbuf = NULL;
-+ union tls_event_data ev;
-+ os_memset(&ev, 0, sizeof(ev));
-+
-+ #ifdef MBEDTLS_SHA256_C
-+ u8 hash[SHA256_DIGEST_LENGTH];
-+ const u8 *addr[] = { (u8 *)crt->raw.p };
-+ if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
-+ ev.peer_cert.hash = hash;
-+ ev.peer_cert.hash_len = sizeof(hash);
-+ }
-+ #endif
-+ ev.peer_cert.depth = depth;
-+ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+ if (depth == 0)
-+ ev.peer_cert.subject = conn->peer_subject;
-+ if (ev.peer_cert.subject == NULL) {
-+ ev.peer_cert.subject = subject;
-+ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+ subject[0] = '\0';
-+ }
-+
-+ char serial_num[128+1];
-+ ev.peer_cert.serial_num =
-+ tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
-+
-+ const mbedtls_x509_sequence *cur;
-+
-+ cur = NULL;
-+ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+ cur = &crt->subject_alt_names;
-+ for (; cur != NULL; cur = cur->next) {
-+ const unsigned char san_type = (unsigned char)cur->buf.tag
-+ & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+ size_t prelen = 4;
-+ const char *pre;
-+ switch (san_type) {
-+ case MBEDTLS_X509_SAN_RFC822_NAME: prelen = 6; pre = "EMAIL:";break;
-+ case MBEDTLS_X509_SAN_DNS_NAME: pre = "DNS:"; break;
-+ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:"; break;
-+ default: continue;
-+ }
-+
-+ char *pos = os_malloc(prelen + cur->buf.len + 1);
-+ if (pos == NULL)
-+ break;
-+ ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
-+ os_memcpy(pos, pre, prelen);
-+ /* data should be properly backslash-escaped if needed,
-+ * so code below does not re-escape, but does replace CTLs */
-+ /*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
-+ /*pos[prelen+cur->buf.len] = '\0';*/
-+ pos += prelen;
-+ for (size_t i = 0; i < cur->buf.len; ++i) {
-+ unsigned char c = cur->buf.p[i];
-+ *pos++ = (c >= 32 && c != 127) ? c : '?';
-+ }
-+ *pos = '\0';
-+
-+ if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
-+ break;
-+ }
-+
-+ cur = NULL;
-+ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
-+ cur = &crt->certificate_policies;
-+ for (; cur != NULL; cur = cur->next) {
-+ if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
-+ continue;
-+ /* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
-+ /* TOD-TOFU "1.3.6.1.4.1.40808.1.3.2" */
-+ #define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
-+ #define OID_TOD_TOFU "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
-+ if (os_memcmp(cur->buf.p,
-+ OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
-+ ev.peer_cert.tod = 1; /* TOD-STRICT */
-+ break;
-+ }
-+ if (os_memcmp(cur->buf.p,
-+ OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
-+ ev.peer_cert.tod = 2; /* TOD-TOFU */
-+ break;
-+ }
-+ }
-+
-+ struct tls_conf *tls_conf = conn->tls_conf;
-+ if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
-+ || init_conf->cert_in_cb) {
-+ certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+ ev.peer_cert.cert = certbuf;
-+ }
-+
-+ init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
-+
-+ wpabuf_free(certbuf);
-+ char **altsubject;
-+ *(const char ***)&altsubject = ev.peer_cert.altsubject;
-+ for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
-+ os_free(altsubject[i]);
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
-+{
-+ /* XXX: N.B. verify code not carefully tested besides hwsim tests
-+ *
-+ * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
-+ * RFE: review and add support for additional TLS_CONN_* flags
-+ * not handling OCSP (not available in mbedtls)
-+ * ... */
-+
-+ struct tls_connection *conn = (struct tls_connection *)arg;
-+ struct tls_conf *tls_conf = conn->tls_conf;
-+ uint32_t flags_in = *flags;
-+
-+ if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
-+ emsg(MSG_WARNING, "client cert chain too long");
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "client cert chain too long",
-+ TLS_FAIL_BAD_CERTIFICATE);
-+ }
-+ else if (tls_conf->verify_depth0_only) {
-+ if (depth > 0)
-+ *flags = 0;
-+ else {
-+ #ifdef MBEDTLS_SHA256_C
-+ u8 hash[SHA256_DIGEST_LENGTH];
-+ const u8 *addr[] = { (u8 *)crt->raw.p };
-+ if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
-+ || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
-+ *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "cert hash mismatch",
-+ TLS_FAIL_UNTRUSTED);
-+ }
-+ else /* hash matches; ignore other issues *except* if revoked)*/
-+ *flags &= MBEDTLS_X509_BADCERT_REVOKED;
-+ #endif
-+ }
-+ }
-+ else if (depth == 0) {
-+ if (!conn->peer_subject)
-+ tls_mbedtls_set_peer_subject(conn, crt);
-+ /*(use same labels to tls_mbedtls_verify_fail_event() as used in
-+ * other TLS modules so that hwsim tests find exact string match)*/
-+ if (!conn->peer_subject) { /* error copying subject string */
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "internal error",
-+ TLS_FAIL_UNSPECIFIED);
-+ }
-+ /*(use os_strstr() for subject match as is done in tls_mbedtls.c
-+ * to follow the same behavior, even though a suffix match would
-+ * make more sense. Also, note that strstr match does not
-+ * normalize whitespace (between components) for comparison)*/
-+ else if (tls_conf->subject_match
-+ && os_strstr(conn->peer_subject,
-+ tls_conf->subject_match) == NULL) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: Subject '%s' did not match with '%s'",
-+ conn->peer_subject, tls_conf->subject_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Subject mismatch",
-+ TLS_FAIL_SUBJECT_MISMATCH);
-+ }
-+ if (tls_conf->altsubject_match
-+ && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: altSubjectName match '%s' not found",
-+ tls_conf->altsubject_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "AltSubject mismatch",
-+ TLS_FAIL_ALTSUBJECT_MISMATCH);
-+ }
-+ if (tls_conf->suffix_match
-+ && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: Domain suffix match '%s' not found",
-+ tls_conf->suffix_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Domain suffix mismatch",
-+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
-+ }
-+ if (tls_conf->domain_match
-+ && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: Domain match '%s' not found",
-+ tls_conf->domain_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Domain mismatch",
-+ TLS_FAIL_DOMAIN_MISMATCH);
-+ }
-+ if (tls_conf->check_cert_subject
-+ && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Distinguished Name",
-+ TLS_FAIL_DN_MISMATCH);
-+ }
-+ if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ /* check RSA modulus size (public key bitlen) */
-+ const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
-+ if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
-+ && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
-+ /* hwsim suite_b RSA tests expect 3072
-+ * suite_b_192_rsa_ecdhe_radius_rsa2048_client
-+ * suite_b_192_rsa_dhe_radius_rsa2048_client */
-+ *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Insufficient RSA modulus size",
-+ TLS_FAIL_INSUFFICIENT_KEY_LEN);
-+ }
-+ }
-+ if (tls_conf->check_crl && tls_conf->crl == NULL) {
-+ /* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
-+ emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "check_crl set but no CRL loaded; "
-+ "reject all?",
-+ TLS_FAIL_BAD_CERTIFICATE);
-+ }
-+ }
-+ else {
-+ if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
-+ *flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
-+ }
-+
-+ if (!tls_conf->check_crl_strict) {
-+ *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
-+ *flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
-+ }
-+
-+ if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
-+ *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
-+ *flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
-+ }
-+
-+ tls_mbedtls_verify_cert_event(conn, crt, depth);
-+
-+ if (*flags) {
-+ if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
-+ |MBEDTLS_X509_BADCERT_CN_MISMATCH
-+ |MBEDTLS_X509_BADCERT_REVOKED)) {
-+ emsg(MSG_WARNING, "client cert not trusted");
-+ }
-+ /* report event if flags set but no additional flags set above */
-+ /* (could translate flags to more detailed TLS_FAIL_* if needed) */
-+ if (!(*flags & ~flags_in)) {
-+ enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
-+ const char *errmsg = "cert verify fail unspecified";
-+ if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
-+ reason = TLS_FAIL_UNTRUSTED;
-+ errmsg = "certificate not trusted";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
-+ reason = TLS_FAIL_REVOKED;
-+ errmsg = "certificate has been revoked";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
-+ reason = TLS_FAIL_NOT_YET_VALID;
-+ errmsg = "certificate not yet valid";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
-+ reason = TLS_FAIL_EXPIRED;
-+ errmsg = "certificate has expired";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
-+ reason = TLS_FAIL_BAD_CERTIFICATE;
-+ errmsg = "certificate uses insecure algorithm";
-+ }
-+ tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
-+ }
-+ #if 0
-+ /* ??? send (again) cert events for all certs in chain ???
-+ * (should already have been called for greater depths) */
-+ /* tls_openssl.c:tls_verify_cb() sends cert events for all certs
-+ * in chain if certificate validation fails, but sends all events
-+ * with depth set to 0 (might be a bug) */
-+ if (depth > 0) {
-+ int pdepth = depth + 1;
-+ for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
-+ tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
-+ }
-+ }
-+ #endif
-+ /*(do not preserve subject if verification failed but was optional)*/
-+ if (depth == 0 && conn->peer_subject) {
-+ os_free(conn->peer_subject);
-+ conn->peer_subject = NULL;
-+ }
-+ }
-+ else if (depth == 0) {
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (tls_conf->ca_cert_probe) {
-+ /* reject server certificate on probe-only run */
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "server chain probe",
-+ TLS_FAIL_SERVER_CHAIN_PROBE);
-+ }
-+ else if (init_conf->event_cb) {
-+ /* ??? send event as soon as depth == 0 is verified ???
-+ * What about rest of chain?
-+ * Follows tls_mbedtls.c behavior: */
-+ init_conf->event_cb(init_conf->cb_ctx,
-+ TLS_CERT_CHAIN_SUCCESS, NULL);
-+ }
-+ }
-+
-+ return 0;
-+}
---- /dev/null
-+++ b/tests/build/build-wpa_supplicant-mbedtls.config
-@@ -0,0 +1,24 @@
-+CONFIG_TLS=mbedtls
-+
-+CONFIG_WPS=y
-+CONFIG_EAP_TLS=y
-+CONFIG_EAP_MSCHAPV2=y
-+
-+CONFIG_EAP_PSK=y
-+CONFIG_EAP_GPSK=y
-+CONFIG_EAP_AKA=y
-+CONFIG_EAP_SIM=y
-+CONFIG_EAP_SAKE=y
-+CONFIG_EAP_PAX=y
-+CONFIG_EAP_FAST=y
-+CONFIG_EAP_IKEV2=y
-+
-+CONFIG_SAE=y
-+CONFIG_FILS=y
-+CONFIG_FILS_SK_PFS=y
-+CONFIG_OWE=y
-+CONFIG_DPP=y
-+CONFIG_SUITEB=y
-+CONFIG_SUITEB192=y
-+
-+CFLAGS += -Werror
---- a/tests/hwsim/example-hostapd.config
-+++ b/tests/hwsim/example-hostapd.config
-@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
- CONFIG_DRIVER_NL80211=y
- CONFIG_RSN_PREAUTH=y
-
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -39,6 +40,9 @@ endif
- ifeq ($(CONFIG_TLS), wolfssl)
- CONFIG_EAP_PWD=y
- endif
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CONFIG_EAP_PWD=y
-+endif
- CONFIG_EAP_EKE=y
- CONFIG_PKCS12=y
- CONFIG_RADIUS_SERVER=y
---- a/tests/hwsim/example-wpa_supplicant.config
-+++ b/tests/hwsim/example-wpa_supplicant.config
-@@ -2,6 +2,7 @@
-
- CONFIG_TLS=openssl
- #CONFIG_TLS=wolfssl
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -41,6 +42,9 @@ endif
- ifeq ($(CONFIG_TLS), wolfssl)
- CONFIG_EAP_PWD=y
- endif
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CONFIG_EAP_PWD=y
-+endif
-
- CONFIG_USIM_SIMULATOR=y
- CONFIG_SIM_SIMULATOR=y
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -1163,6 +1163,29 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
-
-+ifeq ($(CONFIG_TLS), mbedtls)
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls -lmbedx509
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifdef NEED_FIPS186_2_PRF
-+OBJS += ../src/crypto/fips_prf_internal.o
-+SHA1OBJS += ../src/crypto/sha1-internal.o
-+endif
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+LIBS += -lmbedcrypto
-+LIBS_p += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
-@@ -1355,9 +1378,11 @@ endif
-
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_INTERNAL_AES_WRAP=y
- endif
- endif
-+endif
- ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
- # Seems to be needed at least with BoringSSL
- NEED_INTERNAL_AES_WRAP=y
-@@ -1371,9 +1396,11 @@ endif
-
- ifdef NEED_INTERNAL_AES_WRAP
- ifneq ($(CONFIG_TLS), linux)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -1383,35 +1410,45 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_WRAP
- NEED_AES_ENC=y
- ifdef NEED_INTERNAL_AES_WRAP
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_ENC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1426,12 +1463,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -1443,29 +1484,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
- else
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
-
- ifndef CONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- MD5OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
- MD5OBJS += ../src/crypto/md5-internal.o
-@@ -1520,12 +1569,17 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- SHA256OBJS += ../src/crypto/sha256-internal.o
- endif
-@@ -1538,50 +1592,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
- SHA256OBJS += ../src/crypto/sha512-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
- CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- OBJS += $(SHA256OBJS)
- ifdef NEED_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA512
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
-
- ifdef NEED_ASN1
- OBJS += ../src/tls/asn1.o
-@@ -1756,10 +1828,12 @@ ifdef CONFIG_FIPS
- CFLAGS += -DCONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
- endif
- endif
- endif
-+endif
-
- OBJS += $(SHA1OBJS) $(DESOBJS)
-
---- a/wpa_supplicant/defconfig
-+++ b/wpa_supplicant/defconfig
-@@ -10,8 +10,8 @@
- # to override previous values of the variables.
-
-
--# Uncomment following two lines and fix the paths if you have installed OpenSSL
--# or GnuTLS in non-default location
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
- #CFLAGS += -I/usr/local/openssl/include
- #LIBS += -L/usr/local/openssl/lib
-
-@@ -20,6 +20,7 @@
- # used to fix build issues on such systems (krb5.h not found).
- #CFLAGS += -I/usr/include/kerberos
-
-+
- # Driver interface for generic Linux wireless extensions
- # Note: WEXT is deprecated in the current Linux kernel version and no new
- # functionality is added to it. nl80211-based interface is the new
-@@ -326,6 +327,7 @@ CONFIG_BACKEND=file
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/120-mbedtls-fips186_2_prf.patch b/recipes-wifi/hostapd/files/patches-2.10.3/120-mbedtls-fips186_2_prf.patch
deleted file mode 100644
index a487252..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/120-mbedtls-fips186_2_prf.patch
+++ /dev/null
@@ -1,114 +0,0 @@
-From c8dba4bd750269bcc80fed3d546e2077cb4cdf0e Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 19 Jul 2022 20:02:21 -0400
-Subject: [PATCH 2/7] mbedtls: fips186_2_prf()
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- hostapd/Makefile | 4 ---
- src/crypto/crypto_mbedtls.c | 60 +++++++++++++++++++++++++++++++++++++
- wpa_supplicant/Makefile | 4 ---
- 3 files changed, 60 insertions(+), 8 deletions(-)
-
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -759,10 +759,6 @@ endif
- OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
--ifdef NEED_FIPS186_2_PRF
--OBJS += ../src/crypto/fips_prf_internal.o
--SHA1OBJS += ../src/crypto/sha1-internal.o
--endif
- ifeq ($(CONFIG_CRYPTO), mbedtls)
- ifdef CONFIG_DPP
- LIBS += -lmbedx509
---- a/src/crypto/crypto_mbedtls.c
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -132,6 +132,12 @@
- #define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
- #endif
-
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
-+/* EAP_SIM=y EAP_AKA=y */
-+#define CRYPTO_MBEDTLS_FIPS186_2_PRF
-+#endif
-+
- #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
- || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
- #define CRYPTO_MBEDTLS_SHA1_T_PRF
-@@ -813,6 +819,60 @@ int sha1_t_prf(const u8 *key, size_t key
-
- #endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
-
-+#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
-+
-+/* fips_prf_internal.c sha1-internal.c */
-+
-+/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
-+ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
-+ * where xlen is 160 */
-+
-+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-+{
-+ /* FIPS 186-2 + change notice 1 */
-+
-+ mbedtls_sha1_context ctx;
-+ u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
-+ u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
-+ const u32 xstate_init[] =
-+ { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
-+
-+ mbedtls_sha1_init(&ctx);
-+ os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
-+
-+ /* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
-+ for (; xlen >= 20; xlen -= 20) {
-+ /* XSEED_j = 0 */
-+ /* XVAL = (XKEY + XSEED_j) mod 2^b */
-+
-+ /* w_i = G(t, XVAL) */
-+ os_memcpy(xstate, xstate_init, sizeof(xstate_init));
-+ mbedtls_internal_sha1_process(&ctx, xkey);
-+
-+ #if __BYTE_ORDER == __LITTLE_ENDIAN
-+ xstate[0] = host_to_be32(xstate[0]);
-+ xstate[1] = host_to_be32(xstate[1]);
-+ xstate[2] = host_to_be32(xstate[2]);
-+ xstate[3] = host_to_be32(xstate[3]);
-+ xstate[4] = host_to_be32(xstate[4]);
-+ #endif
-+ os_memcpy(x, xstate, 20);
-+ if (xlen == 20) /*(done; skip prep for next loop)*/
-+ break;
-+
-+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
-+ for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
-+ xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
-+ x += 20;
-+ /* x_j = w_0|w_1 (each pair of iterations through loop)*/
-+ }
-+
-+ mbedtls_sha1_free(&ctx);
-+ return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
-+
- #endif /* MBEDTLS_SHA1_C */
-
-
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -1174,10 +1174,6 @@ endif
- OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
--ifdef NEED_FIPS186_2_PRF
--OBJS += ../src/crypto/fips_prf_internal.o
--SHA1OBJS += ../src/crypto/sha1-internal.o
--endif
- ifeq ($(CONFIG_CRYPTO), mbedtls)
- LIBS += -lmbedcrypto
- LIBS_p += -lmbedcrypto
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch b/recipes-wifi/hostapd/files/patches-2.10.3/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch
deleted file mode 100644
index ae7620b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch
+++ /dev/null
@@ -1,421 +0,0 @@
-From 31bd19e0e0254b910cccfd3ddc6a6a9222bbcfc0 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Sun, 9 Oct 2022 05:12:17 -0400
-Subject: [PATCH 3/7] mbedtls: annotate with TEST_FAIL() for hwsim tests
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- src/crypto/crypto_mbedtls.c | 124 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 124 insertions(+)
-
---- a/src/crypto/crypto_mbedtls.c
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -280,6 +280,9 @@ __attribute_noinline__
- static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
- u8 *mac, mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_md_context_t ctx;
- mbedtls_md_init(&ctx);
- if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
-@@ -343,6 +346,9 @@ __attribute_noinline__
- static int sha384_512_vector(size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac, int is384)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_sha512_context ctx;
- mbedtls_sha512_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -375,6 +381,9 @@ int sha384_vector(size_t num_elem, const
- #include <mbedtls/sha256.h>
- int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_sha256_context ctx;
- mbedtls_sha256_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -397,6 +406,9 @@ int sha256_vector(size_t num_elem, const
- #include <mbedtls/sha1.h>
- int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_sha1_context ctx;
- mbedtls_sha1_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -419,6 +431,9 @@ int sha1_vector(size_t num_elem, const u
- #include <mbedtls/md5.h>
- int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_md5_context ctx;
- mbedtls_md5_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -441,6 +456,9 @@ int md5_vector(size_t num_elem, const u8
- #include <mbedtls/md4.h>
- int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_md4_context ctx;
- mbedtls_md4_init(&ctx);
- mbedtls_md4_starts_ret(&ctx);
-@@ -460,6 +478,9 @@ static int hmac_vector(const u8 *key, si
- const u8 *addr[], const size_t *len, u8 *mac,
- mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_md_context_t ctx;
- mbedtls_md_init(&ctx);
- if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
-@@ -571,6 +592,9 @@ static int hmac_kdf_expand(const u8 *prk
- const char *label, const u8 *info, size_t info_len,
- u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
- #ifdef MBEDTLS_HKDF_C
- if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */
-@@ -663,6 +687,9 @@ static int hmac_prf_bits(const u8 *key,
- const u8 *data, size_t data_len, u8 *buf,
- size_t buf_len_bits, mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_md_context_t ctx;
- mbedtls_md_init(&ctx);
- const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-@@ -938,6 +965,9 @@ int pbkdf2_sha1(const char *passphrase,
-
- static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
- if (!aes)
- return NULL;
-@@ -996,6 +1026,9 @@ void aes_decrypt_deinit(void *ctx)
- /* aes-wrap.c */
- int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_nist_kw_context ctx;
- mbedtls_nist_kw_init(&ctx);
- size_t olen;
-@@ -1010,6 +1043,9 @@ int aes_wrap(const u8 *kek, size_t kek_l
- /* aes-unwrap.c */
- int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_nist_kw_context ctx;
- mbedtls_nist_kw_init(&ctx);
- size_t olen;
-@@ -1041,6 +1077,9 @@ int omac1_aes_vector(
- const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_cipher_type_t cipher_type;
- switch (key_len) {
- case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
-@@ -1103,6 +1142,9 @@ int omac1_aes_256(const u8 *key, const u
- /* aes-encblock.c */
- int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_aes_context aes;
- mbedtls_aes_init(&aes);
- int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
-@@ -1118,6 +1160,9 @@ int aes_128_encrypt_block(const u8 *key,
- int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
- u8 *data, size_t data_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
- unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
- os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
-@@ -1160,11 +1205,17 @@ static int aes_128_cbc_oper(const u8 *ke
-
- int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
- }
-
- int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
- }
-
-@@ -1407,6 +1458,10 @@ int crypto_hash_finish(struct crypto_has
- }
- mbedtls_md_free(mctx);
- os_free(mctx);
-+
-+ if (TEST_FAIL())
-+ return -1;
-+
- return 0;
- }
-
-@@ -1421,6 +1476,9 @@ int crypto_hash_finish(struct crypto_has
-
- struct crypto_bignum *crypto_bignum_init(void)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_mpi *bn = os_malloc(sizeof(*bn));
- if (bn)
- mbedtls_mpi_init(bn);
-@@ -1429,6 +1487,9 @@ struct crypto_bignum *crypto_bignum_init
-
- struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_mpi *bn = os_malloc(sizeof(*bn));
- if (bn) {
- mbedtls_mpi_init(bn);
-@@ -1442,6 +1503,9 @@ struct crypto_bignum *crypto_bignum_init
-
- struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- #if 0 /*(hostap use of this interface passes int, not uint)*/
- val = host_to_be32(val);
- return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
-@@ -1467,6 +1531,9 @@ void crypto_bignum_deinit(struct crypto_
- int crypto_bignum_to_bin(const struct crypto_bignum *a,
- u8 *buf, size_t buflen, size_t padlen)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
- if (n < padlen)
- n = padlen;
-@@ -1477,6 +1544,9 @@ int crypto_bignum_to_bin(const struct cr
-
- int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
- #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
- return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
-@@ -1513,6 +1583,9 @@ int crypto_bignum_exptmod(const struct c
- const struct crypto_bignum *c,
- struct crypto_bignum *d)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /* (check if input params match d; d is the result) */
- /* (a == d) is ok in current mbedtls implementation */
- if (b == d || c == d) { /*(not ok; store result in intermediate)*/
-@@ -1540,6 +1613,9 @@ int crypto_bignum_inverse(const struct c
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b) ? -1 : 0;
-@@ -1549,6 +1625,9 @@ int crypto_bignum_sub(const struct crypt
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b) ? -1 : 0;
-@@ -1558,6 +1637,9 @@ int crypto_bignum_div(const struct crypt
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /*(most current use of this crypto.h interface has a == c (result),
- * so store result in an intermediate to avoid overwritten input)*/
- mbedtls_mpi R;
-@@ -1575,6 +1657,9 @@ int crypto_bignum_addmod(const struct cr
- const struct crypto_bignum *c,
- struct crypto_bignum *d)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b)
-@@ -1588,6 +1673,9 @@ int crypto_bignum_mulmod(const struct cr
- const struct crypto_bignum *c,
- struct crypto_bignum *d)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b)
-@@ -1600,6 +1688,9 @@ int crypto_bignum_sqrmod(const struct cr
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- #if 1
- return crypto_bignum_mulmod(a, a, b, c);
- #else
-@@ -1650,6 +1741,9 @@ int crypto_bignum_is_odd(const struct cr
- int crypto_bignum_legendre(const struct crypto_bignum *a,
- const struct crypto_bignum *p)
- {
-+ if (TEST_FAIL())
-+ return -2;
-+
- /* Security Note:
- * mbedtls_mpi_exp_mod() is not documented to run in constant time,
- * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
-@@ -1702,6 +1796,9 @@ int crypto_mod_exp(const u8 *base, size_
- const u8 *modulus, size_t modulus_len,
- u8 *result, size_t *result_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
- mbedtls_mpi_init(&bn_base);
- mbedtls_mpi_init(&bn_exp);
-@@ -1769,6 +1866,9 @@ static int crypto_mbedtls_dh_init_public
- int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
- u8 *pubkey)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
- size_t pubkey_len, pad;
-
-@@ -1810,6 +1910,9 @@ int crypto_dh_derive_secret(u8 generator
- const u8 *pubkey, size_t pubkey_len,
- u8 *secret, size_t *len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- #if 0
- if (pubkey_len > prime_len ||
- (pubkey_len == prime_len &&
-@@ -2512,6 +2615,9 @@ const struct crypto_ec_point * crypto_ec
-
- struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_ecp_point *p = os_malloc(sizeof(*p));
- if (p != NULL)
- mbedtls_ecp_point_init(p);
-@@ -2536,6 +2642,9 @@ int crypto_ec_point_x(struct crypto_ec *
- int crypto_ec_point_to_bin(struct crypto_ec *e,
- const struct crypto_ec_point *point, u8 *x, u8 *y)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
- size_t len = CRYPTO_EC_plen(e);
- if (x) {
-@@ -2563,6 +2672,9 @@ int crypto_ec_point_to_bin(struct crypto
- struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
- const u8 *val)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- size_t len = CRYPTO_EC_plen(e);
- mbedtls_ecp_point *p = os_malloc(sizeof(*p));
- u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-@@ -2615,6 +2727,9 @@ int crypto_ec_point_add(struct crypto_ec
- const struct crypto_ec_point *b,
- struct crypto_ec_point *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /* mbedtls does not provide an mbedtls_ecp_point add function */
- mbedtls_mpi one;
- mbedtls_mpi_init(&one);
-@@ -2631,6 +2746,9 @@ int crypto_ec_point_mul(struct crypto_ec
- const struct crypto_bignum *b,
- struct crypto_ec_point *res)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_ecp_mul(
- (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
- (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
-@@ -2639,6 +2757,9 @@ int crypto_ec_point_mul(struct crypto_ec
-
- int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
- == MBEDTLS_ECP_TYPE_MONTGOMERY) {
- /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
-@@ -2751,6 +2872,9 @@ struct crypto_bignum *
- crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
- const struct crypto_bignum *x)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
- if (y2 == NULL)
- return NULL;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/135-mbedtls-fix-owe-association.patch b/recipes-wifi/hostapd/files/patches-2.10.3/135-mbedtls-fix-owe-association.patch
deleted file mode 100644
index 0c29432..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/135-mbedtls-fix-owe-association.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-The code for hostapd-mbedtls did not work when used for OWE association.
-
-When handling association requests, the buffer offsets and length assumptions were incorrect, leading to never calculating the y point, thus denying association.
-
-Also when crafting the association response, the buffer contained the trailing key-type.
-
-Fix up both issues to adhere to the specification and make hostapd-mbedtls work with the OWE security type.
-
---- a/src/crypto/crypto_mbedtls.c
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -2299,25 +2299,30 @@ struct crypto_ecdh * crypto_ecdh_init2(i
- struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
- {
- mbedtls_ecp_group *grp = &ecdh->grp;
-- size_t len = CRYPTO_EC_plen(grp);
-+ size_t prime_len = CRYPTO_EC_plen(grp);
-+ size_t output_len = prime_len;
-+ u8 output_offset = 0;
-+ u8 buf[256];
-+
- #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
- /* len */
- #endif
- #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-- if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-- len = inc_y ? len*2+1 : len+1;
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
-+ output_offset = 1;
-+ }
- #endif
-- struct wpabuf *buf = wpabuf_alloc(len);
-- if (buf == NULL)
-+
-+ if (output_len > sizeof(buf))
- return NULL;
-+
- inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
-- if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &len,
-- wpabuf_mhead_u8(buf), len) == 0) {
-- wpabuf_put(buf, len);
-- return buf;
-+ if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
-+ buf, output_len) == 0) {
-+ return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
- }
-
-- wpabuf_free(buf);
- return NULL;
- }
-
-@@ -2379,10 +2384,7 @@ struct wpabuf * crypto_ecdh_set_peerkey(
- os_memcpy(buf+2, key, len);
- }
- len >>= 1; /*(repurpose len to prime_len)*/
-- }
-- else if (key[0] == 0x02 || key[0] == 0x03) { /* (inc_y == 0) */
-- --len; /*(repurpose len to prime_len)*/
--
-+ } else { /* (inc_y == 0) */
- /* mbedtls_ecp_point_read_binary() does not currently support
- * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
- * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
-@@ -2390,22 +2392,21 @@ struct wpabuf * crypto_ecdh_set_peerkey(
- /* derive y, amend buf[] with y for UNCOMPRESSED format */
- if (sizeof(buf)-2 < len*2 || len == 0)
- return NULL;
-+
- buf[0] = (u8)(1+len*2);
- buf[1] = 0x04;
-+ os_memcpy(buf+2, key, len);
-+
- mbedtls_mpi bn;
- mbedtls_mpi_init(&bn);
-- int ret = mbedtls_mpi_read_binary(&bn, key+1, len)
-- || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn,
-- key[0] & 1)
-+ int ret = mbedtls_mpi_read_binary(&bn, key, len)
-+ || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
- || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
- mbedtls_mpi_free(&bn);
- if (ret != 0)
- return NULL;
- }
-
-- if (key[0] == 0) /*(repurpose len to prime_len)*/
-- len = CRYPTO_EC_plen(grp);
--
- if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
- return NULL;
- }
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch
deleted file mode 100644
index e967cff..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch
+++ /dev/null
@@ -1,1358 +0,0 @@
-From f24933dc175e0faf44a3cce3330c256a59649ca6 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 19 Jul 2022 23:01:17 -0400
-Subject: [PATCH 4/7] tests/Makefile make run-tests with CONFIG_TLS=...
-
-add test-crypto_module.c to run crypto_module_tests()
-
-adjust some tests/hwsim/*.py for mbed TLS (work in progress)
-
-option to build and run-tests with CONFIG_TLS=internal # (default)
-$ cd tests; make clean
-$ make run-tests
-
-option to build and run-tests with CONFIG_TLS=gnutls
-$ cd tests; make clean CONFIG_TLS=gnutls
-$ make run-tests CONFIG_TLS=gnutls
-
-option to build and run-tests with CONFIG_TLS=mbedtls
-$ cd tests; make clean CONFIG_TLS=mbedtls
-$ make run-tests CONFIG_TLS=mbedtls
-
-option to build and run-tests with CONFIG_TLS=openssl
-$ cd tests; make clean CONFIG_TLS=openssl
-$ make run-tests CONFIG_TLS=openssl
-
-option to build and run-tests with CONFIG_TLS=wolfssl
-$ cd tests; make clean CONFIG_TLS=wolfssl
-$ make run-tests CONFIG_TLS=wolfssl
-
-RFE: Makefile logic for crypto objects should be centralized
- instead of being duplicated in hostapd/Makefile,
- wpa_supplicant/Makefile, src/crypto/Makefile,
- tests/Makefile, ...
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- hostapd/Makefile | 6 +
- src/crypto/Makefile | 129 ++++++++++++++++++++-
- src/crypto/crypto_module_tests.c | 134 ++++++++++++++++++++++
- src/tls/Makefile | 11 ++
- tests/Makefile | 75 +++++++++---
- tests/hwsim/example-hostapd.config | 11 +-
- tests/hwsim/example-wpa_supplicant.config | 12 +-
- tests/hwsim/test_ap_eap.py | 114 +++++++++++++-----
- tests/hwsim/test_ap_ft.py | 4 +-
- tests/hwsim/test_authsrv.py | 9 +-
- tests/hwsim/test_dpp.py | 19 ++-
- tests/hwsim/test_erp.py | 16 +--
- tests/hwsim/test_fils.py | 5 +-
- tests/hwsim/test_pmksa_cache.py | 4 +-
- tests/hwsim/test_sae.py | 7 ++
- tests/hwsim/test_suite_b.py | 3 +
- tests/hwsim/test_wpas_ctrl.py | 2 +-
- tests/hwsim/utils.py | 8 +-
- tests/test-crypto_module.c | 16 +++
- tests/test-https.c | 12 +-
- tests/test-https_server.c | 12 +-
- wpa_supplicant/Makefile | 6 +
- 22 files changed, 524 insertions(+), 91 deletions(-)
- create mode 100644 tests/test-crypto_module.c
-
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -696,6 +696,7 @@ CFLAGS += -DCONFIG_TLSV12
- endif
-
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- CONFIG_CRYPTO=wolfssl
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -716,6 +717,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- CONFIG_CRYPTO=openssl
- ifdef TLS_FUNCS
-@@ -746,6 +748,7 @@ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONF
- endif
-
- ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=mbedtls
- endif
-@@ -776,6 +779,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -806,6 +810,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -884,6 +889,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/crypto_internal-rsa.o
---- a/src/crypto/Makefile
-+++ b/src/crypto/Makefile
-@@ -1,10 +1,121 @@
--CFLAGS += -DCONFIG_CRYPTO_INTERNAL
--CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
--CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- #CFLAGS += -DALL_DH_GROUPS
- CFLAGS += -DCONFIG_SHA256
- CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+
-+# crypto_module_tests.c
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
-+ifeq ($(CONFIG_TLS),mbedtls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DCONFIG_DES
-+CFLAGS += -DEAP_IKEV2
-+CFLAGS += -DEAP_MSCHAPv2
-+CFLAGS += -DEAP_SIM
-+
-+LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
-+LIB_OBJS+= \
-+ aes-eax.o \
-+ aes-siv.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o
-+
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
-+ifndef CONFIG_TLS_DEFAULT_CIPHERS
-+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
-+endif
-+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DEAP_TLS_OPENSSL
-+
-+LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
-+LIB_OBJS+= \
-+ aes-ctr.o \
-+ aes-eax.o \
-+ aes-encblock.o \
-+ aes-siv.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o \
-+ sha1-prf.o \
-+ sha1-tlsprf.o \
-+ sha1-tprf.o \
-+ sha256-kdf.o \
-+ sha256-prf.o \
-+ sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+
-+# (wolfssl libraries must be built with ./configure --enable-wpas)
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
-+CFLAGS += -DWOLFSSL_DER_LOAD
-+CFLAGS += -DCONFIG_DES
-+
-+LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
-+LIB_OBJS+= \
-+ aes-ctr.o \
-+ aes-eax.o \
-+ aes-encblock.o \
-+ aes-siv.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o \
-+ sha1-prf.o \
-+ sha1-tlsprf.o \
-+ sha1-tprf.o \
-+ sha256-kdf.o \
-+ sha256-prf.o \
-+ sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
-+LIB_OBJS = tls_gnutls.o crypto_gnutls.o
-+LIB_OBJS+= \
-+ aes-cbc.o \
-+ aes-ctr.o \
-+ aes-eax.o \
-+ aes-encblock.o \
-+ aes-omac1.o \
-+ aes-siv.o \
-+ aes-unwrap.o \
-+ aes-wrap.o \
-+ dh_group5.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o \
-+ rc4.o \
-+ sha1-pbkdf2.o \
-+ sha1-prf.o \
-+ fips_prf_internal.o \
-+ sha1-internal.o \
-+ sha1-tlsprf.o \
-+ sha1-tprf.o \
-+ sha256-kdf.o \
-+ sha256-prf.o \
-+ sha256-tlsprf.o
-+
-+else
-+
-+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- CFLAGS += -DCONFIG_INTERNAL_SHA384
-
- LIB_OBJS= \
-@@ -13,7 +124,6 @@ LIB_OBJS= \
- aes-ctr.o \
- aes-eax.o \
- aes-encblock.o \
-- aes-gcm.o \
- aes-internal.o \
- aes-internal-dec.o \
- aes-internal-enc.o \
-@@ -37,6 +147,7 @@ LIB_OBJS= \
- sha1-tlsprf.o \
- sha1-tprf.o \
- sha256.o \
-+ sha256-kdf.o \
- sha256-prf.o \
- sha256-tlsprf.o \
- sha256-internal.o \
-@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
- LIB_OBJS += crypto_internal-rsa.o
- LIB_OBJS += tls_internal.o
- LIB_OBJS += fips_prf_internal.o
-+
-+endif
-+endif
-+endif
-+endif
-+
-+
-+# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
-+LIB_OBJS += aes-gcm.o
-+
- ifndef TEST_FUZZ
- LIB_OBJS += random.o
- endif
---- a/src/crypto/crypto_module_tests.c
-+++ b/src/crypto/crypto_module_tests.c
-@@ -2469,6 +2469,139 @@ static int test_hpke(void)
- }
-
-
-+static int test_ecc(void)
-+{
-+#ifdef CONFIG_ECC
-+#ifndef CONFIG_TLS_INTERNAL
-+#ifndef CONFIG_TLS_GNUTLS
-+#if defined(CONFIG_TLS_MBEDTLS) \
-+ || defined(CONFIG_TLS_OPENSSL) \
-+ || defined(CONFIG_TLS_WOLFSSL)
-+ wpa_printf(MSG_INFO, "Testing ECC");
-+ /* Note: some tests below are valid on supported Short Weierstrass
-+ * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
-+ * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
-+ */
-+#ifdef CONFIG_TLS_MBEDTLS
-+ const int grps[] = {19, 20, 21, 25, 26, 28};
-+#endif
-+#ifdef CONFIG_TLS_OPENSSL
-+ const int grps[] = {19, 20, 21, 26};
-+#endif
-+#ifdef CONFIG_TLS_WOLFSSL
-+ const int grps[] = {19, 20, 21, 26};
-+#endif
-+ uint32_t i;
-+ struct crypto_ec *e = NULL;
-+ struct crypto_ec_point *p = NULL, *q = NULL;
-+ struct crypto_bignum *x = NULL, *y = NULL;
-+#ifdef CONFIG_DPP
-+ u8 bin[4096];
-+#endif
-+ for (i = 0; i < ARRAY_SIZE(grps); ++i) {
-+ e = crypto_ec_init(grps[i]);
-+ if (e == NULL
-+ || crypto_ec_prime_len(e) == 0
-+ || crypto_ec_prime_len_bits(e) == 0
-+ || crypto_ec_order_len(e) == 0
-+ || crypto_ec_get_prime(e) == NULL
-+ || crypto_ec_get_order(e) == NULL
-+ || crypto_ec_get_a(e) == NULL
-+ || crypto_ec_get_b(e) == NULL
-+ || crypto_ec_get_generator(e) == NULL) {
-+ break;
-+ }
-+#ifdef CONFIG_DPP
-+ struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
-+ if (key == NULL)
-+ break;
-+ p = crypto_ec_key_get_public_key(key);
-+ q = crypto_ec_key_get_public_key(key);
-+ crypto_ec_key_deinit(key);
-+ if (p == NULL || q == NULL)
-+ break;
-+ if (!crypto_ec_point_is_on_curve(e, p))
-+ break;
-+
-+ /* inverted point should not match original;
-+ * double-invert should match */
-+ if (crypto_ec_point_invert(e, q) != 0
-+ || crypto_ec_point_cmp(e, p, q) == 0
-+ || crypto_ec_point_invert(e, q) != 0
-+ || crypto_ec_point_cmp(e, p, q) != 0) {
-+ break;
-+ }
-+
-+ /* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
-+ * imbalanced interfaces? */
-+ size_t prime_len = crypto_ec_prime_len(e);
-+ if (prime_len * 2 > sizeof(bin))
-+ break;
-+ if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
-+ break;
-+ struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
-+ if (tmp == NULL)
-+ break;
-+ if (crypto_ec_point_cmp(e, p, tmp) != 0) {
-+ crypto_ec_point_deinit(tmp, 0);
-+ break;
-+ }
-+ crypto_ec_point_deinit(tmp, 0);
-+
-+ x = crypto_bignum_init();
-+ y = crypto_bignum_init_set(bin+prime_len, prime_len);
-+ if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
-+ break;
-+ struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
-+ if (y2 == NULL)
-+ break;
-+ if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
-+ || crypto_bignum_cmp(y, y2) != 0) {
-+ crypto_bignum_deinit(y2, 0);
-+ break;
-+ }
-+ crypto_bignum_deinit(y2, 0);
-+ crypto_bignum_deinit(x, 0);
-+ crypto_bignum_deinit(y, 0);
-+ x = NULL;
-+ y = NULL;
-+
-+ x = crypto_bignum_init();
-+ if (x == NULL)
-+ break;
-+ if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
-+ break;
-+ crypto_bignum_deinit(x, 0);
-+ x = NULL;
-+
-+ crypto_ec_point_deinit(p, 0);
-+ p = NULL;
-+ crypto_ec_point_deinit(q, 0);
-+ q = NULL;
-+#endif /* CONFIG_DPP */
-+ crypto_ec_deinit(e);
-+ e = NULL;
-+ }
-+ if (i != ARRAY_SIZE(grps)) {
-+ crypto_bignum_deinit(x, 0);
-+ crypto_bignum_deinit(y, 0);
-+ crypto_ec_point_deinit(p, 0);
-+ crypto_ec_point_deinit(q, 0);
-+ crypto_ec_deinit(e);
-+ wpa_printf(MSG_INFO,
-+ "ECC test case failed tls_id:%d", grps[i]);
-+ return -1;
-+ }
-+
-+ wpa_printf(MSG_INFO, "ECC test cases passed");
-+#endif
-+#endif /* !CONFIG_TLS_GNUTLS */
-+#endif /* !CONFIG_TLS_INTERNAL */
-+#endif /* CONFIG_ECC */
-+ return 0;
-+}
-+
-+
- static int test_ms_funcs(void)
- {
- #ifndef CONFIG_FIPS
-@@ -2590,6 +2723,7 @@ int crypto_module_tests(void)
- test_fips186_2_prf() ||
- test_extract_expand_hkdf() ||
- test_hpke() ||
-+ test_ecc() ||
- test_ms_funcs())
- ret = -1;
-
---- a/src/tls/Makefile
-+++ b/src/tls/Makefile
-@@ -1,3 +1,10 @@
-+LIB_OBJS= asn1.o
-+
-+ifneq ($(CONFIG_TLS),gnutls)
-+ifneq ($(CONFIG_TLS),mbedtls)
-+ifneq ($(CONFIG_TLS),openssl)
-+ifneq ($(CONFIG_TLS),wolfssl)
-+
- CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
- CFLAGS += -DCONFIG_CRYPTO_INTERNAL
- CFLAGS += -DCONFIG_TLSV11
-@@ -21,5 +28,9 @@ LIB_OBJS= \
- tlsv1_server_read.o \
- tlsv1_server_write.o \
- x509v3.o
-+endif
-+endif
-+endif
-+endif
-
- include ../lib.rules
---- a/tests/Makefile
-+++ b/tests/Makefile
-@@ -1,8 +1,10 @@
--ALL=test-base64 test-md4 test-milenage \
-- test-rsa-sig-ver \
-- test-sha1 \
-- test-https test-https_server \
-- test-sha256 test-aes test-x509v3 test-list test-rc4
-+RUN_TESTS= \
-+ test-list \
-+ test-md4 test-rc4 test-sha1 test-sha256 \
-+ test-milenage test-aes \
-+ test-crypto_module
-+
-+ALL=$(RUN_TESTS) test-base64 test-https test-https_server
-
- include ../src/build.rules
-
-@@ -24,13 +26,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
- CFLAGS += -DCONFIG_IEEE80211R
- CFLAGS += -DCONFIG_TDLS
-
-+# test-crypto_module
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DCONFIG_SHA256
-+CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
- CFLAGS += -I../src
- CFLAGS += -I../src/utils
-
- SLIBS = ../src/utils/libutils.a
-
--DLIBS = ../src/crypto/libcrypto.a \
-- ../src/tls/libtls.a
-+DLIBS = ../src/tls/libtls.a \
-+ ../src/crypto/libcrypto.a
-
- _OBJS_VAR := LLIBS
- include ../src/objs.mk
-@@ -42,12 +58,43 @@ include ../src/objs.mk
- LIBS = $(SLIBS) $(DLIBS)
- LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
-
-+ifeq ($(CONFIG_TLS),mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
-+LLIBS += -lssl -lcrypto
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
-+LLIBS += -lgnutls -lgpg-error -lgcrypt
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
-+LLIBS += -lwolfssl -lm
-+else
-+CFLAGS += -DCONFIG_TLS_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
-+ALL += test-rsa-sig-ver
-+ALL += test-x509v3
-+clean-config_tls_internal:
-+ rm -f test_x509v3_nist.out.*
-+ rm -f test_x509v3_nist2.out.*
-+endif
-+endif
-+endif
-+endif
-+
- # glibc < 2.17 needs -lrt for clock_gettime()
- LLIBS += -lrt
-
- test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
- $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
-
-+test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
-+ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
-+
- test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
- $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
-
-@@ -83,17 +130,11 @@ test-x509v3: $(call BUILDOBJ,test-x509v3
-
-
- run-tests: $(ALL)
-- ./test-aes
-- ./test-list
-- ./test-md4
-- ./test-milenage
-- ./test-rsa-sig-ver
-- ./test-sha1
-- ./test-sha256
-+ @set -ex; for i in $(RUN_TESTS); do ./$$i; done
- @echo
- @echo All tests completed successfully.
-
--clean: common-clean
-+clean: common-clean clean-config_tls_internal
- rm -f *~
-- rm -f test_x509v3_nist.out.*
-- rm -f test_x509v3_nist2.out.*
-+
-+.PHONY: run-tests clean-config_tls_internal
---- a/tests/hwsim/example-hostapd.config
-+++ b/tests/hwsim/example-hostapd.config
-@@ -34,15 +34,7 @@ CONFIG_EAP_TNC=y
- CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
- LIBS += -rdynamic
- CONFIG_EAP_UNAUTH_TLS=y
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), mbedtls)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
- CONFIG_EAP_EKE=y
- CONFIG_PKCS12=y
- CONFIG_RADIUS_SERVER=y
-@@ -89,6 +81,7 @@ CFLAGS += -DCONFIG_RADIUS_TEST
- CONFIG_MODULE_TESTS=y
-
- CONFIG_SUITEB=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
-
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
---- a/tests/hwsim/example-wpa_supplicant.config
-+++ b/tests/hwsim/example-wpa_supplicant.config
-@@ -35,16 +35,7 @@ LIBS += -rdynamic
- CONFIG_EAP_FAST=y
- CONFIG_EAP_TEAP=y
- CONFIG_EAP_IKEV2=y
--
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), mbedtls)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
-
- CONFIG_USIM_SIMULATOR=y
- CONFIG_SIM_SIMULATOR=y
-@@ -137,6 +128,7 @@ CONFIG_TESTING_OPTIONS=y
- CONFIG_MODULE_TESTS=y
-
- CONFIG_SUITEB=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
-
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
---- a/tests/hwsim/test_ap_eap.py
-+++ b/tests/hwsim/test_ap_eap.py
-@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
- res = dev.get_capability("eap")
- if method not in res:
- raise HwsimSkip("EAP method %s not supported in the build" % method)
-+ if method == "FAST" or method == "TEAP":
-+ tls = dev.request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
-
- def check_subject_match_support(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
-
- def check_check_cert_subject_support(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
-
- def check_altsubject_match_support(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
-
- def check_domain_match(dev):
-@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
-
- def check_domain_match_full(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
-
- def check_cert_probe_support(dev):
-@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
- raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
-
- def check_ext_cert_check_support(dev):
-+ if not openssl_imported:
-+ raise HwsimSkip("OpenSSL python method not available")
-+
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
-
- def check_ocsp_support(dev):
-@@ -91,14 +126,18 @@ def check_ocsp_support(dev):
- # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
- #if tls.startswith("wolfSSL"):
- # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-
- def check_pkcs5_v15_support(dev):
- tls = dev.request("GET tls_library")
-- if "BoringSSL" in tls or "GnuTLS" in tls:
-+ if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
- raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
-
- def check_tls13_support(dev):
- tls = dev.request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("TLS v1.3 not supported")
- if "run=OpenSSL 1.1.1" not in tls and "run=OpenSSL 3.0" not in tls and "wolfSSL" not in tls:
- raise HwsimSkip("TLS v1.3 not supported")
-
-@@ -118,11 +157,15 @@ def check_pkcs12_support(dev):
- # raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
- if tls.startswith("wolfSSL"):
- raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-
- def check_dh_dsa_support(dev):
- tls = dev.request("GET tls_library")
- if tls.startswith("internal"):
- raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
-
- def check_ec_support(dev):
- tls = dev.request("GET tls_library")
-@@ -1595,7 +1638,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_ma
- eap_connect(dev[0], hapd, "TTLS", "pap user",
- anonymous_identity="ttls", password="password",
- ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
-- subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
-+ check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
- altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
- eap_reauth(dev[0], "TTLS")
-
-@@ -2830,6 +2873,7 @@ def test_ap_wpa2_eap_tls_neg_domain_matc
-
- def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
- """WPA2-Enterprise negative test - subject mismatch"""
-+ check_subject_match_support(dev[0])
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hostapd.add_ap(apdev[0], params)
- dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-@@ -2890,6 +2934,7 @@ def test_ap_wpa2_eap_tls_neg_subject_mat
-
- def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
- """WPA2-Enterprise negative test - altsubject mismatch"""
-+ check_altsubject_match_support(dev[0])
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hostapd.add_ap(apdev[0], params)
-
-@@ -3430,7 +3475,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apde
- dev[0].request("REMOVE_NETWORK all")
-
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("wolfSSL"):
-+ if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
- tests = [(1, "os_get_random;dh_init")]
- else:
- tests = [(1, "crypto_dh_init;dh_init")]
-@@ -4744,7 +4789,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
- params["private_key"] = "auth_serv/iCA-server/server.key"
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4810,6 +4855,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
- run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
-
- def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
-+ check_ocsp_support(dev[0])
- params = int_eap_server_params()
- params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
- params["server_cert"] = "auth_serv/iCA-server/server.pem"
-@@ -4819,7 +4865,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_
- try:
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4855,7 +4901,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_
- try:
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4905,7 +4951,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
- try:
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4972,7 +5018,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
-
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -5230,6 +5276,7 @@ def test_ap_wpa2_eap_ttls_server_cert_ek
-
- def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
- """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
-+ check_pkcs12_support(dev[0])
- skip_with_fips(dev[0])
- params = int_eap_server_params()
- del params["server_cert"]
-@@ -5242,6 +5289,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(
-
- def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
- """EAP-TTLS and server PKCS#12 file with extra certs"""
-+ check_pkcs12_support(dev[0])
- skip_with_fips(dev[0])
- params = int_eap_server_params()
- del params["server_cert"]
-@@ -5264,6 +5312,7 @@ def test_ap_wpa2_eap_ttls_dh_params_serv
-
- def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
- """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
-+ check_dh_dsa_support(dev[0])
- params = int_eap_server_params()
- params["dh_file"] = "auth_serv/dsaparam.pem"
- hapd = hostapd.add_ap(apdev[0], params)
-@@ -5575,8 +5624,8 @@ def test_ap_wpa2_eap_non_ascii_identity2
- def test_openssl_cipher_suite_config_wpas(dev, apdev):
- """OpenSSL cipher suite configuration on wpa_supplicant"""
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-- raise HwsimSkip("TLS library is not OpenSSL: " + tls)
-+ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+ raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hapd = hostapd.add_ap(apdev[0], params)
- eap_connect(dev[0], hapd, "TTLS", "pap user",
-@@ -5602,14 +5651,14 @@ def test_openssl_cipher_suite_config_wpa
- def test_openssl_cipher_suite_config_hapd(dev, apdev):
- """OpenSSL cipher suite configuration on hostapd"""
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-- raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
-+ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+ raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
- params = int_eap_server_params()
- params['openssl_ciphers'] = "AES256"
- hapd = hostapd.add_ap(apdev[0], params)
- tls = hapd.request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-- raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
-+ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+ raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
- eap_connect(dev[0], hapd, "TTLS", "pap user",
- anonymous_identity="ttls", password="password",
- ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
-@@ -6051,13 +6100,17 @@ def test_ap_wpa2_eap_tls_versions(dev, a
- check_tls_ver(dev[0], hapd,
- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
- "TLSv1.2")
-- elif tls.startswith("internal"):
-+ elif tls.startswith("internal") or tls.startswith("mbed TLS"):
- check_tls_ver(dev[0], hapd,
- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
-- check_tls_ver(dev[1], hapd,
-- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-- check_tls_ver(dev[2], hapd,
-- "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
-+ if tls.startswith("mbed TLS"):
-+ check_tls_ver(dev[2], hapd,
-+ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
-+ else:
-+ check_tls_ver(dev[1], hapd,
-+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-+ check_tls_ver(dev[2], hapd,
-+ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
- if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3.0" in tls:
- check_tls_ver(dev[0], hapd,
- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
-@@ -6079,6 +6132,11 @@ def test_ap_wpa2_eap_tls_versions_server
- tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
- ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
- ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+ #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
- for exp, flags in tests:
- hapd.disable()
- hapd.set("tls_flags", flags)
-@@ -7138,6 +7196,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apde
- def test_eap_tls_ext_cert_check(dev, apdev):
- """EAP-TLS and external server certification validation"""
- # With internal server certificate chain validation
-+ check_ext_cert_check_support(dev[0])
- id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
- identity="tls user",
- ca_cert="auth_serv/ca.pem",
-@@ -7150,6 +7209,7 @@ def test_eap_tls_ext_cert_check(dev, apd
- def test_eap_ttls_ext_cert_check(dev, apdev):
- """EAP-TTLS and external server certification validation"""
- # Without internal server certificate chain validation
-+ check_ext_cert_check_support(dev[0])
- id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
- identity="pap user", anonymous_identity="ttls",
- password="password", phase2="auth=PAP",
-@@ -7160,6 +7220,7 @@ def test_eap_ttls_ext_cert_check(dev, ap
- def test_eap_peap_ext_cert_check(dev, apdev):
- """EAP-PEAP and external server certification validation"""
- # With internal server certificate chain validation
-+ check_ext_cert_check_support(dev[0])
- id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
- identity="user", anonymous_identity="peap",
- ca_cert="auth_serv/ca.pem",
-@@ -7170,6 +7231,7 @@ def test_eap_peap_ext_cert_check(dev, ap
-
- def test_eap_fast_ext_cert_check(dev, apdev):
- """EAP-FAST and external server certification validation"""
-+ check_ext_cert_check_support(dev[0])
- check_eap_capa(dev[0], "FAST")
- # With internal server certificate chain validation
- dev[0].request("SET blob fast_pac_auth_ext ")
-@@ -7184,10 +7246,6 @@ def test_eap_fast_ext_cert_check(dev, ap
- run_ext_cert_check(dev, apdev, id)
-
- def run_ext_cert_check(dev, apdev, net_id):
-- check_ext_cert_check_support(dev[0])
-- if not openssl_imported:
-- raise HwsimSkip("OpenSSL python method not available")
--
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hapd = hostapd.add_ap(apdev[0], params)
-
---- a/tests/hwsim/test_ap_ft.py
-+++ b/tests/hwsim/test_ap_ft.py
-@@ -2474,11 +2474,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
- # This will fail to roam
- dev[0].roam(bssid1, check_bssid=False)
-
-- with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+ with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
- # This will fail to roam
- dev[0].roam(bssid1, check_bssid=False)
-
-- with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+ with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
- # This will fail to roam
- dev[0].roam(bssid1, check_bssid=False)
-
---- a/tests/hwsim/test_authsrv.py
-+++ b/tests/hwsim/test_authsrv.py
-@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
- if "FAIL" not in authsrv.request("ENABLE"):
- raise Exception("ENABLE succeeded during OOM")
-
-- with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
-- if "FAIL" not in authsrv.request("ENABLE"):
-- raise Exception("ENABLE succeeded during OOM")
-+ # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
-+ tls = dev[0].request("GET tls_library")
-+ if not tls.startswith("mbed TLS"):
-+ with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
-+ if "FAIL" not in authsrv.request("ENABLE"):
-+ raise Exception("ENABLE succeeded during OOM")
-
- for count in range(1, 3):
- with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
---- a/tests/hwsim/test_dpp.py
-+++ b/tests/hwsim/test_dpp.py
-@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False
- raise HwsimSkip("DPP not supported")
- if brainpool:
- tls = dev.request("GET tls_library")
-- if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
-+ if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
-+ and not tls.startswith("mbed TLS"):
- raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
- capa = dev.request("GET_CAPABILITY dpp")
- ver = 1
-@@ -3892,6 +3893,9 @@ def test_dpp_proto_auth_req_no_i_proto_k
-
- def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
- """DPP protocol testing - invalid I-proto key in Auth Req"""
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
- run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
-
- def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
-@@ -3987,7 +3991,12 @@ def test_dpp_proto_auth_resp_no_r_proto_
-
- def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
- """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
-- run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
-+ run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
-+ else:
-+ run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
-
- def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
- """DPP protocol testing - no R-nonce in Auth Resp"""
-@@ -4349,11 +4358,17 @@ def test_dpp_proto_pkex_exchange_resp_in
-
- def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
- """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
- run_dpp_proto_pkex_req_missing(dev, 47,
- "Peer bootstrapping key is invalid")
-
- def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
- """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
- run_dpp_proto_pkex_resp_missing(dev, 48,
- "Peer bootstrapping key is invalid")
-
---- a/tests/hwsim/test_erp.py
-+++ b/tests/hwsim/test_erp.py
-@@ -12,7 +12,7 @@ import time
-
- import hostapd
- from utils import *
--from test_ap_eap import int_eap_server_params, check_tls13_support
-+from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
- from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
-
- def test_erp_initiate_reauth_start(dev, apdev):
-@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apd
- params['erp_domain'] = 'example.com'
- params['disable_pmksa_caching'] = '1'
- hapd = hostapd.add_ap(apdev[0], params)
-+ tls = dev[0].request("GET tls_library")
-
- erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
- password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
-@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apd
- password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
- erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
- password="hello")
-- if "FAST" in eap_methods:
-+ if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
- erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
- password="password", ca_cert="auth_serv/ca.pem",
- phase2="auth=GTC",
-@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apd
- password="password")
- erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
- password_hex="0123456789abcdef0123456789abcdef")
-- if "MSCHAPV2" in eap_methods:
-+ if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
- erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
- password="password", ca_cert="auth_serv/ca.pem",
- phase2="auth=MSCHAPV2")
-- erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
-- password="password", ca_cert="auth_serv/ca.pem",
-- phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
-+ if check_eap_capa(dev[0], "TEAP"):
-+ erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
-+ password="password", ca_cert="auth_serv/ca.pem",
-+ phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
- erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
- password_hex="0123456789abcdef0123456789abcdef")
- if "PWD" in eap_methods:
-@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
- dev[0].request("REMOVE_NETWORK all")
- dev[0].wait_disconnected()
-
-- for count in range(1, 6):
-+ for count in range(1, 4):
- dev[0].request("ERP_FLUSH")
- with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
- dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
---- a/tests/hwsim/test_fils.py
-+++ b/tests/hwsim/test_fils.py
-@@ -1422,7 +1422,10 @@ def run_fils_sk_pfs(dev, apdev, group, p
- check_erp_capa(dev[0])
-
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("wolfSSL"):
-+ if tls.startswith("mbed TLS"):
-+ if int(group) == 27:
-+ raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
-+ elif not tls.startswith("wolfSSL"):
- if int(group) in [25]:
- if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
- raise HwsimSkip("EC group not supported")
---- a/tests/hwsim/test_pmksa_cache.py
-+++ b/tests/hwsim/test_pmksa_cache.py
-@@ -955,7 +955,7 @@ def test_pmksa_cache_preauth_wpas_oom(de
- eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
- password_hex="0123456789abcdef0123456789abcdef",
- bssid=apdev[0]['bssid'])
-- for i in range(1, 11):
-+ for i in range(1, 10):
- with alloc_fail(dev[0], i, "rsn_preauth_init"):
- res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
- logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
-@@ -963,7 +963,7 @@ def test_pmksa_cache_preauth_wpas_oom(de
- state = dev[0].request('GET_ALLOC_FAIL')
- if state.startswith('0:'):
- break
-- time.sleep(0.05)
-+ time.sleep(0.10)
-
- def test_pmksa_cache_ctrl(dev, apdev):
- """PMKSA cache control interface operations"""
---- a/tests/hwsim/test_sae.py
-+++ b/tests/hwsim/test_sae.py
-@@ -177,6 +177,11 @@ def test_sae_groups(dev, apdev):
- if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
- logger.info("Add Brainpool EC groups since OpenSSL is new enough")
- sae_groups += [27, 28, 29, 30]
-+ if tls.startswith("mbed TLS"):
-+ # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
-+ # does not have code to derive y from compressed format for those curves
-+ sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
-+ sae_groups += [27, 28, 29, 30]
- heavy_groups = [14, 15, 16]
- suitable_groups = [15, 16, 17, 18, 19, 20, 21]
- groups = [str(g) for g in sae_groups]
-@@ -2193,6 +2198,8 @@ def run_sae_pwe_group(dev, apdev, group)
- logger.info("Add Brainpool EC groups since OpenSSL is new enough")
- elif tls.startswith("wolfSSL"):
- logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
-+ elif tls.startswith("mbed TLS"):
-+ logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
- else:
- raise HwsimSkip("Brainpool curve not supported")
- start_sae_pwe_ap(apdev[0], group, 2)
---- a/tests/hwsim/test_suite_b.py
-+++ b/tests/hwsim/test_suite_b.py
-@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False
- return
- if tls.startswith("wolfSSL"):
- return
-+ if tls.startswith("mbed TLS"):
-+ return
- if not tls.startswith("OpenSSL"):
- raise HwsimSkip("TLS library not supported for Suite B: " + tls)
- supported = False
-@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh
-
- dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
- ieee80211w="2",
-+ openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
- phase1="tls_suiteb=1",
- eap="TLS", identity="tls user",
- ca_cert="auth_serv/rsa3072-ca.pem",
---- a/tests/hwsim/test_wpas_ctrl.py
-+++ b/tests/hwsim/test_wpas_ctrl.py
-@@ -1842,7 +1842,7 @@ def _test_wpas_ctrl_oom(dev):
- tls = dev[0].request("GET tls_library")
- if not tls.startswith("internal"):
- tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
-- 4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
-+ 3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
- for cmd, exp, count, func in tests:
- with alloc_fail(dev[0], count, func):
- res = dev[0].request(cmd)
---- a/tests/hwsim/utils.py
-+++ b/tests/hwsim/utils.py
-@@ -141,7 +141,13 @@ def check_imsi_privacy_support(dev):
-
- def check_tls_tod(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("internal"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
-
- def vht_supported():
---- /dev/null
-+++ b/tests/test-crypto_module.c
-@@ -0,0 +1,16 @@
-+/*
-+ * crypto module tests - test program
-+ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/module_tests.h"
-+#include "crypto/crypto_module_tests.c"
-+
-+int main(int argc, char *argv[])
-+{
-+ return crypto_module_tests();
-+}
---- a/tests/test-https.c
-+++ b/tests/test-https.c
-@@ -75,7 +75,7 @@ static int https_client(int s, const cha
- struct tls_connection *conn;
- struct wpabuf *in, *out, *appl;
- int res = -1;
-- int need_more_data;
-+ int need_more_data = 0;
-
- os_memset(&conf, 0, sizeof(conf));
- conf.event_cb = https_tls_event_cb;
-@@ -93,8 +93,12 @@ static int https_client(int s, const cha
-
- for (;;) {
- appl = NULL;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- out = tls_connection_handshake2(tls, conn, in, &appl,
- &need_more_data);
-+#else
-+ out = tls_connection_handshake(tls, conn, in, &appl);
-+#endif
- wpabuf_free(in);
- in = NULL;
- if (out == NULL) {
-@@ -152,11 +156,15 @@ static int https_client(int s, const cha
-
- wpa_printf(MSG_INFO, "Reading HTTP response");
- for (;;) {
-- int need_more_data;
-+ int need_more_data = 0;
- in = https_recv(s);
- if (in == NULL)
- goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+ out = tls_connection_decrypt(tls, conn, in);
-+#endif
- if (need_more_data)
- wpa_printf(MSG_DEBUG, "HTTP: Need more data");
- wpabuf_free(in);
---- a/tests/test-https_server.c
-+++ b/tests/test-https_server.c
-@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s,
- }
-
-
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- static void https_tls_log_cb(void *ctx, const char *msg)
- {
- wpa_printf(MSG_DEBUG, "TLS: %s", msg);
- }
-+#endif
-
-
- static int https_server(int s)
-@@ -79,7 +81,7 @@ static int https_server(int s)
- void *tls;
- struct tls_connection_params params;
- struct tls_connection *conn;
-- struct wpabuf *in, *out, *appl;
-+ struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
- int res = -1;
-
- os_memset(&conf, 0, sizeof(conf));
-@@ -106,7 +108,9 @@ static int https_server(int s)
- return -1;
- }
-
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
-+#endif
-
- for (;;) {
- in = https_recv(s, 5000);
-@@ -147,12 +151,16 @@ static int https_server(int s)
-
- wpa_printf(MSG_INFO, "Reading HTTP request");
- for (;;) {
-- int need_more_data;
-+ int need_more_data = 0;
-
- in = https_recv(s, 5000);
- if (!in)
- goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+ out = tls_connection_decrypt(tls, conn, in);
-+#endif
- wpabuf_free(in);
- in = NULL;
- if (need_more_data) {
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -1122,6 +1122,7 @@ CFLAGS += -DCONFIG_TLSV12
- endif
-
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- ifdef TLS_FUNCS
- CFLAGS += -DWOLFSSL_DER_LOAD
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -1137,6 +1138,7 @@ LIBS_p += -lwolfssl -lm
- endif
-
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- ifdef TLS_FUNCS
- CFLAGS += -DEAP_TLS_OPENSSL
-@@ -1164,6 +1166,7 @@ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONF
- endif
-
- ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=mbedtls
- endif
-@@ -1183,6 +1186,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -1213,6 +1217,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -1293,6 +1298,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- OBJS_p += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/150-add-NULL-checks-encountered-during-tests-hwsim.patch b/recipes-wifi/hostapd/files/patches-2.10.3/150-add-NULL-checks-encountered-during-tests-hwsim.patch
deleted file mode 100644
index c8c3ff3..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/150-add-NULL-checks-encountered-during-tests-hwsim.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 33afce36c54b0cad38643629ded10ff5d727f077 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Fri, 12 Aug 2022 05:34:47 -0400
-Subject: [PATCH 5/7] add NULL checks (encountered during tests/hwsim)
-
-sae_derive_commit_element_ecc NULL pwe_ecc check
-dpp_gen_keypair() NULL curve check
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- src/common/dpp_crypto.c | 6 ++++++
- src/common/sae.c | 7 +++++++
- 2 files changed, 13 insertions(+)
-
---- a/src/common/dpp_crypto.c
-+++ b/src/common/dpp_crypto.c
-@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec
-
- struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
- {
-+ if (curve == NULL) {
-+ wpa_printf(MSG_DEBUG,
-+ "DPP: %s curve must be initialized", __func__);
-+ return NULL;
-+ }
-+
- struct crypto_ec_key *key;
-
- wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
---- a/src/common/sae.c
-+++ b/src/common/sae.c
-@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
- static int sae_derive_commit_element_ecc(struct sae_data *sae,
- struct crypto_bignum *mask)
- {
-+ if (sae->tmp->pwe_ecc == NULL) {
-+ wpa_printf(MSG_DEBUG,
-+ "SAE: %s sae->tmp->pwe_ecc must be initialized",
-+ __func__);
-+ return -1;
-+ }
-+
- /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
- if (!sae->tmp->own_commit_element_ecc) {
- sae->tmp->own_commit_element_ecc =
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/160-dpp_pkex-EC-point-mul-w-value-prime.patch b/recipes-wifi/hostapd/files/patches-2.10.3/160-dpp_pkex-EC-point-mul-w-value-prime.patch
deleted file mode 100644
index db4fcfe..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/160-dpp_pkex-EC-point-mul-w-value-prime.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 54211caa2e0e5163aefef390daf88a971367a702 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 4 Oct 2022 17:09:24 -0400
-Subject: [PATCH 6/7] dpp_pkex: EC point mul w/ value < prime
-
-crypto_ec_point_mul() with mbedtls requires point
-be multiplied by a multiplicand with value < prime
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- src/common/dpp_crypto.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
---- a/src/common/dpp_crypto.c
-+++ b/src/common/dpp_crypto.c
-@@ -1588,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curv
- Pr = crypto_ec_key_get_public_key(Pr_key);
- Qr = crypto_ec_point_init(ec);
- hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
-- if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
-+ if (!Pr || !Qr || !hash_bn ||
-+ crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
-+ crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
- goto fail;
-
- if (crypto_ec_point_is_at_infinity(ec, Qr)) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch b/recipes-wifi/hostapd/files/patches-2.10.3/170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch
deleted file mode 100644
index b0151b0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch
+++ /dev/null
@@ -1,141 +0,0 @@
-From d4c4ef302f98fd6bce173b8636e7e350d8b44981 Mon Sep 17 00:00:00 2001
-From: P Praneesh <ppranees@codeaurora.org>
-Date: Fri, 19 Mar 2021 12:17:27 +0530
-Subject: [PATCH] hostapd: update cfs0 and cfs1 for 160MHz
-
-As per standard Draft P802.11ax_D8.0,( Table 26-9—Setting
-of the VHT Channel Width and VHT NSS at an HE STA
-transmitting the OM Control subfield ), center frequency of
-160MHz should be published in HT information subset 2 of
-HT information when EXT NSS BW field is enabled.
-
-If the supported number of NSS in 160MHz is at least max NSS
-support, then center_freq_seg0 indicates the center frequency of 80MHz and
-center_freq_seg1 indicates the center frequency of 160MHz.
-
-If the supported number of NSS in 160MHz is less than max NSS
-support, then center_freq_seg0 indicates the center frequency of 80MHz and
-center_freq_seg1 is 0. The center frequency of 160MHz is published in HT
-operation information element instead.
-
-Signed-off-by: P Praneesh <ppranees@codeaurora.org>
----
- hostapd/config_file.c | 2 ++
- src/ap/ieee802_11_ht.c | 7 +++++++
- src/ap/ieee802_11_vht.c | 16 ++++++++++++++++
- src/common/hw_features_common.c | 1 +
- src/common/ieee802_11_defs.h | 1 +
- 5 files changed, 27 insertions(+)
-
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1153,6 +1153,8 @@ static int hostapd_config_vht_capab(stru
- conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
- if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
- conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
-+ if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
-+ conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
- return 0;
- }
- #endif /* CONFIG_IEEE80211AC */
---- a/src/ap/ieee802_11_ht.c
-+++ b/src/ap/ieee802_11_ht.c
-@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct
- u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- struct ieee80211_ht_operation *oper;
-+ le32 vht_capabilities_info;
- u8 *pos = eid;
-+ u8 chwidth;
-
- if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
- is_6ghz_op_class(hapd->iconf->op_class))
-@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hos
- oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
- HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
-
-+ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
-+ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
-+ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+ oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
-+ }
-+
- pos += sizeof(*oper);
-
- return pos;
---- a/src/ap/ieee802_11_vht.c
-+++ b/src/ap/ieee802_11_vht.c
-@@ -25,6 +25,7 @@ u8 * hostapd_eid_vht_capabilities(struct
- struct ieee80211_vht_capabilities *cap;
- struct hostapd_hw_modes *mode = hapd->iface->current_mode;
- u8 *pos = eid;
-+ u8 chwidth;
-
- if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
- return eid;
-@@ -62,6 +63,17 @@ u8 * hostapd_eid_vht_capabilities(struct
- host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
- }
-
-+ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+ if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+ cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
-+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
-+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
-+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
-+ } else {
-+ cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
-+ }
-+
- /* Supported MCS set comes from hw */
- os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
-
-@@ -74,6 +86,7 @@ u8 * hostapd_eid_vht_capabilities(struct
- u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- struct ieee80211_vht_operation *oper;
-+ le32 vht_capabilities_info;
- u8 *pos = eid;
- enum oper_chan_width oper_chwidth =
- hostapd_get_oper_chwidth(hapd->iconf);
-@@ -106,6 +119,7 @@ u8 * hostapd_eid_vht_operation(struct ho
- oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
-
- oper->vht_op_info_chwidth = oper_chwidth;
-+ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
- if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
- /*
- * Convert 160 MHz channel width to new style as interop
-@@ -119,6 +133,9 @@ u8 * hostapd_eid_vht_operation(struct ho
- oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
- else
- oper->vht_op_info_chan_center_freq_seg0_idx += 8;
-+
-+ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+ oper->vht_op_info_chan_center_freq_seg1_idx = 0;
- } else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
- /*
- * Convert 80+80 MHz channel width to new style as interop
---- a/src/common/hw_features_common.c
-+++ b/src/common/hw_features_common.c
-@@ -811,6 +811,7 @@ int ieee80211ac_cap_check(u32 hw, u32 co
- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
- VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
- VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
-+ VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
-
- #undef VHT_CAP_CHECK
- #undef VHT_CAP_CHECK_MAX
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -1349,6 +1349,8 @@ struct ieee80211_ampe_ie {
- #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27))
- #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28))
- #define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT ((u32) BIT(30))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK ((u32) BIT(30) | BIT(31))
-
- #define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1))
- #define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch b/recipes-wifi/hostapd/files/patches-2.10.3/180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch
deleted file mode 100644
index 4929c58..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 14 Sep 2023 10:53:50 +0200
-Subject: [PATCH] driver_nl80211: fix setting QoS map on secondary BSSs
-
-The setting is per-BSS, not per PHY
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -11341,7 +11341,7 @@ static int nl80211_set_qos_map(void *pri
- wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
- qos_map_set, qos_map_set_len);
-
-- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
-+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_QOS_MAP)) ||
- nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
- nlmsg_free(msg);
- return -ENOBUFS;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch b/recipes-wifi/hostapd/files/patches-2.10.3/181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch
deleted file mode 100644
index adfb21f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 14 Sep 2023 11:28:03 +0200
-Subject: [PATCH] driver_nl80211: update drv->ifindex on removing the first
- BSS
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -8867,6 +8867,7 @@ static int wpa_driver_nl80211_if_remove(
- if (drv->first_bss->next) {
- drv->first_bss = drv->first_bss->next;
- drv->ctx = drv->first_bss->ctx;
-+ drv->ifindex = drv->first_bss->ifindex;
- os_free(bss);
- } else {
- wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch b/recipes-wifi/hostapd/files/patches-2.10.3/182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch
deleted file mode 100644
index 395c645..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 18 Sep 2023 16:47:41 +0200
-Subject: [PATCH] nl80211: move nl80211_put_freq_params call outside of
- 802.11ax #ifdef
-
-The relevance of this call is not specific to 802.11ax, so it should be done
-even with CONFIG_IEEE80211AX disabled.
-
-Fixes: b3921db426ea ("nl80211: Add frequency info in start AP command")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -5226,6 +5226,9 @@ static int wpa_driver_nl80211_set_ap(voi
- nla_nest_end(msg, ftm);
- }
-
-+ if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
-+ goto fail;
-+
- #ifdef CONFIG_IEEE80211AX
- if (params->he_spr_ctrl) {
- struct nlattr *spr;
-@@ -5260,9 +5263,6 @@ static int wpa_driver_nl80211_set_ap(voi
- nla_nest_end(msg, spr);
- }
-
-- if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
-- goto fail;
--
- if (params->freq && params->freq->he_enabled) {
- struct nlattr *bss_color;
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch b/recipes-wifi/hostapd/files/patches-2.10.3/183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch
deleted file mode 100644
index fe81318..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 20 Sep 2023 13:41:10 +0200
-Subject: [PATCH] hostapd: cancel channel_list_update_timeout in
- hostapd_cleanup_iface_partial
-
-Fixes a crash when disabling an interface during channel list update
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -569,6 +569,7 @@ static void sta_track_deinit(struct host
- void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
- {
- wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
- #ifdef NEED_AP_MLME
- hostapd_stop_setup_timers(iface);
- #endif /* NEED_AP_MLME */
-@@ -598,7 +599,6 @@ void hostapd_cleanup_iface_partial(struc
- static void hostapd_cleanup_iface(struct hostapd_iface *iface)
- {
- wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-- eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
- eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
- NULL);
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/200-multicall.patch b/recipes-wifi/hostapd/files/patches-2.10.3/200-multicall.patch
deleted file mode 100644
index e3ed00f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/200-multicall.patch
+++ /dev/null
@@ -1,355 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -1,6 +1,7 @@
- ALL=hostapd hostapd_cli
- CONFIG_FILE = .config
-
-+-include $(if $(MULTICALL), ../wpa_supplicant/.config)
- include ../src/build.rules
-
- ifdef LIBS
-@@ -199,7 +200,8 @@ endif
-
- ifdef CONFIG_NO_VLAN
- CFLAGS += -DCONFIG_NO_VLAN
--else
-+endif
-+ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
- OBJS += ../src/ap/vlan_init.o
- OBJS += ../src/ap/vlan_ifconfig.o
- OBJS += ../src/ap/vlan.o
-@@ -357,10 +359,14 @@ CFLAGS += -DCONFIG_MBO
- OBJS += ../src/ap/mbo_ap.o
- endif
-
-+ifndef MULTICALL
-+CFLAGS += -DNO_SUPPLICANT
-+endif
-+
- include ../src/drivers/drivers.mak
--OBJS += $(DRV_AP_OBJS)
--CFLAGS += $(DRV_AP_CFLAGS)
--LDFLAGS += $(DRV_AP_LDFLAGS)
-+OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
-+CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
-+LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
- LIBS += $(DRV_AP_LIBS)
-
- ifdef CONFIG_L2_PACKET
-@@ -1380,6 +1386,12 @@ install: $(addprefix $(DESTDIR)$(BINDIR)
- _OBJS_VAR := OBJS
- include ../src/objs.mk
-
-+hostapd_multi.a: $(BCHECK) $(OBJS)
-+ $(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
-+ @$(E) " CC " $<
-+ @rm -f $@
-+ @$(AR) cr $@ hostapd_multi.o $(OBJS)
-+
- hostapd: $(OBJS)
- $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
- @$(E) " LD " $@
-@@ -1460,6 +1472,12 @@ include ../src/objs.mk
- _OBJS_VAR := SOBJS
- include ../src/objs.mk
-
-+dump_cflags:
-+ @printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- nt_password_hash: $(NOBJS)
- $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
- @$(E) " LD " $@
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.servic
- EXTRA_TARGETS=dynamic_eap_methods
-
- CONFIG_FILE=.config
-+-include $(if $(MULTICALL),../hostapd/.config)
- include ../src/build.rules
-
- ifdef CONFIG_BUILD_PASN_SO
-@@ -382,7 +383,9 @@ endif
- ifdef CONFIG_IBSS_RSN
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_IBSS_RSN
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ibss_rsn.o
- endif
-
-@@ -924,6 +927,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
- CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
- LIBS += -ldl -rdynamic
- endif
-+else
-+ ifdef MULTICALL
-+ OBJS += ../src/eap_common/eap_common.o
-+ endif
- endif
-
- ifdef CONFIG_AP
-@@ -931,9 +938,11 @@ NEED_EAP_COMMON=y
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_AP
- OBJS += ap.o
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
- CFLAGS += -DCONFIG_NO_ACCOUNTING
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ../src/ap/hostapd.o
- OBJS += ../src/ap/wpa_auth_glue.o
- OBJS += ../src/ap/utils.o
-@@ -1022,6 +1031,12 @@ endif
- ifdef CONFIG_HS20
- OBJS += ../src/ap/hs20.o
- endif
-+else
-+ ifdef MULTICALL
-+ OBJS += ../src/eap_server/eap_server.o
-+ OBJS += ../src/eap_server/eap_server_identity.o
-+ OBJS += ../src/eap_server/eap_server_methods.o
-+ endif
- endif
-
- ifdef CONFIG_MBO
-@@ -1030,7 +1045,9 @@ CFLAGS += -DCONFIG_MBO
- endif
-
- ifdef NEED_RSN_AUTHENTICATOR
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
-+endif
- NEED_AES_WRAP=y
- OBJS += ../src/ap/wpa_auth.o
- OBJS += ../src/ap/wpa_auth_ie.o
-@@ -2010,6 +2027,12 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
-
- _OBJS_VAR := OBJS
- include ../src/objs.mk
-+wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
-+ $(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
-+ @$(E) " CC " $<
-+ @rm -f $@
-+ @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
-+
- wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
- $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
- @$(E) " LD " $@
-@@ -2142,6 +2165,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
- $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
- @$(E) " sed" $<
-
-+dump_cflags:
-+ @printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- wpa_supplicant.exe: wpa_supplicant
- mv -f $< $@
- wpa_cli.exe: wpa_cli
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -6667,8 +6667,8 @@ union wpa_event_data {
- * Driver wrapper code should call this function whenever an event is received
- * from the driver.
- */
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data);
-+extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- /**
- * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
-@@ -6680,7 +6680,7 @@ void wpa_supplicant_event(void *ctx, enu
- * Same as wpa_supplicant_event(), but we search for the interface in
- * wpa_global.
- */
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data);
-
- /*
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2184,8 +2184,8 @@ err:
- #endif /* CONFIG_OWE */
-
-
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data)
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data)
- {
- struct hostapd_data *hapd = ctx;
- #ifndef CONFIG_NO_STDOUT_DEBUG
-@@ -2489,7 +2489,7 @@ void wpa_supplicant_event(void *ctx, enu
- }
-
-
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data)
- {
- struct hapd_interfaces *interfaces = ctx;
---- a/wpa_supplicant/wpa_priv.c
-+++ b/wpa_supplicant/wpa_priv.c
-@@ -1039,8 +1039,8 @@ static void wpa_priv_send_ft_response(st
- }
-
-
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data)
-+static void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data)
- {
- struct wpa_priv_interface *iface = ctx;
-
-@@ -1103,7 +1103,7 @@ void wpa_supplicant_event(void *ctx, enu
- }
-
-
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data)
- {
- struct wpa_priv_global *global = ctx;
-@@ -1217,6 +1217,8 @@ int main(int argc, char *argv[])
- if (os_program_init())
- return -1;
-
-+ wpa_supplicant_event = supplicant_event;
-+ wpa_supplicant_event_global = supplicant_event_global;
- wpa_priv_fd_workaround();
-
- os_memset(&global, 0, sizeof(global));
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -5353,8 +5353,8 @@ static void wpas_link_reconfig(struct wp
- }
-
-
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data)
-+void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data)
- {
- struct wpa_supplicant *wpa_s = ctx;
- int resched;
-@@ -6272,7 +6272,7 @@ void wpa_supplicant_event(void *ctx, enu
- }
-
-
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data)
- {
- struct wpa_supplicant *wpa_s;
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -7462,7 +7462,6 @@ struct wpa_interface * wpa_supplicant_ma
- return NULL;
- }
-
--
- /**
- * wpa_supplicant_match_existing - Match existing interfaces
- * @global: Pointer to global data from wpa_supplicant_init()
-@@ -7497,6 +7496,11 @@ static int wpa_supplicant_match_existing
-
- #endif /* CONFIG_MATCH_IFACE */
-
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- /**
- * wpa_supplicant_add_iface - Add a new network interface
-@@ -7753,6 +7757,8 @@ struct wpa_global * wpa_supplicant_init(
- #ifndef CONFIG_NO_WPA_MSG
- wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
- #endif /* CONFIG_NO_WPA_MSG */
-+ wpa_supplicant_event = supplicant_event;
-+ wpa_supplicant_event_global = supplicant_event_global;
-
- if (params->wpa_debug_file_path)
- wpa_debug_open_file(params->wpa_debug_file_path);
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -698,6 +698,11 @@ fail:
- return -1;
- }
-
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- #ifdef CONFIG_WPS
- static int gen_uuid(const char *txt_addr)
-@@ -791,6 +796,8 @@ int main(int argc, char *argv[])
- return -1;
- #endif /* CONFIG_DPP */
-
-+ wpa_supplicant_event = hostapd_wpa_event;
-+ wpa_supplicant_event_global = hostapd_wpa_event_global;
- for (;;) {
- c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
- if (c < 0)
---- a/src/drivers/drivers.c
-+++ b/src/drivers/drivers.c
-@@ -10,6 +10,10 @@
- #include "utils/common.h"
- #include "driver.h"
-
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- const struct wpa_driver_ops *const wpa_drivers[] =
- {
---- a/wpa_supplicant/eapol_test.c
-+++ b/wpa_supplicant/eapol_test.c
-@@ -31,7 +31,12 @@
- #include "ctrl_iface.h"
- #include "pcsc_funcs.h"
- #include "wpas_glue.h"
-+#include "drivers/driver.h"
-
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
-
-@@ -1303,6 +1308,10 @@ static void usage(void)
- "option several times.\n");
- }
-
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- int main(int argc, char *argv[])
- {
-@@ -1323,6 +1332,8 @@ int main(int argc, char *argv[])
- if (os_program_init())
- return -1;
-
-+ wpa_supplicant_event = supplicant_event;
-+ wpa_supplicant_event_global = supplicant_event_global;
- hostapd_logger_register_cb(hostapd_logger_cb);
-
- os_memset(&eapol_test, 0, sizeof(eapol_test));
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/300-noscan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/300-noscan.patch
deleted file mode 100644
index 3b5f432..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/300-noscan.patch
+++ /dev/null
@@ -1,58 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3448,6 +3448,10 @@ static int hostapd_config_fill(struct ho
- if (bss->ocv && !bss->ieee80211w)
- bss->ieee80211w = 1;
- #endif /* CONFIG_OCV */
-+ } else if (os_strcmp(buf, "noscan") == 0) {
-+ conf->noscan = atoi(pos);
-+ } else if (os_strcmp(buf, "ht_coex") == 0) {
-+ conf->no_ht_coex = !atoi(pos);
- } else if (os_strcmp(buf, "ieee80211n") == 0) {
- conf->ieee80211n = atoi(pos);
- } else if (os_strcmp(buf, "ht_capab") == 0) {
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1075,6 +1075,8 @@ struct hostapd_config {
-
- int ht_op_mode_fixed;
- u16 ht_capab;
-+ int noscan;
-+ int no_ht_coex;
- int ieee80211n;
- int secondary_channel;
- int no_pri_sec_switch;
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -546,7 +546,8 @@ static int ieee80211n_check_40mhz(struct
- int ret;
-
- /* Check that HT40 is used and PRI / SEC switch is allowed */
-- if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
-+ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
-+ iface->conf->noscan)
- return 0;
-
- hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
---- a/src/ap/ieee802_11_ht.c
-+++ b/src/ap/ieee802_11_ht.c
-@@ -239,6 +239,9 @@ void hostapd_2040_coex_action(struct hos
- return;
- }
-
-+ if (iface->conf->noscan || iface->conf->no_ht_coex)
-+ return;
-+
- if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
- wpa_printf(MSG_DEBUG,
- "Ignore too short 20/40 BSS Coexistence Management frame");
-@@ -399,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_
- if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
- return;
-
-+ if (iface->conf->noscan || iface->conf->no_ht_coex)
-+ return;
-+
- wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
- " in Association Request", MAC2STR(sta->addr));
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/301-mesh-noscan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/301-mesh-noscan.patch
deleted file mode 100644
index ceb6d0c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/301-mesh-noscan.patch
+++ /dev/null
@@ -1,80 +0,0 @@
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -2600,6 +2600,7 @@ static const struct parse_data ssid_fiel
- #else /* CONFIG_MESH */
- { INT_RANGE(mode, 0, 4) },
- #endif /* CONFIG_MESH */
-+ { INT_RANGE(noscan, 0, 1) },
- { INT_RANGE(proactive_key_caching, 0, 1) },
- { INT_RANGE(disabled, 0, 2) },
- { STR(id_str) },
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -775,6 +775,7 @@ static void wpa_config_write_network(FIL
- #endif /* IEEE8021X_EAPOL */
- INT(mode);
- INT(no_auto_peer);
-+ INT(noscan);
- INT(mesh_fwding);
- INT(frequency);
- INT(enable_edmg);
---- a/wpa_supplicant/mesh.c
-+++ b/wpa_supplicant/mesh.c
-@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(stru
- frequency);
- goto out_free;
- }
-+ if (conf->noscan)
-+ ssid->noscan = 1;
-
- if (ssid->mesh_basic_rates == NULL) {
- /*
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -2710,7 +2710,7 @@ static bool ibss_mesh_can_use_vht(struct
- const struct wpa_ssid *ssid,
- struct hostapd_hw_modes *mode)
- {
-- if (mode->mode != HOSTAPD_MODE_IEEE80211A)
-+ if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
- return false;
-
- if (!drv_supports_vht(wpa_s, ssid))
-@@ -2783,7 +2783,7 @@ static void ibss_mesh_select_40mhz(struc
- int i, res;
- unsigned int j;
- static const int ht40plus[] = {
-- 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 165, 173,
-+ 1, 2, 3, 4, 5, 6, 7, 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 165, 173,
- 184, 192
- };
- int ht40 = -1;
-@@ -3033,7 +3033,7 @@ void ibss_mesh_setup_freq(struct wpa_sup
- int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
- enum hostapd_hw_mode hw_mode;
- struct hostapd_hw_modes *mode = NULL;
-- int i, obss_scan = 1;
-+ int i, obss_scan = !(ssid->noscan);
- u8 channel;
- bool is_6ghz;
- bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
-@@ -3080,6 +3080,8 @@ void ibss_mesh_setup_freq(struct wpa_sup
- freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
- ieee80211_mode);
- freq->channel = channel;
-+ if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
-+ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- /* Setup higher BW only for 5 GHz */
- if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
- ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
---- a/wpa_supplicant/config_ssid.h
-+++ b/wpa_supplicant/config_ssid.h
-@@ -1035,6 +1035,8 @@ struct wpa_ssid {
- */
- int no_auto_peer;
-
-+ int noscan;
-+
- /**
- * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
- *
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/310-rescan_immediately.patch b/recipes-wifi/hostapd/files/patches-2.10.3/310-rescan_immediately.patch
deleted file mode 100644
index 6e0244b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/310-rescan_immediately.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -5769,7 +5769,7 @@ wpa_supplicant_alloc(struct wpa_supplica
- if (wpa_s == NULL)
- return NULL;
- wpa_s->scan_req = INITIAL_SCAN_REQ;
-- wpa_s->scan_interval = 5;
-+ wpa_s->scan_interval = 1;
- wpa_s->new_connection = 1;
- wpa_s->parent = parent ? parent : wpa_s;
- wpa_s->p2pdev = wpa_s->parent;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/320-optional_rfkill.patch b/recipes-wifi/hostapd/files/patches-2.10.3/320-optional_rfkill.patch
deleted file mode 100644
index 0153779..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/320-optional_rfkill.patch
+++ /dev/null
@@ -1,61 +0,0 @@
---- a/src/drivers/drivers.mak
-+++ b/src/drivers/drivers.mak
-@@ -54,7 +54,6 @@ NEED_SME=y
- NEED_AP_MLME=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- NEED_RADIOTAP=y
- NEED_LIBNL=y
- endif
-@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
- CONFIG_WIRELESS_EXTENSION=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- endif
-
- ifdef CONFIG_DRIVER_NDIS
-@@ -137,7 +135,6 @@ endif
- ifdef CONFIG_WIRELESS_EXTENSION
- DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
- DRV_WPA_OBJS += ../src/drivers/driver_wext.o
--NEED_RFKILL=y
- endif
-
- ifdef NEED_NETLINK
-@@ -146,6 +143,7 @@ endif
-
- ifdef NEED_RFKILL
- DRV_OBJS += ../src/drivers/rfkill.o
-+DRV_WPA_CFLAGS += -DCONFIG_RFKILL
- endif
-
- ifdef NEED_RADIOTAP
---- a/src/drivers/rfkill.h
-+++ b/src/drivers/rfkill.h
-@@ -18,8 +18,24 @@ struct rfkill_config {
- void (*unblocked_cb)(void *ctx);
- };
-
-+#ifdef CONFIG_RFKILL
- struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
- void rfkill_deinit(struct rfkill_data *rfkill);
- int rfkill_is_blocked(struct rfkill_data *rfkill);
-+#else
-+static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
-+{
-+ return (void *) 1;
-+}
-+
-+static inline void rfkill_deinit(struct rfkill_data *rfkill)
-+{
-+}
-+
-+static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
-+{
-+ return 0;
-+}
-+#endif
-
- #endif /* RFKILL_H */
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/330-nl80211_fix_set_freq.patch b/recipes-wifi/hostapd/files/patches-2.10.3/330-nl80211_fix_set_freq.patch
deleted file mode 100644
index c11c957..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/330-nl80211_fix_set_freq.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -5407,7 +5407,7 @@ static int nl80211_set_channel(struct i8
- freq->he_enabled, freq->eht_enabled, freq->bandwidth,
- freq->center_freq1, freq->center_freq2);
-
-- msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
-+ msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
- NL80211_CMD_SET_WIPHY);
- if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
- nlmsg_free(msg);
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/341-mesh-ctrl-iface-channel-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/341-mesh-ctrl-iface-channel-switch.patch
deleted file mode 100644
index 8784452..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/341-mesh-ctrl-iface-channel-switch.patch
+++ /dev/null
@@ -1,39 +0,0 @@
---- a/wpa_supplicant/ap.c
-+++ b/wpa_supplicant/ap.c
-@@ -1825,15 +1825,35 @@ int ap_switch_channel(struct wpa_supplic
-
-
- #ifdef CONFIG_CTRL_IFACE
-+
-+static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
-+ struct csa_settings *settings)
-+{
-+#ifdef NEED_AP_MLME
-+ if (!iface || !iface->bss[0])
-+ return 0;
-+
-+ return hostapd_switch_channel(iface->bss[0], settings);
-+#else
-+ return -1;
-+#endif
-+}
-+
-+
- int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
- {
- struct csa_settings settings;
- int ret = hostapd_parse_csa_settings(pos, &settings);
-
-+ if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
-+ !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
-+ return -1;
-+
-+ ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
- if (ret)
- return ret;
-
-- return ap_switch_channel(wpa_s, &settings);
-+ return __ap_ctrl_iface_chanswitch(wpa_s->ifmsh, &settings);
- }
- #endif /* CONFIG_CTRL_IFACE */
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/350-nl80211_del_beacon_bss.patch b/recipes-wifi/hostapd/files/patches-2.10.3/350-nl80211_del_beacon_bss.patch
deleted file mode 100644
index 647ca2c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/350-nl80211_del_beacon_bss.patch
+++ /dev/null
@@ -1,35 +0,0 @@
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -3008,12 +3008,12 @@ static int wpa_driver_nl80211_del_beacon
- return 0;
-
- wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
-- drv->ifindex);
-+ bss->ifindex);
- link->beacon_set = 0;
- link->freq = 0;
-
- nl80211_put_wiphy_data_ap(bss);
-- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
-+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
- if (!msg)
- return -ENOBUFS;
-
-@@ -6100,7 +6100,7 @@ static void nl80211_teardown_ap(struct i
- nl80211_mgmt_unsubscribe(bss, "AP teardown");
-
- nl80211_put_wiphy_data_ap(bss);
-- bss->flink->beacon_set = 0;
-+ wpa_driver_nl80211_del_beacon_all(bss);
- }
-
-
-@@ -8859,8 +8859,6 @@ static int wpa_driver_nl80211_if_remove(
- } else {
- wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
- nl80211_teardown_ap(bss);
-- if (!bss->added_if && !drv->first_bss->next)
-- wpa_driver_nl80211_del_beacon_all(bss);
- nl80211_destroy_bss(bss);
- if (!bss->added_if)
- i802_set_iface_flags(bss, 0);
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/380-disable_ctrl_iface_mib.patch b/recipes-wifi/hostapd/files/patches-2.10.3/380-disable_ctrl_iface_mib.patch
deleted file mode 100644
index f7720fc..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/380-disable_ctrl_iface_mib.patch
+++ /dev/null
@@ -1,239 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -221,6 +221,9 @@ endif
- ifdef CONFIG_NO_CTRL_IFACE
- CFLAGS += -DCONFIG_NO_CTRL_IFACE
- else
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- ifeq ($(CONFIG_CTRL_IFACE), udp)
- CFLAGS += -DCONFIG_CTRL_IFACE_UDP
- else
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3314,6 +3314,7 @@ static int hostapd_ctrl_iface_receive_pr
- reply_size);
- } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
- reply_len = hostapd_drv_status(hapd, reply, reply_size);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strcmp(buf, "MIB") == 0) {
- reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
- if (reply_len >= 0) {
-@@ -3355,6 +3356,7 @@ static int hostapd_ctrl_iface_receive_pr
- } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
- reply_size);
-+#endif
- } else if (os_strcmp(buf, "ATTACH") == 0) {
- if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
- reply_len = -1;
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -983,6 +983,9 @@ ifdef CONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- endif
- ifdef CONFIG_CTRL_IFACE
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- OBJS += ../src/ap/ctrl_iface_ap.o
- endif
-
---- a/wpa_supplicant/ctrl_iface.c
-+++ b/wpa_supplicant/ctrl_iface.c
-@@ -2326,7 +2326,7 @@ static int wpa_supplicant_ctrl_iface_sta
- pos += ret;
- }
-
--#ifdef CONFIG_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
- if (wpa_s->ap_iface) {
- pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
- end - pos,
-@@ -12087,6 +12087,7 @@ char * wpa_supplicant_ctrl_iface_process
- reply_len = -1;
- } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
- wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strcmp(buf, "MIB") == 0) {
- reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
- if (reply_len >= 0) {
-@@ -12099,6 +12100,7 @@ char * wpa_supplicant_ctrl_iface_process
- reply_size - reply_len);
- #endif /* CONFIG_MACSEC */
- }
-+#endif
- } else if (os_strncmp(buf, "STATUS", 6) == 0) {
- reply_len = wpa_supplicant_ctrl_iface_status(
- wpa_s, buf + 6, reply, reply_size);
-@@ -12587,6 +12589,7 @@ char * wpa_supplicant_ctrl_iface_process
- reply_len = wpa_supplicant_ctrl_iface_bss(
- wpa_s, buf + 4, reply, reply_size);
- #ifdef CONFIG_AP
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strcmp(buf, "STA-FIRST") == 0) {
- reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
- } else if (os_strncmp(buf, "STA ", 4) == 0) {
-@@ -12595,12 +12598,15 @@ char * wpa_supplicant_ctrl_iface_process
- } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
- reply_size);
-+#endif
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
- if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
- reply_len = -1;
- } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
- if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
- reply_len = -1;
-+#endif
- } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
- if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
- reply_len = -1;
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -26,6 +26,26 @@
- #include "taxonomy.h"
- #include "wnm_ap.h"
-
-+static const char * hw_mode_str(enum hostapd_hw_mode mode)
-+{
-+ switch (mode) {
-+ case HOSTAPD_MODE_IEEE80211B:
-+ return "b";
-+ case HOSTAPD_MODE_IEEE80211G:
-+ return "g";
-+ case HOSTAPD_MODE_IEEE80211A:
-+ return "a";
-+ case HOSTAPD_MODE_IEEE80211AD:
-+ return "ad";
-+ case HOSTAPD_MODE_IEEE80211ANY:
-+ return "any";
-+ case NUM_HOSTAPD_MODES:
-+ return "invalid";
-+ }
-+ return "unknown";
-+}
-+
-+#ifdef CONFIG_CTRL_IFACE_MIB
-
- static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
- size_t curr_len, const u8 *mcs_set)
-@@ -212,26 +232,6 @@ static const char * timeout_next_str(int
- }
-
-
--static const char * hw_mode_str(enum hostapd_hw_mode mode)
--{
-- switch (mode) {
-- case HOSTAPD_MODE_IEEE80211B:
-- return "b";
-- case HOSTAPD_MODE_IEEE80211G:
-- return "g";
-- case HOSTAPD_MODE_IEEE80211A:
-- return "a";
-- case HOSTAPD_MODE_IEEE80211AD:
-- return "ad";
-- case HOSTAPD_MODE_IEEE80211ANY:
-- return "any";
-- case NUM_HOSTAPD_MODES:
-- return "invalid";
-- }
-- return "unknown";
--}
--
--
- static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
- struct sta_info *sta,
- char *buf, size_t buflen)
-@@ -493,6 +493,7 @@ int hostapd_ctrl_iface_sta_next(struct h
- return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
- }
-
-+#endif
-
- #ifdef CONFIG_P2P_MANAGER
- static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
-@@ -884,12 +885,12 @@ int hostapd_ctrl_iface_status(struct hos
- return len;
- len += ret;
- }
--
-+#ifdef CONFIG_CTRL_IFACE_MIB
- if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
- len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
- mode->mcs_set);
- }
--
-+#endif /* CONFIG_CTRL_IFACE_MIB */
- if (iface->current_rates && iface->num_rates) {
- ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
- if (os_snprintf_error(buflen - len, ret))
---- a/src/ap/ieee802_1x.c
-+++ b/src/ap/ieee802_1x.c
-@@ -2834,6 +2834,7 @@ static const char * bool_txt(bool val)
- return val ? "TRUE" : "FALSE";
- }
-
-+#ifdef CONFIG_CTRL_IFACE_MIB
-
- int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
- {
-@@ -3020,6 +3021,7 @@ int ieee802_1x_get_mib_sta(struct hostap
- return len;
- }
-
-+#endif
-
- #ifdef CONFIG_HS20
- static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -5328,6 +5328,7 @@ static const char * wpa_bool_txt(int val
- return val ? "TRUE" : "FALSE";
- }
-
-+#ifdef CONFIG_CTRL_IFACE_MIB
-
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
-@@ -5480,7 +5481,7 @@ int wpa_get_mib_sta(struct wpa_state_mac
-
- return len;
- }
--
-+#endif
-
- void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
- {
---- a/src/rsn_supp/wpa.c
-+++ b/src/rsn_supp/wpa.c
-@@ -3834,6 +3834,8 @@ static u32 wpa_key_mgmt_suite(struct wpa
- }
-
-
-+#ifdef CONFIG_CTRL_IFACE_MIB
-+
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
- ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
-@@ -3915,6 +3917,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, ch
-
- return (int) len;
- }
-+#endif
- #endif /* CONFIG_CTRL_IFACE */
-
-
---- a/wpa_supplicant/ap.c
-+++ b/wpa_supplicant/ap.c
-@@ -1499,7 +1499,7 @@ int wpas_ap_wps_nfc_report_handover(stru
- #endif /* CONFIG_WPS */
-
-
--#ifdef CONFIG_CTRL_IFACE
-+#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
-
- int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
- char *buf, size_t buflen)
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/381-hostapd_cli_UNKNOWN-COMMAND.patch b/recipes-wifi/hostapd/files/patches-2.10.3/381-hostapd_cli_UNKNOWN-COMMAND.patch
deleted file mode 100644
index e9083f6..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/381-hostapd_cli_UNKNOWN-COMMAND.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -757,7 +757,7 @@ static int wpa_ctrl_command_sta(struct w
- }
-
- buf[len] = '\0';
-- if (memcmp(buf, "FAIL", 4) == 0)
-+ if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
- return -1;
- if (print)
- printf("%s", buf);
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/390-wpa_ie_cap_workaround.patch b/recipes-wifi/hostapd/files/patches-2.10.3/390-wpa_ie_cap_workaround.patch
deleted file mode 100644
index 4592c34..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/390-wpa_ie_cap_workaround.patch
+++ /dev/null
@@ -1,56 +0,0 @@
---- a/src/common/wpa_common.c
-+++ b/src/common/wpa_common.c
-@@ -2841,6 +2841,31 @@ u32 wpa_akm_to_suite(int akm)
- }
-
-
-+static void wpa_fixup_wpa_ie_rsn(u8 *assoc_ie, const u8 *wpa_msg_ie,
-+ size_t rsn_ie_len)
-+{
-+ int pos, count;
-+
-+ pos = sizeof(struct rsn_ie_hdr) + RSN_SELECTOR_LEN;
-+ if (rsn_ie_len < pos + 2)
-+ return;
-+
-+ count = WPA_GET_LE16(wpa_msg_ie + pos);
-+ pos += 2 + count * RSN_SELECTOR_LEN;
-+ if (rsn_ie_len < pos + 2)
-+ return;
-+
-+ count = WPA_GET_LE16(wpa_msg_ie + pos);
-+ pos += 2 + count * RSN_SELECTOR_LEN;
-+ if (rsn_ie_len < pos + 2)
-+ return;
-+
-+ if (!assoc_ie[pos] && !assoc_ie[pos + 1] &&
-+ (wpa_msg_ie[pos] || wpa_msg_ie[pos + 1]))
-+ memcpy(&assoc_ie[pos], &wpa_msg_ie[pos], 2);
-+}
-+
-+
- int wpa_compare_rsn_ie(int ft_initial_assoc,
- const u8 *ie1, size_t ie1len,
- const u8 *ie2, size_t ie2len)
-@@ -2848,8 +2873,19 @@ int wpa_compare_rsn_ie(int ft_initial_as
- if (ie1 == NULL || ie2 == NULL)
- return -1;
-
-- if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
-- return 0; /* identical IEs */
-+ if (ie1len == ie2len) {
-+ u8 *ie_tmp;
-+
-+ if (os_memcmp(ie1, ie2, ie1len) == 0)
-+ return 0; /* identical IEs */
-+
-+ ie_tmp = alloca(ie1len);
-+ memcpy(ie_tmp, ie1, ie1len);
-+ wpa_fixup_wpa_ie_rsn(ie_tmp, ie2, ie1len);
-+
-+ if (os_memcmp(ie_tmp, ie2, ie1len) == 0)
-+ return 0; /* only mismatch in RSN capabilties */
-+ }
-
- #ifdef CONFIG_IEEE80211R
- if (ft_initial_assoc) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/400-wps_single_auth_enc_type.patch b/recipes-wifi/hostapd/files/patches-2.10.3/400-wps_single_auth_enc_type.patch
deleted file mode 100644
index edcd985..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/400-wps_single_auth_enc_type.patch
+++ /dev/null
@@ -1,23 +0,0 @@
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(s
- bss->wpa_pairwise |= WPA_CIPHER_GCMP;
- else
- bss->wpa_pairwise |= WPA_CIPHER_CCMP;
-- }
- #ifndef CONFIG_NO_TKIP
-- if (cred->encr_type & WPS_ENCR_TKIP)
-+ } else if (cred->encr_type & WPS_ENCR_TKIP)
- bss->wpa_pairwise |= WPA_CIPHER_TKIP;
- #endif /* CONFIG_NO_TKIP */
- bss->rsn_pairwise = bss->wpa_pairwise;
-@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data
- WPA_CIPHER_GCMP_256)) {
- wps->encr_types |= WPS_ENCR_AES;
- wps->encr_types_rsn |= WPS_ENCR_AES;
-- }
-- if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
-+ } else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
- #ifdef CONFIG_NO_TKIP
- wpa_printf(MSG_INFO, "WPS: TKIP not supported");
- goto fail;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/410-limit_debug_messages.patch b/recipes-wifi/hostapd/files/patches-2.10.3/410-limit_debug_messages.patch
deleted file mode 100644
index 48a5589..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/410-limit_debug_messages.patch
+++ /dev/null
@@ -1,210 +0,0 @@
---- a/src/utils/wpa_debug.c
-+++ b/src/utils/wpa_debug.c
-@@ -206,7 +206,7 @@ void wpa_debug_close_linux_tracing(void)
- *
- * Note: New line '\n' is added to the end of the text when printing to stdout.
- */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- {
- va_list ap;
-
-@@ -255,7 +255,7 @@ void wpa_printf(int level, const char *f
- }
-
-
--static void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
- size_t len, int show, int only_syslog)
- {
- size_t i;
-@@ -382,19 +382,7 @@ static void _wpa_hexdump(int level, cons
- #endif /* CONFIG_ANDROID_LOG */
- }
-
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
--{
-- _wpa_hexdump(level, title, buf, len, 1, 0);
--}
--
--
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
--{
-- _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
--}
--
--
--static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
- size_t len, int show)
- {
- size_t i, llen;
-@@ -507,20 +495,6 @@ file_done:
- }
-
-
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
-- size_t len)
--{
-- _wpa_hexdump_ascii(level, title, buf, len, 1);
--}
--
--
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
-- size_t len)
--{
-- _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
--}
--
--
- #ifdef CONFIG_DEBUG_FILE
- static char *last_path = NULL;
- #endif /* CONFIG_DEBUG_FILE */
-@@ -644,7 +618,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_
- }
-
-
--void wpa_msg(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...)
- {
- va_list ap;
- char *buf;
-@@ -682,7 +656,7 @@ void wpa_msg(void *ctx, int level, const
- }
-
-
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- {
- va_list ap;
- char *buf;
---- a/src/utils/wpa_debug.h
-+++ b/src/utils/wpa_debug.h
-@@ -51,6 +51,17 @@ void wpa_debug_close_file(void);
- void wpa_debug_setup_stdout(void);
- void wpa_debug_stop_log(void);
-
-+/* internal */
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+ size_t len, int show, int only_syslog);
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+ size_t len, int show);
-+extern int wpa_debug_show_keys;
-+
-+#ifndef CONFIG_MSG_MIN_PRIORITY
-+#define CONFIG_MSG_MIN_PRIORITY 0
-+#endif
-+
- /**
- * wpa_debug_printf_timestamp - Print timestamp for debug output
- *
-@@ -71,9 +82,15 @@ void wpa_debug_print_timestamp(void);
- *
- * Note: New line '\n' is added to the end of the text when printing to stdout.
- */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- PRINTF_FORMAT(2, 3);
-
-+#define wpa_printf(level, ...) \
-+ do { \
-+ if (level >= CONFIG_MSG_MIN_PRIORITY) \
-+ _wpa_printf(level, __VA_ARGS__); \
-+ } while(0)
-+
- /**
- * wpa_hexdump - conditional hex dump
- * @level: priority level (MSG_*) of the message
-@@ -85,7 +102,13 @@ PRINTF_FORMAT(2, 3);
- * output may be directed to stdout, stderr, and/or syslog based on
- * configuration. The contents of buf is printed out has hex dump.
- */
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump(level, title, buf, len, 1, 1);
-+}
-
- static inline void wpa_hexdump_buf(int level, const char *title,
- const struct wpabuf *buf)
-@@ -107,7 +130,13 @@ static inline void wpa_hexdump_buf(int l
- * like wpa_hexdump(), but by default, does not include secret keys (passwords,
- * etc.) in debug output.
- */
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
-+}
-
- static inline void wpa_hexdump_buf_key(int level, const char *title,
- const struct wpabuf *buf)
-@@ -129,8 +158,14 @@ static inline void wpa_hexdump_buf_key(i
- * the hex numbers and ASCII characters (for printable range) are shown. 16
- * bytes per line will be shown.
- */
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
-- size_t len);
-+static inline void wpa_hexdump_ascii(int level, const char *title,
-+ const u8 *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump_ascii(level, title, buf, len, 1);
-+}
-
- /**
- * wpa_hexdump_ascii_key - conditional hex dump, hide keys
-@@ -146,8 +181,14 @@ void wpa_hexdump_ascii(int level, const
- * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
- * default, does not include secret keys (passwords, etc.) in debug output.
- */
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
-- size_t len);
-+static inline void wpa_hexdump_ascii_key(int level, const char *title,
-+ const u8 *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
-+}
-
- /*
- * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
-@@ -184,7 +225,12 @@ void wpa_hexdump_ascii_key(int level, co
- *
- * Note: New line '\n' is added to the end of the text when printing to stdout.
- */
--void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+#define wpa_msg(ctx, level, ...) \
-+ do { \
-+ if (level >= CONFIG_MSG_MIN_PRIORITY) \
-+ _wpa_msg(ctx, level, __VA_ARGS__); \
-+ } while(0)
-
- /**
- * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
-@@ -198,8 +244,13 @@ void wpa_msg(void *ctx, int level, const
- * attached ctrl_iface monitors. In other words, it can be used for frequent
- * events that do not need to be sent to syslog.
- */
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- PRINTF_FORMAT(3, 4);
-+#define wpa_msg_ctrl(ctx, level, ...) \
-+ do { \
-+ if (level >= CONFIG_MSG_MIN_PRIORITY) \
-+ _wpa_msg_ctrl(ctx, level, __VA_ARGS__); \
-+ } while(0)
-
- /**
- * wpa_msg_global - Global printf for ctrl_iface monitors
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/420-indicate-features.patch b/recipes-wifi/hostapd/files/patches-2.10.3/420-indicate-features.patch
deleted file mode 100644
index 07df8e5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/420-indicate-features.patch
+++ /dev/null
@@ -1,63 +0,0 @@
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -31,7 +31,7 @@
- #include "config_file.h"
- #include "eap_register.h"
- #include "ctrl_iface.h"
--
-+#include "build_features.h"
-
- struct hapd_global {
- void **drv_priv;
-@@ -799,7 +799,7 @@ int main(int argc, char *argv[])
- wpa_supplicant_event = hostapd_wpa_event;
- wpa_supplicant_event_global = hostapd_wpa_event_global;
- for (;;) {
-- c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
-+ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
- if (c < 0)
- break;
- switch (c) {
-@@ -836,6 +836,8 @@ int main(int argc, char *argv[])
- break;
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
- case 'v':
-+ if (optarg)
-+ exit(!has_feature(optarg));
- show_version();
- exit(1);
- case 'g':
---- a/wpa_supplicant/main.c
-+++ b/wpa_supplicant/main.c
-@@ -12,6 +12,7 @@
- #endif /* __linux__ */
-
- #include "common.h"
-+#include "build_features.h"
- #include "crypto/crypto.h"
- #include "fst/fst.h"
- #include "wpa_supplicant_i.h"
-@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
-
- for (;;) {
- c = getopt(argc, argv,
-- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
-+ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuv::W");
- if (c < 0)
- break;
- switch (c) {
-@@ -302,8 +303,12 @@ int main(int argc, char *argv[])
- break;
- #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
- case 'v':
-- printf("%s\n", wpa_supplicant_version);
-- exitcode = 0;
-+ if (optarg) {
-+ exitcode = !has_feature(optarg);
-+ } else {
-+ printf("%s\n", wpa_supplicant_version);
-+ exitcode = 0;
-+ }
- goto out;
- case 'W':
- params.wait_for_monitor++;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/430-hostapd_cli_ifdef.patch b/recipes-wifi/hostapd/files/patches-2.10.3/430-hostapd_cli_ifdef.patch
deleted file mode 100644
index a21f0bf..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/430-hostapd_cli_ifdef.patch
+++ /dev/null
@@ -1,56 +0,0 @@
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -401,7 +401,6 @@ static int hostapd_cli_cmd_disassociate(
- }
-
-
--#ifdef CONFIG_TAXONOMY
- static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
- {
-@@ -414,7 +413,6 @@ static int hostapd_cli_cmd_signature(str
- os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
- return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_TAXONOMY */
-
-
- static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
-@@ -431,7 +429,6 @@ static int hostapd_cli_cmd_sa_query(stru
- }
-
-
--#ifdef CONFIG_WPS
- static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
- {
-@@ -657,7 +654,6 @@ static int hostapd_cli_cmd_wps_config(st
- ssid_hex, argv[1]);
- return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_WPS */
-
-
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
-@@ -1610,13 +1606,10 @@ static const struct hostapd_cli_cmd host
- { "disassociate", hostapd_cli_cmd_disassociate,
- hostapd_complete_stations,
- "<addr> = disassociate a station" },
--#ifdef CONFIG_TAXONOMY
- { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
- "<addr> = get taxonomy signature for a station" },
--#endif /* CONFIG_TAXONOMY */
- { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
- "<addr> = send SA Query to a station" },
--#ifdef CONFIG_WPS
- { "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
- "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
- { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
-@@ -1641,7 +1634,6 @@ static const struct hostapd_cli_cmd host
- "<SSID> <auth> <encr> <key> = configure AP" },
- { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
- "= show current WPS status" },
--#endif /* CONFIG_WPS */
- { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
- "= send Disassociation Imminent notification" },
- { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/431-wpa_cli_ifdef.patch b/recipes-wifi/hostapd/files/patches-2.10.3/431-wpa_cli_ifdef.patch
deleted file mode 100644
index 65c31c5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/431-wpa_cli_ifdef.patch
+++ /dev/null
@@ -1,18 +0,0 @@
---- a/wpa_supplicant/wpa_cli.c
-+++ b/wpa_supplicant/wpa_cli.c
-@@ -26,6 +26,15 @@
- #include <cutils/properties.h>
- #endif /* ANDROID */
-
-+#ifndef CONFIG_P2P
-+#define CONFIG_P2P
-+#endif
-+#ifndef CONFIG_AP
-+#define CONFIG_AP
-+#endif
-+#ifndef CONFIG_MESH
-+#define CONFIG_MESH
-+#endif
-
- static const char *const wpa_cli_version =
- "wpa_cli v" VERSION_STR "\n"
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch b/recipes-wifi/hostapd/files/patches-2.10.3/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch
deleted file mode 100644
index dc19553..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From 4bb69d15477e0f2b00e166845341dc933de47c58 Mon Sep 17 00:00:00 2001
-From: Antonio Quartulli <ordex@autistici.org>
-Date: Sun, 3 Jun 2012 18:22:56 +0200
-Subject: [PATCHv2 601/602] wpa_supplicant: add new config params to be used
- with the ibss join command
-
-Signed-hostap: Antonio Quartulli <ordex@autistici.org>
----
- src/drivers/driver.h | 6 +++
- wpa_supplicant/config.c | 96 +++++++++++++++++++++++++++++++++++++++
- wpa_supplicant/config_ssid.h | 6 +++
- wpa_supplicant/wpa_supplicant.c | 23 +++++++---
- 4 files changed, 124 insertions(+), 7 deletions(-)
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -19,6 +19,7 @@
-
- #define WPA_SUPPLICANT_DRIVER_VERSION 4
-
-+#include "ap/sta_info.h"
- #include "common/defs.h"
- #include "common/ieee802_11_defs.h"
- #include "common/wpa_common.h"
-@@ -953,6 +954,9 @@ struct wpa_driver_associate_params {
- * responsible for selecting with which BSS to associate. */
- const u8 *bssid;
-
-+ unsigned char rates[WLAN_SUPP_RATES_MAX];
-+ int mcast_rate;
-+
- /**
- * bssid_hint - BSSID of a proposed AP
- *
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -18,6 +18,7 @@
- #include "eap_peer/eap.h"
- #include "p2p/p2p.h"
- #include "fst/fst.h"
-+#include "ap/sta_info.h"
- #include "config.h"
-
-
-@@ -2389,6 +2390,97 @@ static char * wpa_config_write_mac_value
- #endif /* NO_CONFIG_WRITE */
-
-
-+static int wpa_config_parse_mcast_rate(const struct parse_data *data,
-+ struct wpa_ssid *ssid, int line,
-+ const char *value)
-+{
-+ ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
-+
-+ return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_mcast_rate(const struct parse_data *data,
-+ struct wpa_ssid *ssid)
-+{
-+ char *value;
-+ int res;
-+
-+ if (!ssid->mcast_rate == 0)
-+ return NULL;
-+
-+ value = os_malloc(6); /* longest: 300.0 */
-+ if (value == NULL)
-+ return NULL;
-+ res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
-+ if (res < 0) {
-+ os_free(value);
-+ return NULL;
-+ }
-+ return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
-+static int wpa_config_parse_rates(const struct parse_data *data,
-+ struct wpa_ssid *ssid, int line,
-+ const char *value)
-+{
-+ int i;
-+ char *pos, *r, *sptr, *end;
-+ double rate;
-+
-+ pos = (char *)value;
-+ r = strtok_r(pos, ",", &sptr);
-+ i = 0;
-+ while (pos && i < WLAN_SUPP_RATES_MAX) {
-+ rate = 0.0;
-+ if (r)
-+ rate = strtod(r, &end);
-+ ssid->rates[i] = rate * 2;
-+ if (*end != '\0' || rate * 2 != ssid->rates[i])
-+ return 1;
-+
-+ i++;
-+ r = strtok_r(NULL, ",", &sptr);
-+ }
-+
-+ return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_rates(const struct parse_data *data,
-+ struct wpa_ssid *ssid)
-+{
-+ char *value, *pos;
-+ int res, i;
-+
-+ if (ssid->rates[0] <= 0)
-+ return NULL;
-+
-+ value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
-+ if (value == NULL)
-+ return NULL;
-+ pos = value;
-+ for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
-+ res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
-+ if (res < 0) {
-+ os_free(value);
-+ return NULL;
-+ }
-+ pos += res;
-+ }
-+ res = os_snprintf(pos, 6, "%.1f",
-+ (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
-+ if (res < 0) {
-+ os_free(value);
-+ return NULL;
-+ }
-+
-+ value[6 * WLAN_SUPP_RATES_MAX] = '\0';
-+ return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
- /* Helper macros for network block parser */
-
- #ifdef OFFSET
-@@ -2674,6 +2766,8 @@ static const struct parse_data ssid_fiel
- { INT(ap_max_inactivity) },
- { INT(dtim_period) },
- { INT(beacon_int) },
-+ { FUNC(rates) },
-+ { FUNC(mcast_rate) },
- #ifdef CONFIG_MACSEC
- { INT_RANGE(macsec_policy, 0, 1) },
- { INT_RANGE(macsec_integ_only, 0, 1) },
---- a/wpa_supplicant/config_ssid.h
-+++ b/wpa_supplicant/config_ssid.h
-@@ -10,8 +10,10 @@
- #define CONFIG_SSID_H
-
- #include "common/defs.h"
-+#include "ap/sta_info.h"
- #include "utils/list.h"
- #include "eap_peer/eap_config.h"
-+#include "drivers/nl80211_copy.h"
-
-
- #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
-@@ -879,6 +881,9 @@ struct wpa_ssid {
- */
- void *parent_cred;
-
-+ unsigned char rates[WLAN_SUPP_RATES_MAX];
-+ double mcast_rate;
-+
- #ifdef CONFIG_MACSEC
- /**
- * macsec_policy - Determines the policy for MACsec secure session
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -4177,6 +4177,12 @@ static void wpas_start_assoc_cb(struct w
- params.beacon_int = ssid->beacon_int;
- else
- params.beacon_int = wpa_s->conf->beacon_int;
-+ int i = 0;
-+ while (i < WLAN_SUPP_RATES_MAX) {
-+ params.rates[i] = ssid->rates[i];
-+ i++;
-+ }
-+ params.mcast_rate = ssid->mcast_rate;
- }
-
- if (bss && ssid->enable_edmg)
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/463-add-mcast_rate-to-11s.patch b/recipes-wifi/hostapd/files/patches-2.10.3/463-add-mcast_rate-to-11s.patch
deleted file mode 100644
index daa36c2..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/463-add-mcast_rate-to-11s.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From: Sven Eckelmann <sven.eckelmann@openmesh.com>
-Date: Thu, 11 May 2017 08:21:45 +0200
-Subject: [PATCH] set mcast_rate in mesh mode
-
-The wpa_supplicant code for IBSS allows to set the mcast rate. It is
-recommended to increase this value from 1 or 6 Mbit/s to something higher
-when using a mesh protocol on top which uses the multicast packet loss as
-indicator for the link quality.
-
-This setting was unfortunately not applied for mesh mode. But it would be
-beneficial when wpa_supplicant would behave similar to IBSS mode and set
-this argument during mesh join like authsae already does. At least it is
-helpful for companies/projects which are currently switching to 802.11s
-(without mesh_fwding and with mesh_ttl set to 1) as replacement for IBSS
-because newer drivers seem to support 802.11s but not IBSS anymore.
-
-Signed-off-by: Sven Eckelmann <sven.eckelmann@openmesh.com>
-Tested-by: Simon Wunderlich <simon.wunderlich@openmesh.com>
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -1827,6 +1827,7 @@ struct wpa_driver_mesh_join_params {
- #define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008
- unsigned int flags;
- bool handle_dfs;
-+ int mcast_rate;
- };
-
- struct wpa_driver_set_key_params {
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -11667,6 +11667,18 @@ static int nl80211_put_mesh_id(struct nl
- }
-
-
-+static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
-+{
-+ if (mcast_rate > 0) {
-+ wpa_printf(MSG_DEBUG, " * mcast_rate=%.1f",
-+ (double)mcast_rate / 10);
-+ return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
-+ }
-+
-+ return 0;
-+}
-+
-+
- static int nl80211_put_mesh_config(struct nl_msg *msg,
- struct wpa_driver_mesh_bss_params *params)
- {
-@@ -11728,6 +11740,7 @@ static int nl80211_join_mesh(struct i802
- nl80211_put_basic_rates(msg, params->basic_rates) ||
- nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
- nl80211_put_beacon_int(msg, params->beacon_int) ||
-+ nl80211_put_mcast_rate(msg, params->mcast_rate) ||
- nl80211_put_dtim_period(msg, params->dtim_period))
- goto fail;
-
---- a/wpa_supplicant/mesh.c
-+++ b/wpa_supplicant/mesh.c
-@@ -632,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_
-
- params->meshid = ssid->ssid;
- params->meshid_len = ssid->ssid_len;
-+ params->mcast_rate = ssid->mcast_rate;
- ibss_mesh_setup_freq(wpa_s, ssid, ¶ms->freq);
- wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
- wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/464-fix-mesh-obss-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/464-fix-mesh-obss-check.patch
deleted file mode 100644
index 4d7d85f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/464-fix-mesh-obss-check.patch
+++ /dev/null
@@ -1,13 +0,0 @@
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -3040,6 +3040,10 @@ void ibss_mesh_setup_freq(struct wpa_sup
-
- freq->freq = ssid->frequency;
-
-+ if (ssid->fixed_freq) {
-+ obss_scan = 0;
-+ }
-+
- if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
- struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/465-hostapd-config-support-random-BSS-color.patch b/recipes-wifi/hostapd/files/patches-2.10.3/465-hostapd-config-support-random-BSS-color.patch
deleted file mode 100644
index 7d3d946..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/465-hostapd-config-support-random-BSS-color.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From c9304d3303d563ad6d2619f4e07864ed12f96889 Mon Sep 17 00:00:00 2001
-From: David Bauer <mail@david-bauer.net>
-Date: Sat, 14 May 2022 21:41:03 +0200
-Subject: [PATCH] hostapd: config: support random BSS color
-
-Configure the HE BSS color to a random value in case the config defines
-a BSS color which exceeds the max BSS color (63).
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- hostapd/config_file.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3500,6 +3500,8 @@ static int hostapd_config_fill(struct ho
- } else if (os_strcmp(buf, "he_bss_color") == 0) {
- conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- conf->he_op.he_bss_color_disabled = 0;
-+ if (atoi(pos) > 63)
-+ conf->he_op.he_bss_color = os_random() % 63 + 1;
- } else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
- conf->he_op.he_bss_color_partial = atoi(pos);
- } else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/470-survey_data_fallback.patch b/recipes-wifi/hostapd/files/patches-2.10.3/470-survey_data_fallback.patch
deleted file mode 100644
index 79ab48c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/470-survey_data_fallback.patch
+++ /dev/null
@@ -1,30 +0,0 @@
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -455,17 +455,17 @@ static int acs_get_bw_center_chan(int fr
- static int acs_survey_is_sufficient(struct freq_survey *survey)
- {
- if (!(survey->filled & SURVEY_HAS_NF)) {
-+ survey->nf = -95;
- wpa_printf(MSG_INFO,
- "ACS: Survey for freq %d is missing noise floor",
- survey->freq);
-- return 0;
- }
-
- if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
-+ survey->channel_time = 0;
- wpa_printf(MSG_INFO,
- "ACS: Survey for freq %d is missing channel time",
- survey->freq);
-- return 0;
- }
-
- if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
-@@ -473,7 +473,6 @@ static int acs_survey_is_sufficient(stru
- wpa_printf(MSG_INFO,
- "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
- survey->freq);
-- return 0;
- }
-
- return 1;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/500-lto-jobserver-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/500-lto-jobserver-support.patch
deleted file mode 100644
index 67312c5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/500-lto-jobserver-support.patch
+++ /dev/null
@@ -1,59 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -1396,7 +1396,7 @@ hostapd_multi.a: $(BCHECK) $(OBJS)
- @$(AR) cr $@ hostapd_multi.o $(OBJS)
-
- hostapd: $(OBJS)
-- $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
-+ +$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
- @$(E) " LD " $@
-
- ifdef CONFIG_WPA_TRACE
-@@ -1407,7 +1407,7 @@ _OBJS_VAR := OBJS_c
- include ../src/objs.mk
-
- hostapd_cli: $(OBJS_c)
-- $(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
-+ +$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
- @$(E) " LD " $@
-
- NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -2037,31 +2037,31 @@ wpa_supplicant_multi.a: .config $(BCHECK
- @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
-
- wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
-- $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_t
- include ../src/objs.mk
- eapol_test: $(OBJS_t)
-- $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_t2
- include ../src/objs.mk
- preauth_test: $(OBJS_t2)
-- $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_p
- include ../src/objs.mk
- wpa_passphrase: $(OBJS_p)
-- $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_c
- include ../src/objs.mk
- wpa_cli: $(OBJS_c)
-- $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
-+ +$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
- @$(E) " LD " $@
-
- LIBCTRL += ../src/common/wpa_ctrl.o
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/590-rrm-wnm-statistics.patch b/recipes-wifi/hostapd/files/patches-2.10.3/590-rrm-wnm-statistics.patch
deleted file mode 100644
index 0efa6db..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/590-rrm-wnm-statistics.patch
+++ /dev/null
@@ -1,92 +0,0 @@
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -163,6 +163,21 @@ struct hostapd_sae_commit_queue {
- };
-
- /**
-+ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
-+ */
-+struct hostapd_openwrt_stats {
-+ struct {
-+ u64 neighbor_report_tx;
-+ } rrm;
-+
-+ struct {
-+ u64 bss_transition_query_rx;
-+ u64 bss_transition_request_tx;
-+ u64 bss_transition_response_rx;
-+ } wnm;
-+};
-+
-+/**
- * struct hostapd_data - hostapd per-BSS data structure
- */
- struct hostapd_data {
-@@ -182,6 +197,9 @@ struct hostapd_data {
-
- struct hostapd_data *mld_first_bss;
-
-+ /* OpenWrt specific statistics */
-+ struct hostapd_openwrt_stats openwrt_stats;
-+
- int num_sta; /* number of entries in sta_list */
- struct sta_info *sta_list; /* STA info list head */
- #define STA_HASH_SIZE 256
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -386,6 +386,7 @@ static int ieee802_11_send_bss_trans_mgm
- mgmt->u.action.u.bss_tm_req.validity_interval = 1;
- pos = mgmt->u.action.u.bss_tm_req.variable;
-
-+ 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 "
- "validity_interval=%u",
-@@ -790,10 +791,12 @@ int ieee802_11_rx_wnm_action_ap(struct h
- plen);
- return 0;
- case WNM_BSS_TRANS_MGMT_QUERY:
-+ hapd->openwrt_stats.wnm.bss_transition_query_rx++;
- ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
- plen);
- return 0;
- case WNM_BSS_TRANS_MGMT_RESP:
-+ hapd->openwrt_stats.wnm.bss_transition_response_rx++;
- ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
- plen);
- return 0;
-@@ -840,6 +843,7 @@ int wnm_send_disassoc_imminent(struct ho
-
- pos = mgmt->u.action.u.bss_tm_req.variable;
-
-+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
- MACSTR, disassoc_timer, MAC2STR(sta->addr));
- if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
-@@ -921,6 +925,7 @@ int wnm_send_ess_disassoc_imminent(struc
- return -1;
- }
-
-+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- if (disassoc_timer) {
- /* send disassociation frame after time-out */
- set_disassoc_timer(hapd, sta, disassoc_timer);
-@@ -1001,6 +1006,7 @@ int wnm_send_bss_tm_req(struct hostapd_d
- }
- os_free(buf);
-
-+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- if (disassoc_timer) {
- /* send disassociation frame after time-out */
- set_disassoc_timer(hapd, sta, disassoc_timer);
---- a/src/ap/rrm.c
-+++ b/src/ap/rrm.c
-@@ -269,6 +269,8 @@ static void hostapd_send_nei_report_resp
- }
- }
-
-+ hapd->openwrt_stats.rrm.neighbor_report_tx++;
-+
- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
- wpabuf_head(buf), wpabuf_len(buf));
- wpabuf_free(buf);
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/599-wpa_supplicant-fix-warnings.patch b/recipes-wifi/hostapd/files/patches-2.10.3/599-wpa_supplicant-fix-warnings.patch
deleted file mode 100644
index e70dc61..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/599-wpa_supplicant-fix-warnings.patch
+++ /dev/null
@@ -1,19 +0,0 @@
---- a/wpa_supplicant/wps_supplicant.h
-+++ b/wpa_supplicant/wps_supplicant.h
-@@ -9,6 +9,7 @@
- #ifndef WPS_SUPPLICANT_H
- #define WPS_SUPPLICANT_H
-
-+struct wpa_bss;
- struct wpa_scan_results;
-
- #ifdef CONFIG_WPS
-@@ -16,8 +17,6 @@ struct wpa_scan_results;
- #include "wps/wps.h"
- #include "wps/wps_defs.h"
-
--struct wpa_bss;
--
- struct wps_new_ap_settings {
- const char *ssid_hex;
- const char *auth;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/600-ubus_support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/600-ubus_support.patch
deleted file mode 100644
index a6ccf83..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/600-ubus_support.patch
+++ /dev/null
@@ -1,748 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -166,6 +166,12 @@ OBJS += ../src/common/hw_features_common
-
- OBJS += ../src/eapol_auth/eapol_auth_sm.o
-
-+ifdef CONFIG_UBUS
-+CFLAGS += -DUBUS_SUPPORT
-+OBJS += ../src/utils/uloop.o
-+OBJS += ../src/ap/ubus.o
-+LIBS += -lubox -lubus
-+endif
-
- ifdef CONFIG_CODE_COVERAGE
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -18,6 +18,7 @@
- #include "utils/list.h"
- #include "ap_config.h"
- #include "drivers/driver.h"
-+#include "ubus.h"
-
- #define OCE_STA_CFON_ENABLED(hapd) \
- ((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -184,6 +185,7 @@ struct hostapd_data {
- struct hostapd_iface *iface;
- struct hostapd_config *iconf;
- struct hostapd_bss_config *conf;
-+ struct hostapd_ubus_bss ubus;
- int interface_added; /* virtual interface added for this BSS */
- unsigned int started:1;
- unsigned int disabled:1;
-@@ -695,6 +697,7 @@ hostapd_alloc_bss_data(struct hostapd_if
- struct hostapd_bss_config *bss);
- int hostapd_setup_interface(struct hostapd_iface *iface);
- int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
-+void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
- void hostapd_interface_deinit(struct hostapd_iface *iface);
- void hostapd_interface_free(struct hostapd_iface *iface);
- struct hostapd_iface * hostapd_alloc_iface(void);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -435,6 +435,7 @@ void hostapd_free_hapd_data(struct hosta
- hapd->beacon_set_done = 0;
-
- wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
-+ hostapd_ubus_free_bss(hapd);
- accounting_deinit(hapd);
- hostapd_deinit_wpa(hapd);
- vlan_deinit(hapd);
-@@ -1187,6 +1188,8 @@ static int hostapd_start_beacon(struct h
- if (hapd->driver && hapd->driver->set_operstate)
- hapd->driver->set_operstate(hapd->drv_priv, 1);
-
-+ hostapd_ubus_add_bss(hapd);
-+
- return 0;
- }
-
-@@ -2275,6 +2278,7 @@ static int hostapd_setup_interface_compl
- if (err)
- goto fail;
-
-+ hostapd_ubus_add_iface(iface);
- wpa_printf(MSG_DEBUG, "Completing interface initialization");
- if (iface->freq) {
- #ifdef NEED_AP_MLME
-@@ -2494,6 +2498,7 @@ dfs_offload:
-
- fail:
- wpa_printf(MSG_ERROR, "Interface initialization failed");
-+ hostapd_ubus_free_iface(iface);
-
- if (iface->is_no_ir) {
- hostapd_set_state(iface, HAPD_IFACE_NO_IR);
-@@ -2984,6 +2989,7 @@ void hostapd_interface_deinit_free(struc
- (unsigned int) iface->conf->num_bss);
- driver = iface->bss[0]->driver;
- drv_priv = iface->bss[0]->drv_priv;
-+ hostapd_ubus_free_iface(iface);
- hostapd_interface_deinit(iface);
- wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
- __func__, driver, drv_priv);
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -2786,7 +2786,7 @@ static void handle_auth(struct hostapd_d
- u16 auth_alg, auth_transaction, status_code;
- u16 resp = WLAN_STATUS_SUCCESS;
- struct sta_info *sta = NULL;
-- int res, reply_res;
-+ int res, reply_res, ubus_resp;
- u16 fc;
- const u8 *challenge = NULL;
- u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
-@@ -2795,6 +2795,11 @@ static void handle_auth(struct hostapd_d
- struct radius_sta rad_info;
- const u8 *dst, *sa, *bssid;
- bool mld_sta = false;
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_AUTH_REQ,
-+ .mgmt_frame = mgmt,
-+ .ssi_signal = rssi,
-+ };
-
- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
-@@ -2986,6 +2991,13 @@ static void handle_auth(struct hostapd_d
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto fail;
- }
-+ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+ if (ubus_resp) {
-+ wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
-+ MAC2STR(mgmt->sa));
-+ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+ goto fail;
-+ }
- if (res == HOSTAPD_ACL_PENDING)
- return;
-
-@@ -5161,7 +5173,7 @@ static void handle_assoc(struct hostapd_
- int resp = WLAN_STATUS_SUCCESS;
- u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
- const u8 *pos;
-- int left, i;
-+ int left, i, ubus_resp;
- struct sta_info *sta;
- u8 *tmp = NULL;
- #ifdef CONFIG_FILS
-@@ -5374,6 +5386,11 @@ static void handle_assoc(struct hostapd_
- left = res;
- }
- #endif /* CONFIG_FILS */
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_ASSOC_REQ,
-+ .mgmt_frame = mgmt,
-+ .ssi_signal = rssi,
-+ };
-
- /* followed by SSID and Supported rates; and HT capabilities if 802.11n
- * is used */
-@@ -5472,6 +5489,13 @@ static void handle_assoc(struct hostapd_
- }
- #endif /* CONFIG_FILS */
-
-+ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+ if (ubus_resp) {
-+ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+ MAC2STR(mgmt->sa));
-+ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+ goto fail;
-+ }
- fail:
-
- /*
-@@ -5753,6 +5777,7 @@ static void handle_disassoc(struct hosta
- (unsigned long) len);
- return;
- }
-+ hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
-
- sta = ap_get_sta(hapd, mgmt->sa);
- if (!sta) {
-@@ -5784,6 +5809,8 @@ static void handle_deauth(struct hostapd
- /* Clear the PTKSA cache entries for PASN */
- ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
-
-+ hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
-+
- sta = ap_get_sta(hapd, mgmt->sa);
- if (!sta) {
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1036,6 +1036,12 @@ void handle_probe_req(struct hostapd_dat
- u16 csa_offs[2];
- size_t csa_offs_len;
- struct radius_sta rad_info;
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_PROBE_REQ,
-+ .mgmt_frame = mgmt,
-+ .ssi_signal = ssi_signal,
-+ .elems = &elems,
-+ };
-
- if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
- ssi_signal < hapd->iconf->rssi_ignore_probe_request)
-@@ -1222,6 +1228,12 @@ void handle_probe_req(struct hostapd_dat
- }
- #endif /* CONFIG_P2P */
-
-+ if (hostapd_ubus_handle_event(hapd, &req)) {
-+ wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
-+ MAC2STR(mgmt->sa));
-+ return;
-+ }
-+
- /* TODO: verify that supp_rates contains at least one matching rate
- * with AP configuration */
-
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -260,6 +260,10 @@ int hostapd_notif_assoc(struct hostapd_d
- u16 reason = WLAN_REASON_UNSPECIFIED;
- int status = WLAN_STATUS_SUCCESS;
- const u8 *p2p_dev_addr = NULL;
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_ASSOC_REQ,
-+ .addr = addr,
-+ };
-
- if (addr == NULL) {
- /*
-@@ -396,6 +400,12 @@ int hostapd_notif_assoc(struct hostapd_d
- goto fail;
- }
-
-+ if (hostapd_ubus_handle_event(hapd, &req)) {
-+ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+ MAC2STR(req.addr));
-+ goto fail;
-+ }
-+
- #ifdef CONFIG_P2P
- if (elems.p2p) {
- wpabuf_free(sta->p2p_ie);
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -471,6 +471,7 @@ void ap_handle_timer(void *eloop_ctx, vo
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "deauthenticated due to "
- "local deauth request");
-+ hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
- ap_free_sta(hapd, sta);
- return;
- }
-@@ -626,6 +627,7 @@ skip_poll:
- mlme_deauthenticate_indication(
- hapd, sta,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-+ hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
- ap_free_sta(hapd, sta);
- break;
- }
-@@ -1344,15 +1346,28 @@ void ap_sta_set_authorized(struct hostap
- sta->addr, authorized, dev_addr);
-
- if (authorized) {
-+ static const char * const auth_algs[] = {
-+ [WLAN_AUTH_OPEN] = "open",
-+ [WLAN_AUTH_SHARED_KEY] = "shared",
-+ [WLAN_AUTH_FT] = "ft",
-+ [WLAN_AUTH_SAE] = "sae",
-+ [WLAN_AUTH_FILS_SK] = "fils-sk",
-+ [WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
-+ [WLAN_AUTH_FILS_PK] = "fils-pk",
-+ [WLAN_AUTH_PASN] = "pasn",
-+ };
-+ const char *auth_alg = NULL;
- const u8 *dpp_pkhash;
- const char *keyid;
- char dpp_pkhash_buf[100];
- char keyid_buf[100];
- char ip_addr[100];
-+ char alg_buf[100];
-
- dpp_pkhash_buf[0] = '\0';
- keyid_buf[0] = '\0';
- ip_addr[0] = '\0';
-+ alg_buf[0] = '\0';
- #ifdef CONFIG_P2P
- if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
- os_snprintf(ip_addr, sizeof(ip_addr),
-@@ -1362,6 +1377,13 @@ void ap_sta_set_authorized(struct hostap
- }
- #endif /* CONFIG_P2P */
-
-+ if (sta->auth_alg < ARRAY_SIZE(auth_algs))
-+ auth_alg = auth_algs[sta->auth_alg];
-+
-+ if (auth_alg)
-+ os_snprintf(alg_buf, sizeof(alg_buf),
-+ " auth_alg=%s", auth_alg);
-+
- keyid = ap_sta_wpa_get_keyid(hapd, sta);
- if (keyid) {
- os_snprintf(keyid_buf, sizeof(keyid_buf),
-@@ -1380,17 +1402,19 @@ void ap_sta_set_authorized(struct hostap
- dpp_pkhash, SHA256_MAC_LEN);
- }
-
-- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
-- buf, ip_addr, keyid_buf, dpp_pkhash_buf);
-+ hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
-+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
-+ buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
-
- if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx)
- wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-- AP_STA_CONNECTED "%s%s%s%s",
-+ AP_STA_CONNECTED "%s%s%s%s%s",
- buf, ip_addr, keyid_buf,
-- dpp_pkhash_buf);
-+ dpp_pkhash_buf, alg_buf);
- } else {
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
-+ hostapd_ubus_notify(hapd, "disassoc", sta->addr);
-
- if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx)
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -269,6 +269,7 @@ static void hostapd_wpa_auth_psk_failure
- struct hostapd_data *hapd = ctx;
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
- MAC2STR(addr));
-+ hostapd_ubus_notify(hapd, "key-mismatch", addr);
- }
-
-
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -192,6 +192,13 @@ ifdef CONFIG_EAPOL_TEST
- CFLAGS += -Werror -DEAPOL_TEST
- endif
-
-+ifdef CONFIG_UBUS
-+CFLAGS += -DUBUS_SUPPORT
-+OBJS += ubus.o
-+OBJS += ../src/utils/uloop.o
-+LIBS += -lubox -lubus
-+endif
-+
- ifdef CONFIG_CODE_COVERAGE
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage
- LIBS += -lgcov
-@@ -987,6 +994,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
- CFLAGS += -DCONFIG_CTRL_IFACE_MIB
- endif
- OBJS += ../src/ap/ctrl_iface_ap.o
-+ifdef CONFIG_UBUS
-+OBJS += ../src/ap/ubus.o
-+endif
- endif
-
- CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -7595,6 +7595,8 @@ struct wpa_supplicant * wpa_supplicant_a
- }
- #endif /* CONFIG_P2P */
-
-+ wpas_ubus_add_bss(wpa_s);
-+
- return wpa_s;
- }
-
-@@ -7621,6 +7623,8 @@ int wpa_supplicant_remove_iface(struct w
- struct wpa_supplicant *parent = wpa_s->parent;
- #endif /* CONFIG_MESH */
-
-+ wpas_ubus_free_bss(wpa_s);
-+
- /* Remove interface from the global list of interfaces */
- prev = global->ifaces;
- if (prev == wpa_s) {
-@@ -7967,8 +7971,12 @@ int wpa_supplicant_run(struct wpa_global
- eloop_register_signal_terminate(wpa_supplicant_terminate, global);
- eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
-
-+ wpas_ubus_add(global);
-+
- eloop_run();
-
-+ wpas_ubus_free(global);
-+
- return 0;
- }
-
---- a/wpa_supplicant/wpa_supplicant_i.h
-+++ b/wpa_supplicant/wpa_supplicant_i.h
-@@ -21,6 +21,7 @@
- #include "config_ssid.h"
- #include "wmm_ac.h"
- #include "pasn/pasn_common.h"
-+#include "ubus.h"
-
- extern const char *const wpa_supplicant_version;
- extern const char *const wpa_supplicant_license;
-@@ -319,6 +320,8 @@ struct wpa_global {
- #endif /* CONFIG_WIFI_DISPLAY */
-
- struct psk_list_entry *add_psk; /* From group formation */
-+
-+ struct ubus_object ubus_global;
- };
-
-
-@@ -685,6 +688,7 @@ struct wpa_supplicant {
- unsigned char own_addr[ETH_ALEN];
- unsigned char perm_addr[ETH_ALEN];
- char ifname[100];
-+ struct wpas_ubus_bss ubus;
- #ifdef CONFIG_MATCH_IFACE
- int matched;
- #endif /* CONFIG_MATCH_IFACE */
---- a/wpa_supplicant/wps_supplicant.c
-+++ b/wpa_supplicant/wps_supplicant.c
-@@ -33,6 +33,7 @@
- #include "p2p/p2p.h"
- #include "p2p_supplicant.h"
- #include "wps_supplicant.h"
-+#include "ubus.h"
-
-
- #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
-@@ -402,6 +403,8 @@ static int wpa_supplicant_wps_cred(void
- wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
- cred->cred_attr, cred->cred_attr_len);
-
-+ wpas_ubus_notify(wpa_s, cred);
-+
- if (wpa_s->conf->wps_cred_processing == 1)
- return 0;
-
---- a/wpa_supplicant/main.c
-+++ b/wpa_supplicant/main.c
-@@ -203,7 +203,7 @@ int main(int argc, char *argv[])
-
- for (;;) {
- c = getopt(argc, argv,
-- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuv::W");
-+ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
- if (c < 0)
- break;
- switch (c) {
-@@ -268,6 +268,9 @@ int main(int argc, char *argv[])
- params.conf_p2p_dev = optarg;
- break;
- #endif /* CONFIG_P2P */
-+ case 'n':
-+ iface_count = 0;
-+ break;
- case 'o':
- params.override_driver = optarg;
- break;
---- a/src/ap/rrm.c
-+++ b/src/ap/rrm.c
-@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report
- return;
- wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
- MAC2STR(addr), token, rep_mode, report);
-+ if (len < sizeof(struct rrm_measurement_beacon_report))
-+ return;
-+ hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
- }
-
-
-@@ -352,6 +355,9 @@ void hostapd_handle_radio_measurement(st
- mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
-
- switch (mgmt->u.action.u.rrm.action) {
-+ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
-+ hostapd_ubus_handle_link_measurement(hapd, buf, len);
-+ break;
- case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
- hostapd_handle_radio_msmt_report(hapd, buf, len);
- break;
---- a/src/ap/vlan_init.c
-+++ b/src/ap/vlan_init.c
-@@ -22,6 +22,7 @@
- static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- int existsok)
- {
-+ bool vlan_exists = iface_exists(vlan->ifname);
- int ret;
- #ifdef CONFIG_WEP
- int i;
-@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_da
- }
- #endif /* CONFIG_WEP */
-
-- if (!iface_exists(vlan->ifname))
-+ if (!vlan_exists)
- ret = hostapd_vlan_if_add(hapd, vlan->ifname);
- else if (!existsok)
- return -1;
-@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_da
- if (hapd->wpa_auth)
- ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
-
-+ if (!ret && !vlan_exists)
-+ hostapd_ubus_add_vlan(hapd, vlan);
-+
- if (ret == 0)
- return ret;
-
-@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *
- "WPA deinitialization for VLAN %d failed (%d)",
- vlan->vlan_id, ret);
-
-+ hostapd_ubus_remove_vlan(hapd, vlan);
-+
- return hostapd_vlan_if_remove(hapd, vlan->ifname);
- }
-
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1216,6 +1216,8 @@ int hostapd_dfs_pre_cac_expired(struct h
- "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
- freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
-
-+ hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
-+
- /* Proceed only if DFS is not offloaded to the driver */
- if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
- return 0;
---- a/src/ap/airtime_policy.c
-+++ b/src/ap/airtime_policy.c
-@@ -112,8 +112,14 @@ static void set_sta_weights(struct hosta
- {
- struct sta_info *sta;
-
-- for (sta = hapd->sta_list; sta; sta = sta->next)
-- sta_set_airtime_weight(hapd, sta, weight);
-+ for (sta = hapd->sta_list; sta; sta = sta->next) {
-+ unsigned int sta_weight = weight;
-+
-+ if (sta->dyn_airtime_weight)
-+ sta_weight = (weight * sta->dyn_airtime_weight) / 256;
-+
-+ sta_set_airtime_weight(hapd, sta, sta_weight);
-+ }
- }
-
-
-@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostap
- unsigned int weight;
-
- if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
-- weight = get_weight_for_sta(hapd, sta->addr);
-+ if (sta->dyn_airtime_weight)
-+ weight = sta->dyn_airtime_weight;
-+ else
-+ weight = get_weight_for_sta(hapd, sta->addr);
- if (weight)
- return sta_set_airtime_weight(hapd, sta, weight);
- }
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -322,6 +322,7 @@ struct sta_info {
- #endif /* CONFIG_TESTING_OPTIONS */
- #ifdef CONFIG_AIRTIME_POLICY
- unsigned int airtime_weight;
-+ unsigned int dyn_airtime_weight;
- struct os_reltime backlogged_until;
- #endif /* CONFIG_AIRTIME_POLICY */
-
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -455,7 +455,8 @@ static void ieee802_11_rx_bss_trans_mgmt
- MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
- os_free(hex);
-
-- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
-+ if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
-+ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
- }
-
-
-@@ -477,7 +478,7 @@ static void ieee802_11_rx_bss_trans_mgmt
- size_t len)
- {
- u8 dialog_token, status_code, bss_termination_delay;
-- const u8 *pos, *end;
-+ const u8 *pos, *end, *target_bssid = NULL;
- int enabled = hapd->conf->bss_transition;
- struct sta_info *sta;
-
-@@ -524,6 +525,7 @@ static void ieee802_11_rx_bss_trans_mgmt
- wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
- return;
- }
-+ target_bssid = pos;
- sta->agreed_to_steer = 1;
- eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
- eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
-@@ -543,6 +545,10 @@ static void ieee802_11_rx_bss_trans_mgmt
- MAC2STR(addr), status_code, bss_termination_delay);
- }
-
-+ hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
-+ status_code, bss_termination_delay,
-+ target_bssid, pos, end - pos);
-+
- wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
- pos, end - pos);
- }
---- a/src/utils/eloop.c
-+++ b/src/utils/eloop.c
-@@ -77,6 +77,9 @@ struct eloop_sock_table {
- struct eloop_data {
- int max_sock;
-
-+ eloop_timeout_poll_handler timeout_poll_cb;
-+ eloop_poll_handler poll_cb;
-+
- size_t count; /* sum of all table counts */
- #ifdef CONFIG_ELOOP_POLL
- size_t max_pollfd_map; /* number of pollfds_map currently allocated */
-@@ -1121,6 +1124,12 @@ void eloop_run(void)
- os_reltime_sub(&timeout->time, &now, &tv);
- else
- tv.sec = tv.usec = 0;
-+ }
-+
-+ if (eloop.timeout_poll_cb && eloop.timeout_poll_cb(&tv, !!timeout))
-+ timeout = (void *)1;
-+
-+ if (timeout) {
- #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
- timeout_ms = tv.sec * 1000 + tv.usec / 1000;
- #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
-@@ -1190,7 +1199,8 @@ void eloop_run(void)
- eloop.exceptions.changed = 0;
-
- eloop_process_pending_signals();
--
-+ if (eloop.poll_cb)
-+ eloop.poll_cb();
-
- /* check if some registered timeouts have occurred */
- timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
-@@ -1252,6 +1262,14 @@ out:
- return;
- }
-
-+int eloop_register_cb(eloop_poll_handler poll_cb,
-+ eloop_timeout_poll_handler timeout_cb)
-+{
-+ eloop.poll_cb = poll_cb;
-+ eloop.timeout_poll_cb = timeout_cb;
-+
-+ return 0;
-+}
-
- void eloop_terminate(void)
- {
---- a/src/utils/eloop.h
-+++ b/src/utils/eloop.h
-@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(vo
- */
- typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
-
-+typedef bool (*eloop_timeout_poll_handler)(struct os_reltime *tv, bool tv_set);
-+typedef void (*eloop_poll_handler)(void);
-+
- /**
- * eloop_init() - Initialize global event loop data
- * Returns: 0 on success, -1 on failure
-@@ -73,6 +76,9 @@ typedef void (*eloop_signal_handler)(int
- */
- int eloop_init(void);
-
-+int eloop_register_cb(eloop_poll_handler poll_cb,
-+ eloop_timeout_poll_handler timeout_cb);
-+
- /**
- * eloop_register_read_sock - Register handler for read events
- * @sock: File descriptor number for the socket
-@@ -320,6 +326,8 @@ int eloop_register_signal_reconfig(eloop
- */
- int eloop_sock_requeue(void);
-
-+void eloop_add_uloop(void);
-+
- /**
- * eloop_run - Start the event loop
- *
---- /dev/null
-+++ b/src/utils/uloop.c
-@@ -0,0 +1,64 @@
-+#include <libubox/uloop.h>
-+#include "includes.h"
-+#include "common.h"
-+#include "eloop.h"
-+
-+static void eloop_uloop_event_cb(int sock, void *eloop_ctx, void *sock_ctx)
-+{
-+}
-+
-+static void eloop_uloop_fd_cb(struct uloop_fd *fd, unsigned int events)
-+{
-+ unsigned int changed = events ^ fd->flags;
-+
-+ if (changed & ULOOP_READ) {
-+ if (events & ULOOP_READ)
-+ eloop_register_sock(fd->fd, EVENT_TYPE_READ, eloop_uloop_event_cb, fd, fd);
-+ else
-+ eloop_unregister_sock(fd->fd, EVENT_TYPE_READ);
-+ }
-+
-+ if (changed & ULOOP_WRITE) {
-+ if (events & ULOOP_WRITE)
-+ eloop_register_sock(fd->fd, EVENT_TYPE_WRITE, eloop_uloop_event_cb, fd, fd);
-+ else
-+ eloop_unregister_sock(fd->fd, EVENT_TYPE_WRITE);
-+ }
-+}
-+
-+static bool uloop_timeout_poll_handler(struct os_reltime *tv, bool tv_set)
-+{
-+ struct os_reltime tv_uloop;
-+ int timeout_ms = uloop_get_next_timeout();
-+
-+ if (timeout_ms < 0)
-+ return false;
-+
-+ tv_uloop.sec = timeout_ms / 1000;
-+ tv_uloop.usec = (timeout_ms % 1000) * 1000;
-+
-+ if (!tv_set || os_reltime_before(&tv_uloop, tv)) {
-+ *tv = tv_uloop;
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+static void uloop_poll_handler(void)
-+{
-+ uloop_run_timeout(0);
-+}
-+
-+void eloop_add_uloop(void)
-+{
-+ static bool init_done = false;
-+
-+ if (!init_done) {
-+ uloop_init();
-+ uloop_fd_set_cb = eloop_uloop_fd_cb;
-+ init_done = true;
-+ }
-+
-+ eloop_register_cb(uloop_poll_handler, uloop_timeout_poll_handler);
-+}
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/601-ucode_support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/601-ucode_support.patch
deleted file mode 100644
index cfdb51f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/601-ucode_support.patch
+++ /dev/null
@@ -1,671 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -168,9 +168,21 @@ OBJS += ../src/eapol_auth/eapol_auth_sm.
-
- ifdef CONFIG_UBUS
- CFLAGS += -DUBUS_SUPPORT
--OBJS += ../src/utils/uloop.o
- OBJS += ../src/ap/ubus.o
--LIBS += -lubox -lubus
-+LIBS += -lubus
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef CONFIG_UCODE
-+CFLAGS += -DUCODE_SUPPORT
-+OBJS += ../src/utils/ucode.o
-+OBJS += ../src/ap/ucode.o
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef NEED_ULOOP
-+OBJS += ../src/utils/uloop.o
-+LIBS += -lubox
- endif
-
- ifdef CONFIG_CODE_COVERAGE
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -1007,6 +1007,7 @@ int main(int argc, char *argv[])
- }
-
- hostapd_global_ctrl_iface_init(&interfaces);
-+ hostapd_ucode_init(&interfaces);
-
- if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
- wpa_printf(MSG_ERROR, "Failed to start eloop");
-@@ -1016,6 +1017,7 @@ int main(int argc, char *argv[])
- ret = 0;
-
- out:
-+ hostapd_ucode_free();
- hostapd_global_ctrl_iface_deinit(&interfaces);
- /* Deinitialize all interfaces */
- for (i = 0; i < interfaces.count; i++) {
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -19,6 +19,7 @@
- #include "ap_config.h"
- #include "drivers/driver.h"
- #include "ubus.h"
-+#include "ucode.h"
-
- #define OCE_STA_CFON_ENABLED(hapd) \
- ((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -51,6 +52,10 @@ struct hapd_interfaces {
- struct hostapd_config * (*config_read_cb)(const char *config_fname);
- int (*ctrl_iface_init)(struct hostapd_data *hapd);
- void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
-+ int (*ctrl_iface_recv)(struct hostapd_data *hapd,
-+ char *buf, char *reply, int reply_size,
-+ struct sockaddr_storage *from,
-+ socklen_t fromlen);
- int (*for_each_interface)(struct hapd_interfaces *interfaces,
- int (*cb)(struct hostapd_iface *iface,
- void *ctx), void *ctx);
-@@ -186,6 +191,7 @@ struct hostapd_data {
- struct hostapd_config *iconf;
- struct hostapd_bss_config *conf;
- struct hostapd_ubus_bss ubus;
-+ struct hostapd_ucode_bss ucode;
- int interface_added; /* virtual interface added for this BSS */
- unsigned int started:1;
- unsigned int disabled:1;
-@@ -506,6 +512,7 @@ struct hostapd_sta_info {
- */
- struct hostapd_iface {
- struct hapd_interfaces *interfaces;
-+ struct hostapd_ucode_iface ucode;
- void *owner;
- char *config_fname;
- struct hostapd_config *conf;
-@@ -706,6 +713,8 @@ struct hostapd_iface * hostapd_init(stru
- struct hostapd_iface *
- hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
- const char *config_fname, int debug);
-+int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
-+void hostapd_bss_deinit(struct hostapd_data *hapd);
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- int reassoc);
- void hostapd_interface_deinit_free(struct hostapd_iface *iface);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -252,6 +252,8 @@ int hostapd_reload_config(struct hostapd
- struct hostapd_config *newconf, *oldconf;
- size_t j;
-
-+ hostapd_ucode_reload_bss(hapd);
-+
- if (iface->config_fname == NULL) {
- /* Only in-memory config in use - assume it has been updated */
- hostapd_clear_old(iface);
-@@ -435,6 +437,7 @@ void hostapd_free_hapd_data(struct hosta
- hapd->beacon_set_done = 0;
-
- wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
-+ hostapd_ucode_free_bss(hapd);
- hostapd_ubus_free_bss(hapd);
- accounting_deinit(hapd);
- hostapd_deinit_wpa(hapd);
-@@ -600,6 +603,7 @@ void hostapd_cleanup_iface_partial(struc
- static void hostapd_cleanup_iface(struct hostapd_iface *iface)
- {
- wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-+ hostapd_ucode_free_iface(iface);
- eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
- NULL);
-
-@@ -1189,6 +1193,7 @@ static int hostapd_start_beacon(struct h
- hapd->driver->set_operstate(hapd->drv_priv, 1);
-
- hostapd_ubus_add_bss(hapd);
-+ hostapd_ucode_add_bss(hapd);
-
- return 0;
- }
-@@ -1211,8 +1216,7 @@ static int hostapd_start_beacon(struct h
- * initialized. Most of the modules that are initialized here will be
- * deinitialized in hostapd_cleanup().
- */
--static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
-- bool start_beacon)
-+int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
- {
- struct hostapd_bss_config *conf = hapd->conf;
- u8 ssid[SSID_MAX_LEN + 1];
-@@ -2698,7 +2702,7 @@ hostapd_alloc_bss_data(struct hostapd_if
- }
-
-
--static void hostapd_bss_deinit(struct hostapd_data *hapd)
-+void hostapd_bss_deinit(struct hostapd_data *hapd)
- {
- if (!hapd)
- return;
-@@ -3491,7 +3495,8 @@ int hostapd_remove_iface(struct hapd_int
- hapd_iface = interfaces->iface[i];
- if (hapd_iface == NULL)
- return -1;
-- if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
-+ if (!os_strcmp(hapd_iface->phy, buf) ||
-+ !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
- wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
- hapd_iface->driver_ap_teardown =
- !!(hapd_iface->drv_flags &
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -195,8 +195,20 @@ endif
- ifdef CONFIG_UBUS
- CFLAGS += -DUBUS_SUPPORT
- OBJS += ubus.o
-+LIBS += -lubus
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef CONFIG_UCODE
-+CFLAGS += -DUCODE_SUPPORT
-+OBJS += ../src/utils/ucode.o
-+OBJS += ucode.o
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef NEED_ULOOP
- OBJS += ../src/utils/uloop.o
--LIBS += -lubox -lubus
-+LIBS += -lubox
- endif
-
- ifdef CONFIG_CODE_COVERAGE
-@@ -997,6 +1009,9 @@ OBJS += ../src/ap/ctrl_iface_ap.o
- ifdef CONFIG_UBUS
- OBJS += ../src/ap/ubus.o
- endif
-+ifdef CONFIG_UCODE
-+OBJS += ../src/ap/ucode.o
-+endif
- endif
-
- CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -1044,6 +1044,7 @@ void wpa_supplicant_set_state(struct wpa
- sme_sched_obss_scan(wpa_s, 0);
- }
- wpa_s->wpa_state = state;
-+ wpas_ucode_update_state(wpa_s);
-
- #ifdef CONFIG_BGSCAN
- if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
-@@ -7594,6 +7595,7 @@ struct wpa_supplicant * wpa_supplicant_a
- #endif /* CONFIG_P2P */
-
- wpas_ubus_add_bss(wpa_s);
-+ wpas_ucode_add_bss(wpa_s);
-
- return wpa_s;
- }
-@@ -7621,6 +7623,7 @@ int wpa_supplicant_remove_iface(struct w
- struct wpa_supplicant *parent = wpa_s->parent;
- #endif /* CONFIG_MESH */
-
-+ wpas_ucode_free_bss(wpa_s);
- wpas_ubus_free_bss(wpa_s);
-
- /* Remove interface from the global list of interfaces */
-@@ -7931,6 +7934,7 @@ struct wpa_global * wpa_supplicant_init(
-
- eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
- wpas_periodic, global, NULL);
-+ wpas_ucode_init(global);
-
- return global;
- }
-@@ -7969,12 +7973,8 @@ int wpa_supplicant_run(struct wpa_global
- eloop_register_signal_terminate(wpa_supplicant_terminate, global);
- eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
-
-- wpas_ubus_add(global);
--
- eloop_run();
-
-- wpas_ubus_free(global);
--
- return 0;
- }
-
-@@ -8007,6 +8007,8 @@ void wpa_supplicant_deinit(struct wpa_gl
-
- wpas_notify_supplicant_deinitialized(global);
-
-+ wpas_ucode_free();
-+
- eap_peer_unregister_methods();
- #ifdef CONFIG_AP
- eap_server_unregister_methods();
---- a/wpa_supplicant/wpa_supplicant_i.h
-+++ b/wpa_supplicant/wpa_supplicant_i.h
-@@ -22,6 +22,7 @@
- #include "wmm_ac.h"
- #include "pasn/pasn_common.h"
- #include "ubus.h"
-+#include "ucode.h"
-
- extern const char *const wpa_supplicant_version;
- extern const char *const wpa_supplicant_license;
-@@ -689,6 +690,7 @@ struct wpa_supplicant {
- unsigned char perm_addr[ETH_ALEN];
- char ifname[100];
- struct wpas_ubus_bss ubus;
-+ struct wpas_ucode_bss ucode;
- #ifdef CONFIG_MATCH_IFACE
- int matched;
- #endif /* CONFIG_MATCH_IFACE */
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4856,6 +4856,7 @@ try_again:
- return -1;
- }
-
-+ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
- wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
-
- return 0;
-@@ -4957,6 +4958,7 @@ fail:
- os_free(fname);
-
- interface->global_ctrl_sock = s;
-+ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
- eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
- interface, NULL);
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -3787,6 +3787,25 @@ struct wpa_driver_ops {
- const char *ifname);
-
- /**
-+ * if_rename - Rename a virtual interface
-+ * @priv: Private driver interface data
-+ * @type: Interface type
-+ * @ifname: Interface name of the virtual interface to be renamed
-+ * (NULL when renaming the AP BSS interface)
-+ * @new_name: New interface name of the virtual interface
-+ * Returns: 0 on success, -1 on failure
-+ */
-+ int (*if_rename)(void *priv, enum wpa_driver_if_type type,
-+ const char *ifname, const char *new_name);
-+
-+ /**
-+ * set_first_bss - Make a virtual interface the first (primary) bss
-+ * @priv: Private driver interface data
-+ * Returns: 0 on success, -1 on failure
-+ */
-+ int (*set_first_bss)(void *priv);
-+
-+ /**
- * set_sta_vlan - Bind a station into a specific interface (AP only)
- * @priv: Private driver interface data
- * @ifname: Interface (main or virtual BSS or VLAN)
-@@ -6440,6 +6459,7 @@ union wpa_event_data {
-
- /**
- * struct ch_switch
-+ * @count: Count until channel switch activates
- * @freq: Frequency of new channel in MHz
- * @ht_enabled: Whether this is an HT channel
- * @ch_offset: Secondary channel offset
-@@ -6450,6 +6470,7 @@ union wpa_event_data {
- * @punct_bitmap: Puncturing bitmap
- */
- struct ch_switch {
-+ int count;
- int freq;
- int ht_enabled;
- int ch_offset;
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1202,6 +1202,7 @@ static void mlme_event_ch_switch(struct
- struct nlattr *bw, struct nlattr *cf1,
- struct nlattr *cf2,
- struct nlattr *punct_bitmap,
-+ struct nlattr *count,
- int finished)
- {
- struct i802_bss *bss;
-@@ -1265,6 +1266,8 @@ static void mlme_event_ch_switch(struct
- data.ch_switch.cf1 = nla_get_u32(cf1);
- if (cf2)
- data.ch_switch.cf2 = nla_get_u32(cf2);
-+ if (count)
-+ data.ch_switch.count = nla_get_u32(count);
-
- if (finished)
- bss->flink->freq = data.ch_switch.freq;
-@@ -3912,6 +3915,7 @@ static void do_process_drv_event(struct
- tb[NL80211_ATTR_CENTER_FREQ1],
- tb[NL80211_ATTR_CENTER_FREQ2],
- tb[NL80211_ATTR_PUNCT_BITMAP],
-+ tb[NL80211_ATTR_CH_SWITCH_COUNT],
- 0);
- break;
- case NL80211_CMD_CH_SWITCH_NOTIFY:
-@@ -3924,6 +3928,7 @@ static void do_process_drv_event(struct
- tb[NL80211_ATTR_CENTER_FREQ1],
- tb[NL80211_ATTR_CENTER_FREQ2],
- tb[NL80211_ATTR_PUNCT_BITMAP],
-+ NULL,
- 1);
- break;
- case NL80211_CMD_DISCONNECT:
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -5389,6 +5389,7 @@ void supplicant_event(void *ctx, enum wp
- event_to_string(event), event);
- #endif /* CONFIG_NO_STDOUT_DEBUG */
-
-+ wpas_ucode_event(wpa_s, event, data);
- switch (event) {
- case EVENT_AUTH:
- #ifdef CONFIG_FST
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -393,6 +393,23 @@ static inline int hostapd_drv_stop_ap(st
- return hapd->driver->stop_ap(hapd->drv_priv);
- }
-
-+static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
-+ enum wpa_driver_if_type type,
-+ const char *ifname,
-+ const char *new_name)
-+{
-+ if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
-+ return -1;
-+ return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
-+}
-+
-+static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
-+ return 0;
-+ return hapd->driver->set_first_bss(hapd->drv_priv);
-+}
-+
- static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
- struct wpa_channel_info *ci)
- {
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -73,6 +73,16 @@ enum nlmsgerr_attrs {
-
- #endif /* ANDROID */
-
-+static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
-+{
-+ const struct nlmsghdr *nlh;
-+
-+ if (!wpa_netlink_hook)
-+ return;
-+
-+ nlh = nlmsg_hdr(msg);
-+ wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
-+}
-
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
-@@ -379,6 +389,11 @@ static int no_seq_check(struct nl_msg *m
- return NL_OK;
- }
-
-+static int debug_handler(struct nl_msg *msg, void *arg)
-+{
-+ handle_nl_debug_hook(msg, 0);
-+ return NL_OK;
-+}
-
- static void nl80211_nlmsg_clear(struct nl_msg *msg)
- {
-@@ -415,6 +430,7 @@ static int send_and_recv(struct nl80211_
- if (!msg)
- return -ENOMEM;
-
-+ handle_nl_debug_hook(msg, 1);
- cb = nl_cb_clone(global->nl_cb);
- if (!cb)
- goto out;
-@@ -443,6 +459,7 @@ static int send_and_recv(struct nl80211_
-
- err = 1;
-
-+ nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
- nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
- if (ack_handler_custom) {
-@@ -919,6 +936,7 @@ nl80211_get_wiphy_data_ap(struct i802_bs
- os_free(w);
- return NULL;
- }
-+ nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -1333,7 +1351,7 @@ static void wpa_driver_nl80211_event_rtm
- }
- wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
- namebuf, ifname);
-- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+ if (drv->first_bss->ifindex != ifi->ifi_index) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Not the main interface (%s) - do not indicate interface down",
- drv->first_bss->ifname);
-@@ -1369,7 +1387,7 @@ static void wpa_driver_nl80211_event_rtm
- }
- wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
- namebuf, ifname);
-- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+ if (drv->first_bss->ifindex != ifi->ifi_index) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Not the main interface (%s) - do not indicate interface up",
- drv->first_bss->ifname);
-@@ -1992,6 +2010,7 @@ static int wpa_driver_nl80211_init_nl_gl
- /* Continue without vendor events */
- }
-
-+ nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -2160,6 +2179,7 @@ static int nl80211_init_bss(struct i802_
- if (!bss->nl_cb)
- return -1;
-
-+ nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -8432,6 +8452,7 @@ static void *i802_init(struct hostapd_da
- char master_ifname[IFNAMSIZ];
- int ifindex, br_ifindex = 0;
- int br_added = 0;
-+ int err;
-
- bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
- params->global_priv, 1,
-@@ -8491,21 +8512,17 @@ static void *i802_init(struct hostapd_da
- (params->num_bridge == 0 || !params->bridge[0]))
- add_ifidx(drv, br_ifindex, drv->ifindex);
-
-- if (bss->added_if_into_bridge || bss->already_in_bridge) {
-- int err;
--
-- drv->rtnl_sk = nl_socket_alloc();
-- if (drv->rtnl_sk == NULL) {
-- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
-- goto failed;
-- }
-+ drv->rtnl_sk = nl_socket_alloc();
-+ if (drv->rtnl_sk == NULL) {
-+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
-+ goto failed;
-+ }
-
-- err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
-- if (err) {
-- wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
-- nl_geterror(err));
-- goto failed;
-- }
-+ err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
-+ if (err) {
-+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
-+ nl_geterror(err));
-+ goto failed;
- }
-
- if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
-@@ -8875,6 +8892,50 @@ static int wpa_driver_nl80211_if_remove(
- return 0;
- }
-
-+static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
-+ enum wpa_driver_if_type type,
-+ const char *ifname, const char *new_name)
-+{
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct ifinfomsg ifi = {
-+ .ifi_family = AF_UNSPEC,
-+ .ifi_index = bss->ifindex,
-+ };
-+ struct nl_msg *msg;
-+ int res = -ENOMEM;
-+
-+ if (ifname)
-+ ifi.ifi_index = if_nametoindex(ifname);
-+
-+ msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
-+ if (!msg)
-+ return res;
-+
-+ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
-+ goto out;
-+
-+ if (nla_put_string(msg, IFLA_IFNAME, new_name))
-+ goto out;
-+
-+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+ if (res < 0)
-+ goto out;
-+
-+ res = nl_wait_for_ack(drv->rtnl_sk);
-+ if (res) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Renaming device %s to %s failed: %s",
-+ ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
-+ goto out;
-+ }
-+
-+ if (type == WPA_IF_AP_BSS && !ifname)
-+ os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
-+
-+out:
-+ nlmsg_free(msg);
-+ return res;
-+}
-
- static int cookie_handler(struct nl_msg *msg, void *arg)
- {
-@@ -10513,6 +10574,37 @@ static int driver_nl80211_if_remove(void
- }
-
-
-+static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
-+ const char *ifname, const char *new_name)
-+{
-+ struct i802_bss *bss = priv;
-+ return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
-+}
-+
-+
-+static int driver_nl80211_set_first_bss(void *priv)
-+{
-+ struct i802_bss *bss = priv, *tbss;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+
-+ if (drv->first_bss == bss)
-+ return 0;
-+
-+ for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
-+ if (tbss->next != bss)
-+ continue;
-+
-+ tbss->next = bss->next;
-+ bss->next = drv->first_bss;
-+ drv->first_bss = bss;
-+ drv->ctx = bss->ctx;
-+ return 0;
-+ }
-+
-+ return -1;
-+}
-+
-+
- static int driver_nl80211_send_mlme(void *priv, const u8 *data,
- size_t data_len, int noack,
- unsigned int freq,
-@@ -13697,6 +13789,8 @@ const struct wpa_driver_ops wpa_driver_n
- .set_acl = wpa_driver_nl80211_set_acl,
- .if_add = wpa_driver_nl80211_if_add,
- .if_remove = driver_nl80211_if_remove,
-+ .if_rename = driver_nl80211_if_rename,
-+ .set_first_bss = driver_nl80211_set_first_bss,
- .send_mlme = driver_nl80211_send_mlme,
- .get_hw_feature_data = nl80211_get_hw_feature_data,
- .sta_add = wpa_driver_nl80211_sta_add,
---- a/src/utils/wpa_debug.c
-+++ b/src/utils/wpa_debug.c
-@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NU
- #define WPAS_TRACE_PFX "wpas <%d>: "
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
-
-+void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
-+ size_t len);
-+void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
-
- int wpa_debug_level = MSG_INFO;
- int wpa_debug_show_keys = 0;
-@@ -210,6 +214,12 @@ void _wpa_printf(int level, const char *
- {
- va_list ap;
-
-+ if (wpa_printf_hook) {
-+ va_start(ap, fmt);
-+ wpa_printf_hook(level, fmt, ap);
-+ va_end(ap);
-+ }
-+
- if (level >= wpa_debug_level) {
- #ifdef CONFIG_ANDROID_LOG
- va_start(ap, fmt);
-@@ -260,6 +270,9 @@ void _wpa_hexdump(int level, const char
- {
- size_t i;
-
-+ if (wpa_hexdump_hook)
-+ wpa_hexdump_hook(level, title, buf, len);
-+
- #ifdef CONFIG_DEBUG_LINUX_TRACING
- if (wpa_debug_tracing_file != NULL) {
- fprintf(wpa_debug_tracing_file,
---- a/src/utils/wpa_debug.h
-+++ b/src/utils/wpa_debug.h
-@@ -11,6 +11,10 @@
-
- #include "wpabuf.h"
-
-+extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+extern void (*wpa_hexdump_hook)(int level, const char *title,
-+ const void *buf, size_t len);
-+extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
- extern int wpa_debug_level;
- extern int wpa_debug_show_keys;
- extern int wpa_debug_timestamp;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/610-hostapd_cli_ujail_permission.patch b/recipes-wifi/hostapd/files/patches-2.10.3/610-hostapd_cli_ujail_permission.patch
deleted file mode 100644
index a03fcc9..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/610-hostapd_cli_ujail_permission.patch
+++ /dev/null
@@ -1,33 +0,0 @@
---- a/src/common/wpa_ctrl.c
-+++ b/src/common/wpa_ctrl.c
-@@ -135,7 +135,7 @@ try_again:
- return NULL;
- }
- tries++;
--#ifdef ANDROID
-+
- /* Set client socket file permissions so that bind() creates the client
- * socket with these permissions and there is no need to try to change
- * them with chmod() after bind() which would have potential issues with
-@@ -147,7 +147,7 @@ try_again:
- * operations to allow the response to go through. Those are using the
- * no-deference-symlinks version to avoid races. */
- fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
--#endif /* ANDROID */
-+
- if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
- sizeof(ctrl->local)) < 0) {
- if (errno == EADDRINUSE && tries < 2) {
-@@ -165,7 +165,11 @@ try_again:
- return NULL;
- }
-
--#ifdef ANDROID
-+#ifndef ANDROID
-+ /* Set group even if we do not have privileges to change owner */
-+ lchown(ctrl->local.sun_path, -1, 101);
-+ lchown(ctrl->local.sun_path, 101, 101);
-+#else
- /* Set group even if we do not have privileges to change owner */
- lchown(ctrl->local.sun_path, -1, AID_WIFI);
- lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/701-reload_config_inline.patch b/recipes-wifi/hostapd/files/patches-2.10.3/701-reload_config_inline.patch
deleted file mode 100644
index 3c62bf6..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/701-reload_config_inline.patch
+++ /dev/null
@@ -1,33 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4816,7 +4816,12 @@ struct hostapd_config * hostapd_config_r
- int errors = 0;
- size_t i;
-
-- f = fopen(fname, "r");
-+ if (!strncmp(fname, "data:", 5)) {
-+ f = fmemopen((void *)(fname + 5), strlen(fname + 5), "r");
-+ fname = "<inline>";
-+ } else {
-+ f = fopen(fname, "r");
-+ }
- if (f == NULL) {
- wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
- "for reading.", fname);
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(cons
- while (cred_tail && cred_tail->next)
- cred_tail = cred_tail->next;
-
-+ if (!strncmp(name, "data:", 5)) {
-+ f = fmemopen((void *)(name + 5), strlen(name + 5), "r");
-+ name = "<inline>";
-+ } else {
-+ f = fopen(name, "r");
-+ }
- wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
-- f = fopen(name, "r");
- if (f == NULL) {
- wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
- "error: %s", name, strerror(errno));
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/710-vlan_no_bridge.patch b/recipes-wifi/hostapd/files/patches-2.10.3/710-vlan_no_bridge.patch
deleted file mode 100644
index 63d1b8a..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/710-vlan_no_bridge.patch
+++ /dev/null
@@ -1,41 +0,0 @@
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -121,6 +121,7 @@ struct hostapd_ssid {
- #define DYNAMIC_VLAN_OPTIONAL 1
- #define DYNAMIC_VLAN_REQUIRED 2
- int dynamic_vlan;
-+ int vlan_no_bridge;
- #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
- #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
- #define DYNAMIC_VLAN_NAMING_END 2
---- a/src/ap/vlan_full.c
-+++ b/src/ap/vlan_full.c
-@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, st
- if (!vlan)
- return;
-
-+ if (hapd->conf->ssid.vlan_no_bridge)
-+ goto out;
-+
- vlan->configured = 1;
-
- notempty = vlan->vlan_desc.notempty;
-@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, st
- ifname, br_name, tagged[i], hapd);
- }
-
-+out:
- ifconfig_up(ifname);
- }
-
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3351,6 +3351,8 @@ static int hostapd_config_fill(struct ho
- #ifndef CONFIG_NO_VLAN
- } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
- bss->ssid.dynamic_vlan = atoi(pos);
-+ } else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
-+ bss->ssid.vlan_no_bridge = atoi(pos);
- } else if (os_strcmp(buf, "per_sta_vif") == 0) {
- bss->ssid.per_sta_vif = atoi(pos);
- } else if (os_strcmp(buf, "vlan_file") == 0) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/711-wds_bridge_force.patch b/recipes-wifi/hostapd/files/patches-2.10.3/711-wds_bridge_force.patch
deleted file mode 100644
index c0f2c31..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/711-wds_bridge_force.patch
+++ /dev/null
@@ -1,22 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2318,6 +2318,8 @@ static int hostapd_config_fill(struct ho
- sizeof(conf->bss[0]->iface));
- } else if (os_strcmp(buf, "bridge") == 0) {
- os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
-+ if (!bss->wds_bridge[0])
-+ os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
- } else if (os_strcmp(buf, "bridge_hairpin") == 0) {
- bss->bridge_hairpin = atoi(pos);
- } else if (os_strcmp(buf, "vlan_bridge") == 0) {
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -348,8 +348,6 @@ int hostapd_set_wds_sta(struct hostapd_d
- return -1;
- if (hapd->conf->wds_bridge[0])
- bridge = hapd->conf->wds_bridge;
-- else if (hapd->conf->bridge[0])
-- bridge = hapd->conf->bridge;
- return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
- bridge, ifname_wds);
- }
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/720-iface_max_num_sta.patch b/recipes-wifi/hostapd/files/patches-2.10.3/720-iface_max_num_sta.patch
deleted file mode 100644
index 089c1dd..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/720-iface_max_num_sta.patch
+++ /dev/null
@@ -1,81 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2848,6 +2848,14 @@ static int hostapd_config_fill(struct ho
- line, bss->max_num_sta, MAX_STA_COUNT);
- return 1;
- }
-+ } else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
-+ conf->max_num_sta = atoi(pos);
-+ if (conf->max_num_sta < 0 ||
-+ conf->max_num_sta > MAX_STA_COUNT) {
-+ wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
-+ line, conf->max_num_sta, MAX_STA_COUNT);
-+ return 1;
-+ }
- } else if (os_strcmp(buf, "wpa") == 0) {
- bss->wpa = atoi(pos);
- } else if (os_strcmp(buf, "extended_key_id") == 0) {
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -742,6 +742,7 @@ void hostapd_cleanup_cs_params(struct ho
- void hostapd_periodic_iface(struct hostapd_iface *iface);
- int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
- void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
-+int hostapd_check_max_sta(struct hostapd_data *hapd);
-
- void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
- void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -244,6 +244,29 @@ static int hostapd_iface_conf_changed(st
- return 0;
- }
-
-+static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
-+{
-+ int num_sta = 0;
-+ int i;
-+
-+ for (i = 0; i < iface->num_bss; i++)
-+ num_sta += iface->bss[i]->num_sta;
-+
-+ return num_sta;
-+}
-+
-+
-+int hostapd_check_max_sta(struct hostapd_data *hapd)
-+{
-+ if (hapd->num_sta >= hapd->conf->max_num_sta)
-+ return 1;
-+
-+ if (hapd->iconf->max_num_sta &&
-+ hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
-+ return 1;
-+
-+ return 0;
-+}
-
- int hostapd_reload_config(struct hostapd_iface *iface)
- {
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1252,7 +1252,7 @@ void handle_probe_req(struct hostapd_dat
- if (hapd->conf->no_probe_resp_if_max_sta &&
- is_multicast_ether_addr(mgmt->da) &&
- is_multicast_ether_addr(mgmt->bssid) &&
-- hapd->num_sta >= hapd->conf->max_num_sta &&
-+ hostapd_check_max_sta(hapd) &&
- !ap_get_sta(hapd, mgmt->sa)) {
- wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
- " since no room for additional STA",
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1039,6 +1039,8 @@ struct hostapd_config {
- unsigned int track_sta_max_num;
- unsigned int track_sta_max_age;
-
-+ int max_num_sta;
-+
- char country[3]; /* first two octets: country code as described in
- * ISO/IEC 3166-1. Third octet:
- * ' ' (ascii 32): all environments
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/730-ft_iface.patch b/recipes-wifi/hostapd/files/patches-2.10.3/730-ft_iface.patch
deleted file mode 100644
index 0795ed1..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/730-ft_iface.patch
+++ /dev/null
@@ -1,38 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3007,6 +3007,8 @@ static int hostapd_config_fill(struct ho
- wpa_printf(MSG_INFO,
- "Line %d: Obsolete peerkey parameter ignored", line);
- #ifdef CONFIG_IEEE80211R_AP
-+ } else if (os_strcmp(buf, "ft_iface") == 0) {
-+ os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
- } else if (os_strcmp(buf, "mobility_domain") == 0) {
- if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
- hexstr2bin(pos, bss->mobility_domain,
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -283,6 +283,7 @@ struct airtime_sta_weight {
- struct hostapd_bss_config {
- char iface[IFNAMSIZ + 1];
- char bridge[IFNAMSIZ + 1];
-+ char ft_iface[IFNAMSIZ + 1];
- char vlan_bridge[IFNAMSIZ + 1];
- char wds_bridge[IFNAMSIZ + 1];
- int bridge_hairpin; /* hairpin_mode on bridge members */
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -1727,8 +1727,12 @@ int hostapd_setup_wpa(struct hostapd_dat
- wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
- const char *ft_iface;
-
-- ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
-- hapd->conf->iface;
-+ if (hapd->conf->ft_iface[0])
-+ ft_iface = hapd->conf->ft_iface;
-+ else if (hapd->conf->bridge[0])
-+ ft_iface = hapd->conf->bridge;
-+ else
-+ ft_iface = hapd->conf->iface;
- hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
- hostapd_rrb_receive, hapd, 1);
- if (!hapd->l2) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/740-snoop_iface.patch b/recipes-wifi/hostapd/files/patches-2.10.3/740-snoop_iface.patch
deleted file mode 100644
index ce64513..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/740-snoop_iface.patch
+++ /dev/null
@@ -1,139 +0,0 @@
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -284,6 +284,7 @@ struct hostapd_bss_config {
- char iface[IFNAMSIZ + 1];
- char bridge[IFNAMSIZ + 1];
- char ft_iface[IFNAMSIZ + 1];
-+ char snoop_iface[IFNAMSIZ + 1];
- char vlan_bridge[IFNAMSIZ + 1];
- char wds_bridge[IFNAMSIZ + 1];
- int bridge_hairpin; /* hairpin_mode on bridge members */
---- a/src/ap/x_snoop.c
-+++ b/src/ap/x_snoop.c
-@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *ha
-
- hapd->x_snoop_initialized = true;
-
-- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
-+ if (!conf->snoop_iface[0] &&
-+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
- 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable hairpin_mode on the bridge port");
- return -1;
- }
-
-- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
-+ if (!conf->snoop_iface[0] &&
-+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable proxyarp on the bridge port");
- return -1;
- }
-
- if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
-- 1)) {
-+ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
- return -1;
- }
-
- #ifdef CONFIG_IPV6
-- if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
-+ if (!conf->snoop_iface[0] &&
-+ hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable multicast snooping on the bridge");
- return -1;
-@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_dat
- {
- struct hostapd_bss_config *conf = hapd->conf;
- struct l2_packet_data *l2;
-+ const char *ifname = conf->bridge;
-+
-+ if (conf->snoop_iface[0])
-+ ifname = conf->snoop_iface;
-
-- l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
-+ l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
- if (l2 == NULL) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to initialize L2 packet processing %s",
-@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send
-
- void x_snoop_deinit(struct hostapd_data *hapd)
- {
-+ struct hostapd_bss_config *conf = hapd->conf;
-+
- if (!hapd->x_snoop_initialized)
- return;
-- hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
-+ hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
-+ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
- hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
- hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
- hapd->x_snoop_initialized = false;
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2322,6 +2322,8 @@ static int hostapd_config_fill(struct ho
- os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
- } else if (os_strcmp(buf, "bridge_hairpin") == 0) {
- bss->bridge_hairpin = atoi(pos);
-+ } else if (os_strcmp(buf, "snoop_iface") == 0) {
-+ os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
- } else if (os_strcmp(buf, "vlan_bridge") == 0) {
- os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
- } else if (os_strcmp(buf, "wds_bridge") == 0) {
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -366,12 +366,12 @@ static inline int hostapd_drv_br_port_se
-
- static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
- enum drv_br_net_param param,
-- unsigned int val)
-+ const char *ifname, unsigned int val)
- {
- if (hapd->driver == NULL || hapd->drv_priv == NULL ||
- hapd->driver->br_set_net_param == NULL)
- return -1;
-- return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
-+ return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
- }
-
- static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -4209,7 +4209,7 @@ struct wpa_driver_ops {
- * Returns: 0 on success, negative (<0) on failure
- */
- int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
-- unsigned int val);
-+ const char *ifname, unsigned int val);
-
- /**
- * get_wowlan - Get wake-on-wireless status
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -12168,7 +12168,7 @@ static const char * drv_br_net_param_str
-
-
- static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
-- unsigned int val)
-+ const char *ifname, unsigned int val)
- {
- struct i802_bss *bss = priv;
- char path[128];
-@@ -12194,8 +12194,11 @@ static int wpa_driver_br_set_net_param(v
- return -EINVAL;
- }
-
-+ if (!ifname)
-+ ifname = bss->brname;
-+
- os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
-- ip_version, bss->brname, param_txt);
-+ ip_version, ifname, param_txt);
-
- set_val:
- if (linux_write_system_file(path, val))
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/750-qos_map_set_without_interworking.patch b/recipes-wifi/hostapd/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
deleted file mode 100644
index 97c32df..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
+++ /dev/null
@@ -1,97 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1604,6 +1604,8 @@ static int parse_anqp_elem(struct hostap
- return 0;
- }
-
-+#endif /* CONFIG_INTERWORKING */
-+
-
- static int parse_qos_map_set(struct hostapd_bss_config *bss,
- char *buf, int line)
-@@ -1645,8 +1647,6 @@ static int parse_qos_map_set(struct host
- return 0;
- }
-
--#endif /* CONFIG_INTERWORKING */
--
-
- #ifdef CONFIG_HS20
- static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
-@@ -4062,10 +4062,10 @@ static int hostapd_config_fill(struct ho
- bss->gas_frag_limit = val;
- } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
- bss->gas_comeback_delay = atoi(pos);
-+#endif /* CONFIG_INTERWORKING */
- } else if (os_strcmp(buf, "qos_map_set") == 0) {
- if (parse_qos_map_set(bss, pos, line) < 0)
- return 1;
--#endif /* CONFIG_INTERWORKING */
- #ifdef CONFIG_RADIUS_TEST
- } else if (os_strcmp(buf, "dump_msk_file") == 0) {
- os_free(bss->dump_msk_file);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1486,6 +1486,7 @@ int hostapd_setup_bss(struct hostapd_dat
- wpa_printf(MSG_ERROR, "GAS server initialization failed");
- return -1;
- }
-+#endif /* CONFIG_INTERWORKING */
-
- if (conf->qos_map_set_len &&
- hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
-@@ -1493,7 +1494,6 @@ int hostapd_setup_bss(struct hostapd_dat
- wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
- return -1;
- }
--#endif /* CONFIG_INTERWORKING */
-
- if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
- wpa_printf(MSG_ERROR, "BSS Load initialization failed");
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -2683,8 +2683,6 @@ void wnm_bss_keep_alive_deinit(struct wp
- }
-
-
--#ifdef CONFIG_INTERWORKING
--
- static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
- size_t len)
- {
-@@ -2717,8 +2715,6 @@ static void interworking_process_assoc_r
- }
- }
-
--#endif /* CONFIG_INTERWORKING */
--
-
- static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
- {
-@@ -3098,10 +3094,8 @@ static int wpa_supplicant_event_associnf
- wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- data->assoc_info.resp_ies_len);
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_INTERWORKING
- interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- data->assoc_info.resp_ies_len);
--#endif /* CONFIG_INTERWORKING */
- if (wpa_s->hw_capab == CAPAB_VHT &&
- get_ie(data->assoc_info.resp_ies,
- data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
---- a/src/ap/ieee802_11_shared.c
-+++ b/src/ap/ieee802_11_shared.c
-@@ -1116,13 +1116,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_da
- u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *ext_capab_ie, size_t ext_capab_ie_len)
- {
--#ifdef CONFIG_INTERWORKING
- /* check for QoS Map support */
- if (ext_capab_ie_len >= 5) {
- if (ext_capab_ie[4] & 0x01)
- sta->qos_map_enabled = 1;
- }
--#endif /* CONFIG_INTERWORKING */
-
- if (ext_capab_ie_len > 0) {
- sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/751-qos_map_ignore_when_unsupported.patch b/recipes-wifi/hostapd/files/patches-2.10.3/751-qos_map_ignore_when_unsupported.patch
deleted file mode 100644
index f5ebab7..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/751-qos_map_ignore_when_unsupported.patch
+++ /dev/null
@@ -1,12 +0,0 @@
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -927,7 +927,8 @@ int hostapd_start_dfs_cac(struct hostapd
- int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
- const u8 *qos_map_set, u8 qos_map_set_len)
- {
-- if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
-+ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
-+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
- return 0;
- return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
- qos_map_set_len);
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/760-dynamic_own_ip.patch b/recipes-wifi/hostapd/files/patches-2.10.3/760-dynamic_own_ip.patch
deleted file mode 100644
index 2c705a6..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/760-dynamic_own_ip.patch
+++ /dev/null
@@ -1,109 +0,0 @@
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -310,6 +310,7 @@ struct hostapd_bss_config {
- unsigned int eap_sim_db_timeout;
- int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
- struct hostapd_ip_addr own_ip_addr;
-+ int dynamic_own_ip_addr;
- char *nas_identifier;
- struct hostapd_radius_servers *radius;
- int acct_interim_interval;
---- a/src/radius/radius_client.c
-+++ b/src/radius/radius_client.c
-@@ -163,6 +163,8 @@ struct radius_client_data {
- */
- void *ctx;
-
-+ struct hostapd_ip_addr local_ip;
-+
- /**
- * conf - RADIUS client configuration (list of RADIUS servers to use)
- */
-@@ -720,6 +722,30 @@ static void radius_client_list_add(struc
-
-
- /**
-+ * radius_client_send - Get local address for the RADIUS auth socket
-+ * @radius: RADIUS client context from radius_client_init()
-+ * @addr: pointer to store the address
-+ *
-+ * This function returns the local address for the connection to the RADIUS
-+ * auth server. It also opens the socket if it's not available yet.
-+ */
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+ struct hostapd_ip_addr *addr)
-+{
-+ struct hostapd_radius_servers *conf = radius->conf;
-+
-+ if (conf->auth_server && radius->auth_sock < 0)
-+ radius_client_init_auth(radius);
-+
-+ if (radius->auth_sock < 0)
-+ return -1;
-+
-+ memcpy(addr, &radius->local_ip, sizeof(*addr));
-+
-+ return 0;
-+}
-+
-+/**
- * radius_client_send - Send a RADIUS request
- * @radius: RADIUS client context from radius_client_init()
- * @msg: RADIUS message to be sent
-@@ -1238,6 +1264,10 @@ radius_change_server(struct radius_clien
- wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
- inet_ntoa(claddr.sin_addr),
- ntohs(claddr.sin_port));
-+ if (auth) {
-+ radius->local_ip.af = AF_INET;
-+ radius->local_ip.u.v4 = claddr.sin_addr;
-+ }
- }
- break;
- #ifdef CONFIG_IPV6
-@@ -1249,6 +1279,10 @@ radius_change_server(struct radius_clien
- inet_ntop(AF_INET6, &claddr6.sin6_addr,
- abuf, sizeof(abuf)),
- ntohs(claddr6.sin6_port));
-+ if (auth) {
-+ radius->local_ip.af = AF_INET6;
-+ radius->local_ip.u.v6 = claddr6.sin6_addr;
-+ }
- }
- break;
- }
---- a/src/radius/radius_client.h
-+++ b/src/radius/radius_client.h
-@@ -249,6 +249,8 @@ int radius_client_register(struct radius
- void radius_client_set_interim_error_cb(struct radius_client_data *radius,
- void (*cb)(const u8 *addr, void *ctx),
- void *ctx);
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+ struct hostapd_ip_addr * addr);
- int radius_client_send(struct radius_client_data *radius,
- struct radius_msg *msg,
- RadiusType msg_type, const u8 *addr);
---- a/src/ap/ieee802_1x.c
-+++ b/src/ap/ieee802_1x.c
-@@ -598,6 +598,10 @@ int add_common_radius_attr(struct hostap
- struct hostapd_radius_attr *attr;
- int len;
-
-+ if (hapd->conf->dynamic_own_ip_addr)
-+ radius_client_get_local_addr(hapd->radius,
-+ &hapd->conf->own_ip_addr);
-+
- if (!hostapd_config_get_radius_attr(req_attr,
- RADIUS_ATTR_NAS_IP_ADDRESS) &&
- hapd->conf->own_ip_addr.af == AF_INET &&
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2688,6 +2688,8 @@ static int hostapd_config_fill(struct ho
- } else if (os_strcmp(buf, "iapp_interface") == 0) {
- wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
- #endif /* CONFIG_IAPP */
-+ } else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
-+ bss->dynamic_own_ip_addr = atoi(pos);
- } else if (os_strcmp(buf, "own_ip_addr") == 0) {
- if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
- wpa_printf(MSG_ERROR,
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/761-shared_das_port.patch b/recipes-wifi/hostapd/files/patches-2.10.3/761-shared_das_port.patch
deleted file mode 100644
index cbb2a1b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/761-shared_das_port.patch
+++ /dev/null
@@ -1,298 +0,0 @@
---- a/src/radius/radius_das.h
-+++ b/src/radius/radius_das.h
-@@ -44,6 +44,7 @@ struct radius_das_attrs {
- struct radius_das_conf {
- int port;
- const u8 *shared_secret;
-+ const u8 *nas_identifier;
- size_t shared_secret_len;
- const struct hostapd_ip_addr *client_addr;
- unsigned int time_window;
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1423,6 +1423,7 @@ int hostapd_setup_bss(struct hostapd_dat
-
- os_memset(&das_conf, 0, sizeof(das_conf));
- das_conf.port = conf->radius_das_port;
-+ das_conf.nas_identifier = conf->nas_identifier;
- das_conf.shared_secret = conf->radius_das_shared_secret;
- das_conf.shared_secret_len =
- conf->radius_das_shared_secret_len;
---- a/src/radius/radius_das.c
-+++ b/src/radius/radius_das.c
-@@ -12,13 +12,26 @@
- #include "utils/common.h"
- #include "utils/eloop.h"
- #include "utils/ip_addr.h"
-+#include "utils/list.h"
- #include "radius.h"
- #include "radius_das.h"
-
-
--struct radius_das_data {
-+static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
-+
-+struct radius_das_port {
-+ struct dl_list list;
-+ struct dl_list das_data;
-+
-+ int port;
- int sock;
-+};
-+
-+struct radius_das_data {
-+ struct dl_list list;
-+ struct radius_das_port *port;
- u8 *shared_secret;
-+ u8 *nas_identifier;
- size_t shared_secret_len;
- struct hostapd_ip_addr client_addr;
- unsigned int time_window;
-@@ -378,56 +391,17 @@ fail:
- }
-
-
--static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+static void
-+radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
-+ struct sockaddr *from, socklen_t fromlen,
-+ char *abuf, int from_port)
- {
-- struct radius_das_data *das = eloop_ctx;
-- u8 buf[1500];
-- union {
-- struct sockaddr_storage ss;
-- struct sockaddr_in sin;
--#ifdef CONFIG_IPV6
-- struct sockaddr_in6 sin6;
--#endif /* CONFIG_IPV6 */
-- } from;
-- char abuf[50];
-- int from_port = 0;
-- socklen_t fromlen;
-- int len;
-- struct radius_msg *msg, *reply = NULL;
-+ struct radius_msg *reply = NULL;
- struct radius_hdr *hdr;
- struct wpabuf *rbuf;
-+ struct os_time now;
- u32 val;
- int res;
-- struct os_time now;
--
-- fromlen = sizeof(from);
-- len = recvfrom(sock, buf, sizeof(buf), 0,
-- (struct sockaddr *) &from.ss, &fromlen);
-- if (len < 0) {
-- wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
-- return;
-- }
--
-- os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
-- from_port = ntohs(from.sin.sin_port);
--
-- wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
-- len, abuf, from_port);
-- if (das->client_addr.u.v4.s_addr &&
-- das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
-- wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
-- return;
-- }
--
-- msg = radius_msg_parse(buf, len);
-- if (msg == NULL) {
-- wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
-- "from %s:%d failed", abuf, from_port);
-- return;
-- }
--
-- if (wpa_debug_level <= MSG_MSGDUMP)
-- radius_msg_dump(msg);
-
- if (radius_msg_verify_das_req(msg, das->shared_secret,
- das->shared_secret_len,
-@@ -494,9 +468,8 @@ static void radius_das_receive(int sock,
- radius_msg_dump(reply);
-
- rbuf = radius_msg_get_buf(reply);
-- res = sendto(das->sock, wpabuf_head(rbuf),
-- wpabuf_len(rbuf), 0,
-- (struct sockaddr *) &from.ss, fromlen);
-+ res = sendto(das->port->sock, wpabuf_head(rbuf),
-+ wpabuf_len(rbuf), 0, from, fromlen);
- if (res < 0) {
- wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
- abuf, from_port, strerror(errno));
-@@ -508,6 +481,72 @@ fail:
- radius_msg_free(reply);
- }
-
-+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+{
-+ struct radius_das_port *p = eloop_ctx;
-+ struct radius_das_data *das;
-+ u8 buf[1500];
-+ union {
-+ struct sockaddr_storage ss;
-+ struct sockaddr_in sin;
-+#ifdef CONFIG_IPV6
-+ struct sockaddr_in6 sin6;
-+#endif /* CONFIG_IPV6 */
-+ } from;
-+ struct radius_msg *msg;
-+ size_t nasid_len = 0;
-+ u8 *nasid_buf = NULL;
-+ char abuf[50];
-+ int from_port = 0;
-+ socklen_t fromlen;
-+ int found = 0;
-+ int len;
-+
-+ fromlen = sizeof(from);
-+ len = recvfrom(sock, buf, sizeof(buf), 0,
-+ (struct sockaddr *) &from.ss, &fromlen);
-+ if (len < 0) {
-+ wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
-+ return;
-+ }
-+
-+ os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
-+ from_port = ntohs(from.sin.sin_port);
-+
-+ msg = radius_msg_parse(buf, len);
-+ if (msg == NULL) {
-+ wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
-+ "from %s:%d failed", abuf, from_port);
-+ return;
-+ }
-+
-+ wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
-+ len, abuf, from_port);
-+
-+ if (wpa_debug_level <= MSG_MSGDUMP)
-+ radius_msg_dump(msg);
-+
-+ radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
-+ &nasid_buf, &nasid_len, NULL);
-+ dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
-+ if (das->client_addr.u.v4.s_addr &&
-+ das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
-+ continue;
-+
-+ if (das->nas_identifier && nasid_buf &&
-+ (nasid_len != os_strlen(das->nas_identifier) ||
-+ os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
-+ continue;
-+
-+ found = 1;
-+ radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
-+ fromlen, abuf, from_port);
-+ }
-+
-+ if (!found)
-+ wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
-+}
-+
-
- static int radius_das_open_socket(int port)
- {
-@@ -533,6 +572,49 @@ static int radius_das_open_socket(int po
- }
-
-
-+static struct radius_das_port *
-+radius_das_open_port(int port)
-+{
-+ struct radius_das_port *p;
-+
-+ dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
-+ if (p->port == port)
-+ return p;
-+ }
-+
-+ p = os_zalloc(sizeof(*p));
-+ if (p == NULL)
-+ return NULL;
-+
-+ dl_list_init(&p->das_data);
-+ p->port = port;
-+ p->sock = radius_das_open_socket(port);
-+ if (p->sock < 0)
-+ goto free_port;
-+
-+ if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
-+ goto close_port;
-+
-+ dl_list_add(&das_ports, &p->list);
-+
-+ return p;
-+
-+close_port:
-+ close(p->sock);
-+free_port:
-+ os_free(p);
-+
-+ return NULL;
-+}
-+
-+static void radius_das_close_port(struct radius_das_port *p)
-+{
-+ dl_list_del(&p->list);
-+ eloop_unregister_read_sock(p->sock);
-+ close(p->sock);
-+ free(p);
-+}
-+
- struct radius_das_data *
- radius_das_init(struct radius_das_conf *conf)
- {
-@@ -553,6 +635,8 @@ radius_das_init(struct radius_das_conf *
- das->ctx = conf->ctx;
- das->disconnect = conf->disconnect;
- das->coa = conf->coa;
-+ if (conf->nas_identifier)
-+ das->nas_identifier = os_strdup(conf->nas_identifier);
-
- os_memcpy(&das->client_addr, conf->client_addr,
- sizeof(das->client_addr));
-@@ -565,19 +649,15 @@ radius_das_init(struct radius_das_conf *
- }
- das->shared_secret_len = conf->shared_secret_len;
-
-- das->sock = radius_das_open_socket(conf->port);
-- if (das->sock < 0) {
-+ das->port = radius_das_open_port(conf->port);
-+ if (!das->port) {
- wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
- "DAS");
- radius_das_deinit(das);
- return NULL;
- }
-
-- if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
-- {
-- radius_das_deinit(das);
-- return NULL;
-- }
-+ dl_list_add(&das->port->das_data, &das->list);
-
- return das;
- }
-@@ -588,11 +668,14 @@ void radius_das_deinit(struct radius_das
- if (das == NULL)
- return;
-
-- if (das->sock >= 0) {
-- eloop_unregister_read_sock(das->sock);
-- close(das->sock);
-+ if (das->port) {
-+ dl_list_del(&das->list);
-+
-+ if (dl_list_empty(&das->port->das_data))
-+ radius_das_close_port(das->port);
- }
-
-+ os_free(das->nas_identifier);
- os_free(das->shared_secret);
- os_free(das);
- }
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/770-radius_server.patch b/recipes-wifi/hostapd/files/patches-2.10.3/770-radius_server.patch
deleted file mode 100644
index 8837a26..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/770-radius_server.patch
+++ /dev/null
@@ -1,154 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -63,6 +63,10 @@ endif
- OBJS += main.o
- OBJS += config_file.o
-
-+ifdef CONFIG_RADIUS_SERVER
-+OBJS += radius.o
-+endif
-+
- OBJS += ../src/ap/hostapd.o
- OBJS += ../src/ap/wpa_auth_glue.o
- OBJS += ../src/ap/drv_callbacks.o
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -40,6 +40,7 @@ struct hapd_global {
-
- static struct hapd_global global;
-
-+extern int radius_main(int argc, char **argv);
-
- #ifndef CONFIG_NO_HOSTAPD_LOGGER
- static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
-@@ -771,6 +772,11 @@ int main(int argc, char *argv[])
- if (os_program_init())
- return -1;
-
-+#ifdef RADIUS_SERVER
-+ if (strstr(argv[0], "radius"))
-+ return radius_main(argc, argv);
-+#endif
-+
- os_memset(&interfaces, 0, sizeof(interfaces));
- interfaces.reload_config = hostapd_reload_config;
- interfaces.config_read_cb = hostapd_config_read;
---- a/src/radius/radius_server.c
-+++ b/src/radius/radius_server.c
-@@ -63,6 +63,12 @@ struct radius_server_counters {
- u32 unknown_acct_types;
- };
-
-+struct radius_accept_attr {
-+ u8 type;
-+ u16 len;
-+ void *data;
-+};
-+
- /**
- * struct radius_session - Internal RADIUS server data for a session
- */
-@@ -90,7 +96,7 @@ struct radius_session {
- unsigned int macacl:1;
- unsigned int t_c_filtering:1;
-
-- struct hostapd_radius_attr *accept_attr;
-+ struct radius_accept_attr *accept_attr;
-
- u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
- };
-@@ -394,6 +400,7 @@ static void radius_server_session_free(s
- radius_msg_free(sess->last_reply);
- os_free(sess->username);
- os_free(sess->nas_ip);
-+ os_free(sess->accept_attr);
- os_free(sess);
- data->num_sess--;
- }
-@@ -554,6 +561,36 @@ radius_server_erp_find_key(struct radius
- }
- #endif /* CONFIG_ERP */
-
-+static struct radius_accept_attr *
-+radius_server_copy_attr(const struct hostapd_radius_attr *data)
-+{
-+ const struct hostapd_radius_attr *attr;
-+ struct radius_accept_attr *attr_new;
-+ size_t data_size = 0;
-+ void *data_buf;
-+ int n_attr = 1;
-+
-+ for (attr = data; attr; attr = attr->next) {
-+ n_attr++;
-+ data_size += wpabuf_len(attr->val);
-+ }
-+
-+ attr_new = os_zalloc(n_attr * sizeof(*attr) + data_size);
-+ if (!attr_new)
-+ return NULL;
-+
-+ data_buf = &attr_new[n_attr];
-+ for (n_attr = 0, attr = data; attr; attr = attr->next) {
-+ struct radius_accept_attr *cur = &attr_new[n_attr++];
-+
-+ cur->type = attr->type;
-+ cur->len = wpabuf_len(attr->val);
-+ cur->data = memcpy(data_buf, wpabuf_head(attr->val), cur->len);
-+ data_buf += cur->len;
-+ }
-+
-+ return attr_new;
-+}
-
- static struct radius_session *
- radius_server_get_new_session(struct radius_server_data *data,
-@@ -607,7 +644,7 @@ radius_server_get_new_session(struct rad
- eap_user_free(tmp);
- return NULL;
- }
-- sess->accept_attr = tmp->accept_attr;
-+ sess->accept_attr = radius_server_copy_attr(tmp->accept_attr);
- sess->macacl = tmp->macacl;
- eap_user_free(tmp);
-
-@@ -1118,11 +1155,10 @@ radius_server_encapsulate_eap(struct rad
- }
-
- if (code == RADIUS_CODE_ACCESS_ACCEPT) {
-- struct hostapd_radius_attr *attr;
-- for (attr = sess->accept_attr; attr; attr = attr->next) {
-- if (!radius_msg_add_attr(msg, attr->type,
-- wpabuf_head(attr->val),
-- wpabuf_len(attr->val))) {
-+ struct radius_accept_attr *attr;
-+ for (attr = sess->accept_attr; attr->data; attr++) {
-+ if (!radius_msg_add_attr(msg, attr->type, attr->data,
-+ attr->len)) {
- wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
- radius_msg_free(msg);
- return NULL;
-@@ -1211,11 +1247,10 @@ radius_server_macacl(struct radius_serve
- }
-
- if (code == RADIUS_CODE_ACCESS_ACCEPT) {
-- struct hostapd_radius_attr *attr;
-- for (attr = sess->accept_attr; attr; attr = attr->next) {
-- if (!radius_msg_add_attr(msg, attr->type,
-- wpabuf_head(attr->val),
-- wpabuf_len(attr->val))) {
-+ struct radius_accept_attr *attr;
-+ for (attr = sess->accept_attr; attr->data; attr++) {
-+ if (!radius_msg_add_attr(msg, attr->type, attr->data,
-+ attr->len)) {
- wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
- radius_msg_free(msg);
- return NULL;
-@@ -2512,7 +2547,7 @@ static int radius_server_get_eap_user(vo
- ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
- phase2, user);
- if (ret == 0 && user) {
-- sess->accept_attr = user->accept_attr;
-+ sess->accept_attr = radius_server_copy_attr(user->accept_attr);
- sess->remediation = user->remediation;
- sess->macacl = user->macacl;
- sess->t_c_timestamp = user->t_c_timestamp;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch b/recipes-wifi/hostapd/files/patches-2.10.3/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch
deleted file mode 100644
index 5809a3b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From f0e9f5aab52b3eab85d28338cc996972ced4c39c Mon Sep 17 00:00:00 2001
-From: David Bauer <mail@david-bauer.net>
-Date: Tue, 17 May 2022 23:07:59 +0200
-Subject: [PATCH] ctrl: make WNM_AP functions dependant on CONFIG_AP
-
-This fixes linking errors found when compiling wpa_supplicant with
-CONFIG_WNM_AP enabled but CONFIG_AP disabled.
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- wpa_supplicant/ctrl_iface.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/wpa_supplicant/ctrl_iface.c
-+++ b/wpa_supplicant/ctrl_iface.c
-@@ -12763,7 +12763,7 @@ char * wpa_supplicant_ctrl_iface_process
- if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
- reply_len = -1;
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_WNM_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
- } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
- if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
- reply_len = -1;
-@@ -12773,7 +12773,7 @@ char * wpa_supplicant_ctrl_iface_process
- } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
- if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
- reply_len = -1;
--#endif /* CONFIG_WNM_AP */
-+#endif /* CONFIG_AP && CONFIG_WNM_AP */
- } else if (os_strcmp(buf, "FLUSH") == 0) {
- wpa_supplicant_ctrl_iface_flush(wpa_s);
- } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/991-Fix-OpenWrt-13156.patch b/recipes-wifi/hostapd/files/patches-2.10.3/991-Fix-OpenWrt-13156.patch
deleted file mode 100644
index 097d62a..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/991-Fix-OpenWrt-13156.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From 26cd9bafc1d25e602952ee86cd2a5b8c3a995490 Mon Sep 17 00:00:00 2001
-From: Stijn Tintel <stijn@linux-ipv6.be>
-Date: Fri, 28 Jul 2023 16:27:47 +0300
-Subject: [PATCH] Revert "Do prune_association only after the STA is
- authorized"
-
-Commit e978072baaca ("Do prune_association only after the STA is
-authorized") causes issues when an STA roams from one interface to
-another interface on the same PHY. The mt7915 driver is not able to
-handle this properly. While the commits fixes a DoS, there are other
-devices and drivers with the same limitation, so revert to the orginal
-behavior for now, until we have a better solution in place.
-
-Ref: https://github.com/openwrt/openwrt/issues/13156
-Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
----
- src/ap/hostapd.c | 14 +++++++++++---
- src/ap/sta_info.c | 3 ---
- 2 files changed, 11 insertions(+), 6 deletions(-)
-
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -3564,6 +3564,8 @@ int hostapd_remove_iface(struct hapd_int
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- int reassoc)
- {
-+ int mld_assoc_link_id = -1;
-+
- if (hapd->tkip_countermeasures) {
- hostapd_drv_sta_deauth(hapd, sta->addr,
- WLAN_REASON_MICHAEL_MIC_FAILURE);
-@@ -3571,10 +3573,16 @@ void hostapd_new_assoc_sta(struct hostap
- }
-
- #ifdef CONFIG_IEEE80211BE
-- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
-- sta->mld_assoc_link_id != hapd->mld_link_id)
-- return;
-+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
-+ if (sta->mld_assoc_link_id == hapd->mld_link_id) {
-+ mld_assoc_link_id = sta->mld_assoc_link_id;
-+ } else {
-+ return;
-+ }
-+ }
- #endif /* CONFIG_IEEE80211BE */
-+ if (mld_assoc_link_id != -2)
-+ hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
-
- ap_sta_clear_disconnect_timeouts(hapd, sta);
- sta->post_csa_sa_query = 0;
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -1318,9 +1318,6 @@ void ap_sta_set_authorized(struct hostap
- mld_assoc_link_id = -2;
- }
- #endif /* CONFIG_IEEE80211BE */
-- if (mld_assoc_link_id != -2)
-- hostapd_prune_associations(hapd, sta->addr,
-- mld_assoc_link_id);
- sta->flags |= WLAN_STA_AUTHORIZED;
- } else {
- sta->flags &= ~WLAN_STA_AUTHORIZED;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch b/recipes-wifi/hostapd/files/patches-2.10.3/992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch
deleted file mode 100644
index c7b595d..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From: David Bauer <mail@david-bauer.net>
-To: hostap@lists.infradead.org
-Cc: =?utf-8?q?=C3=89tienne_Morice?= <neon.emorice@mail.com>
-Subject: [PATCH] nl80211: add extra-ies only if allowed by driver
-Date: Sun, 30 Jan 2022 20:22:00 +0100
-Message-Id: <20220130192200.10883-1-mail@david-bauer.net>
-List-Id: <hostap.lists.infradead.org>
-
-Upgrading wpa_supplicant from 2.9 to 2.10 breaks broadcom-wl
-based adapters. The reason for it is hostapd tries to install additional
-IEs for scanning while the driver does not support this.
-
-The kernel indicates the maximum number of bytes for additional scan IEs
-using the NL80211_ATTR_MAX_SCAN_IE_LEN attribute. Save this value and
-only add additional scan IEs in case the driver can accommodate these
-additional IEs.
-
-Reported-by: Étienne Morice <neon.emorice@mail.com>
-Tested-by: Étienne Morice <neon.emorice@mail.com>
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- src/drivers/driver.h | 3 +++
- src/drivers/driver_nl80211_capa.c | 4 ++++
- src/drivers/driver_nl80211_scan.c | 2 +-
- 3 files changed, 8 insertions(+), 1 deletion(-)
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -2283,6 +2283,9 @@ struct wpa_driver_capa {
- /** Maximum number of iterations in a single scan plan */
- u32 max_sched_scan_plan_iterations;
-
-+ /** Maximum number of extra IE bytes for scans */
-+ u16 max_scan_ie_len;
-+
- /** Whether sched_scan (offloaded scanning) is supported */
- int sched_scan_supported;
-
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -949,6 +949,10 @@ static int wiphy_info_handler(struct nl_
- nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
- }
-
-+ if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
-+ capa->max_scan_ie_len =
-+ nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
-+
- if (tb[NL80211_ATTR_MAX_MATCH_SETS])
- capa->max_match_sets =
- nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
---- a/src/drivers/driver_nl80211_scan.c
-+++ b/src/drivers/driver_nl80211_scan.c
-@@ -222,7 +222,7 @@ nl80211_scan_common(struct i802_bss *bss
- wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
- }
-
-- if (params->extra_ies) {
-+ if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
- wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
- params->extra_ies, params->extra_ies_len);
- if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch b/recipes-wifi/hostapd/files/patches-2.10.3/993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch
deleted file mode 100644
index 948c51b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 7a733993211ad46cf3032038c1e7e6bdc2322336 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 5 Sep 2023 09:43:25 +0800
-Subject: [PATCH] ACS: Fix typo in bw_40 frequency array
-
-The range for the 5 GHz channel 118 was encoded with an incorrect
-channel number.
-
-Fixes: ed8e13decc71 (ACS: Extract bw40/80/160 freqs out of acs_usable_bwXXX_chan())
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/acs.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -256,7 +256,7 @@ struct bw_item {
- 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 },
-+ { 5580, 5600, 118 }, { 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 },
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
deleted file mode 100644
index 25892c8..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
+++ /dev/null
@@ -1,437 +0,0 @@
-From 154b32c66ff22838dc619d85332eadae16cb0353 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 01/54] mtk: hostapd: Add neighbor report and BSS Termination
- for MBO certification
-
-1. 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.
-
-2. Support including neighbor report elements in ANQP response
-3. Support including neignbor report elements in BTM response
-4. Support configuring BSS Termination TSF by using hostapd_cli command
-5. Disable interface if BSS Termination TSF is set
-6. Support including neighbor report elements in BTM request
-7. Add hostapd_neighbor_set_own_report_pref()
-8. Add hostapd_neighbor_set_pref_by_non_pref_chan()
-
-Revert set_send_disassoc_frame_timer
----
- hostapd/ctrl_iface.c | 5 ++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ctrl_iface_ap.c | 19 ++++++-
- src/ap/gas_serv.c | 29 ++++++++++
- src/ap/gas_serv.h | 2 +
- src/ap/neighbor_db.c | 119 +++++++++++++++++++++++++++++++++++++++++
- src/ap/neighbor_db.h | 9 ++++
- src/ap/wnm_ap.c | 42 ++++++++++++++-
- 9 files changed, 223 insertions(+), 4 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 0a892c915..98d598ea9 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1290,6 +1290,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 298216a47..73b33b42a 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -171,6 +171,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
- bss->pasn_comeback_after = 10;
- bss->pasn_noauth = 1;
- #endif /* CONFIG_PASN */
-+ bss->bss_termination_tsf = 0;
- }
-
-
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 0790478ee..82338e213 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -558,6 +558,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;
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 42c959387..0e173f174 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1274,6 +1274,10 @@ 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));
- }
-@@ -1300,14 +1304,25 @@ 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"))
-+ 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
-+ hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
-+
- pos = os_strstr(cmd, "mbo=");
- if (pos) {
- unsigned int mbo_reason, cell_pref, reassoc_delay;
-diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
-index 4642e4927..cce6df41c 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 7646a98a4..ce492b53f 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.
-diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
-index 5b276e8da..1c14b3201 100644
---- a/src/ap/neighbor_db.c
-+++ b/src/ap/neighbor_db.c
-@@ -89,6 +89,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);
-@@ -325,3 +357,90 @@ 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;
-+ }
-+ }
-+ }
-+ }
-+}
-+
-+#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 992671b62..a1ddc075b 100644
---- a/src/ap/neighbor_db.h
-+++ b/src/ap/neighbor_db.h
-@@ -24,4 +24,13 @@ 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);
-+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 */
-diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index ba1dd2ed1..939d4471b 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 "
-@@ -890,6 +911,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)
-@@ -979,6 +1016,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/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
deleted file mode 100644
index 976c625..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 135e2f4368bba88d8823da63a134fdcc587fe698 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 20 Sep 2022 19:33:45 +0800
-Subject: [PATCH 02/54] mtk: hostapd: print sae groups by hostapd ctrl
-
----
- hostapd/ctrl_iface.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 98d598ea9..c03e6f608 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1364,6 +1364,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
- if (os_snprintf_error(buflen, res))
- return -1;
- return res;
-+ } else if (os_strcmp(cmd, "sae_group_capability") == 0) {
-+#ifdef CONFIG_SAE
-+ /* see sae_set_group() */
-+ res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
-+ dh_groups_get(15) ? "15 ": "",
-+ dh_groups_get(16) ? "16 ": "",
-+ dh_groups_get(17) ? "17 ": "",
-+ dh_groups_get(18) ? "18 ": "");
-+
-+ if (os_snprintf_error(buflen, res))
-+ return -1;
-+ return res;
-+#endif /* CONFIG_SAE */
- }
-
- return -1;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
deleted file mode 100644
index 29b4c03..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
+++ /dev/null
@@ -1,211 +0,0 @@
-From 55220dcc0fcd43270edf583720e0b36e453dc2d7 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 31 May 2022 21:15:54 +0800
-Subject: [PATCH 03/54] mtk: hostapd: add support for runtime set in-band
- discovery
-
-Usage:
-hostapd_cli unsolic_probe_resp [tx_type] [interval]
-
-0: disable all in-band discovery
-1: enable unsolicited probe response
-2: enable FILS discovery
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
----
- hostapd/ctrl_iface.c | 66 ++++++++++++++++++++++++++++++++++++
- hostapd/hostapd_cli.c | 20 +++++++++++
- src/ap/beacon.c | 5 ++-
- src/drivers/driver_nl80211.c | 10 ++++--
- src/drivers/nl80211_copy.h | 1 +
- 5 files changed, 98 insertions(+), 4 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index c03e6f608..ee6d492f8 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -770,6 +770,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
-
- #endif /* CONFIG_INTERWORKING */
-
-+static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
-+ const char *cmd)
-+{
-+ struct hostapd_bss_config *conf = hapd->conf;
-+ const char *pos = cmd;
-+ int tx_type, interval, ret;
-+
-+ tx_type = atoi(pos);
-+ if (tx_type < 0 || tx_type > 2) {
-+ wpa_printf(MSG_ERROR, "Invalid tx type\n");
-+ return -1;
-+ }
-+
-+ pos = os_strchr(pos, ' ');
-+ if(!pos)
-+ return -1;
-+ pos++;
-+ interval = atoi(pos);
-+ if (interval < 0 || interval > 20) {
-+ wpa_printf(MSG_ERROR, "Invalid interval value\n");
-+ return -1;
-+ }
-+
-+ wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
-+ tx_type, interval);
-+
-+#define DISABLE_INBAND_DISC 0
-+#define UNSOL_PROBE_RESP 1
-+#define FILS_DISCOVERY 2
-+
-+#ifdef CONFIG_FILS
-+ conf->fils_discovery_max_int = 0;
-+ conf->fils_discovery_min_int = 0;
-+#endif /* CONFIG_FILS */
-+ conf->unsol_bcast_probe_resp_interval = 0;
-+
-+ switch (tx_type) {
-+ case DISABLE_INBAND_DISC:
-+ default:
-+ /* Disable both Unsolicited probe response and FILS discovery*/
-+ break;
-+ case UNSOL_PROBE_RESP:
-+ /* Enable Unsolicited probe response */
-+ conf->unsol_bcast_probe_resp_interval = interval;
-+ break;
-+#ifdef CONFIG_FILS
-+ case FILS_DISCOVERY:
-+ /* Enable FILS discovery */
-+ conf->fils_discovery_min_int = interval;
-+ conf->fils_discovery_max_int = interval;
-+ break;
-+#endif /* CONFIG_FILS */
-+ }
-+
-+ ret = ieee802_11_update_beacons(hapd->iface);
-+ if(ret) {
-+ wpa_printf(MSG_DEBUG,
-+ "Failed to update with inband discovery parameters\n");
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-
- #ifdef CONFIG_WNM_AP
-
-@@ -3483,6 +3546,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
- reply_len = -1;
- #endif /* CONFIG_WNM_AP */
-+ } else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
-+ if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
-+ reply_len = -1;
- } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
- reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
- reply_size);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 61f8cba12..dfc996d49 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -655,6 +655,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
- return wpa_ctrl_command(ctrl, buf);
- }
-
-+static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ char buf[300];
-+ int res;
-+
-+ if (argc < 2) {
-+ printf("Invalid 'inband_discovery' command - two arguments"
-+ "tx_type interval\n");
-+ return -1;
-+ }
-+
-+ res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
-+ argv[0], argv[1]);
-+ if (os_snprintf_error(sizeof(buf), res))
-+ return -1;
-+ return wpa_ctrl_command(ctrl, buf);
-+}
-
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
-@@ -1773,6 +1791,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- { "driver", hostapd_cli_cmd_driver, NULL,
- "<driver sub command> [<hex formatted data>] = send driver command data" },
- #endif /* ANDROID */
-+ { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
-+ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 6366d77f0..d160675cb 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1648,6 +1648,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- struct wpa_driver_ap_params *params)
- {
- params->fd_max_int = hapd->conf->fils_discovery_max_int;
-+ params->unsol_bcast_probe_resp_interval =
-+ hapd->conf->unsol_bcast_probe_resp_interval;
- if (is_6ghz_op_class(hapd->iconf->op_class) &&
- params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
- params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
-@@ -1656,7 +1658,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- if (params->fd_min_int > params->fd_max_int)
- params->fd_min_int = params->fd_max_int;
-
-- if (params->fd_max_int)
-+ if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
-+ !params->unsol_bcast_probe_resp_interval))
- return hostapd_gen_fils_discovery(hapd,
- ¶ms->fd_frame_tmpl_len);
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 98510f1cf..a3e436e95 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -4706,9 +4706,10 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
- params->fd_max_int) ||
- (params->fd_frame_tmpl &&
- nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
-- params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
-+ params->fd_frame_tmpl_len, params->fd_frame_tmpl)) ||
-+ nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INTE,
-+ params->unsol_bcast_probe_resp_interval))
- return -1;
--
- nla_nest_end(msg, attr);
- return 0;
- }
-@@ -5320,7 +5321,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
- #endif /* CONFIG_SAE */
-
- #ifdef CONFIG_FILS
-- if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
-+ if ((params->fd_max_int ||
-+ ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
-+ !(params->unsol_bcast_probe_resp_interval))) &&
-+ nl80211_fils_discovery(bss, msg, params) < 0)
- goto fail;
- #endif /* CONFIG_FILS */
-
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index c59fec406..82860ae32 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -7591,6 +7591,7 @@ enum nl80211_fils_discovery_attributes {
- NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
- NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
- NL80211_FILS_DISCOVERY_ATTR_TMPL,
-+ NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INTE,
-
- /* keep last */
- __NL80211_FILS_DISCOVERY_ATTR_LAST,
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch
deleted file mode 100644
index 16949b7..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch
+++ /dev/null
@@ -1,216 +0,0 @@
-From 2bba6f165367d21c44cb4da8b74904ecee956d55 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 15:04:57 +0800
-Subject: [PATCH 04/54] mtk: hostapd: Add mtk_vendor.h
-
----
- src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 197 insertions(+)
- create mode 100644 src/common/mtk_vendor.h
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-new file mode 100644
-index 000000000..4a19d2fc9
---- /dev/null
-+++ b/src/common/mtk_vendor.h
-@@ -0,0 +1,197 @@
-+// SPDX-License-Identifier: ISC
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+#ifndef MTK_VENDOR_H
-+#define MTK_VENDOR_H
-+
-+#define OUI_MTK 0x000ce7
-+
-+enum mtk_nl80211_vendor_subcmds {
-+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
-+ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
-+ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl {
-+ MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
-+
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+ EDCCA_CTRL_SET_EN = 0,
-+ EDCCA_CTRL_SET_THERS,
-+ EDCCA_CTRL_GET_EN,
-+ EDCCA_CTRL_GET_THERS,
-+ EDCCA_CTRL_NUM,
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+enum mtk_vendor_attr_csi_ctrl {
-+ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
-+ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
-+ MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_DATA,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
-+ MTK_VENDOR_ATTR_CSI_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_csi_data {
-+ MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
-+ MTK_VENDOR_ATTR_CSI_DATA_PAD,
-+
-+ MTK_VENDOR_ATTR_CSI_DATA_VER,
-+ MTK_VENDOR_ATTR_CSI_DATA_TS,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSSI,
-+ MTK_VENDOR_ATTR_CSI_DATA_SNR,
-+ MTK_VENDOR_ATTR_CSI_DATA_BW,
-+ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
-+ MTK_VENDOR_ATTR_CSI_DATA_TA,
-+ MTK_VENDOR_ATTR_CSI_DATA_I,
-+ MTK_VENDOR_ATTR_CSI_DATA_Q,
-+ MTK_VENDOR_ATTR_CSI_DATA_INFO,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
-+ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
-+ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
-+ MTK_VENDOR_ATTR_CSI_DATA_MODE,
-+ MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-+ MTK_VENDOR_ATTR_CSI_DATA_MAX =
-+ NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_ctrl {
-+ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_CTRL_SET,
-+ MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
-+ MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_set {
-+ MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_SET_INDEX,
-+ MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_SET,
-+ MTK_VENDOR_ATTR_AMNT_SET_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_dump {
-+ MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_ctrl {
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_rfeature_ctrl {
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
-+};
-+
-+#define CSI_MAX_COUNT 256
-+#define ETH_ALEN 6
-+
-+struct csi_data {
-+ s16 data_i[CSI_MAX_COUNT];
-+ s16 data_q[CSI_MAX_COUNT];
-+ s8 rssi;
-+ u8 snr;
-+ u32 ts;
-+ u8 data_bw;
-+ u8 pri_ch_idx;
-+ u8 ta[ETH_ALEN];
-+ u32 info;
-+ u8 rx_mode;
-+ u32 h_idx;
-+ u16 tx_idx;
-+ u16 rx_idx;
-+};
-+
-+struct amnt_data {
-+ u8 idx;
-+ u8 addr[ETH_ALEN];
-+ s8 rssi[4];
-+ u32 last_seen;
-+};
-+#endif /* MTK_VENDOR_H */
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
deleted file mode 100644
index 37f5172..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
+++ /dev/null
@@ -1,631 +0,0 @@
-From 7e1b6b0dc2167af5b9d58466ce693b67e6b5dbf2 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 16:31:34 +0800
-Subject: [PATCH 05/54] mtk: hostapd: Support EDCCA hostapd configuration
-
-edcca_enable and edcca_compensation and implement edcca related handlers.
----
- hostapd/config_file.c | 34 ++++++
- hostapd/ctrl_iface.c | 125 +++++++++++++++++++++
- src/ap/ap_config.c | 4 +
- src/ap/ap_config.h | 30 ++++++
- src/ap/ap_drv_ops.c | 24 +++++
- src/ap/ap_drv_ops.h | 4 +
- src/ap/hostapd.c | 7 ++
- src/common/mtk_vendor.h | 20 ++--
- src/drivers/driver.h | 4 +
- src/drivers/driver_nl80211.c | 174 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 7 ++
- 12 files changed, 428 insertions(+), 6 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 4b0f99fd2..d281026e8 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4809,6 +4809,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- #endif /* CONFIG_IEEE80211BE */
-+ } else if (os_strcmp(buf, "edcca_threshold") == 0) {
-+ if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
-+ conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
-+ conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
-+ conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
-+ conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
-+ wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
-+ line);
-+ return 1;
-+ }
-+ } else if (os_strcmp(buf, "edcca_enable") == 0) {
-+ int mode = atoi(pos);
-+ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+ wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
-+ " allowed value 0 (Force Disable) or 1(Auto) ",
-+ line, mode);
-+ return 1;
-+ }
-+ conf->edcca_enable = (u8) mode;
-+ } else if (os_strcmp(buf, "edcca_compensation") == 0) {
-+ int val = atoi(pos);
-+ if (val < EDCCA_MIN_COMPENSATION ||
-+ val > EDCCA_MAX_COMPENSATION) {
-+ wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
-+ " value %d; allowed value %d ~ %d.",
-+ line, val, EDCCA_MIN_COMPENSATION,
-+ EDCCA_MAX_COMPENSATION);
-+ return 1;
-+ }
-+ conf->edcca_compensation = (s8) val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ee6d492f8..cad3f863c 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -542,6 +542,19 @@ static const char * pbc_status_str(enum pbc_status status)
- }
-
-
-+static const char *edcca_mode_str(enum edcca_mode status)
-+{
-+ switch (status) {
-+ case EDCCA_MODE_FORCE_DISABLE:
-+ return "Force Disable";
-+ case EDCCA_MODE_AUTO:
-+ return "Auto";
-+ default:
-+ return "Unknown";
-+ }
-+}
-+
-+
- static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
- char *buf, size_t buflen)
- {
-@@ -3369,6 +3382,112 @@ static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
- #endif /* ANDROID */
-
-
-+static int
-+hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *config, *value;
-+ config = cmd;
-+ pos = os_strchr(config, ' ');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if (pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strcmp(config, "enable") == 0) {
-+ int mode = atoi(value);
-+ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+ wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
-+ return -1;
-+ }
-+ hapd->iconf->edcca_enable = (u8) mode;
-+ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+ return -1;
-+ } else if (os_strcmp(config, "compensation") == 0) {
-+ int compensation = atoi(value);
-+ if (compensation < EDCCA_MIN_COMPENSATION ||
-+ compensation > EDCCA_MAX_COMPENSATION) {
-+ wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
-+ return -1;
-+ }
-+ hapd->iconf->edcca_compensation = (s8) compensation;
-+ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+ return -1;
-+ } else if (os_strcmp(config, "threshold") == 0) {
-+ char *thres_value;
-+ thres_value = os_strchr(value, ':');
-+ if (thres_value == NULL)
-+ return -1;
-+ *thres_value++ = '\0';
-+
-+ if (thres_value == NULL)
-+ return -1;
-+ int bw_idx = atoi(value);
-+ int threshold = atoi(thres_value);
-+
-+ if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported Bandwidth idx %d for SET_EDCCA",
-+ bw_idx);
-+ return -1;
-+ }
-+ if (threshold < EDCCA_MIN_CONFIG_THRES ||
-+ threshold > EDCCA_MAX_CONFIG_THRES) {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported threshold %d for SET_EDCCA",
-+ threshold);
-+ return -1;
-+ }
-+
-+ int threshold_arr[EDCCA_MAX_BW_NUM];
-+ /* 0x7f means keep the origival value in firmware */
-+ os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
-+ threshold_arr[bw_idx] = threshold;
-+
-+ if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
-+ return -1;
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for SET_EDCCA", config);
-+ return -1;
-+ }
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
-+ size_t buflen)
-+{
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+ u8 value[EDCCA_MAX_BW_NUM] = {0};
-+
-+ if (os_strcmp(cmd, "enable") == 0) {
-+ return os_snprintf(pos, end - pos, "Enable: %s\n",
-+ edcca_mode_str(hapd->iconf->edcca_enable));
-+ } else if (os_strcmp(cmd, "compensation") == 0) {
-+ return os_snprintf(pos, end - pos, "Compensation: %d\n",
-+ hapd->iconf->edcca_compensation);
-+ } else if (os_strcmp(cmd, "threshold") == 0) {
-+ if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
-+ return -1;
-+ return os_snprintf(pos, end - pos,
-+ "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
-+ value[0], value[1], value[2], value[3]);
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for GET_EDCCA", cmd);
-+ return -1;
-+ }
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -3922,6 +4041,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
- reply_size);
- #endif /* ANDROID */
-+ } else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
-+ reply_size);
-+ } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
-+ reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 73b33b42a..8e56d1082 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -295,6 +295,9 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
- #endif /* CONFIG_AIRTIME_POLICY */
-
-+ conf->edcca_enable = EDCCA_MODE_AUTO;
-+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+
- return conf;
- }
-
-@@ -1008,6 +1011,7 @@ void hostapd_config_free(struct hostapd_config *conf)
- #ifdef CONFIG_ACS
- os_free(conf->acs_chan_bias);
- #endif /* CONFIG_ACS */
-+ os_free(conf->edcca_threshold);
- wpabuf_free(conf->lci);
- wpabuf_free(conf->civic);
-
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 82338e213..24d540dbf 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1193,8 +1193,38 @@ struct hostapd_config {
- MBSSID_ENABLED = 1,
- ENHANCED_MBSSID_ENABLED = 2,
- } mbssid;
-+
-+ u8 edcca_enable;
-+ s8 edcca_compensation;
-+ int *edcca_threshold;
-+};
-+
-+enum edcca_mode {
-+ EDCCA_MODE_FORCE_DISABLE = 0,
-+ EDCCA_MODE_AUTO = 1,
-+};
-+
-+enum edcca_bw_id {
-+ EDCCA_BW_20 = 0,
-+ EDCCA_BW_40,
-+ EDCCA_BW_80,
-+ EDCCA_BW_160,
-+ EDCCA_MAX_BW_NUM,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+ EDCCA_CTRL_SET_EN = 0,
-+ EDCCA_CTRL_SET_THRES,
-+ EDCCA_CTRL_GET_EN,
-+ EDCCA_CTRL_GET_THRES,
-+ EDCCA_CTRL_NUM,
- };
-
-+#define EDCCA_DEFAULT_COMPENSATION -6
-+#define EDCCA_MIN_COMPENSATION -126
-+#define EDCCA_MAX_COMPENSATION 126
-+#define EDCCA_MIN_CONFIG_THRES -126
-+#define EDCCA_MAX_CONFIG_THRES 0
-
- static inline enum oper_chan_width
- hostapd_get_oper_chwidth(struct hostapd_config *conf)
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 75ddfa15c..99ba973aa 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1137,3 +1137,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms);
- }
- #endif /* CONFIG_PASN */
-+
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->configure_edcca_enable)
-+ return 0;
-+ return hapd->driver->configure_edcca_enable(hapd->drv_priv,
-+ hapd->iconf->edcca_enable,
-+ hapd->iconf->edcca_compensation);
-+}
-+
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+ const int *threshold)
-+{
-+ if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
-+ return 0;
-+ return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
-+}
-+
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
-+{
-+ if (!hapd->driver || !hapd->driver->get_edcca)
-+ return 0;
-+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 96c8c4e2c..6ca693b0b 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -144,6 +144,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- u8 ltf_keyseed_len,
- const u8 *ltf_keyseed, u32 action);
-
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+ const int *threshold);
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index a203546b6..f7c80c73b 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2511,6 +2511,13 @@ dfs_offload:
- }
- #endif /* CONFIG_MESH */
-
-+ if (hostapd_drv_configure_edcca_enable(hapd) < 0)
-+ goto fail;
-+
-+ if (hostapd_drv_configure_edcca_threshold(hapd,
-+ hapd->iconf->edcca_threshold) < 0)
-+ goto fail;
-+
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
- if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 4a19d2fc9..6121857dd 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
- NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
- };
-
--enum mtk_vendor_attr_edcca_ctrl_mode {
-- EDCCA_CTRL_SET_EN = 0,
-- EDCCA_CTRL_SET_THERS,
-- EDCCA_CTRL_GET_EN,
-- EDCCA_CTRL_GET_THERS,
-- EDCCA_CTRL_NUM,
-+enum mtk_vendor_attr_edcca_dump {
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
-+
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
-
-+
- static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
- [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index c5cc26737..7d71aa783 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5105,6 +5105,10 @@ struct wpa_driver_ops {
- const u8 *match, size_t match_len,
- bool multicast);
- #endif /* CONFIG_TESTING_OPTIONS */
-+ int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
-+ const s8 edcca_compensation);
-+ int (*configure_edcca_threshold)(void *priv, const int *threshold);
-+ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index a3e436e95..1a2f52b77 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -37,6 +37,8 @@
- #include "radiotap_iter.h"
- #include "rfkill.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
-+#include "ap/ap_config.h"
-
-
- #ifndef NETLINK_CAP_ACK
-@@ -13768,6 +13770,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
-
- #endif /* CONFIG_TESTING_OPTIONS */
-
-+static int nl80211_configure_edcca_enable(void *priv,
-+ const u8 edcca_enable,
-+ const s8 edcca_compensation)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_edcca_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting EDCCA enable");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+ edcca_compensation)) {
-+ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_edcca_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting EDCCA threshold");
-+ return 0;
-+ }
-+
-+ if (!threshold) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Input EDCCA threshold is empty!");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
-+ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+
-+static int edcca_info_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *info = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info++ = nla_get_u8(attr);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info++ = nla_get_u8(attr);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info++ = nla_get_u8(attr);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info = nla_get_u8(attr);
-+ return NL_SKIP;
-+}
-+
-+
-+static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_edcca_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting EDCCA threshold");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
-+ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, edcca_info_handler, value, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
-@@ -13924,4 +14094,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .register_frame = testing_nl80211_register_frame,
- .radio_disable = testing_nl80211_radio_disable,
- #endif /* CONFIG_TESTING_OPTIONS */
-+/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
-+ .configure_edcca_enable = nl80211_configure_edcca_enable,
-+ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
-+ .get_edcca = nl80211_get_edcca,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index aee8c4512..51b3fbec8 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
- unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
- unsigned int puncturing:1;
- unsigned int qca_ap_allowed_freqs:1;
-+ unsigned int mtk_edcca_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index f01a526a0..47654f65b 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -18,6 +18,7 @@
- #include "common/qca-vendor-attr.h"
- #include "common/brcm_vendor.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
-
-
- static int protocol_feature_handler(struct nl_msg *msg, void *arg)
-@@ -1111,6 +1112,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- break;
- }
- #endif /* CONFIG_DRIVER_NL80211_BRCM */
-+ } else if (vinfo->vendor_id == OUI_MTK) {
-+ switch (vinfo->subcmd) {
-+ case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
-+ drv->mtk_edcca_vendor_cmd_avail = 1;
-+ break;
-+ }
- }
-
- wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
deleted file mode 100644
index e0bdc50..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
+++ /dev/null
@@ -1,454 +0,0 @@
-From 8ed06968aea1170b6fbb7d88fbf546764c404eae Mon Sep 17 00:00:00 2001
-From: TomLiu <tomml.liu@mediatek.com>
-Date: Tue, 9 Aug 2022 10:23:44 -0700
-Subject: [PATCH 06/54] mtk: hostapd: Add hostapd MU SET/GET control
-
----
- hostapd/config_file.c | 9 +++
- hostapd/ctrl_iface.c | 66 ++++++++++++++++++
- hostapd/hostapd_cli.c | 18 +++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ap_drv_ops.c | 14 ++++
- src/ap/ap_drv_ops.h | 2 +
- src/ap/hostapd.c | 2 +
- src/common/mtk_vendor.h | 15 ++++
- src/drivers/driver.h | 13 ++++
- src/drivers/driver_nl80211.c | 110 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 13 files changed, 255 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index d281026e8..ec3b41abe 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3677,6 +3677,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- conf->mbssid = mbssid;
-+ } else if (os_strcmp(buf, "mu_onoff") == 0) {
-+ int val = atoi(pos);
-+ if (val < 0 || val > 15) {
-+ wpa_printf(MSG_ERROR,
-+ "Line %d: invalid mu_onoff value",
-+ line);
-+ return 1;
-+ }
-+ conf->mu_onoff = val;
- #endif /* CONFIG_IEEE80211AX */
- } else if (os_strcmp(buf, "max_listen_interval") == 0) {
- bss->max_listen_interval = atoi(pos);
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index cad3f863c..b10483652 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3488,6 +3488,67 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
- }
-
-
-+static int
-+hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *config, *value;
-+ config = cmd;
-+ pos = os_strchr(config, ' ');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if(pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strcmp(config, "onoff") == 0) {
-+ int mu = atoi(value);
-+ if (mu < 0 || mu > 15) {
-+ wpa_printf(MSG_ERROR, "Invalid value for mu");
-+ return -1;
-+ }
-+ hapd->iconf->mu_onoff = (u8) mu;
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for SET_MU", config);
-+ return -1;
-+ }
-+
-+ if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+ return os_snprintf(buf, buflen, "OK\n");
-+ } else {
-+ return -1;
-+ }
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ u8 mu_onoff;
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hapd->iface->state != HAPD_IFACE_ENABLED)
-+ return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
-+ hostapd_state_text(hapd->iface->state));
-+
-+ if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
-+ hapd->iconf->mu_onoff = mu_onoff;
-+ return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
-+ !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
-+ } else {
-+ wpa_printf(MSG_INFO, "ctrl iface failed to call");
-+ return -1;
-+ }
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4047,6 +4108,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
- reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- reply_size);
-+ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
-+ reply_size);
-+ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index dfc996d49..98892ee9d 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1400,6 +1400,20 @@ static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
-+}
-+
-+
-+static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
-+}
-+
-+
- #ifdef CONFIG_DPP
-
- static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
-@@ -1729,6 +1743,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- " = send FTM range request"},
- { "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
- " = show supported driver flags"},
-+ { "set_mu", hostapd_cli_cmd_set_mu, NULL,
-+ "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
-+ { "get_mu", hostapd_cli_cmd_get_mu, NULL,
-+ " = show mu onoff value in 0-15 bitmap"},
- #ifdef CONFIG_DPP
- { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
- "report a scanned DPP URI from a QR Code" },
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 8e56d1082..cf7f56392 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -281,6 +281,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->he_6ghz_max_ampdu_len_exp = 7;
- conf->he_6ghz_rx_ant_pat = 1;
- conf->he_6ghz_tx_ant_pat = 1;
-+ conf->mu_onoff = 15;
- #endif /* CONFIG_IEEE80211AX */
-
- /* The third octet of the country string uses an ASCII space character
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 24d540dbf..421e6a647 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1146,6 +1146,7 @@ struct hostapd_config {
- u8 he_6ghz_tx_ant_pat;
- u8 he_6ghz_reg_pwr_type;
- bool require_he;
-+ u8 mu_onoff;
- #endif /* CONFIG_IEEE80211AX */
-
- /* VHT enable/disable config from CHAN_SWITCH */
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 99ba973aa..44f494ed9 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1161,3 +1161,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- return 0;
- return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-+
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->mu_ctrl)
-+ return 0;
-+ return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+}
-+
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-+{
-+ if (!hapd->driver || !hapd->driver->mu_dump)
-+ return 0;
-+ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 6ca693b0b..8a7d98128 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -148,6 +148,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f7c80c73b..65fdc47da 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2517,6 +2517,8 @@ dfs_offload:
- if (hostapd_drv_configure_edcca_threshold(hapd,
- hapd->iconf->edcca_threshold) < 0)
- goto fail;
-+ if (hostapd_drv_mu_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6121857dd..60bc4cd4c 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
- MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
- MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- };
-
-@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
- NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
- };
-
-+enum mtk_vendor_attr_mu_ctrl {
-+ MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
-+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-+ MTK_VENDOR_ATTR_MU_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
-+};
-+
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 7d71aa783..a23de244f 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -176,6 +176,11 @@ struct hostapd_channel_data {
- * punct_bitmap - RU puncturing bitmap
- */
- u16 punct_bitmap;
-+
-+ /**
-+ * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
-+ */
-+ u8 mu_onoff;
- };
-
- #define HE_MAC_CAPAB_0 0
-@@ -5109,6 +5114,14 @@ struct wpa_driver_ops {
- const s8 edcca_compensation);
- int (*configure_edcca_threshold)(void *priv, const int *threshold);
- int (*get_edcca)(void *priv, const u8 mode, u8 *value);
-+
-+ /**
-+ * mu_ctrl - ctrl on off for UL/DL MURU
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+ int (*mu_dump)(void *priv, u8 *mu_onoff);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 1a2f52b77..f7f157bf0 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13633,6 +13633,114 @@ fail:
- }
-
-
-+#ifdef CONFIG_IEEE80211AX
-+static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_mu_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting mu control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if(ret){
-+ wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+
-+static int mu_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *mu_onoff = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ static const struct nla_policy
-+ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
-+ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+ };
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
-+ return NL_SKIP;
-+ }
-+
-+ *mu_onoff = nla_get_u8(attr);
-+ wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
-+
-+ return 0;
-+}
-+
-+static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *attr;
-+ int ret;
-+
-+ if (!drv->mtk_mu_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting mu control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+
-+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!attr) {
-+ nlmsg_free(msg);
-+ return -1;
-+ }
-+
-+ nla_nest_end(msg, attr);
-+
-+ ret = send_and_recv_msgs(drv, msg, mu_dump_handler, mu_onoff, NULL, NULL);
-+
-+ if(ret){
-+ wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+}
-+#endif /* CONFIG_IEEE80211AX */
-+
-+
- #ifdef CONFIG_DPP
- static int nl80211_dpp_listen(void *priv, bool enable)
- {
-@@ -14085,6 +14193,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .update_connect_params = nl80211_update_connection_params,
- .send_external_auth_status = nl80211_send_external_auth_status,
- .set_4addr_mode = nl80211_set_4addr_mode,
-+ .mu_ctrl = nl80211_mu_onoff,
-+ .mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- .dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 51b3fbec8..bd5d28404 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
- unsigned int puncturing:1;
- unsigned int qca_ap_allowed_freqs:1;
- unsigned int mtk_edcca_vendor_cmd_avail:1;
-+ unsigned int mtk_mu_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 47654f65b..07f6cb133 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1117,6 +1117,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
- drv->mtk_edcca_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
-+ drv->mtk_mu_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
deleted file mode 100644
index c24b78a..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 022b58d6277d12517ada28d8b5581a75e501d779 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 2 Sep 2022 01:03:23 +0800
-Subject: [PATCH 07/54] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
- command
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c | 4 ++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 13 ++++++++++++
- src/ap/ap_drv_ops.c | 11 +++++++++++
- src/ap/ap_drv_ops.h | 1 +
- src/ap/hostapd.c | 2 ++
- src/common/mtk_vendor.h | 16 +++++++++++++++
- src/drivers/driver.h | 8 ++++++++
- src/drivers/driver_nl80211.c | 33 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +++
- 11 files changed, 93 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index ec3b41abe..d515b6ea9 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4852,6 +4852,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- conf->edcca_compensation = (s8) val;
-+ } else if (os_strcmp(buf, "three_wire_enable") == 0) {
-+ u8 en = atoi(pos);
-+
-+ conf->three_wire_enable = en;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index cf7f56392..8b1154553 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -298,6 +298,7 @@ struct hostapd_config * hostapd_config_defaults(void)
-
- conf->edcca_enable = EDCCA_MODE_AUTO;
- conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 421e6a647..52df2e0c0 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1198,6 +1198,19 @@ struct hostapd_config {
- u8 edcca_enable;
- s8 edcca_compensation;
- int *edcca_threshold;
-+ u8 three_wire_enable;
-+};
-+
-+enum three_wire_mode {
-+ THREE_WIRE_MODE_DISABLE,
-+ THREE_WIRE_MODE_EXT0_ENABLE,
-+ THREE_WIRE_MODE_EXT1_ENABLE,
-+ THREE_WIRE_MODE_ALL_ENABLE,
-+
-+ /* keep last */
-+ NUM_THREE_WIRE_MODE,
-+ THREE_WIRE_MODE_MAX =
-+ NUM_THREE_WIRE_MODE - 1
- };
-
- enum edcca_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 44f494ed9..2f15f99f4 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1175,3 +1175,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
- return 0;
- return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
- }
-+
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->three_wire_ctrl)
-+ return 0;
-+ if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
-+ wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
-+ return 0;
-+ }
-+ return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 8a7d98128..ed3b4cf11 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -150,6 +150,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 65fdc47da..5487c9489 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2519,6 +2519,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_mu_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 60bc4cd4c..99ecbaf71 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
- };
-
-+enum mtk_vendor_attr_3wire_ctrl {
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_csi_ctrl {
- MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index a23de244f..03d268b2e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5122,6 +5122,14 @@ struct wpa_driver_ops {
- */
- int (*mu_ctrl)(void *priv, u8 mu_onoff);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-+
-+ /**
-+ * three_wire_ctrl - set three_wire_ctrl mode
-+ * @priv: Private driver interface data
-+ * @three_wire_enable: three_wire_ctrl mode
-+ *
-+ */
-+ int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index f7f157bf0..d5c0ea81a 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14046,6 +14046,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
- return ret;
- }
-
-+static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ /* Prepare nl80211 cmd */
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_3wire_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting three wire control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
-@@ -14208,4 +14240,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .configure_edcca_enable = nl80211_configure_edcca_enable,
- .configure_edcca_threshold = nl80211_configure_edcca_threshold,
- .get_edcca = nl80211_get_edcca,
-+ .three_wire_ctrl = nl80211_enable_three_wire,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index bd5d28404..99af8b075 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
- unsigned int qca_ap_allowed_freqs:1;
- unsigned int mtk_edcca_vendor_cmd_avail:1;
- unsigned int mtk_mu_vendor_cmd_avail:1;
-+ unsigned int mtk_3wire_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 07f6cb133..47ba17933 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1120,6 +1120,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
- drv->mtk_mu_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
-+ drv->mtk_3wire_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch
deleted file mode 100644
index 8ff3fe9..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch
+++ /dev/null
@@ -1,431 +0,0 @@
-From 23191149c4b3be30766166cd68db4beb6d57af78 Mon Sep 17 00:00:00 2001
-From: mtk27835 <shurong.wen@mediatek.com>
-Date: Wed, 7 Sep 2022 14:41:51 -0700
-Subject: [PATCH 08/54] mtk: hostapd: Add hostapd iBF control
-
-Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
----
- hostapd/config_file.c | 3 +
- hostapd/ctrl_iface.c | 26 +++++++
- hostapd/hostapd_cli.c | 9 +++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 2 +
- src/ap/ap_drv_ops.c | 14 ++++
- src/ap/ap_drv_ops.h | 2 +
- src/ap/hostapd.c | 2 +
- src/common/mtk_vendor.h | 35 +++++++++-
- src/drivers/driver.h | 19 ++++++
- src/drivers/driver_nl80211.c | 108 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 13 files changed, 224 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index d515b6ea9..f8560a721 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4856,6 +4856,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- u8 en = atoi(pos);
-
- conf->three_wire_enable = en;
-+ } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
-+ int val = atoi(pos);
-+ conf->ibf_enable = !!val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index b10483652..cf7cc3923 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3549,6 +3549,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
- }
-
-
-+static int
-+hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ u8 ibf_enable;
-+ int ret;
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
-+ hapd->iconf->ibf_enable = ibf_enable;
-+ ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
-+ ibf_enable);
-+ }
-+
-+ if (os_snprintf_error(end - pos, ret))
-+ return 0;
-+
-+ return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4113,6 +4137,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_size);
- } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 98892ee9d..4fa2d323d 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1606,6 +1606,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- #endif /* ANDROID */
-
-
-+static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- const char *cmd;
- int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1811,6 +1818,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- #endif /* ANDROID */
- { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
- "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
-+ { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
-+ " = show iBF state (enabled/disabled)"},
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 8b1154553..c9b9683bb 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -299,6 +299,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->edcca_enable = EDCCA_MODE_AUTO;
- conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
-+ conf->ibf_enable = IBF_DEFAULT_ENABLE;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 52df2e0c0..ffbc4fb4f 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1199,6 +1199,7 @@ struct hostapd_config {
- s8 edcca_compensation;
- int *edcca_threshold;
- u8 three_wire_enable;
-+ u8 ibf_enable;
- };
-
- enum three_wire_mode {
-@@ -1324,6 +1325,7 @@ hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
- conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
- }
-
-+#define IBF_DEFAULT_ENABLE 0
-
- int hostapd_mac_comp(const void *a, const void *b);
- struct hostapd_config * hostapd_config_defaults(void);
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 2f15f99f4..41e76aa54 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1186,3 +1186,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
- }
- return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
- }
-+
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->ibf_ctrl)
-+ return 0;
-+ return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
-+}
-+
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
-+{
-+ if (!hapd->driver || !hapd->driver->ibf_dump)
-+ return 0;
-+ return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index ed3b4cf11..295866134 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -151,6 +151,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 5487c9489..15bc9f486 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2521,6 +2521,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_ibf_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99ecbaf71..9811f266e 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
-+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
-+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
- NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
- };
-
-+enum mtk_vendor_attr_ibf_ctrl {
-+ MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
-+ MTK_VENDOR_ATTR_IBF_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_ibf_dump {
-+ MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
-+ MTK_VENDOR_ATTR_IBF_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
-+};
-+
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
-
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 03d268b2e..58a681b7a 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -181,6 +181,11 @@ struct hostapd_channel_data {
- * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
- */
- u8 mu_onoff;
-+
-+ /**
-+ * ibf_enable=<val>
-+ */
-+ u8 ibf_enable;
- };
-
- #define HE_MAC_CAPAB_0 0
-@@ -5130,6 +5135,20 @@ struct wpa_driver_ops {
- *
- */
- int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
-+
-+ /**
-+ * ibf_ctrl - ctrl disable/enable for ibf
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*ibf_ctrl)(void *priv, u8 ibf_enable);
-+
-+ /**
-+ * ibf_dump - dump ibf
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*ibf_dump)(void *priv, u8 *ibf_enable);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index d5c0ea81a..daa05882f 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14079,6 +14079,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
- return ret;
- }
-
-+static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_ibf_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ibf control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int ibf_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *ibf_enable = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
-+ return NL_SKIP;
-+ }
-+
-+ *ibf_enable = nla_get_u8(attr);
-+
-+ return NL_SKIP;
-+}
-+
-+static int
-+nl80211_ibf_dump(void *priv, u8 *ibf_enable)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+ if (!data)
-+ goto fail;
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, ibf_dump_handler, ibf_enable, NULL, NULL);
-+
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14241,4 +14347,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .configure_edcca_threshold = nl80211_configure_edcca_threshold,
- .get_edcca = nl80211_get_edcca,
- .three_wire_ctrl = nl80211_enable_three_wire,
-+ .ibf_ctrl = nl80211_ibf_enable,
-+ .ibf_dump = nl80211_ibf_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 99af8b075..4e64e7d31 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_edcca_vendor_cmd_avail:1;
- unsigned int mtk_mu_vendor_cmd_avail:1;
- unsigned int mtk_3wire_vendor_cmd_avail:1;
-+ unsigned int mtk_ibf_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 47ba17933..5b659f490 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1123,6 +1123,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
- drv->mtk_3wire_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
-+ drv->mtk_ibf_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
deleted file mode 100644
index e8ad881..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 037b1cdac457c03ba44a470a7df38798ba1d5d88 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 22 Sep 2022 16:08:09 +0800
-Subject: [PATCH 09/54] mtk: hostapd: Do not include HE capab IE if associated
- sta's HE capab IE is invalid
-
----
- src/ap/ieee802_11.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index db404a6c7..110ad8c2e 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4863,7 +4863,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- #endif /* CONFIG_IEEE80211AC */
-
- #ifdef CONFIG_IEEE80211AX
-- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
-+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
-+ sta->flags & WLAN_STA_HE) {
- p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
- p = hostapd_eid_he_operation(hapd, p);
- p = hostapd_eid_cca(hapd, p);
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch
deleted file mode 100644
index a0f9de1..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From 8ee7b5e713067c29aab2f7a4389cc806b545c5d8 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:55:49 +0800
-Subject: [PATCH 10/54] mtk: hostapd: Add DFS detection mode
-
-Add DFS detection mode for testing radar detection rate.
-If DFS detection mode is on, AP will not switch channels when receiving
-a radar signal.
-This detection mode also supports background chain.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c | 4 ++++
- hostapd/ctrl_iface.c | 23 +++++++++++++++++++++++
- src/ap/ap_config.h | 13 +++++++++++++
- src/ap/dfs.c | 10 ++++++++++
- 4 files changed, 50 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index f8560a721..50e299303 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4859,6 +4859,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
- int val = atoi(pos);
- conf->ibf_enable = !!val;
-+ } else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
-+ u8 en = strtol(pos, NULL, 10);
-+
-+ conf->dfs_detect_mode = en;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index cf7cc3923..327533f80 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3573,6 +3573,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
- }
-
-
-+static int
-+hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
-+ char *buf, size_t buflen)
-+{
-+ u8 dfs_detect_mode;
-+
-+ if (!value)
-+ return -1;
-+
-+ dfs_detect_mode = strtol(value, NULL, 10);
-+ if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
-+ wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
-+ return -1;
-+ }
-+ hapd->iconf->dfs_detect_mode = dfs_detect_mode;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4139,6 +4159,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
- reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
-+ reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index ffbc4fb4f..6576d791d 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1200,6 +1200,7 @@ struct hostapd_config {
- int *edcca_threshold;
- u8 three_wire_enable;
- u8 ibf_enable;
-+ u8 dfs_detect_mode;
- };
-
- enum three_wire_mode {
-@@ -1214,6 +1215,18 @@ enum three_wire_mode {
- NUM_THREE_WIRE_MODE - 1
- };
-
-+enum dfs_mode {
-+ DFS_DETECT_MODE_DISABLE,
-+ DFS_DETECT_MODE_AP_ENABLE,
-+ DFS_DETECT_MODE_BACKGROUND_ENABLE,
-+ DFS_DETECT_MODE_ALL_ENABLE,
-+
-+ /* keep last */
-+ NUM_DFS_DETECT_MODE,
-+ DFS_DETECT_MODE_MAX =
-+ NUM_DFS_DETECT_MODE - 1
-+};
-+
- enum edcca_mode {
- EDCCA_MODE_FORCE_DISABLE = 0,
- EDCCA_MODE_AUTO = 1,
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 29d268351..2e138e225 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1327,6 +1327,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- __func__, iface->radar_background.cac_started ? "yes" : "no",
- hostapd_csa_in_progress(iface) ? "yes" : "no");
-
-+ /* Skip channel switch when background dfs detect mode is on */
-+ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
-+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+ return 0;
-+
- /* Check if CSA in progress */
- if (hostapd_csa_in_progress(iface))
- return 0;
-@@ -1375,6 +1380,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
- __func__, iface->cac_started ? "yes" : "no",
- hostapd_csa_in_progress(iface) ? "yes" : "no");
-
-+ /* Skip channel switch when dfs detect mode is on */
-+ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
-+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+ return 0;
-+
- /* Check if CSA in progress */
- if (hostapd_csa_in_progress(iface))
- return 0;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
deleted file mode 100644
index ae9c59e..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
+++ /dev/null
@@ -1,192 +0,0 @@
-From 60b4911627763adee4fba3107acd2979ff024f10 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:56:55 +0800
-Subject: [PATCH 11/54] mtk: hostapd: Add DFS offchan channel switch
-
-Add DFS background chain channel switch command for testing purpose.
-This feature is implemented via hostapd_cli command.
-Command format:
-hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
- src/ap/dfs.c | 25 ++++++---------
- src/ap/dfs.h | 15 +++++++++
- 3 files changed, 96 insertions(+), 16 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 327533f80..84a6127d1 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3593,6 +3593,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
- }
-
-
-+static int
-+hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ struct hostapd_iface *iface = hapd->iface;
-+ char *pos, *param;
-+ enum hostapd_hw_mode hw_mode;
-+ bool chan_found = false;
-+ int i, num_available_chandefs, channel, chan_width, sec = 0;
-+ int sec_chan_idx_80p80 = -1;
-+ u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
-+ struct hostapd_channel_data *chan;
-+ enum dfs_channel_type type = DFS_NO_CAC_YET;
-+
-+ param = os_strchr(cmd, ' ');
-+ if (!param)
-+ return -1;
-+ *param++ = '\0';
-+
-+ pos = os_strstr(param, "chan=");
-+ if (pos)
-+ channel = strtol(pos + 5, NULL, 10);
-+ else
-+ return -1;
-+
-+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
-+ for (i = 0; i < num_available_chandefs; i++) {
-+ dfs_find_channel(iface, &chan, i, type);
-+ if (chan->chan == channel) {
-+ chan_found = true;
-+ break;
-+ }
-+ }
-+
-+ if (!chan_found)
-+ return -1;
-+
-+ if (iface->conf->secondary_channel)
-+ sec = 1;
-+
-+ dfs_adjust_center_freq(iface, chan,
-+ sec,
-+ sec_chan_idx_80p80,
-+ &oper_centr_freq_seg0_idx,
-+ &oper_centr_freq_seg1_idx);
-+
-+ if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+ chan->freq, chan->chan,
-+ iface->conf->ieee80211n,
-+ iface->conf->ieee80211ac,
-+ iface->conf->ieee80211ax,
-+ iface->conf->ieee80211be,
-+ sec, hostapd_get_oper_chwidth(iface->conf),
-+ oper_centr_freq_seg0_idx,
-+ oper_centr_freq_seg1_idx, true)) {
-+ wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
-+ iface->radar_background.channel = -1;
-+ return -1;
-+ }
-+
-+ iface->radar_background.channel = chan->chan;
-+ iface->radar_background.freq = chan->freq;
-+ iface->radar_background.secondary_channel = sec;
-+ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
-+ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4162,6 +4232,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
- reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 2e138e225..23e6527b3 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -19,13 +19,6 @@
- #include "dfs.h"
- #include "crypto/crypto.h"
-
--
--enum dfs_channel_type {
-- DFS_ANY_CHANNEL,
-- DFS_AVAILABLE, /* non-radar or radar-available */
-- DFS_NO_CAC_YET, /* radar-not-yet-available */
--};
--
- static struct hostapd_channel_data *
- dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
- u8 *oper_centr_freq_seg0_idx,
-@@ -238,9 +231,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
- * - hapd->vht/he_oper_centr_freq_seg0_idx
- * - hapd->vht/he_oper_centr_freq_seg1_idx
- */
--static int dfs_find_channel(struct hostapd_iface *iface,
-- struct hostapd_channel_data **ret_chan,
-- int idx, enum dfs_channel_type type)
-+int dfs_find_channel(struct hostapd_iface *iface,
-+ struct hostapd_channel_data **ret_chan,
-+ int idx, enum dfs_channel_type type)
- {
- struct hostapd_hw_modes *mode;
- struct hostapd_channel_data *chan;
-@@ -300,12 +293,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
- }
-
-
--static void dfs_adjust_center_freq(struct hostapd_iface *iface,
-- struct hostapd_channel_data *chan,
-- int secondary_channel,
-- int sec_chan_idx_80p80,
-- u8 *oper_centr_freq_seg0_idx,
-- u8 *oper_centr_freq_seg1_idx)
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+ struct hostapd_channel_data *chan,
-+ int secondary_channel,
-+ int sec_chan_idx_80p80,
-+ u8 *oper_centr_freq_seg0_idx,
-+ u8 *oper_centr_freq_seg1_idx)
- {
- if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
- return;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 606c1b393..c2556d2d9 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -9,6 +9,12 @@
- #ifndef DFS_H
- #define DFS_H
-
-+enum dfs_channel_type {
-+ DFS_ANY_CHANNEL,
-+ DFS_AVAILABLE, /* non-radar or radar-available */
-+ DFS_NO_CAC_YET, /* radar-not-yet-available */
-+};
-+
- int hostapd_handle_dfs(struct hostapd_iface *iface);
-
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
-@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
- int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
- int center_freq);
-+int dfs_find_channel(struct hostapd_iface *iface,
-+ struct hostapd_channel_data **ret_chan,
-+ int idx, enum dfs_channel_type type);
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+ struct hostapd_channel_data *chan,
-+ int secondary_channel,
-+ int sec_chan_idx_80p80,
-+ u8 *oper_centr_freq_seg0_idx,
-+ u8 *oper_centr_freq_seg1_idx);
-
- #endif /* DFS_H */
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
deleted file mode 100644
index f2d0484..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
+++ /dev/null
@@ -1,400 +0,0 @@
-From cfd3d079808b3a7d5585da349c1426351728b442 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 16 Dec 2022 03:57:11 +0800
-Subject: [PATCH 12/54] mtk: hostapd: Add amsdu set get ctrl
-
----
- hostapd/config_file.c | 9 +++
- hostapd/ctrl_iface.c | 26 +++++++
- hostapd/hostapd_cli.c | 9 +++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ap_drv_ops.c | 14 ++++
- src/ap/ap_drv_ops.h | 2 +
- src/ap/hostapd.c | 2 +
- src/common/mtk_vendor.h | 17 ++++-
- src/drivers/driver.h | 9 +++
- src/drivers/driver_nl80211.c | 114 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 13 files changed, 207 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 50e299303..0b2f3dc32 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4863,6 +4863,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- u8 en = strtol(pos, NULL, 10);
-
- conf->dfs_detect_mode = en;
-+ } else if (os_strcmp(buf, "amsdu") == 0) {
-+ int val = atoi(pos);
-+ if (val < 0 || val > 1) {
-+ wpa_printf(MSG_ERROR,
-+ "Line %d: invalid amsdu value",
-+ line);
-+ return 1;
-+ }
-+ conf->amsdu = val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 84a6127d1..57addb22d 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3663,6 +3663,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
- }
-
-
-+static int
-+hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ u8 amsdu;
-+ int ret;
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
-+ hapd->iconf->amsdu = amsdu;
-+ ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
-+ hapd->iconf->amsdu);
-+ }
-+
-+ if (os_snprintf_error(end - pos, ret))
-+ return 0;
-+
-+ return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4234,6 +4258,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply, reply_size);
- } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
- reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
-+ } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 4fa2d323d..d59373062 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1613,6 +1613,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- const char *cmd;
- int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1820,6 +1827,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
- " = show iBF state (enabled/disabled)"},
-+ { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
-+ " = show AMSDU state"},
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index c9b9683bb..f519a769b 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -300,6 +300,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- conf->ibf_enable = IBF_DEFAULT_ENABLE;
-+ conf->amsdu = 1;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 6576d791d..9f3cea205 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1201,6 +1201,7 @@ struct hostapd_config {
- u8 three_wire_enable;
- u8 ibf_enable;
- u8 dfs_detect_mode;
-+ u8 amsdu;
- };
-
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 41e76aa54..a7226cfa9 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1199,4 +1199,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
- if (!hapd->driver || !hapd->driver->ibf_dump)
- return 0;
- return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-+
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->amsdu_ctrl)
-+ return 0;
-+ return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
-+}
-+
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
-+{
-+ if (!hapd->driver || !hapd->driver->amsdu_dump)
-+ return 0;
-+ return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
- }
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 295866134..88bc430d2 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,6 +153,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 15bc9f486..fcf346d36 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2523,6 +2523,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_ibf_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 9811f266e..7b4d7c11a 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
- MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-
-@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
- NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
- };
-
-+enum mtk_vendor_attr_wireless_dump {
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_rfeature_ctrl {
- MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 58a681b7a..577c34c07 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5149,6 +5149,15 @@ struct wpa_driver_ops {
- *
- */
- int (*ibf_dump)(void *priv, u8 *ibf_enable);
-+
-+ /**
-+ * amsdu_ctrl - enable/disable amsdu
-+ * amsdu_dump - get current amsdu status
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*amsdu_ctrl)(void *priv, u8 amsdu);
-+ int (*amsdu_dump)(void *priv, u8 *amsdu);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index daa05882f..c1e3fcb69 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14185,6 +14185,118 @@ fail:
- return -ENOBUFS;
- }
-
-+static int nl80211_enable_amsdu(void *priv, u8 amsdu)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_wireless_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap wireless control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *amsdu = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr_amsdu;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
-+ if (!attr_amsdu ){
-+ wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
-+ return NL_SKIP;
-+ }
-+
-+ *amsdu = nla_get_u8(attr_amsdu);
-+
-+ return NL_SKIP;
-+}
-+
-+static int
-+nl80211_dump_amsdu(void *priv, u8 *amsdu)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_wireless_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support ap_wireless control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, dump_amsdu_handler, amsdu, NULL, NULL);
-+
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14349,4 +14461,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .three_wire_ctrl = nl80211_enable_three_wire,
- .ibf_ctrl = nl80211_ibf_enable,
- .ibf_dump = nl80211_ibf_dump,
-+ .amsdu_ctrl = nl80211_enable_amsdu,
-+ .amsdu_dump = nl80211_dump_amsdu,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 4e64e7d31..0100314ba 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_mu_vendor_cmd_avail:1;
- unsigned int mtk_3wire_vendor_cmd_avail:1;
- unsigned int mtk_ibf_vendor_cmd_avail:1;
-+ unsigned int mtk_wireless_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 5b659f490..0e70b7321 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1126,6 +1126,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
- drv->mtk_ibf_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
-+ drv->mtk_wireless_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch
deleted file mode 100644
index 32ee385..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From 6d57a4121c23048f3473991435aa8673b51763ad Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Thu, 12 Jan 2023 15:18:19 +0800
-Subject: [PATCH 13/54] mtk: hostapd: Add he_ldpc configuration
-
----
- hostapd/config_file.c | 2 ++
- hostapd/hostapd.conf | 5 +++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ieee802_11_he.c | 7 +++++++
- src/common/ieee802_11_defs.h | 3 +++
- 6 files changed, 19 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 0b2f3dc32..9e3dbb24a 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3515,6 +3515,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- conf->he_phy_capab.he_su_beamformee = atoi(pos);
- } else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
- conf->he_phy_capab.he_mu_beamformer = atoi(pos);
-+ } else if (os_strcmp(buf, "he_ldpc") == 0) {
-+ conf->he_phy_capab.he_ldpc = atoi(pos);
- } else if (os_strcmp(buf, "he_bss_color") == 0) {
- conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- conf->he_op.he_bss_color_disabled = 0;
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index bafc9232b..f16e3b08d 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -833,6 +833,11 @@ wmm_ac_vo_acm=0
- # 1 = supported
- #he_mu_beamformer=1
-
-+#he_ldpc: HE LDPC support
-+# 0 = not supported
-+# 1 = supported (default)
-+#he_ldpc=1
-+
- # he_bss_color: BSS color (1-63)
- #he_bss_color=1
-
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index f519a769b..223db56eb 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -269,6 +269,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- #endif /* CONFIG_ACS */
-
- #ifdef CONFIG_IEEE80211AX
-+ conf->he_phy_capab.he_ldpc = 1;
- conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
- HE_OPERATION_RTS_THRESHOLD_OFFSET;
- /* Set default basic MCS/NSS set to single stream MCS 0-7 */
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 9f3cea205..d0e27b28d 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -959,6 +959,7 @@ struct hostapd_bss_config {
- * struct he_phy_capabilities_info - HE PHY capabilities
- */
- struct he_phy_capabilities_info {
-+ bool he_ldpc;
- bool he_su_beamformer;
- bool he_su_beamformee;
- bool he_mu_beamformer;
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index 548a44821..9407dd6e5 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -138,6 +138,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
- os_memcpy(&cap->optional[mcs_nss_size],
- mode->he_capab[opmode].ppet, ppet_size);
-
-+ if (hapd->iface->conf->he_phy_capab.he_ldpc)
-+ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
-+ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+ else
-+ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
-+ ~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+
- if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
- cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
- HE_PHYCAP_SU_BEAMFORMER_CAPAB;
-diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
-index e7c3f17e1..69f15913e 100644
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -2358,6 +2358,9 @@ struct ieee80211_spatial_reuse {
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G ((u8) BIT(3))
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G ((u8) BIT(4))
-
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX 1
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD ((u8) BIT(5))
-+
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7))
- #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
deleted file mode 100644
index cd53d89..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 45bfb188573dae7bcc4c24fb22311c51c82a22d1 Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Tue, 24 Jan 2023 19:06:44 +0800
-Subject: [PATCH 14/54] mtk: hostapd: Add vendor command attribute for RTS BW
- signaling.
-
-Signed-off-by: himanshu.goyal <himanshu.goyal@mediatek.com>
----
- src/common/mtk_vendor.h | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 7b4d7c11a..ace993bc8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -172,6 +172,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch
deleted file mode 100644
index 653ca37..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 808d6d8261761d9cab0cf1500eddd812344bf7bf Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 13 Feb 2023 11:03:53 +0800
-Subject: [PATCH 15/54] mtk: hostapd: 6G band does not require DFS
-
----
- src/ap/dfs.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 23e6527b3..0a8486a1e 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1516,6 +1516,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- !iface->conf->ieee80211h) ||
- !iface->current_mode ||
-+ is_6ghz_freq(iface->freq) ||
- iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
- return 0;
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
deleted file mode 100644
index 9e68c12..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From bae45334df7f087ea31ddd4bc419610636ab45ea Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 11:01:18 +0800
-Subject: [PATCH 16/54] mtk: hostapd: Fix sending wrong VHT operation IE in CSA
- while using ZWDFS
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 14 +++++++++-----
- 1 file changed, 9 insertions(+), 5 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 0a8486a1e..cfc350879 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1120,6 +1120,14 @@ static int
- hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- {
- u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
-+ int ret;
-+
-+ ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
-+ iface->radar_background.freq,
-+ iface->radar_background.secondary_channel,
-+ current_vht_oper_chwidth,
-+ iface->radar_background.centr_freq_seg0_idx,
-+ iface->radar_background.centr_freq_seg1_idx);
-
- iface->conf->channel = iface->radar_background.channel;
- iface->freq = iface->radar_background.freq;
-@@ -1132,11 +1140,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
-
- hostpad_dfs_update_background_chain(iface);
-
-- return hostapd_dfs_request_channel_switch(
-- iface, iface->conf->channel, iface->freq,
-- iface->conf->secondary_channel, current_vht_oper_chwidth,
-- hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
-- hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
-+ return ret;
- }
-
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
deleted file mode 100644
index 3c36b27..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From 7782f39aa86e067030ee8277c7942423c6e64389 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 10:51:47 +0800
-Subject: [PATCH 17/54] mtk: hostapd: Add sta-assisted DFS state update
- mechanism
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 20 ++++++++++++++++++++
- src/ap/dfs.h | 3 +++
- src/ap/drv_callbacks.c | 28 ++++++++++++++++++++++++++++
- src/common/wpa_ctrl.h | 1 +
- src/drivers/driver.h | 14 ++++++++++++++
- src/drivers/driver_nl80211_event.c | 6 ++++++
- src/drivers/nl80211_copy.h | 6 ++++++
- 7 files changed, 78 insertions(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index cfc350879..9d002cfad 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1513,6 +1513,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- }
-
-
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, u32 state)
-+{
-+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
-+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
-+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
-+ (state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
-+
-+ /* Proceed only if DFS is not offloaded to the driver */
-+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
-+ return 0;
-+
-+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
-+ cf1, cf2, state);
-+
-+ return 0;
-+}
-+
-+
- int hostapd_is_dfs_required(struct hostapd_iface *iface)
- {
- int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index c2556d2d9..25ba29ca1 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- int ht_enabled,
- int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, u32 state);
- int hostapd_is_dfs_required(struct hostapd_iface *iface);
- int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index f6093c11c..e7f1f19ce 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2086,6 +2086,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- radar->cf1, radar->cf2);
- }
-
-+static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
-+ struct dfs_event *radar)
-+{
-+ wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
-+ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+ radar->chan_offset, radar->chan_width,
-+ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
-+}
-+
-+static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
-+ struct dfs_event *radar)
-+{
-+ wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
-+ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+ radar->chan_offset, radar->chan_width,
-+ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
-+}
-+
- #endif /* NEED_AP_MLME */
-
-
-@@ -2407,6 +2425,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- break;
- hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- break;
-+ case EVENT_DFS_STA_CAC_SKIPPED:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
-+ break;
-+ case EVENT_DFS_STA_CAC_EXPIRED:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
-+ break;
- case EVENT_CHANNEL_LIST_CHANGED:
- /* channel list changed (regulatory?), update channel list */
- /* TODO: check this. hostapd_get_hw_features() initializes
-diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
-index 416e0d6a8..62f042e05 100644
---- a/src/common/wpa_ctrl.h
-+++ b/src/common/wpa_ctrl.h
-@@ -374,6 +374,7 @@ extern "C" {
- #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
- #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
- #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
-+#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
-
- #define AP_CSA_FINISHED "AP-CSA-FINISHED "
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 577c34c07..24ab656fa 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5785,6 +5785,20 @@ enum wpa_event_type {
- * EVENT_LINK_RECONFIG - Notification that AP links removed
- */
- EVENT_LINK_RECONFIG,
-+
-+ /**
-+ * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
-+ *
-+ * The channel in the notification is now marked as available.
-+ */
-+ EVENT_DFS_STA_CAC_SKIPPED,
-+
-+ /**
-+ * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
-+ *
-+ * The channel in the notification is now marked as usable.
-+ */
-+ EVENT_DFS_STA_CAC_EXPIRED,
- };
-
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 701c32e7b..63d44017c 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2514,6 +2514,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- case NL80211_RADAR_CAC_STARTED:
- wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- break;
-+ case NL80211_RADAR_STA_CAC_SKIPPED:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
-+ break;
-+ case NL80211_RADAR_STA_CAC_EXPIRED:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
-+ break;
- default:
- wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
- "received", event_type);
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index 82860ae32..225864b94 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6643,6 +6643,10 @@ enum nl80211_smps_mode {
- * applicable for ETSI dfs domain where pre-CAC is valid for ever.
- * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
- * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-+ * when receiving CSA/assoc resp
-+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-+ * when STA is disconnected or leaving the channel
- */
- enum nl80211_radar_event {
- NL80211_RADAR_DETECTED,
-@@ -6651,6 +6655,8 @@ enum nl80211_radar_event {
- NL80211_RADAR_NOP_FINISHED,
- NL80211_RADAR_PRE_CAC_EXPIRED,
- NL80211_RADAR_CAC_STARTED,
-+ NL80211_RADAR_STA_CAC_SKIPPED,
-+ NL80211_RADAR_STA_CAC_EXPIRED,
- };
-
- /**
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
deleted file mode 100644
index ef35c2d..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 8a52855a8e3425d62b7ffba05ccf1ddd08223f78 Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Fri, 3 Mar 2023 12:45:42 +0800
-Subject: [PATCH 18/54] mtk: hostapd: Mark DFS channel as available for CSA.
-
----
- hostapd/ctrl_iface.c | 10 ++++++++++
- hostapd/hostapd_cli.c | 2 +-
- src/ap/ctrl_iface_ap.c | 1 +
- 3 files changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 57addb22d..ba2137969 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -2751,6 +2751,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
- break;
- }
-
-+ if (settings.freq_params.radar_background) {
-+ hostapd_dfs_sta_update_state(iface,
-+ settings.freq_params.freq,
-+ settings.freq_params.ht_enabled,
-+ settings.freq_params.sec_channel_offset,
-+ bandwidth, settings.freq_params.center_freq1,
-+ settings.freq_params.center_freq2,
-+ HOSTAPD_CHAN_DFS_AVAILABLE);
-+ }
-+
- if (settings.freq_params.center_freq1)
- dfs_range += hostapd_is_dfs_overlap(
- iface, bandwidth, settings.freq_params.center_freq1);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index d59373062..0a374be8e 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1715,7 +1715,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<addr> = send QoS Map Configure frame" },
- { "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
- "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
-- " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
-+ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
- " = initiate channel switch announcement" },
- { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
- "<addr> <url>\n"
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 0e173f174..7bdefb4cf 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1014,6 +1014,7 @@ int hostapd_parse_csa_settings(const char *pos,
- settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
- settings->freq_params.he_enabled = !!os_strstr(pos, " he");
- settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
-+ settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
- settings->block_tx = !!os_strstr(pos, " blocktx");
- #undef SET_CSA_SETTING
- #undef SET_CSA_SETTING_EXT
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch
deleted file mode 100644
index 4f2c43b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch
+++ /dev/null
@@ -1,477 +0,0 @@
-From 41e104c896bfa30a027924302a39401245e02767 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Thu, 26 Jan 2023 09:16:00 +0800
-Subject: [PATCH 19/54] mtk: hostapd: Add available color bitmap
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
----
- hostapd/ctrl_iface.c | 74 +++++++++++
- hostapd/hostapd_cli.c | 18 +++
- src/ap/ap_drv_ops.c | 10 +-
- src/ap/ap_drv_ops.h | 2 +
- src/common/mtk_vendor.h | 11 ++
- src/drivers/driver.h | 8 ++
- src/drivers/driver_nl80211.c | 199 +++++++++++++++++++++++++++++-
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 9 files changed, 324 insertions(+), 2 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ba2137969..e45e574be 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3696,6 +3696,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
- return ret;
- }
-
-+static int
-+hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ int ret;
-+ char *pos, *end;
-+ int i;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hapd->iface->conf->he_op.he_bss_color_disabled)
-+ ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
-+ else
-+ ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
-+ hapd->iface->conf->he_op.he_bss_color);
-+
-+ pos += ret;
-+
-+ return pos - buf;
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ int ret;
-+ char *pos, *end;
-+ int i;
-+ u64 aval_color_bmp = 0;
-+
-+ hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
-+ hapd->color_collision_bitmap = ~aval_color_bmp;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ ret = os_snprintf(buf, buflen,
-+ "available color bitmap=0x%llx\n",
-+ aval_color_bmp);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
-+ int bit = !!((aval_color_bmp >> i) & 1LLU);
-+
-+ if (i % 8 == 0) {
-+ ret = os_snprintf(pos, end - pos, "%2d: ", i);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "%d ", bit);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ if (i % 8 == 7) {
-+ ret = os_snprintf(pos, end - pos, "\n");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+ }
-+ return pos - buf;
-+}
-+
-
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
-@@ -4270,6 +4340,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
- reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 0a374be8e..e9e156d28 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1598,6 +1598,20 @@ static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
-+}
-+
-+
-+static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
-+}
-+
-+
- #ifdef ANDROID
- static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
-@@ -1819,6 +1833,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
- { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
- "= reload wpa_psk_file only" },
-+ { "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
-+ "= get current BSS color" },
-+ { "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
-+ "= get available BSS color bitmap" },
- #ifdef ANDROID
- { "driver", hostapd_cli_cmd_driver, NULL,
- "<driver sub command> [<hex formatted data>] = send driver command data" },
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index a7226cfa9..9615ca8ce 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1213,4 +1213,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
- if (!hapd->driver || !hapd->driver->amsdu_dump)
- return 0;
- return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
--}
-\ No newline at end of file
-+}
-+
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
-+{
-+ if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
-+ hapd->iface->conf->he_op.he_bss_color_disabled)
-+ return 0;
-+ return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 88bc430d2..ecaa71f99 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -155,6 +155,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
- int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
-+ u64 *aval_color_bmp);
-
- #include "drivers/driver.h"
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index ace993bc8..e27fe69b3 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
-+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
- [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
- };
-
-+enum mtk_vendor_attr_bss_color_ctrl {
-+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
-+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
-+};
-
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 24ab656fa..869b0442f 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5158,6 +5158,14 @@ struct wpa_driver_ops {
- */
- int (*amsdu_ctrl)(void *priv, u8 amsdu);
- int (*amsdu_dump)(void *priv, u8 *amsdu);
-+
-+ /**
-+ * get_aval_color_bmp - get available BSS color bitmap
-+ * @priv: Private driver interface data
-+ * @aval_color_bmp: available bss color bitmap
-+ *
-+ */
-+ int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c1e3fcb69..7b5a50ea6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -12869,7 +12869,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
- num, MAC2STR(candidate->bssid), buf);
- }
-
--
- static int
- nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
- {
-@@ -14297,6 +14296,203 @@ fail:
- return -ENOBUFS;
- }
-
-+static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
-+{
-+ u64 *aval_color_bmp = arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ static const struct nla_policy
-+ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
-+ [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
-+ };
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ *aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
-+
-+ return 0;
-+}
-+
-+static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *attr;
-+ int ret;
-+
-+ if (!drv->mtk_bss_color_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support BSS COLOR vendor cmd");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
-+ return -ENOBUFS;
-+
-+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!attr) {
-+ nlmsg_free(msg);
-+ return -1;
-+ }
-+
-+ nla_nest_end(msg, attr);
-+
-+ ret = send_and_recv_msgs(drv, msg,
-+ nl80211_get_aval_color_bmp_handler, aval_color_bmp, NULL, NULL);
-+
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_wireless_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap wireless control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
-+ nla_put_u16(msg, sub_vendor_id, (u16) value);
-+ else
-+ nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap rfeatures control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data, *data2;
-+ int ret;
-+
-+ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap rfeatures control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
-+ if (!data2)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
-+
-+ nla_nest_end(msg, data2);
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14463,4 +14659,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .ibf_dump = nl80211_ibf_dump,
- .amsdu_ctrl = nl80211_enable_amsdu,
- .amsdu_dump = nl80211_dump_amsdu,
-+ .get_aval_color_bmp = nl80211_get_aval_color_bmp,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 0100314ba..fd1e57cc2 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_3wire_vendor_cmd_avail:1;
- unsigned int mtk_ibf_vendor_cmd_avail:1;
- unsigned int mtk_wireless_vendor_cmd_avail:1;
-+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 0e70b7321..3e8eb8cb1 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1129,6 +1129,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
- drv->mtk_wireless_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
-+ drv->mtk_bss_color_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
deleted file mode 100644
index 3e30ba2..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
+++ /dev/null
@@ -1,210 +0,0 @@
-From 0e01d6aa229b84b92c9462f7101314db62857b86 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Mar 2023 16:08:30 +0800
-Subject: [PATCH 20/54] mtk: hostapd: Fix ZWDFS issue in BW 160
-
-When background radar is enabled and bandwidth is set to 160, AP will
-fail to startup due to the lack of non-DFS channel.
-Under this circumstance, AP should perform CAC itself, and the background
-chain could also perform CAC simultaneously.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
- 1 file changed, 79 insertions(+), 19 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 9d002cfad..3b1df6dc6 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -69,15 +69,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
- static int dfs_channel_available(struct hostapd_channel_data *chan,
- enum dfs_channel_type type)
- {
-+ int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
-+
-+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
-+ return -1;
-+
- if (type == DFS_NO_CAC_YET) {
- /* 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)
-+ if (!(chan->flag & HOSTAPD_CHAN_RADAR))
-+ return 0;
-+
-+ if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
- return 1;
-- return 0;
-+
-+ return -1;
- }
-
- /*
-@@ -86,16 +93,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
- * channel for CSA, unless they are available for immediate use.
- */
- if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
-- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
-- HOSTAPD_CHAN_DFS_AVAILABLE))
-- return 0;
-+ (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
-+ return -1;
-
-- if (chan->flag & HOSTAPD_CHAN_DISABLED)
-- return 0;
- if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
-- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
-- HOSTAPD_CHAN_DFS_UNAVAILABLE))
-- return 0;
-+ ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
-+ (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
-+ return -1;
-+
- return 1;
- }
-
-@@ -167,7 +172,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- enum dfs_channel_type type)
- {
- struct hostapd_channel_data *first_chan, *chan;
-- int i;
-+ int i, available = 0, ret = 0;
- u32 bw = num_chan_to_bw(num_chans);
-
- if (first_chan_idx + num_chans > mode->num_channels) {
-@@ -203,14 +208,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- return 0;
- }
-
-- if (!dfs_channel_available(chan, type)) {
-+ ret = dfs_channel_available(chan, type);
-+ if (ret < 0) {
- wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
- first_chan->freq + i * 20);
- return 0;
- }
-+
-+ available |= ret;
- }
-
-- return 1;
-+ return available;
- }
-
-
-@@ -836,8 +844,12 @@ 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 sec = 0, skip_radar = 0;
-+ u8 cf1 = 0, cf2 = 0;
-+ bool use_radar_background = dfs_use_radar_background(iface);
-+ enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
-
- if (is_6ghz_freq(iface->freq))
- return 1;
-@@ -900,7 +912,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- /* Finally start CAC */
- hostapd_set_state(iface, HAPD_IFACE_DFS);
- wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
-- dfs_use_radar_background(iface) ? " (background)" : "");
-+ use_radar_background ? " (background)" : "");
- 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,
-@@ -910,6 +922,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
- iface->dfs_cac_ms / 1000);
-
-+ if (use_radar_background) {
-+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
-+ /*
-+ * AP cannot get any random available channel.
-+ * Let AP and dedicated radar chain both perform CAC.
-+ */
-+ if (!channel)
-+ use_radar_background = false;
-+ }
-+
- res = hostapd_start_dfs_cac(
- iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
- iface->conf->ieee80211n, iface->conf->ieee80211ac,
-@@ -918,14 +940,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- hostapd_get_oper_chwidth(iface->conf),
- hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
- hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
-- dfs_use_radar_background(iface));
-+ use_radar_background);
-
- if (res) {
- wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
- return -1;
- }
-
-- if (dfs_use_radar_background(iface)) {
-+ if (use_radar_background) {
- /* Cache background radar parameters. */
- iface->radar_background.channel = iface->conf->channel;
- iface->radar_background.secondary_channel =
-@@ -946,6 +968,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
-
- iface->radar_background.temp_ch = 1;
- return 1;
-+ } else if (dfs_use_radar_background(iface)) {
-+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
-+ channel_type = DFS_ANY_CHANNEL;
-+
-+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
-+
-+ if (!channel ||
-+ (channel->chan == iface->conf->channel &&
-+ cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
-+ cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
-+ wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
-+ iface->radar_background.channel = -1;
-+ return 0;
-+ }
-+
-+ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+ channel->freq, channel->chan,
-+ iface->conf->ieee80211n,
-+ iface->conf->ieee80211ac,
-+ iface->conf->ieee80211ax,
-+ iface->conf->ieee80211be,
-+ sec, hostapd_get_oper_chwidth(iface->conf),
-+ cf1, cf2, true);
-+
-+ iface->radar_background.channel = channel->chan;
-+ iface->radar_background.freq = channel->freq;
-+ iface->radar_background.secondary_channel = sec;
-+ iface->radar_background.centr_freq_seg0_idx = cf1;
-+ iface->radar_background.centr_freq_seg1_idx = cf2;
- }
-
- return 0;
-@@ -1195,6 +1246,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- hostapd_setup_interface_complete(iface, 0);
- iface->cac_started = 0;
- }
-+
-+ /*
-+ * When background radar is enabled but the CAC completion
-+ * is not received from the background chain.
-+ * Then, reset radar background chain.
-+ */
-+ if (dfs_use_radar_background(iface) &&
-+ iface->radar_background.channel == -1)
-+ hostpad_dfs_update_background_chain(iface);
- }
- } else if (hostapd_dfs_is_background_event(iface, freq)) {
- iface->radar_background.cac_started = 0;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
deleted file mode 100644
index 804aa16..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
+++ /dev/null
@@ -1,396 +0,0 @@
-From 6dee226e8db428c4434115b4c792f7ffd8a759dd Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 17 Mar 2023 16:17:14 +0800
-Subject: [PATCH 21/54] mtk: hostapd: Add vendor for CAPI certification
- commands
-
----
- hostapd/ctrl_iface.c | 99 +++++++++++++++++++++++++++++++
- src/ap/ap_drv_ops.c | 21 +++++++
- src/ap/ap_drv_ops.h | 3 +
- src/common/mtk_vendor.h | 33 +----------
- src/drivers/driver.h | 22 +++++++
- src/drivers/driver_nl80211.c | 55 +++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 8 files changed, 206 insertions(+), 31 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index e45e574be..05606eb43 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -69,6 +69,7 @@
- #include "config_file.h"
- #include "ctrl_iface.h"
-
-+#include "common/mtk_vendor.h"
-
- #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
-
-@@ -3766,6 +3767,100 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
- return pos - buf;
- }
-
-+static int
-+hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *value, *config = cmd;
-+ enum mtk_vendor_attr_wireless_ctrl sub_cmd;
-+
-+ pos = os_strchr(config, '=');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if(pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strncmp(config, "fixed_mcs", 9) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
-+ else if (os_strncmp(config, "ofdma", 5) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
-+ else if (os_strncmp(config, "ppdu_type", 9) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
-+ else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
-+ else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
-+ else if (os_strncmp(config, "mimo", 4) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
-+ else if (os_strncmp(config, "cert", 4) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
-+ else if (os_strncmp(config, "amsdu", 5) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
-+ else if (os_strncmp(config, "rts_sigta", 9) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
-+ else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for ap_wireless", config);
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+ return -1;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *value, *type, *config = cmd;
-+ enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
-+
-+ pos = os_strchr(config, '=');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if(pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strncmp(config, "he_gi", 5) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
-+ else if (os_strncmp(config, "he_ltf", 6) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
-+ else if (os_strncmp(config, "trig_type", 9) == 0) {
-+ pos = os_strchr(value, ',');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+ if(pos == NULL)
-+ return -1;
-+ type = pos;
-+ goto trigtype;
-+ } else if (os_strcmp(config, "ack_policy") == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
-+ else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for ap_rfeatures", config);
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+ return -1;
-+ goto exit;
-+
-+trigtype:
-+ if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
-+ return -1;
-+
-+exit:
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
-@@ -4344,6 +4439,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
- } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
- reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
-+ reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
-+ } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
-+ reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 9615ca8ce..11444c7eb 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1222,3 +1222,24 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
- return 0;
- return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
- }
-+
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+ if (!hapd->driver || !hapd->driver->ap_wireless)
-+ return 0;
-+ return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+ if (!hapd->driver || !hapd->driver->ap_rfeatures)
-+ return 0;
-+ return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
-+{
-+ if (!hapd->driver || !hapd->driver->ap_trigtype)
-+ return 0;
-+ return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index ecaa71f99..32e6fc151 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -157,6 +157,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
- int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
- u64 *aval_color_bmp);
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
-
- #include "drivers/driver.h"
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e27fe69b3..0b23c76ad 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
- NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
-
--
--static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_3wire_ctrl {
- MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-
-@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
- NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
- };
-
--static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-- [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_csi_ctrl {
- MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-
-@@ -172,7 +157,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
-
- /* keep last */
-@@ -192,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
- NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
- };
-
--static const struct nla_policy
--wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-- [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_rfeature_ctrl {
- MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-
-@@ -206,6 +186,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
- MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
- MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
- MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-@@ -247,16 +228,6 @@ enum mtk_vendor_attr_ibf_dump {
- NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
- };
-
--static struct nla_policy
--ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-- [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
--};
--
--static struct nla_policy
--ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-- [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_bss_color_ctrl {
- MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 869b0442f..2ef1a3fcd 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5166,6 +5166,28 @@ struct wpa_driver_ops {
- *
- */
- int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
-+
-+ /**
-+ * ap_wireless - set wireless command
-+ * @priv: Private driver interface data
-+ * @value: value
-+ */
-+ int (*ap_wireless)(void *priv, u8 mode, int value);
-+
-+ /**
-+ * ap_rfeatures - set ap rf features command
-+ * @priv: Private driver interface data
-+ * @value: value
-+ */
-+ int (*ap_rfeatures)(void *priv, u8 mode, int value);
-+
-+ /**
-+ * ap_trigtype - set trigger type
-+ * @priv: Private driver interface data
-+ * @enable: enable or disable
-+ * @type: trigger type
-+ */
-+ int (*ap_trigtype)(void *priv, u8 enable, u8 type);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 7b5a50ea6..3ee3ec1eb 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -86,6 +86,58 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
- wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
- }
-
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- struct nl_sock *handle;
-@@ -14660,4 +14712,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .amsdu_ctrl = nl80211_enable_amsdu,
- .amsdu_dump = nl80211_dump_amsdu,
- .get_aval_color_bmp = nl80211_get_aval_color_bmp,
-+ .ap_wireless = nl80211_ap_wireless,
-+ .ap_rfeatures = nl80211_ap_rfeatures,
-+ .ap_trigtype = nl80211_ap_trigtype,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index fd1e57cc2..fc5217d61 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_ibf_vendor_cmd_avail:1;
- unsigned int mtk_wireless_vendor_cmd_avail:1;
- unsigned int mtk_bss_color_vendor_cmd_avail:1;
-+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 3e8eb8cb1..16306a121 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1132,6 +1132,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- drv->mtk_bss_color_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
-+ drv->mtk_rfeatures_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
deleted file mode 100644
index e2d1e7f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
+++ /dev/null
@@ -1,506 +0,0 @@
-From 8a578ff51002e783b4d9a64d24402c78529b244b Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:18:48 +0800
-Subject: [PATCH 22/54] mtk: hostapd: Air Monitor support in hostapd by vendor
-
-Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
----
- hostapd/ctrl_iface.c | 113 +++++++++++++++++++
- hostapd/hostapd_cli.c | 15 +++
- src/ap/ap_drv_ops.c | 14 +++
- src/ap/ap_drv_ops.h | 3 +
- src/common/mtk_vendor.h | 8 ++
- src/drivers/driver.h | 16 +++
- src/drivers/driver_nl80211.c | 180 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 2 +
- 9 files changed, 352 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 05606eb43..ab2768e76 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3809,6 +3809,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
-
- if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
- return -1;
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *tmp, sta_mac[ETH_ALEN] = {0};
-+ int amnt_idx = 0;
-+
-+ tmp = strtok_r(cmd, " ", &cmd);
-+
-+ if (!tmp) {
-+ wpa_printf(MSG_ERROR, "Error in command format\n");
-+ return -1;
-+ }
-+
-+ amnt_idx = strtol(tmp, &tmp, 10);
-+
-+ if (amnt_idx < 0 || amnt_idx > 15) {
-+ wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
-+ return -1;
-+ }
-+
-+ if (!cmd) {
-+ wpa_printf(MSG_ERROR, "Error in command format\n");
-+ return -1;
-+ }
-+
-+ if (hwaddr_aton(cmd, sta_mac) < 0) {
-+ wpa_printf(MSG_ERROR, "station mac is not right.\n");
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
-+ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+ return -1;
-+ }
-
- return os_snprintf(buf, buflen, "OK\n");
- }
-@@ -3862,6 +3900,75 @@ exit:
- return os_snprintf(buf, buflen, "OK\n");
- }
-
-+static int
-+hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *tmp;
-+ int amnt_idx = 0, ret = 0;
-+ struct amnt_resp_data *resp_buf;
-+ char *pos, *end;
-+ struct amnt_data *res;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ tmp = strtok_r(cmd, " ", &cmd);
-+
-+ if (!tmp) {
-+ wpa_printf(MSG_ERROR, "Error in command format\n");
-+ return -1;
-+ }
-+
-+ amnt_idx = strtoul(tmp, &tmp, 0);
-+
-+ if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
-+ wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
-+ return -1;
-+ }
-+
-+ if (amnt_idx == 0xff)
-+ resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
-+ * sizeof(struct amnt_data) + 1);
-+ else
-+ resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
-+
-+ if (resp_buf == NULL) {
-+ wpa_printf(MSG_ERROR, "Error in memory allocation\n");
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
-+ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+ os_free(resp_buf);
-+ return -1;
-+ }
-+
-+ for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
-+ res = &resp_buf->resp_data[i];
-+ ret = os_snprintf(pos, end - pos,
-+ "[hostapd_cli] amnt_idx: %d, addr="MACSTR
-+ ", rssi=%d/%d/%d/%d, last_seen=%u\n",
-+ res->idx,
-+ MAC2STR(res->addr), res->rssi[0],
-+ res->rssi[1], res->rssi[2],
-+ res->rssi[3], res->last_seen);
-+ if (os_snprintf_error(end - pos, ret)) {
-+ os_free(resp_buf);
-+ return 0;
-+ }
-+ pos = pos + ret;
-+ }
-+
-+ os_free(resp_buf);
-+
-+ if (pos == buf)
-+ return os_snprintf(buf, buflen, "Index %d is not monitored\n",
-+ amnt_idx);
-+ else
-+ return pos - buf;
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4443,6 +4550,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
- } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
- reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
-+ } else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
-+ reply, reply_size);
-+ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
-+ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
-+ reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index e9e156d28..6d763f327 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1633,6 +1633,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
- return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
- }
-
-+static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
-+}
-
- struct hostapd_cli_cmd {
- const char *cmd;
-@@ -1847,6 +1858,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- " = show iBF state (enabled/disabled)"},
- { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
- " = show AMSDU state"},
-+ { "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
-+ " = Set Station index and mac to monitor"},
-+ { "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
-+ " = Dump RSSI of monitoring Station"},
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 11444c7eb..b90dd5722 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1243,3 +1243,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
- return 0;
- return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
- }
-+
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+ if (!hapd->driver || !hapd->driver->amnt_set)
-+ return 0;
-+ return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
-+}
-+
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
-+{
-+ if (!hapd->driver || !hapd->driver->amnt_dump)
-+ return 0;
-+ return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 32e6fc151..8a97e0fea 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -161,6 +161,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
- int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
- int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
-
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+
- #include "drivers/driver.h"
-
- int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 0b23c76ad..dd1ca2164 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -258,10 +258,18 @@ struct csi_data {
- u16 rx_idx;
- };
-
-+#define AIR_MONITOR_MAX_ENTRY 16
-+
- struct amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
- s8 rssi[4];
- u32 last_seen;
- };
-+
-+struct amnt_resp_data {
-+ u8 sta_num;
-+ struct amnt_data resp_data[0];
-+};
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 2ef1a3fcd..24c9b96df 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5188,6 +5188,22 @@ struct wpa_driver_ops {
- * @type: trigger type
- */
- int (*ap_trigtype)(void *priv, u8 enable, u8 type);
-+
-+ /**
-+ * amnt_set - add/delete station from monitoring
-+ * @priv: Private driver interface data
-+ * @amnt_idx: Monitor Index
-+ * @amnt_sta_mac: station mac address
-+ */
-+ int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
-+
-+ /**
-+ * amnt_dump - Dump particular/ all station
-+ * @priv: Private driver interface data
-+ * @amnt_idx: Monitor Index
-+ * @amnt_dump_buf: Buffer to print
-+ */
-+ int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3ee3ec1eb..3a1c32a6d 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -138,6 +138,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
- [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
- };
-
-+static struct nla_policy
-+amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
-+ [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
-+};
-+
-+static struct nla_policy
-+amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- struct nl_sock *handle;
-@@ -14545,6 +14558,171 @@ fail:
- return -ENOBUFS;
- }
-
-+static int
-+nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ void *tb1;
-+ int ret;
-+
-+ if (!drv->mtk_amnt_vendor_cmd_avail) {
-+ wpa_printf(MSG_ERROR,
-+ "nl80211: Driver does not support air monitor");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+ if (!data)
-+ goto fail;
-+
-+ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
-+ if (!tb1)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
-+
-+ nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
-+
-+ nla_nest_end(msg, tb1);
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
-+ ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+
-+}
-+
-+static int
-+mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
-+{
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
-+ struct nlattr *attr, *cur, *data;
-+ struct amnt_data *res;
-+ int len = 0, rem;
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ attr = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!attr)
-+ return NL_SKIP;
-+
-+ nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
-+ attr, amnt_ctrl_policy);
-+
-+ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
-+ return NL_SKIP;
-+
-+ nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
-+
-+ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
-+ return NL_SKIP;
-+
-+ len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
-+ if (!len)
-+ return 0;
-+
-+ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
-+ return NL_SKIP;
-+
-+ data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
-+
-+ nla_for_each_nested(cur, data, rem) {
-+ if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
-+ return NL_SKIP;
-+ res = (struct amnt_data *) nla_data(cur);
-+ wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
-+ "addr="MACSTR", "
-+ "rssi=%d/%d/%d/%d, last_seen=%u\n",
-+ res->idx,
-+ MAC2STR(res->addr),
-+ res->rssi[0], res->rssi[1], res->rssi[2],
-+ res->rssi[3], res->last_seen);
-+ os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
-+ sizeof(struct amnt_data));
-+ amnt_dump->sta_num++;
-+ }
-+ return 0;
-+}
-+
-+static int
-+nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ void *tb1;
-+ int ret;
-+
-+ if (!drv->mtk_amnt_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support air monitor");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+ if (!data)
-+ goto fail;
-+
-+ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
-+ | NLA_F_NESTED);
-+ if (!tb1)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
-+
-+ nla_nest_end(msg, tb1);
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, mt76_amnt_dump_cb,
-+ dump_buf, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
-+ , ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14715,4 +14893,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .ap_wireless = nl80211_ap_wireless,
- .ap_rfeatures = nl80211_ap_rfeatures,
- .ap_trigtype = nl80211_ap_trigtype,
-+ .amnt_set = nl80211_amnt_set,
-+ .amnt_dump = nl80211_amnt_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index fc5217d61..0d85adfee 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_wireless_vendor_cmd_avail:1;
- unsigned int mtk_bss_color_vendor_cmd_avail:1;
- unsigned int mtk_rfeatures_vendor_cmd_avail:1;
-+ unsigned int mtk_amnt_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 16306a121..4bd15f348 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1131,6 +1131,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- break;
- case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- drv->mtk_bss_color_vendor_cmd_avail = 1;
-+ case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
-+ drv->mtk_amnt_vendor_cmd_avail = 1;
- break;
- case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- drv->mtk_rfeatures_vendor_cmd_avail = 1;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch
deleted file mode 100644
index 84be9e0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From da108d74412d83264355fe453a8d2ffc6f99fa86 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:23:00 +0800
-Subject: [PATCH 23/54] mtk: hostapd: Fix setting wrong seg0 index for 5G
- center chan 159 BW40
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/ap_config.h | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d0e27b28d..f03a957b7 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1308,7 +1308,8 @@ hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
- #ifdef CONFIG_IEEE80211BE
- if (conf->ieee80211be)
- conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
-- if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
-+ if (is_6ghz_op_class(conf->op_class) &&
-+ center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
- oper_centr_freq_seg0_idx +=
- conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
- #endif /* CONFIG_IEEE80211BE */
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch
deleted file mode 100644
index d1c6e2c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch
+++ /dev/null
@@ -1,228 +0,0 @@
-From 906bc021d4e4ddd62edc985dd37d7ad61d39fdb7 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:24:19 +0800
-Subject: [PATCH 24/54] mtk: hostapd: Add muru user number debug command
-
----
- hostapd/ctrl_iface.c | 13 ++++++++++++-
- src/ap/ap_drv_ops.c | 4 ++--
- src/ap/ap_drv_ops.h | 2 +-
- src/ap/hostapd.c | 3 ++-
- src/common/mtk_vendor.h | 7 +++++++
- src/drivers/driver.h | 4 ++--
- src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
- 7 files changed, 55 insertions(+), 15 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ab2768e76..4515583cf 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3398,6 +3398,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
- char *buf, size_t buflen)
- {
- char *pos, *config, *value;
-+ u8 mode;
-+
- config = cmd;
- pos = os_strchr(config, ' ');
- if (pos == NULL)
-@@ -3504,6 +3506,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- char *buf, size_t buflen)
- {
- char *pos, *config, *value;
-+ u8 mode;
-+
- config = cmd;
- pos = os_strchr(config, ' ');
- if (pos == NULL)
-@@ -3521,13 +3525,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- return -1;
- }
- hapd->iconf->mu_onoff = (u8) mu;
-+ mode = MU_CTRL_ONOFF;
-+ } else if (os_strcmp(config, "ul_user_cnt") == 0) {
-+ mode = MU_CTRL_UL_USER_CNT;
-+ wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
-+ } else if (os_strcmp(config, "dl_user_cnt") == 0) {
-+ mode = MU_CTRL_DL_USER_CNT;
-+ wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
- } else {
- wpa_printf(MSG_ERROR,
- "Unsupported parameter %s for SET_MU", config);
- return -1;
- }
-
-- if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+ if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
- return os_snprintf(buf, buflen, "OK\n");
- } else {
- return -1;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index b90dd5722..0aec9e925 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1162,11 +1162,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
- {
- if (!hapd->driver || !hapd->driver->mu_ctrl)
- return 0;
-- return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
- }
-
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 8a97e0fea..464efbae1 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -148,7 +148,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index fcf346d36..2d7fb6d39 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -57,6 +57,7 @@
- #include "wpa_auth_kay.h"
- #include "hw_features.h"
-
-+#include "common/mtk_vendor.h"
-
- static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
- #ifdef CONFIG_WEP
-@@ -2517,7 +2518,7 @@ dfs_offload:
- if (hostapd_drv_configure_edcca_threshold(hapd,
- hapd->iconf->edcca_threshold) < 0)
- goto fail;
-- if (hostapd_drv_mu_ctrl(hapd) < 0)
-+ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
- goto fail;
- if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index dd1ca2164..99371bf73 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,6 +199,8 @@ enum mtk_vendor_attr_mu_ctrl {
-
- MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
-+ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -272,4 +274,9 @@ struct amnt_resp_data {
- struct amnt_data resp_data[0];
- };
-
-+enum {
-+ MU_CTRL_ONOFF,
-+ MU_CTRL_DL_USER_CNT,
-+ MU_CTRL_UL_USER_CNT,
-+};
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 24c9b96df..83d347338 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5121,11 +5121,11 @@ struct wpa_driver_ops {
- int (*get_edcca)(void *priv, const u8 mode, u8 *value);
-
- /**
-- * mu_ctrl - ctrl on off for UL/DL MURU
-+ * mu_ctrl - ctrl for UL/DL MURU
- * @priv: Private driver interface data
- *
- */
-- int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+ int (*mu_ctrl)(void *priv, u8 mode, u8 val);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3a1c32a6d..8226bebc4 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13698,13 +13698,13 @@ fail:
-
-
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl_msg *msg;
- struct nlattr *data;
-- int ret;
-+ int ret = -ENOBUFS;
-
- if (!drv->mtk_mu_vendor_cmd_avail) {
- wpa_printf(MSG_INFO,
-@@ -13715,17 +13715,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-- !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
-- nlmsg_free(msg);
-- return -ENOBUFS;
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
-+ goto fail;
-+
-+ switch (mode) {
-+ case MU_CTRL_ONOFF:
-+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
-+ goto fail;
-+ break;
-+ case MU_CTRL_UL_USER_CNT:
-+ case MU_CTRL_DL_USER_CNT:
-+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
-+ goto fail;
-+ break;
-+ default:
-+ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+ ret = -EINVAL;
-+ goto fail;
- }
-+
- nla_nest_end(msg, data);
-+
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
- if(ret){
-- wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+ wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
- }
- return ret;
-+
-+fail:
-+ nl80211_nlmsg_clear(msg);
-+ nlmsg_free(msg);
-+ return ret;
- }
-
-
-@@ -14869,7 +14890,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .update_connect_params = nl80211_update_connection_params,
- .send_external_auth_status = nl80211_send_external_auth_status,
- .set_4addr_mode = nl80211_set_4addr_mode,
-- .mu_ctrl = nl80211_mu_onoff,
-+ .mu_ctrl = nl80211_mu_ctrl,
- .mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- .dpp_listen = nl80211_dpp_listen,
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
deleted file mode 100644
index db8ae73..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
+++ /dev/null
@@ -1,639 +0,0 @@
-From 738370924d0153163300f44c87a68e19a5220272 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Sat, 3 Jun 2023 17:12:15 +0800
-Subject: [PATCH 25/54] mtk: hostapd: add connac3 PHY MURU manual mode config
- support
-
-This commit supports read the following two formats to set MU/RU manual
-mode:
-1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
-2. hostapd_cli -i <intf> set_mu <field> <value>
-
-For the <field>, we support the following field:
-1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
-2. ul_comm_bw/dl_comm_bw: set the bandwith
-3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
-allocate idx
-4. ul_user_mcs/dl_user_mcs: set the mcs for each user
-5. ul_user_ssAlloc_raru: set the number of ss for each user
-6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
-7. dl_comm_toneplan: fix ru toneplan allocation
-8. dl_comm_ack_policy: fix station ack policy
-9. update : trigger driver to send mcu command to set muru manual mode.
-
-For the value of each field, please check wiki to learn the details:
-https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
-
-For the fields that mt76 support to use, we will update in this wiki:
-https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
-
-Please noted that this commit is only for connac 3 gen chips. If this
-feature is to be used in other generations, the following actions must
-be taken:
-1. Different data structue needs to be defined for different
-generations, e.g. connac4_muru_comm, connac4_muru_dl.
-2. hostapd_ctrl_iface_set_mu() shall be modified.
-3. A new code level configuration shall be defined to differentiate the
-code flow that different generations will go through.
----
- hostapd/ctrl_iface.c | 235 +++++++++++++++++++++++++++++++----
- src/ap/ap_config.h | 1 +
- src/ap/ap_drv_ops.c | 4 +-
- src/ap/ap_drv_ops.h | 2 +-
- src/ap/hostapd.c | 2 +-
- src/common/mtk_vendor.h | 166 ++++++++++++++++++++++++-
- src/drivers/driver.h | 2 +-
- src/drivers/driver_nl80211.c | 21 ++--
- 8 files changed, 390 insertions(+), 43 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 4515583cf..ae61cf625 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3500,22 +3500,61 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
- }
- }
-
-+static int
-+hostapd_parse_argument_helper(char *value, u16 **ptr_input)
-+{
-+#define MAX_MU_CTRL_NUM 17
-+
-+ u16 *input;
-+ char *endptr;
-+ int cnt = 0;
-+
-+ input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
-+ if (input == NULL) {
-+ wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
-+ return -1;
-+ }
-+ while (value) {
-+ u8 val = strtol(value, &endptr, 10);
-+
-+ if (value != endptr) {
-+ input[cnt++] = val;
-+ value = os_strchr(endptr, ':');
-+ if (value)
-+ value++;
-+ } else {
-+ break;
-+ }
-+ }
-
-+ *ptr_input = input;
-+ return cnt;
-+}
-+
-+#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do { \
-+ if ((le_to_host32(_val) & (_mask)) != _mask) { \
-+ wpa_printf(MSG_ERROR, "Set %s first\n", #_mask); \
-+ goto fail; \
-+ } \
-+ } while(0)
- static int
- hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
-- char *buf, size_t buflen)
-+ char *buf, size_t buflen)
- {
- char *pos, *config, *value;
-- u8 mode;
-+ u8 i;
-+ int cnt = 0, ret;
-+ u16 *val;
-+ struct connac3_muru *muru;
-+ struct connac3_muru_dl *dl;
-+ struct connac3_muru_ul *ul;
-+ struct connac3_muru_comm *comm;
-
- config = cmd;
- pos = os_strchr(config, ' ');
-- if (pos == NULL)
-- return -1;
-- *pos++ = '\0';
-+ if (pos != NULL)
-+ *pos++ = '\0';
-
-- if(pos == NULL)
-- return -1;
- value = pos;
-
- if (os_strcmp(config, "onoff") == 0) {
-@@ -3525,24 +3564,167 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- return -1;
- }
- hapd->iconf->mu_onoff = (u8) mu;
-- mode = MU_CTRL_ONOFF;
-- } else if (os_strcmp(config, "ul_user_cnt") == 0) {
-- mode = MU_CTRL_UL_USER_CNT;
-- wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
-- } else if (os_strcmp(config, "dl_user_cnt") == 0) {
-- mode = MU_CTRL_DL_USER_CNT;
-- wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
-- } else {
-- wpa_printf(MSG_ERROR,
-- "Unsupported parameter %s for SET_MU", config);
-- return -1;
-+
-+ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
-+ return os_snprintf(buf, buflen, "OK\n");
-+ else
-+ goto fail;
- }
-
-- if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
-- return os_snprintf(buf, buflen, "OK\n");
-+ if (hapd->iconf->muru_config == NULL)
-+ hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
-+
-+ muru = hapd->iconf->muru_config;
-+ dl = &muru->dl;
-+ ul = &muru->ul;
-+ comm = &muru->comm;
-+
-+ if (os_strncmp(config, "update", 6) == 0) {
-+ ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
-+
-+ os_free(hapd->iconf->muru_config);
-+ hapd->iconf->muru_config = NULL;
-+
-+ if (ret)
-+ goto fail;
-+ } else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
-+ ul->user_num = (u8)atoi(value);
-+ comm->ppdu_format |= MURU_PPDU_HE_TRIG;
-+ comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
-+ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
-+ } else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
-+ dl->user_num = (u8)atoi(value);
-+ comm->ppdu_format |= MURU_PPDU_HE_MU;
-+ comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
-+ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
-+ } else if (os_strcmp(config, "dl_comm_bw") == 0) {
-+ dl->bw = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
-+ } else if (os_strcmp(config, "ul_comm_bw") == 0) {
-+ ul->bw = (u8)atoi(value);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
-+ } else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != (dl->user_num * 2))
-+ goto para_fail;
-+ for (i = 0; i < dl->user_num; i++) {
-+ dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+ dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+ dl->usr[i].ru_idx = val[(2 * i) + 1];
-+ }
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
-+ } else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != (ul->user_num * 2))
-+ goto para_fail;
-+ for (i = 0; i < ul->user_num; i++) {
-+ ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+ ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+ ul->usr[i].ru_idx = val[(2 * i) + 1];
-+ }
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
-+ } else if (os_strcmp(config, "dl_user_mcs") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != dl->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ dl->usr[i].mcs = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
-+ } else if (os_strcmp(config, "ul_user_mcs") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != ul->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ ul->usr[i].mcs = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
-+ } else if (os_strcmp(config, "dl_user_cod") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != dl->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ dl->usr[i].ldpc = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
-+ } else if (os_strcmp(config, "ul_user_cod") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != ul->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ ul->usr[i].ldpc = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
-+ } else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != ul->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ ul->usr[i].nss = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
-+ } else if (os_strcmp(config, "dl_comm_gi") == 0) {
-+ dl->gi = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
-+ } else if (os_strcmp(config, "dl_comm_ltf") == 0) {
-+ dl->ltf = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
-+ } else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
-+ ul->gi_ltf = (u8)atoi(value);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
-+ } else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
-+ dl->ack_policy = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
-+ } else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ i = pow(2, dl->bw);
-+ if (cnt != i)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ dl->ru[i] = host_to_le16(val[i]);
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
- } else {
-- return -1;
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for SET_MU", config);
-+ goto fail;
- }
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+
-+para_fail:
-+ os_free(val);
-+ wpa_printf(MSG_ERROR, "Incorrect input number\n");
-+fail:
-+ return os_snprintf(buf, buflen, "FAIL\n");
- }
-
-
-@@ -4540,8 +4722,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- reply_size);
- } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
-- reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
-- reply_size);
-+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
- } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-@@ -4567,6 +4748,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
-+ // Replace first ':' with a single space ' '
-+ char *pos = buf + 23;
-+
-+ pos = os_strchr(pos, ':');
-+ if (pos)
-+ *pos = ' ';
-+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index f03a957b7..7c0d12a3b 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1203,6 +1203,7 @@ struct hostapd_config {
- u8 ibf_enable;
- u8 dfs_detect_mode;
- u8 amsdu;
-+ void *muru_config;
- };
-
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 0aec9e925..721bfa053 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1162,11 +1162,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
- {
- if (!hapd->driver || !hapd->driver->mu_ctrl)
- return 0;
-- return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
-+ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
- }
-
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 464efbae1..1e7ae7a8d 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -148,7 +148,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 2d7fb6d39..a28466405 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2518,7 +2518,7 @@ dfs_offload:
- if (hostapd_drv_configure_edcca_threshold(hapd,
- hapd->iconf->edcca_threshold) < 0)
- goto fail;
-- if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
-+ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
- goto fail;
- if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99371bf73..e140de60b 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,8 +199,11 @@ enum mtk_vendor_attr_mu_ctrl {
-
- MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
-- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
-+ /**
-+ * The above attrs are also used by connac 2. It is best not to modify the
-+ * above data structure.
-+ */
-+ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -275,8 +278,163 @@ struct amnt_resp_data {
- };
-
- enum {
-+ MU_CTRL_UPDATE,
- MU_CTRL_ONOFF,
-- MU_CTRL_DL_USER_CNT,
-- MU_CTRL_UL_USER_CNT,
- };
-+
-+struct connac3_muru_comm {
-+ u8 pda_pol;
-+ u8 band;
-+ u8 spe_idx;
-+ u8 proc_type;
-+
-+ le16 mlo_ctrl;
-+ u8 sch_type;
-+ u8 ppdu_format;
-+ u8 ac;
-+ u8 _rsv[3];
-+};
-+
-+struct connac3_muru_dl {
-+ u8 user_num;
-+ u8 tx_mode;
-+ u8 bw;
-+ u8 gi;
-+
-+ u8 ltf;
-+ u8 mcs;
-+ u8 dcm;
-+ u8 cmprs;
-+
-+ le16 ru[16];
-+
-+ u8 c26[2];
-+ u8 ack_policy;
-+ u8 tx_power;
-+
-+ le16 mu_ppdu_duration;
-+ u8 agc_disp_order;
-+ u8 _rsv1;
-+
-+ u8 agc_disp_pol;
-+ u8 agc_disp_ratio;
-+ le16 agc_disp_linkMFG;
-+
-+ le16 prmbl_punc_bmp;
-+ u8 _rsv2[2];
-+
-+ struct {
-+ le16 wlan_idx;
-+ u8 ru_alloc_seg;
-+ u8 ru_idx;
-+ u8 ldpc;
-+ u8 nss;
-+ u8 mcs;
-+ u8 mu_group_idx;
-+ u8 vht_groud_id;
-+ u8 vht_up;
-+ u8 he_start_stream;
-+ u8 he_mu_spatial;
-+ le16 tx_power_alpha;
-+ u8 ack_policy;
-+ u8 ru_allo_ps160;
-+ } usr[16];
-+};
-+
-+struct connac3_muru_ul {
-+ u8 user_num;
-+ u8 tx_mode;
-+
-+ u8 ba_type;
-+ u8 _rsv;
-+
-+ u8 bw;
-+ u8 gi_ltf;
-+ le16 ul_len;
-+
-+ le16 trig_cnt;
-+ u8 pad;
-+ u8 trig_type;
-+
-+ le16 trig_intv;
-+ u8 trig_ta[ETH_ALEN];
-+ le16 ul_ru[16];
-+
-+ u8 c26[2];
-+ le16 agc_disp_linkMFG;
-+
-+ u8 agc_disp_mu_len;
-+ u8 agc_disp_pol;
-+ u8 agc_disp_ratio;
-+ u8 agc_disp_pu_idx;
-+
-+ struct {
-+ le16 wlan_idx;
-+ u8 ru_alloc_seg;
-+ u8 ru_idx;
-+ u8 ldpc;
-+ u8 nss;
-+ u8 mcs;
-+ u8 target_rssi;
-+ le32 trig_pkt_size;
-+ u8 ru_allo_ps160;
-+ u8 _rsv2[3];
-+ } usr[16];
-+};
-+
-+struct connac3_muru_dbg {
-+ /* HE TB RX Debug */
-+ le32 rx_hetb_nonsf_en_bitmap;
-+ le32 rx_hetb_cfg[2];
-+};
-+
-+struct connac3_muru {
-+ le32 cfg_comm;
-+ le32 cfg_dl;
-+ le32 cfg_ul;
-+ le32 cfg_dbg;
-+
-+ struct connac3_muru_comm comm;
-+ struct connac3_muru_dl dl;
-+ struct connac3_muru_ul ul;
-+ struct connac3_muru_dbg dbg;
-+};
-+
-+#define MURU_OFDMA_SCH_TYPE_DL BIT(0)
-+#define MURU_OFDMA_SCH_TYPE_UL BIT(1)
-+#define MURU_PPDU_HE_TRIG BIT(2)
-+#define MURU_PPDU_HE_MU BIT(3)
-+
-+/* Common Config */
-+#define MURU_COMM_PPDU_FMT BIT(0)
-+#define MURU_COMM_BAND BIT(2)
-+#define MURU_COMM_WMM BIT(3)
-+#define MURU_COMM_SPE_IDX BIT(4)
-+#define MURU_COMM_SET (MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
-+ MURU_COMM_WMM | MURU_COMM_SPE_IDX)
-+
-+/* DL Common config */
-+#define MURU_FIXED_DL_BW BIT(0)
-+#define MURU_FIXED_DL_GI BIT(1)
-+#define MURU_FIXED_DL_TONE_PLAN BIT(3)
-+#define MURU_FIXED_DL_TOTAL_USER_CNT BIT(4)
-+#define MURU_FIXED_DL_LTF BIT(5)
-+#define MURU_FIXED_DL_ACK_PLY BIT(9)
-+
-+/* DL Per User Config */
-+#define MURU_FIXED_USER_DL_COD BIT(17)
-+#define MURU_FIXED_USER_DL_MCS BIT(18)
-+#define MURU_FIXED_USER_DL_RU_ALLOC BIT(20)
-+
-+/* UL Common Config */
-+#define MURU_FIXED_UL_TOTAL_USER_CNT BIT(4)
-+#define MURU_FIXED_UL_BW BIT(5)
-+#define MURU_FIXED_UL_GILTF BIT(6)
-+
-+/* UL Per User Config */
-+#define MURU_FIXED_USER_UL_COD BIT(18)
-+#define MURU_FIXED_USER_UL_MCS BIT(19)
-+#define MURU_FIXED_USER_UL_NSS BIT(20)
-+#define MURU_FIXED_USER_UL_RU_ALLOC BIT(21)
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 83d347338..8da93e025 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5125,7 +5125,7 @@ struct wpa_driver_ops {
- * @priv: Private driver interface data
- *
- */
-- int (*mu_ctrl)(void *priv, u8 mode, u8 val);
-+ int (*mu_ctrl)(void *priv, u8 mode, void *config);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 8226bebc4..b3897e61d 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13698,12 +13698,13 @@ fail:
-
-
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
- {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl_msg *msg;
- struct nlattr *data;
-+ struct hostapd_config *cfg = config;
- int ret = -ENOBUFS;
-
- if (!drv->mtk_mu_vendor_cmd_avail) {
-@@ -13720,17 +13721,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
-
- switch (mode) {
- case MU_CTRL_ONOFF:
-- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
-- goto fail;
-+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
-+ goto fail;
- break;
-- case MU_CTRL_UL_USER_CNT:
-- case MU_CTRL_DL_USER_CNT:
-- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
-- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
-- goto fail;
-+ case MU_CTRL_UPDATE:
-+ if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-+ sizeof(struct connac3_muru), cfg->muru_config))
-+ goto fail;
- break;
- default:
-- wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
- ret = -EINVAL;
- goto fail;
- }
-@@ -13738,9 +13738,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- nla_nest_end(msg, data);
-
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-- if(ret){
-+ if (ret)
- wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
-- }
- return ret;
-
- fail:
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch
deleted file mode 100644
index 3801eb3..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 1468c2bb212faf091cb4a0c0ba1394722eb2c94e Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 9 Jun 2023 09:03:05 +0800
-Subject: [PATCH 26/54] mtk: hostapd: Add HE capabilities check
-
-Add HE capabilities check.
-Since "HE capabilities" check has been removed by driver,
-add the support for "HE capabilities" check in hostapd.
----
- src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
- 1 file changed, 26 insertions(+)
-
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index 9d2272522..9a36bcb10 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -709,6 +709,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
- #ifdef CONFIG_IEEE80211AX
- static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
- {
-+ struct hostapd_hw_modes *mode = iface->current_mode;
-+ struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
-+ struct hostapd_config *conf = iface->conf;
-+
-+#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap) \
-+ do { \
-+ if (cfg_cap && !(hw_cap[phy_idx] & field)) { \
-+ wpa_printf(MSG_ERROR, "Driver does not support configured" \
-+ " HE capability [%s]", #field); \
-+ return 0; \
-+ } \
-+ } while (0)
-+
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
-+ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
-+ conf->he_phy_capab.he_ldpc);
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
-+ HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
-+ conf->he_phy_capab.he_su_beamformer);
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
-+ HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
-+ conf->he_phy_capab.he_su_beamformee);
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
-+ HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
-+ conf->he_phy_capab.he_mu_beamformer);
-+
- return 1;
- }
- #endif /* CONFIG_IEEE80211AX */
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
deleted file mode 100644
index 6a98b5d..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 163efaffce1344a6c2611a2a75d863eb88b17137 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:44:15 +0800
-Subject: [PATCH 27/54] mtk: hostapd: Fix background channel overlapping
- operating channel issue
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 3b1df6dc6..6f7635436 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -812,6 +812,20 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
- }
-
-
-+static void dfs_check_background_overlapped(struct hostapd_iface *iface)
-+{
-+ int width = hostapd_get_oper_chwidth(iface->conf);
-+
-+ if (!dfs_use_radar_background(iface))
-+ return;
-+
-+ if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
-+ width, iface->radar_background.centr_freq_seg0_idx,
-+ iface->radar_background.centr_freq_seg1_idx))
-+ iface->radar_background.channel = -1;
-+}
-+
-+
- static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
- int start_chan_idx, int n_chans)
- {
-@@ -1132,6 +1146,8 @@ static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
- &oper_centr_freq_seg1_idx,
- &channel_type);
- if (!channel ||
-+ channel->chan == iface->conf->channel ||
-+ channel->chan == iface->radar_background.channel ||
- hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
- channel->freq, channel->chan,
- iface->conf->ieee80211n,
-@@ -1366,6 +1382,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
- hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
- oper_centr_freq_seg1_idx);
- err = 0;
-+ dfs_check_background_overlapped(iface);
-
- hostapd_setup_interface_complete(iface, err);
- return err;
-@@ -1493,6 +1510,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
- hostapd_set_oper_centr_freq_seg1_idx(
- iface->conf, oper_centr_freq_seg1_idx);
-
-+ dfs_check_background_overlapped(iface);
- hostapd_disable_iface(iface);
- hostapd_enable_iface(iface);
- return 0;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
deleted file mode 100644
index 84ca877..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From fdbb7f5123ea8f3c5c568b4f5b311e33aa396e50 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:47:20 +0800
-Subject: [PATCH 28/54] mtk: hostapd: Fix hostapd_dfs_start_cac log
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 6f7635436..95119a391 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1655,9 +1655,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- /* TODO: How to check CAC time for ETSI weather channels? */
- iface->dfs_cac_ms = 60000;
- wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
-- "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
-+ "freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
- "seg1=%d cac_time=%ds%s",
-- freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
-+ freq, (freq - 5000) / 5, chan_offset,
-+ channel_width_to_string(chan_width),
-+ (cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
- iface->dfs_cac_ms / 1000,
- hostapd_dfs_is_background_event(iface, freq) ?
- " (background)" : "");
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
deleted file mode 100644
index 57b8060..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From a08f87c88e8abd539dc75b7fcb15fef786d5a61d Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 13 Jul 2023 13:14:26 +0800
-Subject: [PATCH 29/54] mtk: hostapd: Check the bridge after ioctl SIOCBRADDIF
- failed
-
-If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
-already be bridged by others, and linux_br_add_if should not indicate an
-error in the case.
-
-This patch checks whether the interface is correctly brigded when ioctl
-returns EBUSY.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/drivers/linux_ioctl.c | 16 ++++++++++++++--
- 1 file changed, 14 insertions(+), 2 deletions(-)
-
-diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
-index 7edb9df2e..b8c1af2e3 100644
---- a/src/drivers/linux_ioctl.c
-+++ b/src/drivers/linux_ioctl.c
-@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
- int linux_br_add_if(int sock, const char *brname, const char *ifname)
- {
- struct ifreq ifr;
-- int ifindex;
-+ int ifindex, ret;
-+ char in_br[IFNAMSIZ];
-
- ifindex = if_nametoindex(ifname);
- if (ifindex == 0)
-@@ -164,8 +165,19 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
-
- wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
- "%s: %s", ifname, brname, strerror(errno));
-+
-+ /* If ioctl returns -EBUSY when adding interface into bridge,
-+ * the interface might already be added by netifd, so here we
-+ * check whether the interface is currently on the right
-+ * bridge. */
-+ if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
-+ os_strcmp(in_br, brname) == 0)
-+ ret = 0;
-+ else
-+ ret = -1;
-+
- errno = saved_errno;
-- return -1;
-+ return ret;
- }
-
- return 0;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
deleted file mode 100644
index 28761c5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 8e8590fe62c5b1c9daceb7588798a96b95df6039 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 14 Jul 2023 17:19:13 +0800
-Subject: [PATCH 30/54] mtk: hostapd: Update parameter_set_count in MU EDCA IE
-
-without this patch, MU EDCA Parameter update count not equal to
-WMM Parameter set count.
----
- src/ap/ieee802_11_he.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index 9407dd6e5..353e8156c 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -316,6 +316,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
- edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
- os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
-
-+ if (hapd->conf->wmm_enabled)
-+ edca->he_qos_info = hapd->parameter_set_count % 0xf;
-+
- wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
- pos, sizeof(*edca));
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
deleted file mode 100644
index 0ad3c2d..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From be61f9b1bd2f3c7bcd935968d8709565f6b231b5 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Mon, 24 Jul 2023 11:30:27 +0800
-Subject: [PATCH 31/54] mtk: hostapd: add extension IE list for non-inherit IE
- in mbssid
-
-Certain clients do not scan all non tx profiles due to absence of
-element ID extension list which is mandatory field in non inheritance
-IE. Non inheritance Element ID is followed by extension element ID.
-Length is expected to be mentioned. Currently we do not support any
-extension element and hence filling length as 0.
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 9 +++++++--
- 1 file changed, 7 insertions(+), 2 deletions(-)
- mode change 100644 => 100755 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100644
-new mode 100755
-index 110ad8c2e..e05a06b09
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7659,7 +7659,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
- else if (hapd->conf->xrates_supported)
- ie_count++;
- if (ie_count)
-- nontx_profile_len += 4 + ie_count;
-+ nontx_profile_len += 5 + ie_count;
-
- if (len + nontx_profile_len > 255)
- break;
-@@ -7800,11 +7800,16 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
- non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
- if (ie_count) {
- *eid++ = WLAN_EID_EXTENSION;
-- *eid++ = 2 + ie_count;
-+ *eid++ = 3 + ie_count;
- *eid++ = WLAN_EID_EXT_NON_INHERITANCE;
- *eid++ = ie_count;
- os_memcpy(eid, non_inherit_ie, ie_count);
- eid += ie_count;
-+ /* Element ID extension list is mandatory part of non inheritance IE.
-+ * It has a length field followed by extension IEs. Currently no
-+ * extension IEs are supported so filling length as 0.
-+ */
-+ *eid++ = 0;
- }
-
- *eid_len_pos = (eid - eid_len_pos) - 1;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch
deleted file mode 100644
index 15c97a8..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From fea4788692076f9c0701ecec68a85198a70d05c0 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Mon, 7 Aug 2023 15:27:27 +0800
-Subject: [PATCH 32/54] mtk: hostapd: Fix rnr ie length when no need to report
- bss
-
-Fix rnr ie length when no need to report bss. If we don't have content in
-TBTT then don't change the length of the ie (*size_offset).
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- src/ap/ieee802_11.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index e05a06b09..7b6aabbff 100755
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7511,8 +7511,10 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- }
-
- start = i;
-- *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
-- *size_offset = (eid - size_offset) - 1;
-+ if (tbtt_count != 0) {
-+ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
-+ *size_offset = (eid - size_offset) - 1;
-+ }
- }
-
- if (tbtt_count == 0)
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
deleted file mode 100644
index ebc4408..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 765215ac465b90e20c52dd48d51859d849834c80 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 8 Aug 2023 19:21:41 +0800
-Subject: [PATCH 33/54] mtk: hostapd: add back ht vht cap missing field before
- dfs channel fallback
-
-hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
-on the bandwidth of switched channel.
-For example, vht bw 160 support field would be cleared if we switch to
-non bw 160 channel.
-This design works fine with NON-DFS channel switch.
-However, for those DFS channels who require CAC, channel switch command
-calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
-This is simply restarting the interface not CHANNEL SWITCHING, so
-hostapd will not receive any ch_switch event from kernel.
-Therefore, the cleared field in vht_capab will not be set back to 1,
-even if we channel switch to dfs channel bw 160.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/hostapd.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index a28466405..ec7677f6a 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4112,6 +4112,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
- break;
- }
-
-+ if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
-+ freq_params->bandwidth > 20)
-+ iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
-+ if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
-+ freq_params->bandwidth == 160)
-+ iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
-+
- iface->freq = freq_params->freq;
- iface->conf->channel = freq_params->channel;
- iface->conf->secondary_channel = freq_params->sec_channel_offset;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch
deleted file mode 100644
index 6afdc0b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 9a82dbf6c16899eeb6b98178cc6e341f164acfa6 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 23 Aug 2023 17:44:50 +0800
-Subject: [PATCH 34/54] mtk: hostapd: update op_class when AP channel switching
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/drv_callbacks.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index e7f1f19ce..f749b33dc 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1041,7 +1041,7 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- {
- #ifdef NEED_AP_MLME
- int channel, chwidth, is_dfs0, is_dfs;
-- u8 seg0_idx = 0, seg1_idx = 0;
-+ u8 seg0_idx = 0, seg1_idx = 0, op_class, chan_no;
- size_t i;
-
- hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-@@ -1169,6 +1169,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- hapd->iconf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
-
- hapd->iconf->secondary_channel = offset;
-+ ieee80211_freq_to_channel_ext(freq, offset, chwidth,
-+ &op_class, &chan_no);
-+ hapd->iconf->op_class = op_class;
- hostapd_set_oper_chwidth(hapd->iconf, chwidth);
- hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
- hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
deleted file mode 100644
index 5d8edfd..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From aa65ab579fe3c032034b2ca641a91aa62285b59f Mon Sep 17 00:00:00 2001
-From: mtk23510 <rudra.shahi@mediatek.com>
-Date: Fri, 26 May 2023 14:52:35 +0800
-Subject: [PATCH 35/54] mtk: hostapd: Add support for gtk rekeying in hostapd
- cli
-
-Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
----
- hostapd/hostapd_cli.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 6d763f327..363a6bb93 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1256,6 +1256,15 @@ static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+#ifdef CONFIG_TESTING_OPTIONS
-+static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return wpa_ctrl_command(ctrl, "REKEY_GTK");
-+}
-+#endif
-+
-+
- static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
- char cmd[256];
-@@ -1761,6 +1770,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "= disable hostapd on current interface" },
- { "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
- "= update Beacon frame contents\n"},
-+#ifdef CONFIG_TESTING_OPTIONS
-+ { "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
-+ "= rekey gtk\n"},
-+#endif
- { "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
- "= drop all ERP keys"},
- { "log_level", hostapd_cli_cmd_log_level, NULL,
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
deleted file mode 100644
index 7fb99bd..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From a8d74e867652c6feda7332ee2cb40611635179f0 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 11 Jul 2023 14:17:43 +0800
-Subject: [PATCH 36/54] mtk: hostapd: Set WMM and TX queue parameters for
- wpa_supplicant
-
-Since most of the time, wpa_supplicant will be used to setup an STA
-interface, it's default WMM and TX queue parameters should be set for
-STA.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/config.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index 2652c8a97..cd4025884 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -4673,19 +4673,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
- const struct hostapd_wmm_ac_params ac_bk =
- { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
- const struct hostapd_wmm_ac_params ac_be =
-- { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
-+ { aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
- const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
-- { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
-+ { aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
- const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
-- { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
-+ { aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
- const struct hostapd_tx_queue_params txq_bk =
- { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- const struct hostapd_tx_queue_params txq_be =
-- { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
-+ { 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- const struct hostapd_tx_queue_params txq_vi =
-- { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
-+ { 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
- const struct hostapd_tx_queue_params txq_vo =
-- { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
-+ { 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
- (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
-
- #undef ecw2cw
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
deleted file mode 100644
index aab9689..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From 7a02e5965652229833be92a9bd8f675e2015144a Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 7 Jul 2023 17:16:11 +0800
-Subject: [PATCH 37/54] mtk: hostapd: Set STA TX queue parameters configuration
- after association
-
-This patch adds the way for wpa_supplicant to set driver's TX queue
-parameters.
-Since STA parses and apply TX queue parameters from AP beacon's WMM IE
-during association, wpa_supplicant set driver's TX queue parameters
-after the association.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/driver_i.h | 12 ++++++++++++
- wpa_supplicant/events.c | 16 ++++++++++++++++
- 2 files changed, 28 insertions(+)
-
-diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
-index dcf576487..36ff854ad 100644
---- a/wpa_supplicant/driver_i.h
-+++ b/wpa_supplicant/driver_i.h
-@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
- return 0;
- }
-
-+static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
-+ int q, int aifs, int cw_min,
-+ int cw_max, int burst_time)
-+{
-+ int link_id = -1;
-+ if (wpa_s->driver->set_tx_queue_params)
-+ return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
-+ aifs, cw_min, cw_max,
-+ burst_time, link_id);
-+ return 0;
-+}
-+
- static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
- const u8 *data, size_t data_len, int noack,
- unsigned int freq, unsigned int wait)
-diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
-index 3b8596d6f..f5ac62eda 100644
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -3559,6 +3559,20 @@ out:
- return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
- }
-
-+static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
-+ struct hostapd_tx_queue_params *p;
-+
-+ for (int i = 0; i < NUM_TX_QUEUES; i++){
-+ p = &wpa_s->conf->tx_queue[i];
-+ if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
-+ p->cwmin, p->cwmax,
-+ p->burst)) {
-+ wpa_printf(MSG_DEBUG, "Failed to set TX queue "
-+ "parameters for queue %d.", i);
-+ /* Continue anyway */
-+ }
-+ }
-+}
-
- static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
-@@ -3886,6 +3900,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
-
- if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
- wpa_supplicant_set_4addr_mode(wpa_s);
-+
-+ wpa_supplicant_tx_queue_params(wpa_s);
- }
-
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
deleted file mode 100644
index 23ca582..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 695e63f81f817c302e4594e44b49e1f7a8c7969c Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 1 Sep 2023 15:31:24 +0800
-Subject: [PATCH 38/54] mtk: hostapd: avoid color switch when beacon is not set
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/hostapd.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index ec7677f6a..2b563a572 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4250,7 +4250,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
- {
- struct os_reltime now;
-
-- if (hapd->cca_in_progress)
-+ if (hapd->cca_in_progress || !hapd->beacon_set_done)
- return;
-
- if (os_get_reltime(&now))
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
deleted file mode 100644
index 0a5a04f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 4fe45b586ebb86cfbdd61ab91d5ebef110ce74aa Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Wed, 13 Sep 2023 19:29:51 +0800
-Subject: [PATCH 39/54] mtk: hostapd: 6g bss connect do not consider ht
- operation
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
- mode change 100755 => 100644 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100755
-new mode 100644
-index 7b6aabbff..38fce3e82
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5451,7 +5451,8 @@ static void handle_assoc(struct hostapd_data *hapd,
- ieee802_11_set_beacons(hapd->iface);
- }
-
-- update_ht_state(hapd, sta);
-+ if (!is_6ghz_op_class(hapd->iconf->op_class))
-+ update_ht_state(hapd, sta);
-
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch
deleted file mode 100644
index 7c3853f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-From 49b5d37f77fa48f215b3abc9c3ce74c26bbabefc Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 4 Oct 2023 11:12:52 +0800
-Subject: [PATCH 40/54] mtk: hostapd: avoid unnecessary beacon update for 6 GHz
- co-location
-
-There are two reasons to update beacon for 6 GHz co-location:
-1. 6 GHz out-of-band discovery
-2. MLD operational parameters update
-
-BSS load update is unrelated with the above two reasons, and therefore is
-not a case to update beacon for 6 GHz co-location.
-Moreover, updating beacon for 6 GHz co-location when BSS load update
-makes hostapd set beacon too frequently in a multiple BSSes case.
-
-Besides, it is also not necessary to update beacon for 6 GHz BSS when
-setting 2/5 GHz beacon. (i.e., no need for 2/5 GHz co-location)
-
-This patch adds an new function to update beacon only for current BSS,
-and uses the function duriong BSS load update.
-Also it changes the condition check to make beacon update only for 6 GHz
-co-location.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
-Signed-off-by: Money Wang <money.wang@mediatek.com>
----
- src/ap/beacon.c | 6 ++++++
- src/ap/beacon.h | 1 +
- src/ap/bss_load.c | 2 +-
- 3 files changed, 8 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index d160675cb..b74396a8d 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2216,6 +2216,12 @@ fail:
- }
-
-
-+void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
-+{
-+ __ieee802_11_set_beacon(hapd);
-+}
-+
-+
- int ieee802_11_set_beacon(struct hostapd_data *hapd)
- {
- struct hostapd_iface *iface = hapd->iface;
-diff --git a/src/ap/beacon.h b/src/ap/beacon.h
-index c320825f3..b32b2a7d0 100644
---- a/src/ap/beacon.h
-+++ b/src/ap/beacon.h
-@@ -15,6 +15,7 @@ struct ieee80211_mgmt;
- void handle_probe_req(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len,
- int ssi_signal);
-+void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd);
- int ieee802_11_set_beacon(struct hostapd_data *hapd);
- int ieee802_11_set_beacons(struct hostapd_iface *iface);
- int ieee802_11_update_beacons(struct hostapd_iface *iface);
-diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
-index 725d3cd34..e9baafc96 100644
---- a/src/ap/bss_load.c
-+++ b/src/ap/bss_load.c
-@@ -55,7 +55,7 @@ static void update_channel_utilization(void *eloop_data, void *user_data)
- return;
- }
-
-- ieee802_11_set_beacon(hapd);
-+ ieee802_11_set_beacon_per_bss_only(hapd);
-
- if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
- return;
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch
deleted file mode 100644
index d1e24d5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch
+++ /dev/null
@@ -1,168 +0,0 @@
-From 5daee94e29d5d7a3db5b8c8f03b15aa4a914f85f Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 24 Aug 2023 10:04:15 +0800
-Subject: [PATCH 41/54] mtk: hostapd: refactor the flow to create Wide
- Bandwidth Channel Switch IE
-
-This patch changes the flow to create Wide Bandwidth Channel Switch IE:
-1. 2 GHz: Wide Bandwidth Channel Switch IE should not present.
-2. 5 GHz: fill the subfields according to VHT operation.
-3. 6 GHz: fill the subfields according to VHT operation and HE operation
- in HE mode and EHT mode, respectively.
- This is because the definition of the subfields of Wide Bandwidth
- Channel Switch IE is ambiguous:
- 1. 802.11ac: the definition of subfields follows VHT operation
- (IEEE80211-2020 9.4.2.160)
- 2. 802.11ax: the definition of subfields is not specified
- 3. 802.11be: the definition of subfields follows VHT operation in 5
- GHz and HE operation in 6 GHz (IEEE P802.11be D3.2 9.4.2.159)
-
-To support 320 MHz
- channel switch, set width to 4
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ieee802_11.c | 99 ++++++++++++++++++++++++++++++++++-----------
- 1 file changed, 76 insertions(+), 23 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 38fce3e82..d46c5a42b 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7083,57 +7083,110 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
-
- u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
- {
-- u8 bw, chan1, chan2 = 0;
-- int freq1;
-+ u8 new_bw_field, ccfs0_chan, ccfs1_chan = 0;
-+ int ccfs0_freq = 0, ccfs1_freq = 0;
-+ int control_freq, center_freq1, center_freq2, bandwidth;
-+ int base_freq, offset;
-+ bool is_6ghz, use_he_oper;
-
- if (!hapd->cs_freq_params.channel ||
-+ hapd->cs_freq_params.bandwidth == 20 ||
- (!hapd->cs_freq_params.vht_enabled &&
- !hapd->cs_freq_params.he_enabled &&
- !hapd->cs_freq_params.eht_enabled))
- return eid;
-
-- /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80, 4: 320 */
-- switch (hapd->cs_freq_params.bandwidth) {
-+ control_freq = hapd->cs_freq_params.freq;
-+ center_freq1 = hapd->cs_freq_params.center_freq1;
-+ center_freq2 = hapd->cs_freq_params.center_freq2;
-+ bandwidth = hapd->cs_freq_params.bandwidth;
-+
-+ /* center_freq2 is used if and only if bandwidth is
-+ * 80+80 MHz and phy mode is not EHT
-+ */
-+ if (center_freq2 &&
-+ (bandwidth != 80 || hapd->cs_freq_params.eht_enabled))
-+ return eid;
-+
-+ is_6ghz = is_6ghz_freq(control_freq);
-+ use_he_oper = is_6ghz && hapd->cs_freq_params.eht_enabled;
-+ base_freq = is_6ghz ? 5955 : 5180;
-+
-+ /* About the subfields of the Wide Bandwidth Channel Switch IE,
-+ * IEEE802.11-2020 9.4.2.160 specifies that the subfields New
-+ * Channel Width, New Channel Center Frequency Segment 0 and New
-+ * Channel Center Frequency Segment 1 have the same definition as
-+ * they are in the VHT operation information field.
-+ * However, the standard does not specify the definition of these
-+ * subfields when it comes to HE phy-mode in 6 GHz.
-+ * And in IEEE P802.11be D3.2 9.4.2.159, it specifies that the
-+ * defition should follow VHT operation in 5 GHz, and follow HE
-+ * oepration in 6 GHz.
-+ * Problem happens here for some HE STAs in 6 GHz, they might still
-+ * use VHT operation to parse these subfields.
-+ *
-+ * Here we follow the new Standard to build the IE, meanwhile we have
-+ * a workaround for HE mode in 6 GHz.
-+ *
-+ * 5 GHz: VHT operation
-+ * HE mode in 6 GHz: VHT operation
-+ * EHT mode in 6 GHz: HE operation
-+ */
-+ ccfs0_freq = center_freq1;
-+ ccfs1_freq = center_freq2;
-+ switch (bandwidth) {
- case 40:
-- bw = 0;
-+ new_bw_field = use_he_oper ? 1 : 0;
- break;
- case 80:
-- /* check if it's 80+80 */
-- if (!hapd->cs_freq_params.center_freq2)
-- bw = 1;
-+ if (ccfs1_freq)
-+ new_bw_field = use_he_oper ? 3 : 1;
- else
-- bw = 3;
-+ new_bw_field = use_he_oper ? 2 : 1;
- break;
- case 160:
-- bw = 2;
-+ new_bw_field = use_he_oper ? 3 : 1;
-+
-+ /* ccfs0 is primary 80 MHz
-+ * ccfs1 is center frequency
-+ */
-+ offset = (control_freq - base_freq) / 20;
-+ ccfs0_freq = control_freq + 30 - (offset & 3) * 20;
-+ ccfs1_freq = center_freq1;
- break;
- case 320:
-- bw = 4;
-+ /* TODO switch to bandwidth 320 MHz should be
-+ * indicated by Bandwidth indication IE.
-+ */
-+ new_bw_field = 4;
-+
-+ /* ccfs0 is primary 160 MHz
-+ * ccfs1 is center frequency
-+ */
-+ offset = (control_freq - base_freq) / 20;
-+ ccfs0_freq = control_freq + 70 - (offset & 7) * 20;
-+ ccfs1_freq = center_freq1;
- break;
- default:
-- /* not valid VHT bandwidth or not in CSA */
-+ /* not a valid VHT/HE bandwidth or not in CSA */
- return eid;
- }
-
-- freq1 = hapd->cs_freq_params.center_freq1 ?
-- hapd->cs_freq_params.center_freq1 :
-- hapd->cs_freq_params.freq;
-- if (ieee80211_freq_to_chan(freq1, &chan1) !=
-- HOSTAPD_MODE_IEEE80211A)
-+ if (ieee80211_freq_to_chan(ccfs0_freq, &ccfs0_chan) !=
-+ HOSTAPD_MODE_IEEE80211A)
- return eid;
-
-- if (hapd->cs_freq_params.center_freq2 &&
-- ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
-- &chan2) != HOSTAPD_MODE_IEEE80211A)
-+ if (ccfs1_freq && ieee80211_freq_to_chan(ccfs1_freq, &ccfs1_chan) !=
-+ HOSTAPD_MODE_IEEE80211A)
- return eid;
-
- *eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
- *eid++ = 5; /* Length of Channel Switch Wrapper */
- *eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
- *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
-- *eid++ = bw; /* New Channel Width */
-- *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
-- *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
-+ *eid++ = new_bw_field; /* New Channel Width */
-+ *eid++ = ccfs0_chan; /* New Channel Center Frequency Segment 0 */
-+ *eid++ = ccfs1_chan; /* New Channel Center Frequency Segment 1 */
-
- return eid;
- }
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
deleted file mode 100644
index 6508476..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From 5e51c981aa0540864803b5f5b68e715a8d98a338 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Sun, 8 Oct 2023 11:50:06 +0800
-Subject: [PATCH 42/54] mtk: hostapd: Add ACS chanlist info in get_config
-
-This patch is used to add ACS chanlist info displaying
-for upper layer application obtaining.
-
-Command format:
-hostapd_cli -i phy0-ap0 get_config
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 59 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ae61cf625..aba22e1e6 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1048,6 +1048,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- {
- int ret;
- char *pos, *end;
-+ int i;
-
- pos = buf;
- end = buf + buflen;
-@@ -1227,6 +1228,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- pos += ret;
- }
-
-+ /* dump chanlist */
-+ if (hapd->iface->conf->acs_ch_list.num > 0) {
-+ ret = os_snprintf(pos, end - pos, "chanlist=");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
-+ if (i > 0) {
-+ ret = os_snprintf(pos, end - pos, ", ");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "%d-%d",
-+ hapd->iface->conf->acs_ch_list.range[i].min,
-+ hapd->iface->conf->acs_ch_list.range[i].max);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "\n");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ /* dump freqlist */
-+ if (hapd->iface->conf->acs_freq_list.num > 0) {
-+ ret = os_snprintf(pos, end - pos, "freqlist=");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
-+ if (i > 0) {
-+ ret = os_snprintf(pos, end - pos, ", ");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "%d-%d",
-+ hapd->iface->conf->acs_freq_list.range[i].min,
-+ hapd->iface->conf->acs_freq_list.range[i].max);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "\n");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
- return pos - buf;
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
deleted file mode 100644
index aae2359..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From c21e12bb63b9c3906143ffb368297e3b6a155eb3 Mon Sep 17 00:00:00 2001
-From: mtk25255 <rohit.kamat@mediatek.com>
-Date: Thu, 12 Oct 2023 14:29:23 +0800
-Subject: [PATCH 43/54] mtk: hostapd: Fix RSNXE Interop issue with STA
-
----
- src/ap/ieee802_11.c | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index d46c5a42b..1db66a783 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5181,6 +5181,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- int delay_assoc = 0;
- #endif /* CONFIG_FILS */
- int omit_rsnxe = 0;
-+ bool sae_pk = false;
-
- if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
- sizeof(mgmt->u.assoc_req))) {
-@@ -5399,7 +5400,17 @@ static void handle_assoc(struct hostapd_data *hapd,
- if (resp != WLAN_STATUS_SUCCESS)
- goto fail;
- omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
--
-+#ifdef CONFIG_SAE_PK
-+ sae_pk = hostapd_sae_pk_in_use(hapd->conf);
-+#endif /* CONFIG_SAE_PK */
-+ if (omit_rsnxe) {
-+ if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
-+ (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-+ hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
-+ wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
-+ omit_rsnxe = 0;
-+ }
-+ }
- if (hostapd_get_aid(hapd, sta) < 0) {
- hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "No room for more AIDs");
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
deleted file mode 100644
index 2816120..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From a56c6b0ea29d278bd7ec67e45c298eac8526a055 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Wed, 1 Nov 2023 19:58:05 +0800
-Subject: [PATCH 44/54] mtk: hostapd: 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.
-
-Solution:
-In the hostapd_switch_channel_fallback, need to disable acs if channel
-is valid.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/hostapd.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 2b563a572..f01f607f3 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4121,6 +4121,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
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0045-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0045-mtk-hostapd-update-eht-operation-element.patch
deleted file mode 100644
index 5b1c065..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0045-mtk-hostapd-update-eht-operation-element.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 9d81aac8e0c2a6e1aa968581a9c77cec7468cd60 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 10 May 2023 13:11:34 +0800
-Subject: [PATCH 45/54] mtk: hostapd: update eht operation element
-
----
- src/ap/ieee802_11_eht.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
-index 9a07f7501..f132e1d9c 100644
---- a/src/ap/ieee802_11_eht.c
-+++ b/src/ap/ieee802_11_eht.c
-@@ -215,9 +215,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
-
- /* TODO: Fill in appropriate EHT-MCS max Nss information */
- oper->basic_eht_mcs_nss_set[0] = 0x11;
-- oper->basic_eht_mcs_nss_set[1] = 0x00;
-- oper->basic_eht_mcs_nss_set[2] = 0x00;
-- oper->basic_eht_mcs_nss_set[3] = 0x00;
-+ oper->basic_eht_mcs_nss_set[1] = 0x11;
-+ oper->basic_eht_mcs_nss_set[2] = 0x11;
-+ oper->basic_eht_mcs_nss_set[3] = 0x11;
-
- if (is_6ghz_op_class(conf->op_class))
- chwidth = op_class_to_ch_width(conf->op_class);
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
deleted file mode 100644
index be86061..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From c73597bd17673ac6f33a314458cce14009a15fb0 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 30 Aug 2023 04:23:37 +0800
-Subject: [PATCH 46/54] mtk: hostapd: ucode: add support for ucode to parse
- BW320MHz info
-
----
- src/utils/ucode.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 29c753c32..4b6ed3a94 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- case 2:
- chanwidth = CONF_OPER_CHWIDTH_160MHZ;
- break;
-+ case 9:
-+ width = 3;
-+ chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+ break;
- default:
- return NULL;
- }
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
deleted file mode 100644
index c508d77..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-From 9238758c9fb0efb9b979eacef7d09914332186af Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 11 Sep 2023 10:16:35 +0800
-Subject: [PATCH 47/54] mtk: hostapd: synchronize bandwidth in AP/STA support
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c | 41 +++++++++++++++++++--
- src/utils/ucode.c | 12 +++++--
- wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
- 3 files changed, 117 insertions(+), 18 deletions(-)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 16d1b5153..98b2a3bf2 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -489,6 +489,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
- int i;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
-+ iface->phy, hostapd_state_text(iface->state));
-+
- if (!iface)
- return NULL;
-
-@@ -515,6 +518,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- uint64_t intval;
- int i;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
-+ iface->phy, hostapd_state_text(iface->state));
-+
- if (!iface)
- return NULL;
-
-@@ -537,7 +543,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- UPDATE_VAL(op_class, "op_class");
- UPDATE_VAL(hw_mode, "hw_mode");
- UPDATE_VAL(channel, "channel");
-- UPDATE_VAL(secondary_channel, "sec_channel");
-+
-+ intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
-+ if (!errno) {
-+ conf->secondary_channel = intval;
-+ changed = true;
-+ }
-+
- if (!changed &&
- (iface->bss[0]->beacon_set_done ||
- iface->state == HAPD_IFACE_DFS))
-@@ -583,6 +595,18 @@ out:
- return ucv_boolean_new(true);
- }
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
-+ wpa_printf(MSG_INFO, " * channel: %d\n", conf->channel);
-+ wpa_printf(MSG_INFO, " * op_class: %d\n", conf->op_class);
-+ wpa_printf(MSG_INFO, " * secondary channel: %d\n",
-+ conf->secondary_channel);
-+ wpa_printf(MSG_INFO, " * seg0: %d\n",
-+ hostapd_get_oper_centr_freq_seg0_idx(conf));
-+ wpa_printf(MSG_INFO, " * seg1: %d\n",
-+ hostapd_get_oper_centr_freq_seg0_idx(conf));
-+ wpa_printf(MSG_INFO, " * oper_chwidth: %d\n",
-+ hostapd_get_oper_chwidth(conf));
-+
- for (i = 0; i < iface->num_bss; i++) {
- struct hostapd_data *hapd = iface->bss[i];
- int ret;
-@@ -617,6 +641,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- uint64_t intval;
- int i, ret = 0;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
- if (!iface || ucv_type(info) != UC_OBJECT)
- return NULL;
-
-@@ -636,7 +661,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- if (errno)
- intval = hostapd_get_oper_chwidth(conf);
- if (intval)
-- csa.freq_params.bandwidth = 40 << intval;
-+ csa.freq_params.bandwidth = 40 <<
-+ (intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
- else
- csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
-
-@@ -647,6 +673,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
- csa.freq_params.center_freq2 = intval;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
-+ wpa_printf(MSG_INFO, " * freq is %d\n", csa.freq_params.freq);
-+ wpa_printf(MSG_INFO, " * bandwidth is %d\n",
-+ csa.freq_params.bandwidth);
-+ wpa_printf(MSG_INFO, " * sec_chan_offset is %d\n",
-+ csa.freq_params.sec_channel_offset);
-+ wpa_printf(MSG_INFO, " * center_freq1 is %d\n",
-+ csa.freq_params.center_freq1);
-+ wpa_printf(MSG_INFO, " * center_freq2 is %d\n",
-+ csa.freq_params.center_freq2);
-+
- for (i = 0; i < iface->num_bss; i++)
- ret = hostapd_switch_channel(iface->bss[i], &csa);
-
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 4b6ed3a94..6f82382f3 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- uc_value_t *freq = uc_fn_arg(0);
- uc_value_t *sec = uc_fn_arg(1);
- int width = ucv_uint64_get(uc_fn_arg(2));
-+ int bw320_offset = 1;
- int freq_val, center_idx, center_ofs;
- enum oper_chan_width chanwidth;
- enum hostapd_hw_mode hw_mode;
-@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- case 9:
- width = 3;
- chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+
-+ /* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
-+ bw320_offset = ucv_uint64_get(uc_fn_arg(3));
- break;
- default:
- return NULL;
-@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
- ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
- ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
-+ ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
-
-- if (!sec_channel)
-+ if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
-+ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
-+ ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
- return ret;
-+ }
-
- if (freq_val >= 5900)
-- center_ofs = 0;
-+ center_ofs = 32 * (1 - bw320_offset);
- else if (freq_val >= 5745)
- center_ofs = 20;
- else
-diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-index 397f85bde..542ca25c9 100644
---- a/wpa_supplicant/ucode.c
-+++ b/wpa_supplicant/ucode.c
-@@ -7,6 +7,8 @@
- #include "wps_supplicant.h"
- #include "bss.h"
- #include "ucode.h"
-+#include "driver_i.h"
-+#include "common/ieee802_11_common.h"
-
- static struct wpa_global *wpa_global;
- static uc_resource_type_t *global_type, *iface_type;
-@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- {
- const char *state;
- uc_value_t *val;
-+ enum oper_chan_width ch_width;
-+ int center_freq1, bw320_offset = 1;
-
- if (event != EVENT_CH_SWITCH_STARTED)
- return;
-@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- uc_value_push(ucv_get(val));
-
- if (event == EVENT_CH_SWITCH_STARTED) {
-+ center_freq1 = data->ch_switch.cf1;
-+
-+ switch (data->ch_switch.ch_width) {
-+ case CHAN_WIDTH_80:
-+ ch_width = CONF_OPER_CHWIDTH_80MHZ;
-+ break;
-+ case CHAN_WIDTH_80P80:
-+ ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
-+ break;
-+ case CHAN_WIDTH_160:
-+ ch_width = CONF_OPER_CHWIDTH_160MHZ;
-+ break;
-+ case CHAN_WIDTH_320:
-+ ch_width = CONF_OPER_CHWIDTH_320MHZ;
-+ break;
-+ case CHAN_WIDTH_20_NOHT:
-+ case CHAN_WIDTH_20:
-+ case CHAN_WIDTH_40:
-+ default:
-+ ch_width = CONF_OPER_CHWIDTH_USE_HT;
-+ break;
-+ }
-+
-+ /* Check bandwidth 320 MHz-2 */
-+ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+ (center_freq1 == 6265) || center_freq1 == 6585 ||
-+ center_freq1 == 6905)
-+ bw320_offset = 2;
-+
- ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
- ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
- ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
-- ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
-+ ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
- ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
-+ ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
-+ ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
- }
-
- ucv_put(wpa_ucode_call(4));
-@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
- struct wpa_bss *bss;
- uc_value_t *ret, *val;
-+ struct wpa_channel_info ci;
-+ u8 op_class, channel;
-+ enum oper_chan_width ch_width;
-+ int center_freq1, bw320_offset = 1, is_24ghz;
-+ enum hostapd_hw_mode hw_mode;
-
- if (!wpa_s)
- return NULL;
-@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- bss = wpa_s->current_bss;
- if (bss) {
- int sec_chan = 0;
-- const u8 *ie;
--
-- ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
-- if (ie && ie[1] >= 2) {
-- const struct ieee80211_ht_operation *ht_oper;
-- int sec;
--
-- ht_oper = (const void *) (ie + 2);
-- sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-- if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-- sec_chan = 1;
-- else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-- sec_chan = -1;
-+
-+ hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
-+ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
-+ hw_mode == HOSTAPD_MODE_IEEE80211B;
-+
-+ wpa_drv_channel_info(wpa_s, &ci);
-+ center_freq1 = ci.center_frq1;
-+
-+ if (bss->freq != center_freq1) {
-+ if (is_24ghz)
-+ sec_chan = (bss->freq < center_freq1) ? 1 : -1;
-+ else
-+ sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
-+ }
-+
-+ if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
-+ sec_chan, &op_class, &channel))
-+ return NULL;
-+
-+ ch_width = op_class_to_ch_width(op_class);
-+ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+ (center_freq1 == 6265) || center_freq1 == 6585 ||
-+ center_freq1 == 6905) {
-+ /* Bandwidth 320 MHz-2 */
-+ bw320_offset = 2;
- }
-
- ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
- ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
-+ ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
-+ ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
- }
-
- #ifdef CONFIG_MESH
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch
deleted file mode 100644
index ab4ff5b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch
+++ /dev/null
@@ -1,339 +0,0 @@
-From 8590ccd17827862e02b9f40ccca29a03dc3e0ab6 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:25:01 +0800
-Subject: [PATCH 48/54] mtk: hostapd: Add support for updating background
- channel by driver
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 107 ++++++++++++++++++++++++++++-
- src/ap/dfs.h | 3 +
- src/ap/drv_callbacks.c | 22 ++++++
- src/ap/hostapd.h | 5 ++
- src/drivers/driver.h | 12 ++++
- src/drivers/driver_nl80211_event.c | 6 ++
- src/drivers/nl80211_copy.h | 6 ++
- 7 files changed, 160 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 95119a391..008596bb2 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -814,11 +814,14 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
-
- static void dfs_check_background_overlapped(struct hostapd_iface *iface)
- {
-- int width = hostapd_get_oper_chwidth(iface->conf);
-+ int width = iface->radar_background.new_chwidth;
-
- if (!dfs_use_radar_background(iface))
- return;
-
-+ if (!width)
-+ width = hostapd_get_oper_chwidth(iface->conf);
-+
- if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
- width, iface->radar_background.centr_freq_seg0_idx,
- iface->radar_background.centr_freq_seg1_idx))
-@@ -983,6 +986,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- iface->radar_background.temp_ch = 1;
- return 1;
- } else if (dfs_use_radar_background(iface)) {
-+ /*
-+ * AP is going to perform CAC, so reset temp_ch to 0,
-+ * when dedicated rx has already started CAC.
-+ */
-+ if (iface->radar_background.cac_started) {
-+ iface->radar_background.temp_ch = 0;
-+ return 0;
-+ }
-+
- if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
- channel_type = DFS_ANY_CHANNEL;
-
-@@ -1116,6 +1128,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
- * ch_switch_notify event is received */
- wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
-
-+ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
-+
- return 0;
- }
-
-@@ -1167,6 +1181,9 @@ static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
- iface->radar_background.secondary_channel = sec;
- iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
- iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+ /* if main channel do not require dfs, then set temp_ch = 1 */
-+ if (!hostapd_is_dfs_required(iface))
-+ iface->radar_background.temp_ch = 1;
-
- wpa_printf(MSG_DEBUG,
- "%s: setting background chain to chan %d (%d MHz)",
-@@ -1189,6 +1206,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
- int ret;
-
-+ if (iface->radar_background.new_chwidth) {
-+ hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
-+ iface->radar_background.new_chwidth = 0;
-+ }
- ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
- iface->radar_background.freq,
- iface->radar_background.secondary_channel,
-@@ -1211,6 +1232,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- }
-
-
-+static void
-+hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
-+{
-+ struct hostapd_hw_modes *mode = iface->current_mode;
-+ struct hostapd_channel_data *chan;
-+ int i, channel, width = channel_width_to_int(chan_width);
-+
-+ if (iface->conf->channel - iface->radar_background.channel == width / 5)
-+ channel = iface->radar_background.channel;
-+ else if (iface->radar_background.channel - iface->conf->channel == width / 5)
-+ channel = iface->conf->channel;
-+ else
-+ return;
-+
-+ for (i = 0; i < mode->num_channels; i++) {
-+ chan = &mode->channels[i];
-+ if (chan->chan == channel)
-+ break;
-+ }
-+
-+ if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
-+ return;
-+
-+ switch (chan_width) {
-+ case CHAN_WIDTH_20_NOHT:
-+ case CHAN_WIDTH_20:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+ break;
-+ case CHAN_WIDTH_40:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+ break;
-+ case CHAN_WIDTH_80:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+ break;
-+ default:
-+ return;
-+ }
-+
-+ iface->radar_background.freq = channel * 5 + 5000;
-+ iface->radar_background.channel = channel;
-+ iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
-+ iface->radar_background.secondary_channel = 1;
-+ iface->radar_background.expand_ch = 0;
-+}
-+
-+
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- int ht_enabled, int chan_offset, int chan_width,
- int cf1, int cf2)
-@@ -1244,6 +1311,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- return 0;
-
- iface->radar_background.temp_ch = 0;
-+
-+ if (iface->radar_background.expand_ch)
-+ hostapd_dfs_background_expand(iface, chan_width);
-+
- return hostapd_dfs_start_channel_switch_background(iface);
- }
-
-@@ -1274,6 +1345,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- }
- } else if (hostapd_dfs_is_background_event(iface, freq)) {
- iface->radar_background.cac_started = 0;
-+ iface->radar_background.temp_ch = 0;
-+ iface->radar_background.expand_ch = 0;
- hostpad_dfs_update_background_chain(iface);
- }
-
-@@ -1406,6 +1479,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
- return 0;
-
-+ iface->radar_background.temp_ch = 0;
-+ iface->radar_background.expand_ch = 0;
-+
- /* Check if CSA in progress */
- if (hostapd_csa_in_progress(iface))
- return 0;
-@@ -1640,6 +1716,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- }
-
-
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, bool expand)
-+{
-+ switch (chan_width) {
-+ case CHAN_WIDTH_80:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+ break;
-+ case CHAN_WIDTH_160:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+ break;
-+ default:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+ break;
-+ };
-+
-+ iface->radar_background.freq = freq;
-+ iface->radar_background.channel = (freq - 5000) / 5;
-+ iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
-+ iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
-+ if (expand) {
-+ iface->radar_background.temp_ch = 1;
-+ iface->radar_background.expand_ch = 1;
-+ }
-+
-+ return 0;
-+}
-+
-+
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- int ht_enabled, int chan_offset, int chan_width,
- int cf1, int cf2)
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 25ba29ca1..a1a2be5ec 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- int ht_enabled,
- int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, bool expand);
- int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
- int ht_enabled, int chan_offset, int chan_width,
- int cf1, int cf2, u32 state);
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index f749b33dc..12419c6d4 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2089,6 +2089,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- radar->cf1, radar->cf2);
- }
-
-+
-+static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
-+ struct dfs_event *radar, bool expand)
-+{
-+ wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
-+ expand ? "expand" : "update", radar->freq);
-+ hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
-+ radar->chan_offset, radar->chan_width,
-+ radar->cf1, radar->cf2, expand);
-+}
-+
-+
- static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
- struct dfs_event *radar)
- {
-@@ -2428,6 +2440,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- break;
- hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- break;
-+ case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
-+ break;
-+ case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
-+ break;
- case EVENT_DFS_STA_CAC_SKIPPED:
- if (!data)
- break;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 1849f38d8..ea8d7254a 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -602,6 +602,11 @@ struct hostapd_iface {
- unsigned int temp_ch:1;
- /* CAC started on radar offchain */
- unsigned int cac_started:1;
-+ /* Main chain should expand its width according to the
-+ * current offchain channel after CAC detection on radar offchain.
-+ */
-+ unsigned int expand_ch:1;
-+ int new_chwidth;
- } radar_background;
-
- u16 hw_flags;
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 8da93e025..b0a6e3514 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5845,6 +5845,18 @@ enum wpa_event_type {
- * The channel in the notification is now marked as usable.
- */
- EVENT_DFS_STA_CAC_EXPIRED,
-+
-+ /**
-+ * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
-+ * channel has been updated.
-+ */
-+ EVENT_DFS_BACKGROUND_CHAN_UPDATE,
-+
-+ /**
-+ * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
-+ * channel has been updated and operating channel should expand its width.
-+ */
-+ EVENT_DFS_BACKGROUND_CHAN_EXPAND,
- };
-
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 63d44017c..c1a65eb4e 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2514,6 +2514,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- case NL80211_RADAR_CAC_STARTED:
- wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- break;
-+ case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
-+ break;
-+ case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
-+ break;
- case NL80211_RADAR_STA_CAC_SKIPPED:
- wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
- break;
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index 225864b94..9b0a8171a 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6643,6 +6643,10 @@ enum nl80211_smps_mode {
- * applicable for ETSI dfs domain where pre-CAC is valid for ever.
- * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
- * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
-+ * driver.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
-+ * driver and required to expand main operating channel.
- * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
- * when receiving CSA/assoc resp
- * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-@@ -6655,6 +6659,8 @@ enum nl80211_radar_event {
- NL80211_RADAR_NOP_FINISHED,
- NL80211_RADAR_PRE_CAC_EXPIRED,
- NL80211_RADAR_CAC_STARTED,
-+ NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
-+ NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
- NL80211_RADAR_STA_CAC_SKIPPED,
- NL80211_RADAR_STA_CAC_EXPIRED,
- };
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
deleted file mode 100644
index 623e3fc..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
+++ /dev/null
@@ -1,277 +0,0 @@
-From e878e546ee5d1e86f174b2713b7a46fb05b6a7b9 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 2 Aug 2023 19:00:34 +0800
-Subject: [PATCH 49/54] mtk: hostapd: add zwdfs mode ctrl for eagle efem hwits
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- hostapd/ctrl_iface.c | 30 +++++++++++++++++++++++++++
- src/ap/ap_config.h | 6 ++++++
- src/ap/ap_drv_ops.c | 14 +++++++++++++
- src/ap/dfs.c | 6 ++++++
- src/common/mtk_vendor.h | 12 +++++++++++
- src/drivers/driver.h | 7 +++++++
- src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +++
- 10 files changed, 115 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 9e3dbb24a..a75199345 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3183,6 +3183,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- conf->acs_exclude_6ghz_non_psc = atoi(pos);
- } else if (os_strcmp(buf, "enable_background_radar") == 0) {
- conf->enable_background_radar = atoi(pos);
-+ } else if (os_strcmp(buf, "background_radar_mode") == 0) {
-+ conf->background_radar_mode = atoi(pos);
- } else if (os_strcmp(buf, "min_tx_power") == 0) {
- int val = atoi(pos);
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index aba22e1e6..f7c7a09f3 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4221,6 +4221,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
- return pos - buf;
- }
-
-+static int
-+hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ struct hostapd_iface *iface = hapd->iface;
-+ char *pos, *param;
-+
-+ param = os_strchr(cmd, ' ');
-+ if (!param)
-+ return -1;
-+ *param++ = '\0';
-+
-+ pos = os_strstr(param, "mode=");
-+ if (!pos)
-+ return -1;
-+
-+ if (os_strncmp(pos + 5, "cert", 4) == 0)
-+ iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
-+ else if (os_strncmp(pos + 5, "normal", 6) == 0)
-+ iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
-+
-+ if (hostapd_drv_background_radar_mode(hapd) < 0)
-+ return -1;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4815,6 +4842,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- if (pos)
- *pos = ' ';
- reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
-+ } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
-+ reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 7c0d12a3b..b6f05e7af 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1025,6 +1025,7 @@ struct hostapd_config {
- bool hw_mode_set;
- int acs_exclude_6ghz_non_psc;
- int enable_background_radar;
-+ int background_radar_mode;
- enum {
- LONG_PREAMBLE = 0,
- SHORT_PREAMBLE = 1
-@@ -1218,6 +1219,11 @@ enum three_wire_mode {
- NUM_THREE_WIRE_MODE - 1
- };
-
-+enum background_radar_mode {
-+ BACKGROUND_RADAR_NORMAL_MODE,
-+ BACKGROUND_RADAR_CERT_MODE,
-+};
-+
- enum dfs_mode {
- DFS_DETECT_MODE_DISABLE,
- DFS_DETECT_MODE_AP_ENABLE,
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 721bfa053..5b93ea647 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1257,3 +1257,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
- return 0;
- return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
- }
-+
-+int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->background_radar_mode ||
-+ !(hapd->iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND) ||
-+ !hapd->iface->conf->enable_background_radar)
-+ return 0;
-+ if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
-+ wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
-+ return 0;
-+ }
-+ return hapd->driver->background_radar_mode(hapd->drv_priv,
-+ hapd->iconf->background_radar_mode);
-+}
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 008596bb2..2564168bb 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -983,6 +983,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- if (res < 0)
- return res;
-
-+ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+ return -1;
-+
- iface->radar_background.temp_ch = 1;
- return 1;
- } else if (dfs_use_radar_background(iface)) {
-@@ -1023,6 +1026,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- iface->radar_background.secondary_channel = sec;
- iface->radar_background.centr_freq_seg0_idx = cf1;
- iface->radar_background.centr_freq_seg1_idx = cf2;
-+
-+ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+ return -1;
- }
-
- return 0;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e140de60b..5bc1e0444 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
-+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -244,6 +245,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
- NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
- };
-
-+enum mtk_vendor_attr_background_radar_ctrl {
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index b0a6e3514..0c1b2e452 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5204,6 +5204,13 @@ struct wpa_driver_ops {
- * @amnt_dump_buf: Buffer to print
- */
- int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
-+
-+ /**
-+ * background_radar_mode - set background radar mode
-+ * @priv: Private driver interface data
-+ * @background_radar_mode: background radar mode
-+ */
-+ int (*background_radar_mode)(void *priv, u8 background_radar_mode);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index b3897e61d..3cbf8610a 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14743,6 +14743,39 @@ fail:
- return -ENOBUFS;
- }
-
-+static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ /* Prepare nl80211 cmd */
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_background_radar_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting background radar mode");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14915,4 +14948,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .ap_trigtype = nl80211_ap_trigtype,
- .amnt_set = nl80211_amnt_set,
- .amnt_dump = nl80211_amnt_dump,
-+ .background_radar_mode = nl80211_background_radar_mode,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 0d85adfee..74ee9b191 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_bss_color_vendor_cmd_avail:1;
- unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- unsigned int mtk_amnt_vendor_cmd_avail:1;
-+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 4bd15f348..74c3e0d86 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1137,6 +1137,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- drv->mtk_rfeatures_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
-+ drv->mtk_background_radar_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
deleted file mode 100644
index fc9a02d..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
+++ /dev/null
@@ -1,374 +0,0 @@
-From d0f78e1b2efd0d0842c98a8733b06a7b59a9bd07 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 21 Sep 2023 10:29:46 +0800
-Subject: [PATCH 50/54] mtk: hostapd: add support enable/disable preamble
- puncture from mtk vendor command
-
-This commit supports two ways to enable/disable preamble puncture
-feature.
-
-1. Add new hostapd configuration "pp_mode". The possible value could be
-1 to 3. When the value is 0, it means that the firmware will turn off
-the pp algorithm. When the value is 1, it means that the firmware will
-enable the pp algorithm, allowing the algorithm to determine whether pp
-could be applied on each txcmd. When the value is 2, it means that pp
-feature is manually configured by the user. Please noted that for
-current implementation, the default configuration is 0.
-
-2. $ hostapd_cli -i <intf_name> raw set_pp mode val
-The argument "val" could be 0 for PP feature disabled or 1 to configure
-PP feature as auto mode.
-
-This commit also let user check whether pp feature is enabled by
-hostapd_cli command. The usage shows as below:
-$ hostapd_cli -i <intf_name> raw get_pp mode
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c | 11 ++++++
- hostapd/ctrl_iface.c | 59 +++++++++++++++++++++++++++++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 7 ++++
- src/ap/ap_drv_ops.c | 9 +++++
- src/ap/ap_drv_ops.h | 1 +
- src/ap/hostapd.c | 2 ++
- src/common/mtk_vendor.h | 12 +++++++
- src/drivers/driver.h | 6 ++++
- src/drivers/driver_nl80211.c | 49 +++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 ++
- 12 files changed, 161 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index a75199345..278f6b347 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4801,6 +4801,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- conf->eht_phy_capab.mu_beamformer = atoi(pos);
- } else if (os_strcmp(buf, "punct_bitmap") == 0) {
- conf->punct_bitmap = atoi(pos);
-+ conf->pp_mode = PP_MANUAL_MODE;
- } else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
- int val = atoi(pos);
-
-@@ -4876,6 +4877,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- conf->amsdu = val;
-+ } else if (os_strcmp(buf, "pp_mode") == 0) {
-+ int val = atoi(pos);
-+
-+ if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
-+ val < PP_DISABLE || val > PP_MANUAL_MODE) {
-+ wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
-+ line);
-+ return 1;
-+ }
-+ conf->pp_mode = (u8) val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index f7c7a09f3..64bab0228 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4248,6 +4248,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
- return os_snprintf(buf, buflen, "OK\n");
- }
-
-+static int
-+hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+ size_t buflen)
-+{
-+ char *pos, *config, *value;
-+
-+ config = cmd;
-+ pos = os_strchr(config, ' ');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if (pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strcmp(config, "mode") == 0) {
-+ int val = atoi(value);
-+
-+ if (val < PP_DISABLE || val > PP_AUTO_MODE) {
-+ wpa_printf(MSG_ERROR, "Invalid value for set_pp");
-+ return -1;
-+ }
-+ hapd->iconf->pp_mode = (u8) val;
-+ if (hostapd_drv_pp_mode_set(hapd) != 0)
-+ return -1;
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for set_pp", config);
-+ return -1;
-+ }
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+ size_t buflen)
-+{
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (os_strcmp(cmd, "mode") == 0) {
-+ return os_snprintf(pos, end - pos, "pp_mode: %d\n",
-+ hapd->iconf->pp_mode);
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for get_pp", cmd);
-+ return -1;
-+ }
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4834,6 +4887,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "set_pp", 6) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
-+ reply_size);
-+ } else if (os_strncmp(buf, "get_pp", 6) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
-+ reply_size);
- } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
- // Replace first ':' with a single space ' '
- char *pos = buf + 23;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 223db56eb..d8dd5495a 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -302,6 +302,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- conf->ibf_enable = IBF_DEFAULT_ENABLE;
- conf->amsdu = 1;
-+ conf->pp_mode = PP_DISABLE;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index b6f05e7af..9e39e8285 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1205,6 +1205,7 @@ struct hostapd_config {
- u8 dfs_detect_mode;
- u8 amsdu;
- void *muru_config;
-+ u8 pp_mode;
- };
-
- enum three_wire_mode {
-@@ -1257,6 +1258,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
- EDCCA_CTRL_NUM,
- };
-
-+enum pp_mode {
-+ PP_DISABLE = 0,
-+ PP_AUTO_MODE,
-+ PP_MANUAL_MODE,
-+};
-+
- #define EDCCA_DEFAULT_COMPENSATION -6
- #define EDCCA_MIN_COMPENSATION -126
- #define EDCCA_MAX_COMPENSATION 126
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 5b93ea647..d0d8279e2 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1271,3 +1271,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
- return hapd->driver->background_radar_mode(hapd->drv_priv,
- hapd->iconf->background_radar_mode);
- }
-+
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->pp_mode_set ||
-+ hapd->iconf->pp_mode > PP_AUTO_MODE)
-+ return 0;
-+ return hapd->driver->pp_mode_set(hapd->drv_priv,
-+ hapd->iconf->pp_mode);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 1e7ae7a8d..e4c2827bf 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -163,6 +163,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
-
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f01f607f3..2ffa3d4ea 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2526,6 +2526,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_amsdu_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_pp_mode_set(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 5bc1e0444..6275c141d 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
-+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
- NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
- };
-
-+enum mtk_vendor_attr_pp_ctrl {
-+ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_PP_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-+ MTK_VENDOR_ATTR_PP_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 0c1b2e452..fb463aaac 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5211,6 +5211,12 @@ struct wpa_driver_ops {
- * @background_radar_mode: background radar mode
- */
- int (*background_radar_mode)(void *priv, u8 background_radar_mode);
-+ /**
-+ * pp_mode_set - Set preamble puncture operation mode
-+ * @priv: Private driver interface data
-+ * @pp_mode: Value is defined in enum pp_mode
-+ */
-+ int (*pp_mode_set)(void *priv, const u8 pp_mode);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3cbf8610a..cbe793cc6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -151,6 +151,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
- [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
- };
-
-+static struct nla_policy
-+pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
-+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- struct nl_sock *handle;
-@@ -14776,6 +14781,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
- return ret;
- }
-
-+static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_pp_vendor_cmd_avail) {
-+ wpa_printf(MSG_DEBUG,
-+ "nl80211: Driver does not support setting preamble puncture");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
-+ ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14949,4 +14997,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .amnt_set = nl80211_amnt_set,
- .amnt_dump = nl80211_amnt_dump,
- .background_radar_mode = nl80211_background_radar_mode,
-+ .pp_mode_set = nl80211_pp_mode_set,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 74ee9b191..1bba5b14e 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -211,6 +211,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- unsigned int mtk_amnt_vendor_cmd_avail:1;
- unsigned int mtk_background_radar_vendor_cmd_avail:1;
-+ unsigned int mtk_pp_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 74c3e0d86..0e044b03c 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1140,6 +1140,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
- drv->mtk_background_radar_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
-+ drv->mtk_pp_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
deleted file mode 100644
index 5f7489e..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 1ca50d17101a4812a424ed5eb6ae10d54635b055 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 22 Nov 2023 21:41:34 +0800
-Subject: [PATCH 51/54] mtk: hostapd: add no_beacon vendor command for cert
-
-Add the vendor command to disable/enable beacon
-
-[Usage]
-hostapd_cli -i <interface> no_beacon <value>
- <value>
- 0: enable beacon
- 1: disable beacon
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- hostapd/ctrl_iface.c | 21 +++++++++++++++++++
- hostapd/hostapd_cli.c | 7 +++++++
- src/ap/ap_drv_ops.c | 8 ++++++++
- src/ap/ap_drv_ops.h | 1 +
- src/common/mtk_vendor.h | 12 +++++++++++
- src/drivers/driver.h | 7 +++++++
- src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +++
- 9 files changed, 94 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 64bab0228..7406aef38 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4301,6 +4301,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- }
- }
-
-+static int
-+hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
-+ char *buf, size_t buflen)
-+{
-+ int disable_beacon = atoi(value);
-+
-+ if (disable_beacon < 0) {
-+ wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
-+ return os_snprintf(buf, buflen, "OK\n");
-+ else
-+ return -1;
-+
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4904,6 +4922,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
- reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
-+ reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
-+ reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 363a6bb93..d5b6d59f3 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1422,6 +1422,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
- return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
- }
-
-+static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
-+}
-
- #ifdef CONFIG_DPP
-
-@@ -1799,6 +1804,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
- { "get_mu", hostapd_cli_cmd_get_mu, NULL,
- " = show mu onoff value in 0-15 bitmap"},
-+ { "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
-+ "<value> 0: Enable beacon, 1: Disable beacon"},
- #ifdef CONFIG_DPP
- { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
- "report a scanned DPP URI from a QR Code" },
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index d0d8279e2..073c05de8 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1280,3 +1280,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
- return hapd->driver->pp_mode_set(hapd->drv_priv,
- hapd->iconf->pp_mode);
- }
-+
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
-+{
-+ if (!hapd->driver || !hapd->driver->beacon_ctrl)
-+ return 0;
-+ return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
-+}
-+
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index e4c2827bf..6b9e454de 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -164,6 +164,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
-
- #include "drivers/driver.h"
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6275c141d..5531802b8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
-+ MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -268,6 +269,17 @@ enum mtk_vendor_attr_pp_ctrl {
- NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
- };
-
-+enum mtk_vendor_attr_beacon_ctrl {
-+ MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
-+ MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index fb463aaac..40f6150d7 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5128,6 +5128,13 @@ struct wpa_driver_ops {
- int (*mu_ctrl)(void *priv, u8 mode, void *config);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-
-+ /**
-+ * beacon_ctrl - ctrl on off for beacon
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*beacon_ctrl)(void *priv, u8 beacon_mode);
-+
- /**
- * three_wire_ctrl - set three_wire_ctrl mode
- * @priv: Private driver interface data
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index cbe793cc6..9a10a93f0 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13827,6 +13827,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-
- return ret;
- }
-+static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
-+ wpa_printf(MSG_ERROR,
-+ "nl80211: Driver does not support setting beacon control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+}
-+
- #endif /* CONFIG_IEEE80211AX */
-
-
-@@ -14972,6 +15005,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .set_4addr_mode = nl80211_set_4addr_mode,
- .mu_ctrl = nl80211_mu_ctrl,
- .mu_dump = nl80211_mu_dump,
-+ .beacon_ctrl = nl80211_beacon_ctrl,
- #ifdef CONFIG_DPP
- .dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 1bba5b14e..300c0d11a 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_amnt_vendor_cmd_avail:1;
- unsigned int mtk_background_radar_vendor_cmd_avail:1;
- unsigned int mtk_pp_vendor_cmd_avail:1;
-+ unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 0e044b03c..2f844053d 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1143,6 +1143,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
- drv->mtk_pp_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
-+ drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
deleted file mode 100644
index f5b5c24..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
+++ /dev/null
@@ -1,379 +0,0 @@
-From 1f6c4857cb55c8ba81a50f3fe0a1465efee10513 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Thu, 28 Sep 2023 18:03:08 +0800
-Subject: [PATCH 52/54] mtk: hostapd: ACS: Add EHT320 and HT40- support, fix
- issue
-
-1. Add 6G EHT320 support;
-2. Add 2.4G HT40- support;
-3. Fix issue: selected best channel is out of channels;
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/acs.c | 191 +++++++++++++++++++++++++++++++++------------------
- 1 file changed, 124 insertions(+), 67 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index af3140542..e4871921f 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -245,6 +245,7 @@ enum bw_type {
- ACS_BW40,
- ACS_BW80,
- ACS_BW160,
-+ ACS_BW320,
- };
-
- struct bw_item {
-@@ -286,10 +287,16 @@ static const struct bw_item bw_160[] = {
- { 6435, 6575, 111 }, { 6595, 6735, 143 },
- { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
- };
-+static const struct bw_item bw_320[] = {
-+ { 5955, 6255, 31 }, { 6115, 6415, 63 }, { 6275, 6575, 95 },
-+ { 6435, 6735, 127 }, { 6595, 6895, 159 }, { 6755, 7055, 191 },
-+ { -1, -1, -1 }
-+};
- static const struct bw_item *bw_desc[] = {
- [ACS_BW40] = bw_40,
- [ACS_BW80] = bw_80,
- [ACS_BW160] = bw_160,
-+ [ACS_BW320] = bw_320,
- };
-
-
-@@ -775,10 +782,19 @@ 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;
-@@ -786,6 +802,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- bool update_best = 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
-@@ -817,7 +836,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 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);
-@@ -838,7 +857,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
-- (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
-+ (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+ iface->conf->ieee80211be)) {
- if (hostapd_get_oper_chwidth(iface->conf) ==
- CONF_OPER_CHWIDTH_80MHZ &&
- !acs_usable_bw_chan(chan, ACS_BW80)) {
-@@ -856,63 +876,86 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- chan->chan);
- continue;
- }
-- }
-
-- factor = 0;
-- if (acs_usable_chan(chan))
-- factor = chan->interference_factor;
-- total_weight = 1;
--
-- 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)) {
-+ if (iface->conf->ieee80211be &&
-+ hostapd_get_oper_chwidth(iface->conf) ==
-+ CONF_OPER_CHWIDTH_320MHZ &&
-+ !acs_usable_bw_chan(chan, ACS_BW320)) {
- wpa_printf(MSG_DEBUG,
-- "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
-- chan->chan, adj_chan->chan, bw);
-- break;
-+ "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth",
-+ chan->chan);
-+ continue;
- }
-+ }
-
-- if (acs_usable_chan(adj_chan)) {
-- factor += adj_chan->interference_factor;
-+ factor = 0;
-+ total_weight = 0;
-+
-+ 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 (!is_in_chanlist(iface, adj_chan) || !is_in_freqlist(iface, adj_chan))
-+ 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;
-- }
-+ 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;
-+ }
-+
-+ update_best = true;
-+ if (acs_usable_chan(adj_chan)) {
-+ factor += adj_chan->interference_factor;
-+ total_weight += 1;
-+ } else {
-+ update_best = false;
-+ }
-
-- /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
-- * channel interference factor. */
-- if (is_24ghz_mode(mode->mode)) {
-+ /* find the best channel in this segment */
-+ if (update_best &&
-+ adj_chan->interference_factor < best->interference_factor)
-+ best = adj_chan;
-+ }
-+
-+ if (j != n_chans) {
-+ 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 * sec_ch_factor * 20));
-+ 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 * sec_ch_factor * 20) - 5);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -920,7 +963,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- adj_chan = acs_find_chan(iface, chan->freq +
-- (j * 20) - 10);
-+ (j * sec_ch_factor * 20) - 10);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_NEXT_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -928,7 +971,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- adj_chan = acs_find_chan(iface, chan->freq +
-- (j * 20) + 5);
-+ (j * sec_ch_factor * 20) + 5);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -936,7 +979,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- adj_chan = acs_find_chan(iface, chan->freq +
-- (j * 20) + 10);
-+ (j * sec_ch_factor * 20) + 10);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_NEXT_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -945,7 +988,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) {
-@@ -964,11 +1008,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);
- }
-@@ -1021,19 +1065,12 @@ 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;
-
-- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+ iface->conf->ieee80211be) {
- switch (hostapd_get_oper_chwidth(iface->conf)) {
- case CONF_OPER_CHWIDTH_80MHZ:
- n_chans = 4;
-@@ -1043,6 +1080,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- break;
- default:
- break;
-+ /* 320 is supported only in 6GHz 11be mode */
- }
- }
-
-@@ -1063,7 +1101,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);
-
- #ifdef CONFIG_IEEE80211BE
-@@ -1078,6 +1116,21 @@ bw_selected:
- return rand_chan;
- }
-
-+static int acs_get_center_freq_320mhz(int channel)
-+{
-+ if (channel >= 1 && channel <= 45)
-+ return 31;
-+ else if (channel >= 49 && channel <= 77)
-+ return 63;
-+ else if (channel >= 81 && channel <= 109)
-+ return 95;
-+ else if (channel >= 113 && channel <= 141)
-+ return 127;
-+ else if (channel >= 145 && channel <= 173)
-+ return 159;
-+ else
-+ return 191;
-+}
-
- static void acs_adjust_secondary(struct hostapd_iface *iface)
- {
-@@ -1105,7 +1158,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- {
- int center;
-
-- wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
-+ wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
-
- switch (hostapd_get_oper_chwidth(iface->conf)) {
- case CONF_OPER_CHWIDTH_USE_HT:
-@@ -1121,6 +1174,9 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- case CONF_OPER_CHWIDTH_80MHZ:
- center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
- break;
-+ case CONF_OPER_CHWIDTH_320MHZ:
-+ center = acs_get_center_freq_320mhz(iface->conf->channel);
-+ break;
- case CONF_OPER_CHWIDTH_160MHZ:
- center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
- break;
-@@ -1128,7 +1184,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- /* TODO: How can this be calculated? Adjust
- * acs_find_ideal_chan() */
- wpa_printf(MSG_INFO,
-- "ACS: Only VHT20/40/80/160 is supported now");
-+ "ACS: Only VHT20/40/80/160 EHT320 is supported now");
- return;
- }
-
-@@ -1191,7 +1247,8 @@ static void acs_study(struct hostapd_iface *iface)
- iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
-
-- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+ iface->conf->ieee80211be) {
- acs_adjust_secondary(iface);
- acs_adjust_center_freq(iface);
- }
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch
deleted file mode 100644
index 77677d0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch
+++ /dev/null
@@ -1,190 +0,0 @@
-From cb9841c4361d5c1d236b7d257e2d513ecc1c7c91 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 17 Oct 2023 11:11:40 +0800
-Subject: [PATCH 53/54] mtk: hostapd: add eht_bw320_offset configuration option
-
-This patch introduces a new configuration option, "eht_bw320_offset",
-which enables devices to specify a preferred channelization for 320 MHz
-BSSs when using automatic channel selection (ACS).
-This option is only applicable when the channel is not already decided
-and the bandwidth is set to 320 MHz.
-
-The value and meaning of the option:
-0: auto-detected by hostapd
-1: 320 MHz-1
-2: 320 MHz-2
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- hostapd/config_file.c | 3 +++
- hostapd/hostapd.conf | 8 ++++++++
- src/ap/ap_config.c | 6 ++++++
- src/ap/ap_config.h | 37 +++++++++++++++++++++++++++++++++++++
- src/ap/ctrl_iface_ap.c | 11 +++++++++++
- src/ap/drv_callbacks.c | 2 ++
- 6 files changed, 67 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 278f6b347..721685baf 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4822,6 +4822,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- line);
- return 1;
- }
-+ } else if (os_strcmp(buf, "eht_bw320_offset") == 0) {
-+ u8 val = atoi(pos);
-+ conf->eht_bw320_offset = val;
- #endif /* CONFIG_IEEE80211BE */
- } else if (os_strcmp(buf, "edcca_threshold") == 0) {
- if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index f16e3b08d..290504317 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -1032,6 +1032,14 @@ wmm_ac_vo_acm=0
- #eht_oper_chwidth (see vht_oper_chwidth)
- #eht_oper_centr_freq_seg0_idx
-
-+#eht_bw320_offset: For automatic channel selection (ACS) to indicate a prefered
-+# 320 MHz channelization in EHT mode.
-+# If the channel is decided or the bandwidth is not 320 MHz, this option is meaningless.
-+# 0 = auto-detect by hostapd
-+# 1 = 320 MHz-1
-+# 2 = 320 MHz-2
-+#eht_bw320_offset=0
-+
- # Disabled subchannel bitmap (16 bits) as per IEEE P802.11be/3.0,
- # Figure 9-1002c (EHT Operation Information field format). Each bit corresponds
- # to a 20 MHz channel, the lowest bit corresponds to the lowest frequency. A
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index d8dd5495a..3fb98d08f 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -304,6 +304,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->amsdu = 1;
- conf->pp_mode = PP_DISABLE;
-
-+ hostapd_set_and_check_bw320_offset(conf, 0);
- return conf;
- }
-
-@@ -1515,6 +1516,7 @@ static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
- int hostapd_config_check(struct hostapd_config *conf, int full_config)
- {
- size_t i;
-+ u8 bw320_offset = 0;
-
- if (full_config && is_6ghz_op_class(conf->op_class) &&
- !conf->hw_mode_set) {
-@@ -1566,6 +1568,8 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
- "Cannot set ieee80211be without ieee80211ax");
- return -1;
- }
-+
-+ bw320_offset = conf->eht_bw320_offset;
- #endif /* CONFIG_IEEE80211BE */
-
- if (full_config && conf->mbssid && !conf->ieee80211ax) {
-@@ -1574,6 +1578,8 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
- return -1;
- }
-
-+ hostapd_set_and_check_bw320_offset(conf, bw320_offset);
-+
- for (i = 0; i < conf->num_bss; i++) {
- if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
- return -1;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 9e39e8285..3e0505594 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1184,6 +1184,7 @@ struct hostapd_config {
- struct eht_phy_capabilities_info eht_phy_capab;
- u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
- u8 punct_acs_threshold;
-+ u8 eht_bw320_offset;
- #endif /* CONFIG_IEEE80211BE */
-
- /* EHT enable/disable config from CHAN_SWITCH */
-@@ -1355,6 +1356,42 @@ hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
- conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
- }
-
-+static inline u8
-+hostapd_get_bw320_offset(struct hostapd_config *conf)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
-+ hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ)
-+ return conf->eht_bw320_offset;
-+#endif /* CONFIG_IEEE80211BE */
-+ return 0;
-+}
-+
-+static inline void
-+hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
-+ u8 bw320_offset)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
-+ hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ) {
-+ if (conf->channel) {
-+ /* If the channel is set, then calculate bw320_offset
-+ * by center frequency segment 0.
-+ */
-+ u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
-+ conf->eht_bw320_offset = (seg0 - 31) % 64 ? 2 : 1;
-+ } else {
-+ /* If the channel is not set, bw320_offset indicates
-+ * prefered offset of 320 MHz.
-+ */
-+ conf->eht_bw320_offset = bw320_offset;
-+ }
-+ } else {
-+ conf->eht_bw320_offset = 0;
-+ }
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
- #define IBF_DEFAULT_ENABLE 0
-
- int hostapd_mac_comp(const void *a, const void *b);
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 7bdefb4cf..e686fb8b7 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -831,6 +831,17 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
- if (os_snprintf_error(buflen - len, ret))
- return len;
- len += ret;
-+
-+ if (is_6ghz_op_class(iface->conf->op_class) &&
-+ hostapd_get_oper_chwidth(iface->conf) ==
-+ CONF_OPER_CHWIDTH_320MHZ) {
-+ ret = os_snprintf(buf + len, buflen - len,
-+ "eht_bw320_offset=%d\n",
-+ iface->conf->eht_bw320_offset);
-+ if (os_snprintf_error(buflen - len, ret))
-+ return len;
-+ len += ret;
-+ }
- }
- #endif /* CONFIG_IEEE80211BE */
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 12419c6d4..b0d9420e8 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1175,6 +1175,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- hostapd_set_oper_chwidth(hapd->iconf, chwidth);
- hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
- hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
-+ /* Auto-detect new bw320_offset */
-+ hostapd_set_and_check_bw320_offset(hapd->iconf, 0);
- #ifdef CONFIG_IEEE80211BE
- hapd->iconf->punct_bitmap = punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
deleted file mode 100644
index fab7706..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From 44d5b6d691a45b71d2e8896f3302b4b946ae4d33 Mon Sep 17 00:00:00 2001
-From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
-Date: Wed, 13 Dec 2023 18:13:01 +0530
-Subject: [PATCH 54/54] mtk: hostapd: WPS added change to configure AP PIN lock
- timout
-
-added config paramter ap_pin_lockout_time to configure
-AP PIN timeout from hosatpd.conf
-
-
-Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- src/ap/ap_config.h | 1 +
- src/ap/wps_hostapd.c | 9 ++++++---
- 3 files changed, 9 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 721685baf..9dd86e037 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3709,6 +3709,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- bss->wps_independent = atoi(pos);
- } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
- bss->ap_setup_locked = atoi(pos);
-+ } else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
-+ bss->ap_pin_lockout_time = atoi(pos);
- } else if (os_strcmp(buf, "uuid") == 0) {
- if (uuid_str2bin(pos, bss->uuid)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 3e0505594..c03fa11b6 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -491,6 +491,7 @@ struct hostapd_bss_config {
- #ifdef CONFIG_WPS
- int wps_independent;
- int ap_setup_locked;
-+ unsigned int ap_pin_lockout_time;
- u8 uuid[16];
- char *wps_pin_requests;
- char *device_name;
-diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
-index 0c351af5f..ea2608917 100644
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
- eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
- wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
- } else if (!hapd->conf->ap_setup_locked) {
-- if (hapd->ap_pin_lockout_time == 0)
-- hapd->ap_pin_lockout_time = 60;
-- else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
-+ if (hapd->ap_pin_lockout_time == 0) {
-+ if (hapd->conf->ap_pin_lockout_time)
-+ hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
-+ else
-+ hapd->ap_pin_lockout_time = 60;
-+ } else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
- (hapd->ap_pin_failures % 3) == 0)
- hapd->ap_pin_lockout_time *= 2;
-
---
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc b/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
index 325e353..e7987dd 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
@@ -1,120 +1,106 @@
#patch patches (come from openwrt/lede/target/linux/mediatek)
SRC_URI_append = " \
- file://001-wolfssl-init-RNG-with-ECC-key.patch \
- file://010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch \
- file://011-mesh-use-deterministic-channel-on-channel-switch.patch \
- file://021-fix-sta-add-after-previous-connection.patch \
- file://022-hostapd-fix-use-of-uninitialized-stack-variables.patch \
- file://023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch \
- file://030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch \
- file://040-mesh-allow-processing-authentication-frames-in-block.patch \
- file://050-build_fix.patch \
- file://110-mbedtls-TLS-crypto-option-initial-port.patch \
- file://120-mbedtls-fips186_2_prf.patch \
- file://130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch \
- file://135-mbedtls-fix-owe-association.patch \
- file://140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch \
- file://150-add-NULL-checks-encountered-during-tests-hwsim.patch \
- file://160-dpp_pkex-EC-point-mul-w-value-prime.patch \
- file://170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch \
- file://180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch \
- file://181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch \
- file://182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch \
- file://183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch \
- file://200-multicall.patch \
- file://300-noscan.patch \
- file://301-mesh-noscan.patch \
- file://310-rescan_immediately.patch \
- file://320-optional_rfkill.patch \
- file://330-nl80211_fix_set_freq.patch \
- file://341-mesh-ctrl-iface-channel-switch.patch \
- file://350-nl80211_del_beacon_bss.patch \
- file://380-disable_ctrl_iface_mib.patch \
- file://381-hostapd_cli_UNKNOWN-COMMAND.patch \
- file://390-wpa_ie_cap_workaround.patch \
- file://400-wps_single_auth_enc_type.patch \
- file://410-limit_debug_messages.patch \
- file://420-indicate-features.patch \
- file://430-hostapd_cli_ifdef.patch \
- file://431-wpa_cli_ifdef.patch \
- file://460-wpa_supplicant-add-new-config-params-to-be-used-with.patch \
- file://463-add-mcast_rate-to-11s.patch \
- file://464-fix-mesh-obss-check.patch \
- file://465-hostapd-config-support-random-BSS-color.patch \
- file://470-survey_data_fallback.patch \
- file://500-lto-jobserver-support.patch \
- file://590-rrm-wnm-statistics.patch \
- file://599-wpa_supplicant-fix-warnings.patch \
- file://600-ubus_support.patch \
- file://601-ucode_support.patch \
- file://610-hostapd_cli_ujail_permission.patch \
- file://701-reload_config_inline.patch \
- file://710-vlan_no_bridge.patch \
- file://711-wds_bridge_force.patch \
- file://720-iface_max_num_sta.patch \
- file://730-ft_iface.patch \
- file://740-snoop_iface.patch \
- file://750-qos_map_set_without_interworking.patch \
- file://751-qos_map_ignore_when_unsupported.patch \
- file://760-dynamic_own_ip.patch \
- file://761-shared_das_port.patch \
- file://770-radius_server.patch \
- file://990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch \
- file://991-Fix-OpenWrt-13156.patch \
- file://992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch \
- file://993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch \
- file://mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
- file://mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch \
- file://mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
- file://mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch \
- file://mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
- file://mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
- file://mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
- file://mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch \
- file://mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
- file://mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch \
- file://mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
- file://mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
- file://mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch \
- file://mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch \
- file://mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch \
- file://mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
- file://mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
- file://mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
- file://mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch \
- file://mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
- file://mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
- file://mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
- file://mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch \
- file://mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch \
- file://mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
- file://mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch \
- file://mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
- file://mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
- file://mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
- file://mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
- file://mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
- file://mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch \
- file://mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
- file://mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch \
- file://mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
- file://mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
- file://mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
- file://mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
- file://mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
- file://mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch \
- file://mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch \
- file://mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
- file://mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
- file://mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
- file://mtk-0045-mtk-hostapd-update-eht-operation-element.patch \
- file://mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch \
- file://mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
- file://mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch \
- file://mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch \
- file://mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
- file://mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
- file://mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch \
- file://mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch \
- file://mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
+ file://0001-hostapd-MLO-fix-for_each_mld_link-macro.patch \
+ file://0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch \
+ file://0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch \
+ file://0004-hostapd-MLO-send-link_id-on-sta_deauth.patch \
+ file://0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch \
+ file://0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch \
+ file://0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch \
+ file://0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch \
+ file://0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch \
+ file://0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch \
+ file://0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch \
+ file://0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch \
+ file://0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch \
+ file://0014-hostapd-MLO-update-all-partner-s-link-beacon.patch \
+ file://0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch \
+ file://0016-hostapd-MLO-Enhance-wpa-state-machine.patch \
+ file://0017-hostapd-MLO-add-support-for-MLO-rekey.patch \
+ file://0018-hostapd-MLO-send-link-id-during-flushing-stations.patch \
+ file://0019-hostapd-MLO-display-link-details-in-status-command.patch \
+ file://0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch \
+ file://0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch \
+ file://0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch \
+ file://0023-backport-hostapd-afcd-add-AFC-daemon-support.patch \
+ file://0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch \
+ file://0025-backport-hostapd-ap-add-AFC-client-support.patch \
+ file://0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch \
+ file://0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch \
+ file://0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch \
+ file://0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
+ file://0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch \
+ file://0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
+ file://0032-mtk-hostapd-Add-mtk_vendor.h.patch \
+ file://0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
+ file://0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
+ file://0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
+ file://0036-mtk-hostapd-Add-hostapd-iBF-control.patch \
+ file://0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
+ file://0038-mtk-hostapd-Add-DFS-detection-mode.patch \
+ file://0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
+ file://0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
+ file://0041-mtk-hostapd-Add-he_ldpc-configuration.patch \
+ file://0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch \
+ file://0043-mtk-hostapd-6G-band-does-not-require-DFS.patch \
+ file://0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
+ file://0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
+ file://0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
+ file://0047-mtk-hostapd-Add-available-color-bitmap.patch \
+ file://0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
+ file://0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
+ file://0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
+ file://0051-mtk-hostapd-Add-muru-user-number-debug-command.patch \
+ file://0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
+ file://0053-mtk-hostapd-Add-HE-capabilities-check.patch \
+ file://0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
+ file://0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
+ file://0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
+ file://0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
+ file://0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
+ file://0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
+ file://0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
+ file://0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
+ file://0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
+ file://0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
+ file://0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
+ file://0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
+ file://0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
+ file://0067-mtk-hostapd-update-eht-operation-element.patch \
+ file://0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch \
+ file://0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
+ file://0070-mtk-hostapd-Add-support-for-updating-background-chan.patch \
+ file://0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch \
+ file://0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
+ file://0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
+ file://0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
+ file://0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch \
+ file://0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
+ file://0077-Revert-ACS-upstream-changes.patch \
+ file://0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch \
+ file://0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch \
+ file://0080-mtk-hostapd-fix-mld_assoc_link_id.patch \
+ file://0081-mtk-wpa_s-correctly-get-assoc-frequency.patch \
+ file://0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch \
+ file://0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch \
+ file://0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch \
+ file://0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch \
+ file://0086-mtk-hostapd-add-mld_primary-option.patch \
+ file://0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch \
+ file://0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch \
+ file://0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch \
+ file://0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch \
+ file://0091-mtk-wifi-hostapd-add-wds-mlo-support.patch \
+ file://0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch \
+ file://0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch \
+ file://0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch \
+ file://0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch \
+ file://0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch \
+ file://0098-mtk-hostapd-add-connac3-csi-control-interface.patch \
+ file://0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch \
+ file://0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch \
+ file://0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch \
+ file://0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch \
+ file://0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch \
+ file://0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch \
"
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.c b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.c
index 66eba99..8689494 100644
--- a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.c
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.c
@@ -774,7 +774,8 @@
mode ? &mode->he_capab[IEEE80211_MODE_AP] :
NULL,
mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
- NULL);
+ NULL,
+ hostapd_get_punct_bitmap(hapd));
for (i = 0; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.h b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.h
index b0f7c44..22767d6 100644
--- a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.h
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.h
@@ -8,6 +8,8 @@
#ifndef __HOSTAPD_UBUS_H
#define __HOSTAPD_UBUS_H
+#include "sta_info.h"
+
enum hostapd_ubus_event_type {
HOSTAPD_UBUS_PROBE_REQ,
HOSTAPD_UBUS_AUTH_REQ,
@@ -27,6 +29,7 @@
struct hostapd_data;
struct hapd_interfaces;
struct rrm_measurement_beacon_report;
+struct sta_info;
#ifdef UBUS_SUPPORT
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.c b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.c
index 16d1b51..d344190 100644
--- a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.c
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.c
@@ -51,7 +51,7 @@
int i;
list = ucv_array_new(vm);
- for (i = 0; i < iface->num_bss; i++) {
+ for (i = 0; iface->bss && i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
diff --git a/recipes-wifi/hostapd/files/wdev.uc b/recipes-wifi/hostapd/files/wdev.uc
index ff4d629..d505e30 100644
--- a/recipes-wifi/hostapd/files/wdev.uc
+++ b/recipes-wifi/hostapd/files/wdev.uc
@@ -32,10 +32,11 @@
wdev_config.macaddr = phydev.macaddr_next();
wdev_create(phy, ifname, wdev_config);
wdev_set_up(ifname, true);
+ let htmode = wdev.htmode || "NOHT";
if (wdev.freq)
- system(`iw dev ${ifname} set freq ${wdev.freq} ${wdev.htmode}`);
+ system(`iw dev ${ifname} set freq ${wdev.freq} ${htmode}`);
if (wdev.mode == "adhoc") {
- let cmd = ["iw", "dev", ifname, "ibss", "join", wdev.ssid, wdev.freq, wdev.htmode, "fixed-freq" ];
+ let cmd = ["iw", "dev", ifname, "ibss", "join", wdev.ssid, wdev.freq, htmode, "fixed-freq" ];
if (wdev.bssid)
push(cmd, wdev.bssid);
for (let key in [ "beacon-interval", "basic-rates", "mcast-rate", "keys" ])
@@ -43,7 +44,7 @@
push(cmd, key, wdev[key]);
system(cmd);
} else if (wdev.mode == "mesh") {
- let cmd = [ "iw", "dev", ifname, "mesh", "join", wdev.ssid, "freq", wdev.freq, wdev.htmode ];
+ let cmd = [ "iw", "dev", ifname, "mesh", "join", wdev.ssid, "freq", wdev.freq, htmode ];
for (let key in [ "mcast-rate", "beacon-interval" ])
if (wdev[key])
push(cmd, key, wdev[key]);
diff --git a/recipes-wifi/hostapd/files/wpa_supplicant.uc b/recipes-wifi/hostapd/files/wpa_supplicant.uc
index e320330..e19a4e1 100644
--- a/recipes-wifi/hostapd/files/wpa_supplicant.uc
+++ b/recipes-wifi/hostapd/files/wpa_supplicant.uc
@@ -37,7 +37,7 @@
wpas.data.iface_phy[ifname] = phy;
wdev_remove(ifname);
- let ret = wdev_create(phy, ifname, wdev_config);
+ let ret = wdev_create("phy0", ifname, wdev_config);
if (ret)
wpas.printf(`Failed to create device ${ifname}: ${ret}`);
wdev_set_up(ifname, true);
@@ -257,10 +257,9 @@
ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
}
-function iface_hostapd_notify(phy, ifname, iface, state)
+function iface_hostapd_notify(phy, ifname, iface, state, link_id)
{
let ubus = wpas.data.ubus;
- let status = iface.status();
let msg = { phy: phy };
wpas.printf(`ucode: mtk: wpa_s in state ${state} notifies hostapd`);
@@ -275,11 +274,13 @@
msg.up = true;
break;
case "COMPLETED":
+ let status = iface.status(link_id);
msg.up = true;
msg.frequency = status.frequency;
msg.sec_chan_offset = status.sec_chan_offset;
msg.ch_width = status.ch_width;
msg.bw320_offset = status.bw320_offset;
+ msg.band_idx = status.band_idx;
break;
default:
return;
@@ -317,12 +318,25 @@
},
state: function(ifname, iface, state) {
let phy = wpas.data.iface_phy[ifname];
+ let ret = iface.get_valid_links();
+ let link_id = 0, valid_links = ret.valid_links;
+
if (!phy) {
wpas.printf(`no PHY for ifname ${ifname}`);
return;
}
+ if (valid_links) {
+ while (valid_links) {
+ if (valid_links & 1)
+ iface_hostapd_notify(phy, ifname, iface, state, link_id);
+
- iface_hostapd_notify(phy, ifname, iface, state);
+ link_id++;
+ valid_links >>= 1;
+ }
+ } else {
+ iface_hostapd_notify(phy, ifname, iface, state, -1);
+ }
if (state != "COMPLETED")
return;
diff --git a/recipes-wifi/hostapd/hostapd_2.10.3.bb b/recipes-wifi/hostapd/hostapd_2.10.3.bb
index 649df23..bd511b4 100644
--- a/recipes-wifi/hostapd/hostapd_2.10.3.bb
+++ b/recipes-wifi/hostapd/hostapd_2.10.3.bb
@@ -12,7 +12,7 @@
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-${PV}:"
-SRCREV ?= "e5ccbfc69ecf297590341ae8b461edba9d8e964c"
+SRCREV ?= "07c9f183ea744ac04585fb6dd10220c75a5e2e74"
SRC_URI = " \
git://w1.fi/hostap.git;protocol=https;branch=main \
file://hostapd-full.config \
@@ -22,7 +22,7 @@
file://hostapd-5G-7915.conf \
file://hostapd-5G-7916.conf \
file://hostapd.service \
- file://hostapd-init.sh \
+ file://hostapd-init-EHT.sh \
file://mac80211-EHT.sh \
file://init-uci-config.service \
file://hostapd.uc \
@@ -44,13 +44,7 @@
SYSTEMD_SERVICE_${PN} = "hostapd.service"
SYSTEMD_SERVICE_${PN} += " init-uci-config.service"
INSANE_SKIP_${PN} += "file-rdeps"
-do_unpack_append() {
- bb.build.exec_func('do_copy_openwrt_src', d)
-}
-do_copy_openwrt_src() {
- cp -Rfp ${WORKDIR}/src-${PV}/* ${S}/
-}
do_configure_append() {
install -m 0644 ${WORKDIR}/hostapd-full.config ${B}/.config
@@ -76,6 +70,7 @@
echo "CONFIG_UCODE=y" >> ${B}/.config
echo "CONFIG_LIBNL20=y" >> ${B}/.config
echo "CONFIG_LIBNL_TINY=y" >> ${B}/.config
+ echo "CONFIG_AFC=y" >> ${B}/.config
}
do_filogic_patches() {
@@ -104,7 +99,7 @@
install -m 0644 ${WORKDIR}/hostapd-5G-7915.conf ${D}${sysconfdir}
install -m 0644 ${WORKDIR}/hostapd-5G-7916.conf ${D}${sysconfdir}
install -m 0644 ${WORKDIR}/hostapd.service ${D}${systemd_unitdir}/system
- install -m 0755 ${WORKDIR}/hostapd-init.sh ${D}${base_libdir}/rdk
+ install -m 0755 ${WORKDIR}/hostapd-init-EHT.sh ${D}${base_libdir}/rdk/hostapd-init.sh
install -m 0644 ${WORKDIR}/init-uci-config.service ${D}${systemd_unitdir}/system
install -m 0755 ${WORKDIR}/mac80211-EHT.sh ${D}${sbindir}/mac80211.sh
install -m 0755 ${WORKDIR}/hostapd.uc ${D}${datadir}/hostap
diff --git a/recipes-wifi/iw/iw_5.19.bb b/recipes-wifi/iw/iw_5.19.bb
index 861bf50..ac009b2 100644
--- a/recipes-wifi/iw/iw_5.19.bb
+++ b/recipes-wifi/iw/iw_5.19.bb
@@ -16,8 +16,10 @@
SRC_URI[sha256sum] = "f167bbe947dd53bb9ebc0c1dcef5db6ad73ac1d6084f2c6f9376c5c360cc4d4e"
-FILESEXTRAPATHS_prepend := "${THISDIR}/patches:"
-require patches/patches.inc
+PATCH_SRC = "${@bb.utils.contains('DISTRO_FEATURES', 'wifi_eht', 'patches-mlo', 'patches', d)}"
+
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PATCH_SRC}:"
+require ${PATCH_SRC}/patches.inc
SRC_URI_remove = "${@bb.utils.contains('DISTRO_FEATURES', 'wifi_eht', 'file://001-nl80211_h_sync.patch', '', d)}"
SRC_URI_remove = "${@bb.utils.contains('DISTRO_FEATURES', 'wifi_eht', 'file://120-antenna_gain.patch', '', d)}"
diff --git a/recipes-wifi/iw/patches-mlo/0001-iw-info-print-PMSR-capabilities.patch b/recipes-wifi/iw/patches-mlo/0001-iw-info-print-PMSR-capabilities.patch
new file mode 100644
index 0000000..9169bd9
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0001-iw-info-print-PMSR-capabilities.patch
@@ -0,0 +1,156 @@
+From ad2f2f8a2c0b4d450a53c24d28769cd187071dee Mon Sep 17 00:00:00 2001
+From: Jaewan Kim <jaewan@google.com>
+Date: Tue, 11 Oct 2022 14:34:05 +0900
+Subject: [PATCH 01/28] iw: info: print PMSR capabilities
+
+Print PMSR and FTM capabilities if any.
+
+Signed-off-by: Jaewan Kim <jaewan@google.com>
+Link: https://lore.kernel.org/r/20221011053405.332375-1-jaewan@google.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ info.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 121 insertions(+)
+
+diff --git a/info.c b/info.c
+index 21ed07b..eb257f8 100644
+--- a/info.c
++++ b/info.c
+@@ -169,6 +169,124 @@ static void ext_feat_print(enum nl80211_ext_feature_index idx)
+ }
+ }
+
++static void __print_ftm_capability(struct nlattr *ftm_capa)
++{
++#define PRINT_FTM_FLAG(T, NAME) \
++ do { \
++ if (T[NL80211_PMSR_FTM_CAPA_ATTR_##NAME]) \
++ printf("\t\t\t" #NAME "\n"); \
++ } while (0)
++
++#define PRINT_FTM_U8(T, NAME) \
++ do { \
++ if (T[NL80211_PMSR_FTM_CAPA_ATTR_##NAME]) \
++ printf("\t\t\t" #NAME ": %d\n", \
++ nla_get_u8(T[NL80211_PMSR_FTM_CAPA_ATTR_##NAME])); \
++ } while (0)
++
++ struct nlattr *tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
++ int ret;
++
++ printf("\t\tFTM (Fine time measurement or Flight time measurement)\n");
++
++ ret = nla_parse_nested(tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX, ftm_capa,
++ NULL);
++ if (ret)
++ return;
++
++ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES]) {
++#define PRINT_PREAMBLE(P, V) \
++ do { \
++ if (P | NL80211_PREAMBLE_##V) \
++ printf(" " #V); \
++ } while (0)
++
++ uint32_t preambles =
++ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES]);
++ printf("\t\t\tPreambles:");
++
++ PRINT_PREAMBLE(preambles, LEGACY);
++ PRINT_PREAMBLE(preambles, HT);
++ PRINT_PREAMBLE(preambles, VHT);
++ PRINT_PREAMBLE(preambles, DMG);
++ printf("\n");
++#undef PRINT_PREAMBLE
++ }
++ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS]) {
++#define PRINT_BANDWIDTH(B, V) \
++ do { \
++ if (B | NL80211_CHAN_WIDTH_##V) \
++ printf(" " #V); \
++ } while (0)
++
++ uint32_t bandwidth =
++ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS]);
++ printf("\t\t\tBandwidth:");
++ PRINT_BANDWIDTH(bandwidth, 20_NOHT);
++ PRINT_BANDWIDTH(bandwidth, 20);
++ PRINT_BANDWIDTH(bandwidth, 40);
++ PRINT_BANDWIDTH(bandwidth, 80);
++ PRINT_BANDWIDTH(bandwidth, 80P80);
++ PRINT_BANDWIDTH(bandwidth, 160);
++ PRINT_BANDWIDTH(bandwidth, 5);
++ PRINT_BANDWIDTH(bandwidth, 10);
++ PRINT_BANDWIDTH(bandwidth, 1);
++ PRINT_BANDWIDTH(bandwidth, 2);
++ PRINT_BANDWIDTH(bandwidth, 4);
++ PRINT_BANDWIDTH(bandwidth, 8);
++ PRINT_BANDWIDTH(bandwidth, 16);
++ PRINT_BANDWIDTH(bandwidth, 320);
++ printf("\n");
++#undef PRINT_BANDWIDTH
++ }
++ PRINT_FTM_U8(tb, MAX_BURSTS_EXPONENT);
++ PRINT_FTM_U8(tb, MAX_FTMS_PER_BURST);
++ PRINT_FTM_FLAG(tb, ASAP);
++ PRINT_FTM_FLAG(tb, NON_ASAP);
++ PRINT_FTM_FLAG(tb, REQ_LCI);
++ PRINT_FTM_FLAG(tb, REQ_CIVICLOC);
++ PRINT_FTM_FLAG(tb, TRIGGER_BASED);
++ PRINT_FTM_FLAG(tb, NON_TRIGGER_BASED);
++
++#undef PRINT_FTM_U8
++#undef PRINT_FTM_FLAG
++}
++
++static void print_pmsr_capabilities(struct nlattr *pmsr_capa)
++{
++ struct nlattr *tb[NL80211_PMSR_ATTR_MAX + 1];
++ struct nlattr *nla;
++ int size;
++ int ret;
++
++ printf("\tPeer measurement (PMSR)\n");
++ ret = nla_parse_nested(tb, NL80211_PMSR_ATTR_MAX, pmsr_capa, NULL);
++ if (ret) {
++ printf("\t\tMalformed PMSR\n");
++ return;
++ }
++
++ if (tb[NL80211_PMSR_ATTR_MAX_PEERS])
++ printf("\t\tMax peers: %d\n",
++ nla_get_u32(tb[NL80211_PMSR_ATTR_MAX_PEERS]));
++ if (tb[NL80211_PMSR_ATTR_REPORT_AP_TSF])
++ printf("\t\tREPORT_AP_TSF\n");
++ if (tb[NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR])
++ printf("\t\tRANDOMIZE_MAC_ADDR\n");
++
++ if (tb[NL80211_PMSR_ATTR_TYPE_CAPA]) {
++ nla_for_each_nested(nla, tb[NL80211_PMSR_ATTR_TYPE_CAPA], size) {
++ switch (nla_type(nla)) {
++ case NL80211_PMSR_TYPE_FTM:
++ __print_ftm_capability(nla);
++ break;
++ }
++ }
++ } else {
++ printf("\t\tPMSR type is missing\n");
++ }
++}
++
+ static int print_phy_handler(struct nl_msg *msg, void *arg)
+ {
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+@@ -741,6 +859,9 @@ broken_combination:
+ pat->max_pattern_len, pat->max_pkt_offset, rule->max_delay);
+ }
+
++ if (tb_msg[NL80211_ATTR_PEER_MEASUREMENTS])
++ print_pmsr_capabilities(tb_msg[NL80211_ATTR_PEER_MEASUREMENTS]);
++
+ if (tb_msg[NL80211_ATTR_MAX_AP_ASSOC_STA])
+ printf("\tMaximum associated stations in AP mode: %u\n",
+ nla_get_u16(tb_msg[NL80211_ATTR_MAX_AP_ASSOC_STA]));
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0002-iw-add-cac-background-command.patch b/recipes-wifi/iw/patches-mlo/0002-iw-add-cac-background-command.patch
new file mode 100644
index 0000000..235a0dc
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0002-iw-add-cac-background-command.patch
@@ -0,0 +1,53 @@
+From 82e6fba06a1489a007ef20f9db17cc066f6f53d2 Mon Sep 17 00:00:00 2001
+From: Janusz Dziedzic <janusz.dziedzic@gmail.com>
+Date: Thu, 25 Aug 2022 11:51:11 +0200
+Subject: [PATCH 02/28] iw: add cac background command
+
+Add command that request background CAC radar scan.
+Tested on mt7915.
+
+Signed-off-by: Janusz Dziedzic <janusz.dziedzic@gmail.com>
+Link: https://lore.kernel.org/r/20220825095111.1026649-1-janusz.dziedzic@gmail.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ phy.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/phy.c b/phy.c
+index 2d489ef..0a57ecb 100644
+--- a/phy.c
++++ b/phy.c
+@@ -301,6 +301,15 @@ static int handle_cac_trigger(struct nl80211_state *state,
+ return put_chandef(msg, &chandef);
+ }
+
++static int handle_cac_background(struct nl80211_state *state,
++ struct nl_msg *msg,
++ int argc, char **argv,
++ enum id_input id)
++{
++ nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND);
++ return handle_cac_trigger(state, msg, argc, argv, id);
++}
++
+ static int no_seq_check(struct nl_msg *msg, void *arg)
+ {
+ return NL_OK;
+@@ -381,6 +390,14 @@ COMMAND(cac, trigger,
+ "Start or trigger a channel availability check (CAC) looking to look for\n"
+ "radars on the given channel.");
+
++COMMAND(cac, background,
++ "channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
++ "freq <frequency> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
++ "freq <frequency> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
++ NL80211_CMD_RADAR_DETECT, 0, CIB_NETDEV, handle_cac_background,
++ "Start background channel availability check (CAC) looking to look for\n"
++ "radars on the given channel.");
++
+ static int handle_fragmentation(struct nl80211_state *state,
+ struct nl_msg *msg,
+ int argc, char **argv,
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0003-iw-info-fix-bug-reading-preambles-and-bandwidths.patch b/recipes-wifi/iw/patches-mlo/0003-iw-info-fix-bug-reading-preambles-and-bandwidths.patch
new file mode 100644
index 0000000..6f97d62
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0003-iw-info-fix-bug-reading-preambles-and-bandwidths.patch
@@ -0,0 +1,40 @@
+From c4743bbc1b1953bdc035b871ea1899ca94c1c6ac Mon Sep 17 00:00:00 2001
+From: Jaewan Kim <jaewan@google.com>
+Date: Tue, 10 Jan 2023 00:14:55 +0900
+Subject: [PATCH 03/28] iw: info: fix bug reading preambles and bandwidths
+
+Preambles and bandwidths values are considered as bit shifts
+when they're are used for capabilities.
+
+Signed-off-by: Jaewan Kim <jaewan@google.com>
+Link: https://lore.kernel.org/r/20230109151455.325793-1-jaewan@google.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ info.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/info.c b/info.c
+index eb257f8..5229d44 100644
+--- a/info.c
++++ b/info.c
+@@ -197,7 +197,7 @@ static void __print_ftm_capability(struct nlattr *ftm_capa)
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES]) {
+ #define PRINT_PREAMBLE(P, V) \
+ do { \
+- if (P | NL80211_PREAMBLE_##V) \
++ if (P & BIT(NL80211_PREAMBLE_##V)) \
+ printf(" " #V); \
+ } while (0)
+
+@@ -215,7 +215,7 @@ static void __print_ftm_capability(struct nlattr *ftm_capa)
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS]) {
+ #define PRINT_BANDWIDTH(B, V) \
+ do { \
+- if (B | NL80211_CHAN_WIDTH_##V) \
++ if (B & BIT(NL80211_CHAN_WIDTH_##V)) \
+ printf(" " #V); \
+ } while (0)
+
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0004-iw-add-support-for-retrieving-keys.patch b/recipes-wifi/iw/patches-mlo/0004-iw-add-support-for-retrieving-keys.patch
new file mode 100644
index 0000000..2685804
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0004-iw-add-support-for-retrieving-keys.patch
@@ -0,0 +1,123 @@
+From cc660ccb9a83ec23b672eef178e9b494d95e763d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rapha=C3=ABl=20M=C3=A9lotte?= <raphael.melotte@mind.be>
+Date: Thu, 12 Jan 2023 13:25:25 +0100
+Subject: [PATCH 04/28] iw: add support for retrieving keys
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For debugging purposes, it can be useful to be able to retrieve keys.
+
+Add a "iw key get" command, to be able to retrieve keys when the key
+index is known. A new "key" section is also introduced, in preparation
+for future key-related commands.
+
+Example retrieving a pairwise key:
+iw dev wlan0 key get 0 02:02:03:04:05:06
+
+Example retrieving a group key:
+iw dev wlan0 key get 1
+
+Note that only the outer ATTR_KEY_DATA (and seq) is reported, the
+nested KEY_DATA (and seq) within ATTR_KEY is not.
+
+Signed-off-by: Raphaël Mélotte <raphael.melotte@mind.be>
+Link: https://lore.kernel.org/r/20230112122525.2257298-1-raphael.melotte@mind.be
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ keys.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 83 insertions(+)
+ create mode 100644 keys.c
+
+diff --git a/keys.c b/keys.c
+new file mode 100644
+index 0000000..65aa426
+--- /dev/null
++++ b/keys.c
+@@ -0,0 +1,83 @@
++#include <errno.h>
++#include <netlink/genl/genl.h>
++#include <netlink/genl/family.h>
++#include <netlink/genl/ctrl.h>
++#include <netlink/msg.h>
++#include <netlink/attr.h>
++#include "nl80211.h"
++#include "iw.h"
++
++SECTION(key);
++
++static int print_keys(struct nl_msg *msg, void *arg)
++{
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ if (!tb[NL80211_ATTR_KEY_IDX]) {
++ fprintf(stderr, "KEY_IDX missing!\n");
++ return NL_SKIP;
++ }
++
++ if (!tb[NL80211_ATTR_KEY_DATA]) {
++ fprintf(stderr, "ATTR_KEY_DATA missing!\n");
++ return NL_SKIP;
++ }
++
++ iw_hexdump("Key", nla_data(tb[NL80211_ATTR_KEY_DATA]),
++ nla_len(tb[NL80211_ATTR_KEY_DATA]));
++
++ if (!tb[NL80211_ATTR_KEY_SEQ]) {
++ fprintf(stderr, "ATTR_KEY_SEQ missing!\n");
++ return NL_SKIP;
++ }
++
++ iw_hexdump("Key seq", nla_data(tb[NL80211_ATTR_KEY_SEQ]),
++ nla_len(tb[NL80211_ATTR_KEY_SEQ]));
++
++ return NL_OK;
++}
++
++static int handle_get_key(struct nl80211_state *state,
++ struct nl_msg *msg, int argc, char **argv,
++ enum id_input id)
++{
++ char *end;
++ unsigned char mac[6];
++
++ /* key index */
++ if (argc) {
++ nla_put_u8(msg, NL80211_ATTR_KEY_IDX, strtoul(argv[0], &end, 10));
++ if (*end != '\0')
++ return -EINVAL;
++ argv++;
++ argc--;
++ }
++
++ /* mac */
++ if (argc) {
++ if (mac_addr_a2n(mac, argv[0]) == 0) {
++ NLA_PUT(msg, NL80211_ATTR_MAC, 6, mac);
++ nla_put_u32(msg, NL80211_ATTR_KEY_TYPE,
++ NL80211_KEYTYPE_PAIRWISE);
++ argv++;
++ argc--;
++ } else {
++ return -EINVAL;
++ }
++ } else {
++ nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, NL80211_KEYTYPE_GROUP);
++ }
++
++ register_handler(print_keys, NULL);
++ return 0;
++
++ nla_put_failure:
++ return -ENOSPC;
++}
++COMMAND(key, get, "<key index> <MAC address>",
++ NL80211_CMD_GET_KEY, 0, CIB_NETDEV, handle_get_key,
++ "Retrieve a key and key sequence.\n");
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0005-iw-event-fix-printf-format-error.patch b/recipes-wifi/iw/patches-mlo/0005-iw-event-fix-printf-format-error.patch
new file mode 100644
index 0000000..00c57f0
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0005-iw-event-fix-printf-format-error.patch
@@ -0,0 +1,38 @@
+From d6fd2757f7aab638022ffa635e32e21594ec382d Mon Sep 17 00:00:00 2001
+From: Stefan Weil <sw@weilnetz.de>
+Date: Sat, 21 Jan 2023 20:36:37 +0100
+Subject: [PATCH 05/28] iw: event: fix printf format error
+
+tv_usec can be a 64 bit integer which causes a compiler warning:
+
+event.c: In function 'print_event':
+event.c:930:41: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'suseconds_t' {aka 'long long int'} [-Wformat=]
+ 930 | printf("[%s.%06lu]: ", buf, args->ts.tv_usec);
+ | ~~~~^ ~~~~~~~~~~~~~~~~
+ | | |
+ | long unsigned int suseconds_t {aka long long int}
+ | %06llu
+
+Signed-off-by: Stefan Weil <sw@weilnetz.de>
+Link: https://lore.kernel.org/r/20230121193637.347109-1-sw@weilnetz.de
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ event.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/event.c b/event.c
+index 4c37297..fa2e125 100644
+--- a/event.c
++++ b/event.c
+@@ -942,7 +942,7 @@ static int print_event(struct nl_msg *msg, void *arg)
+
+ memset(buf, 0, 255);
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
+- printf("[%s.%06lu]: ", buf, args->ts.tv_usec);
++ printf("[%s.%06lu]: ", buf, (unsigned long )args->ts.tv_usec);
+ } else {
+ printf("%llu.%06llu: ", usecs/1000000, usecs % 1000000);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0006-update-nl80211.h.patch b/recipes-wifi/iw/patches-mlo/0006-update-nl80211.h.patch
new file mode 100644
index 0000000..38d68b2
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0006-update-nl80211.h.patch
@@ -0,0 +1,425 @@
+From de3da80461f48946b9493fb2b2d54ce6dcdc3e12 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 14 Apr 2023 12:50:11 +0200
+Subject: [PATCH 06/28] update nl80211.h
+
+Bring in nl80211.h from 6.4-rc.
+
+Change-Id: I96b818a987d243b5cf97e2cc9c62d57637e17165
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ info.c | 4 +
+ nl80211.h | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 224 insertions(+), 10 deletions(-)
+
+diff --git a/info.c b/info.c
+index 5229d44..9955e5e 100644
+--- a/info.c
++++ b/info.c
+@@ -166,6 +166,10 @@ static void ext_feat_print(enum nl80211_ext_feature_index idx)
+ ext_feat_case(BSS_COLOR, "BSS coloring support");
+ ext_feat_case(FILS_CRYPTO_OFFLOAD, "FILS crypto offload");
+ ext_feat_case(RADAR_BACKGROUND, "Radar background support");
++ ext_feat_case(POWERED_ADDR_CHANGE, "can change MAC address while up");
++ ext_feat_case(PUNCT, "preamble puncturing in AP mode");
++ ext_feat_case(SECURE_NAN, "secure NAN support");
++ ext_feat_case(AUTH_AND_DEAUTH_RANDOM_TA, "random auth/deauth transmitter address");
+ }
+ }
+
+diff --git a/nl80211.h b/nl80211.h
+index d9490e3..c59fec4 100644
+--- a/nl80211.h
++++ b/nl80211.h
+@@ -323,6 +323,17 @@
+ * Once the association is done, the driver cleans the FILS AAD data.
+ */
+
++/**
++ * DOC: Multi-Link Operation
++ *
++ * In Multi-Link Operation, a connection between to MLDs utilizes multiple
++ * links. To use this in nl80211, various commands and responses now need
++ * to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
++ * Additionally, various commands that need to operate on a specific link
++ * now need to be given the %NL80211_ATTR_MLO_LINK_ID attribute, e.g. to
++ * use %NL80211_CMD_START_AP or similar functions.
++ */
++
+ /**
+ * enum nl80211_commands - supported nl80211 commands
+ *
+@@ -366,14 +377,22 @@
+ * the non-transmitting interfaces are deleted as well.
+ *
+ * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
+- * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
++ * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC
++ * represents peer's MLD address for MLO pairwise key. For MLO group key,
++ * the link is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
+ * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
++ * For MLO connection, the link to set default key is identified by
++ * %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
+ * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
+- * and %NL80211_ATTR_KEY_SEQ attributes.
++ * and %NL80211_ATTR_KEY_SEQ attributes. %NL80211_ATTR_MAC represents
++ * peer's MLD address for MLO pairwise key. The link to add MLO
++ * group key is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
+- * or %NL80211_ATTR_MAC.
++ * or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC represents peer's MLD address
++ * for MLO pairwise key. The link to delete group key is identified by
++ * %NL80211_ATTR_MLO_LINK_ID.
+ *
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+@@ -405,7 +424,8 @@
+ * interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
+ * or, if no MAC address given, all stations, on the interface identified
+- * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
++ * by %NL80211_ATTR_IFINDEX. For MLD station, MLD address is used in
++ * %NL80211_ATTR_MAC. %NL80211_ATTR_MGMT_SUBTYPE and
+ * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ * of disconnection indication should be sent to the station
+ * (Deauthentication or Disassociation frame and reason code for that
+@@ -753,6 +773,13 @@
+ * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ * counters which will be updated to the current value. This attribute
+ * is used during CSA period.
++ * For TX on an MLD, the frequency can be omitted and the link ID be
++ * specified, or if transmitting to a known peer MLD (with MLD addresses
++ * in the frame) both can be omitted and the link will be selected by
++ * lower layers.
++ * For RX notification, %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
++ * indicate the frame RX timestamp and %NL80211_ATTR_TX_HW_TIMESTAMP may
++ * be included to indicate the ack TX timestamp.
+ * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ * command may be used with the corresponding cookie to cancel the wait
+ * time if it is known that it is no longer necessary. This command is
+@@ -763,7 +790,9 @@
+ * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
+ * the TX command and %NL80211_ATTR_FRAME includes the contents of the
+ * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
+- * the frame.
++ * the frame. %NL80211_ATTR_TX_HW_TIMESTAMP may be included to indicate the
++ * tx timestamp and %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
++ * indicate the ack RX timestamp.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ * backward compatibility.
+ *
+@@ -1108,6 +1137,12 @@
+ * has been received. %NL80211_ATTR_FRAME is used to specify the
+ * frame contents. The frame is the raw EAPoL data, without ethernet or
+ * 802.11 headers.
++ * For an MLD transmitter, the %NL80211_ATTR_MLO_LINK_ID may be given and
++ * its effect will depend on the destination: If the destination is known
++ * to be an MLD, this will be used as a hint to select the link to transmit
++ * the frame on. If the destination is not an MLD, this will select both
++ * the link to transmit on and the source address will be set to the link
++ * address of that link.
+ * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
+ * indicating the protocol type of the received frame; whether the frame
+@@ -1132,6 +1167,23 @@
+ * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH
+ * command interface.
+ *
++ * Host driver sends MLD address of the AP with %NL80211_ATTR_MLD_ADDR in
++ * %NL80211_CMD_EXTERNAL_AUTH event to indicate user space to enable MLO
++ * during the authentication offload in STA mode while connecting to MLD
++ * APs. Host driver should check %NL80211_ATTR_MLO_SUPPORT flag capability
++ * in %NL80211_CMD_CONNECT to know whether the user space supports enabling
++ * MLO during the authentication offload or not.
++ * User space should enable MLO during the authentication only when it
++ * receives the AP MLD address in authentication offload request. User
++ * space shouldn't enable MLO when the authentication offload request
++ * doesn't indicate the AP MLD address even if the AP is MLO capable.
++ * User space should use %NL80211_ATTR_MLD_ADDR as peer's MLD address and
++ * interface address identified by %NL80211_ATTR_IFINDEX as self MLD
++ * address. User space and host driver to use MLD addresses in RA, TA and
++ * BSSID fields of the frames between them, and host driver translates the
++ * MLD addresses to/from link addresses based on the link chosen for the
++ * authentication.
++ *
+ * Host driver reports this status on an authentication failure to the
+ * user space through the connect result as the user space would have
+ * initiated the connection through the connect request.
+@@ -1237,6 +1289,26 @@
+ * to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
+ * specify the timeout value.
+ *
++ * @NL80211_CMD_ADD_LINK: Add a new link to an interface. The
++ * %NL80211_ATTR_MLO_LINK_ID attribute is used for the new link.
++ * @NL80211_CMD_REMOVE_LINK: Remove a link from an interface. This may come
++ * without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links
++ * in preparation for e.g. roaming to a regular (non-MLO) AP.
++ *
++ * @NL80211_CMD_ADD_LINK_STA: Add a link to an MLD station
++ * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
++ * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
++ *
++ * @NL80211_CMD_SET_HW_TIMESTAMP: Enable/disable HW timestamping of Timing
++ * measurement and Fine timing measurement frames. If %NL80211_ATTR_MAC
++ * is included, enable/disable HW timestamping only for frames to/from the
++ * specified MAC address. Otherwise enable/disable HW timestamping for
++ * all TM/FTM frames (including ones that were enabled with specific MAC
++ * address). If %NL80211_ATTR_HW_TIMESTAMP_ENABLED is not included, disable
++ * HW timestamping.
++ * The number of peers that HW timestamping can be enabled for concurrently
++ * is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
++ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+@@ -1481,6 +1553,15 @@ enum nl80211_commands {
+
+ NL80211_CMD_ASSOC_COMEBACK,
+
++ NL80211_CMD_ADD_LINK,
++ NL80211_CMD_REMOVE_LINK,
++
++ NL80211_CMD_ADD_LINK_STA,
++ NL80211_CMD_MODIFY_LINK_STA,
++ NL80211_CMD_REMOVE_LINK_STA,
++
++ NL80211_CMD_SET_HW_TIMESTAMP,
++
+ /* add new commands above here */
+
+ /* used to define NL80211_CMD_MAX below */
+@@ -2340,8 +2421,10 @@ enum nl80211_commands {
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+- * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
+- * interface type.
++ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities and
++ * other interface-type specific capabilities per interface type. For MLO,
++ * %NL80211_ATTR_EML_CAPABILITY and %NL80211_ATTR_MLD_CAPA_AND_OPS are
++ * present.
+ *
+ * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
+ * groupID for monitor mode.
+@@ -2663,6 +2746,65 @@ enum nl80211_commands {
+ * association request when used with NL80211_CMD_NEW_STATION). Can be set
+ * only if %NL80211_STA_FLAG_WME is set.
+ *
++ * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with
++ * various commands that need a link ID to operate.
++ * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
++ * per-link information and a link ID.
++ * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as
++ * authenticate/associate.
++ *
++ * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO
++ * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not
++ * included in NL80211_CMD_CONNECT drivers must not perform MLO connection.
++ *
++ * @NL80211_ATTR_MAX_NUM_AKM_SUITES: U16 attribute. Indicates maximum number of
++ * AKM suites allowed for %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and
++ * %NL80211_CMD_START_AP in %NL80211_CMD_GET_WIPHY response. If this
++ * attribute is not present userspace shall consider maximum number of AKM
++ * suites allowed as %NL80211_MAX_NR_AKM_SUITES which is the legacy maximum
++ * number prior to the introduction of this attribute.
++ *
++ * @NL80211_ATTR_EML_CAPABILITY: EML Capability information (u16)
++ * @NL80211_ATTR_MLD_CAPA_AND_OPS: MLD Capabilities and Operations (u16)
++ *
++ * @NL80211_ATTR_TX_HW_TIMESTAMP: Hardware timestamp for TX operation in
++ * nanoseconds (u64). This is the device clock timestamp so it will
++ * probably reset when the device is stopped or the firmware is reset.
++ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the frame TX
++ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
++ * the ack TX timestamp.
++ * @NL80211_ATTR_RX_HW_TIMESTAMP: Hardware timestamp for RX operation in
++ * nanoseconds (u64). This is the device clock timestamp so it will
++ * probably reset when the device is stopped or the firmware is reset.
++ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
++ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
++ * the incoming frame RX timestamp.
++ * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
++ * (re)associations.
++ *
++ * @NL80211_ATTR_PUNCT_BITMAP: (u32) Preamble puncturing bitmap, lowest
++ * bit corresponds to the lowest 20 MHz channel. Each bit set to 1
++ * indicates that the sub-channel is punctured. Higher 16 bits are
++ * reserved.
++ *
++ * @NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: Maximum number of peers that HW
++ * timestamping can be enabled for concurrently (u16), a wiphy attribute.
++ * A value of 0xffff indicates setting for all peers (i.e. not specifying
++ * an address with %NL80211_CMD_SET_HW_TIMESTAMP) is supported.
++ * @NL80211_ATTR_HW_TIMESTAMP_ENABLED: Indicates whether HW timestamping should
++ * be enabled or not (flag attribute).
++ *
++ * @NL80211_ATTR_EMA_RNR_ELEMS: Optional nested attribute for
++ * reduced neighbor report (RNR) elements. This attribute can be used
++ * only when NL80211_MBSSID_CONFIG_ATTR_EMA is enabled.
++ * Userspace is responsible for splitting the RNR into multiple
++ * elements such that each element excludes the non-transmitting
++ * profiles already included in the MBSSID element
++ * (%NL80211_ATTR_MBSSID_ELEMS) at the same index. Each EMA beacon
++ * will be generated by adding MBSSID and RNR elements at the same
++ * index. If the userspace includes more RNR elements than number of
++ * MBSSID elements then these will be added in every EMA beacon.
++ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3177,6 +3319,28 @@ enum nl80211_attrs {
+
+ NL80211_ATTR_DISABLE_EHT,
+
++ NL80211_ATTR_MLO_LINKS,
++ NL80211_ATTR_MLO_LINK_ID,
++ NL80211_ATTR_MLD_ADDR,
++
++ NL80211_ATTR_MLO_SUPPORT,
++
++ NL80211_ATTR_MAX_NUM_AKM_SUITES,
++
++ NL80211_ATTR_EML_CAPABILITY,
++ NL80211_ATTR_MLD_CAPA_AND_OPS,
++
++ NL80211_ATTR_TX_HW_TIMESTAMP,
++ NL80211_ATTR_RX_HW_TIMESTAMP,
++ NL80211_ATTR_TD_BITMAP,
++
++ NL80211_ATTR_PUNCT_BITMAP,
++
++ NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
++ NL80211_ATTR_HW_TIMESTAMP_ENABLED,
++
++ NL80211_ATTR_EMA_RNR_ELEMS,
++
+ /* add attributes here, update the policy in nl80211.c */
+
+ __NL80211_ATTR_AFTER_LAST,
+@@ -3231,6 +3395,11 @@ enum nl80211_attrs {
+ #define NL80211_HE_MIN_CAPABILITY_LEN 16
+ #define NL80211_HE_MAX_CAPABILITY_LEN 54
+ #define NL80211_MAX_NR_CIPHER_SUITES 5
++
++/*
++ * NL80211_MAX_NR_AKM_SUITES is obsolete when %NL80211_ATTR_MAX_NUM_AKM_SUITES
++ * present in %NL80211_CMD_GET_WIPHY response.
++ */
+ #define NL80211_MAX_NR_AKM_SUITES 2
+ #define NL80211_EHT_MIN_CAPABILITY_LEN 13
+ #define NL80211_EHT_MAX_CAPABILITY_LEN 51
+@@ -3892,6 +4061,10 @@ enum nl80211_band_iftype_attr {
+ * @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ * the allowed channel bandwidth configurations.
+ * Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
++ * @NL80211_BAND_ATTR_S1G_MCS_NSS_SET: S1G capabilities, supported S1G-MCS and NSS
++ * set subfield, as in the S1G information IE, 5 bytes
++ * @NL80211_BAND_ATTR_S1G_CAPA: S1G capabilities information subfield as in the
++ * S1G information IE, 10 bytes
+ * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
+ * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
+ */
+@@ -3912,6 +4085,9 @@ enum nl80211_band_attr {
+ NL80211_BAND_ATTR_EDMG_CHANNELS,
+ NL80211_BAND_ATTR_EDMG_BW_CONFIG,
+
++ NL80211_BAND_ATTR_S1G_MCS_NSS_SET,
++ NL80211_BAND_ATTR_S1G_CAPA,
++
+ /* keep last */
+ __NL80211_BAND_ATTR_AFTER_LAST,
+ NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
+@@ -4853,6 +5029,8 @@ enum nl80211_bss_scan_width {
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
+ * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
++ * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
++ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+@@ -4878,6 +5056,8 @@ enum nl80211_bss {
+ NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
+ NL80211_BSS_FREQUENCY_OFFSET,
++ NL80211_BSS_MLO_LINK_ID,
++ NL80211_BSS_MLD_ADDR,
+
+ /* keep last */
+ __NL80211_BSS_AFTER_LAST,
+@@ -5757,6 +5937,7 @@ enum plink_actions {
+ #define NL80211_KEK_LEN 16
+ #define NL80211_KCK_EXT_LEN 24
+ #define NL80211_KEK_EXT_LEN 32
++#define NL80211_KCK_EXT_LEN_32 32
+ #define NL80211_REPLAY_CTR_LEN 8
+
+ /**
+@@ -5874,7 +6055,7 @@ enum nl80211_ap_sme_features {
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ * the connected inactive stations in AP mode.
+ * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
+- * to work properly to suppport receiving regulatory hints from
++ * to work properly to support receiving regulatory hints from
+ * cellular base stations.
+ * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
+ * here to reserve the value for API/ABI compatibility)
+@@ -6174,6 +6355,23 @@ enum nl80211_feature_flags {
+ * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
+ * detection.
+ *
++ * @NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE: Device can perform a MAC address
++ * change without having to bring the underlying network device down
++ * first. For example, in station mode this can be used to vary the
++ * origin MAC address prior to a connection to a new AP for privacy
++ * or other reasons. Note that certain driver specific restrictions
++ * might apply, e.g. no scans in progress, no offchannel operations
++ * in progress, and no active connections.
++ *
++ * @NL80211_EXT_FEATURE_PUNCT: Driver supports preamble puncturing in AP mode.
++ *
++ * @NL80211_EXT_FEATURE_SECURE_NAN: Device supports NAN Pairing which enables
++ * authentication, data encryption and message integrity.
++ *
++ * @NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA: Device supports randomized TA
++ * in authentication and deauthentication frames sent to unassociated peer
++ * using @NL80211_CMD_FRAME.
++ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+@@ -6241,6 +6439,10 @@ enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_BSS_COLOR,
+ NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
+ NL80211_EXT_FEATURE_RADAR_BACKGROUND,
++ NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
++ NL80211_EXT_FEATURE_PUNCT,
++ NL80211_EXT_FEATURE_SECURE_NAN,
++ NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+@@ -6355,8 +6557,16 @@ enum nl80211_timeout_reason {
+ * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
+ * %NL80211_ATTR_SCAN_FREQ_KHZ. This also means
+ * %NL80211_ATTR_SCAN_FREQUENCIES will not be included.
+- * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for colocated APs reported by
+- * 2.4/5 GHz APs
++ * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for collocated APs reported by
++ * 2.4/5 GHz APs. When the flag is set, the scan logic will use the
++ * information from the RNR element found in beacons/probe responses
++ * received on the 2.4/5 GHz channels to actively scan only the 6GHz
++ * channels on which APs are expected to be found. Note that when not set,
++ * the scan logic would scan all 6GHz channels, but since transmission of
++ * probe requests on non PSC channels is limited, it is highly likely that
++ * these channels would passively be scanned. Also note that when the flag
++ * is set, in addition to the colocated APs, PSC channels would also be
++ * scanned if the user space has asked for it.
+ */
+ enum nl80211_scan_flags {
+ NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0007-util-add-support-for-320Mhz-bandwidth.patch b/recipes-wifi/iw/patches-mlo/0007-util-add-support-for-320Mhz-bandwidth.patch
new file mode 100644
index 0000000..5794606
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0007-util-add-support-for-320Mhz-bandwidth.patch
@@ -0,0 +1,29 @@
+From 81d112f13d290d109e0c64541c0f02a0931a7a8d Mon Sep 17 00:00:00 2001
+From: Mordechay Goodstein <mordechay.goodstein@intel.com>
+Date: Sun, 29 May 2022 19:29:55 +0300
+Subject: [PATCH 07/28] util: add support for 320Mhz bandwidth
+
+In the new standard 11be we can set up to 320Mhz bandwidth so add it
+in parsing params.
+
+Signed-off-by: Mordechay Goodstein <mordechay.goodstein@intel.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ util.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/util.c b/util.c
+index 8a2ba10..93269ab 100644
+--- a/util.c
++++ b/util.c
+@@ -471,6 +471,7 @@ enum nl80211_chan_width str_to_bw(const char *str)
+ { .name = "80", .val = NL80211_CHAN_WIDTH_80, },
+ { .name = "80+80", .val = NL80211_CHAN_WIDTH_80P80, },
+ { .name = "160", .val = NL80211_CHAN_WIDTH_160, },
++ { .name = "320", .val = NL80211_CHAN_WIDTH_320, },
+ };
+ unsigned int i;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0008-util-add-support-for-320MHz-bandwidth-without-cf1.patch b/recipes-wifi/iw/patches-mlo/0008-util-add-support-for-320MHz-bandwidth-without-cf1.patch
new file mode 100644
index 0000000..15aed5d
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0008-util-add-support-for-320MHz-bandwidth-without-cf1.patch
@@ -0,0 +1,79 @@
+From 997e5f13e7bc2267b28e541a2904d7f7d84227d0 Mon Sep 17 00:00:00 2001
+From: Mordechay Goodstein <mordechay.goodstein@intel.com>
+Date: Sun, 29 May 2022 19:29:55 +0300
+Subject: [PATCH 08/28] util: add support for 320MHz bandwidth without cf1
+
+Based on user input for control central freq and 320 BW find the data
+central freq (cf1).
+
+Signed-off-by: Mordechay Goodstein <mordechay.goodstein@intel.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ phy.c | 4 ++--
+ util.c | 16 +++++++++++++++-
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/phy.c b/phy.c
+index 0a57ecb..15cea32 100644
+--- a/phy.c
++++ b/phy.c
+@@ -199,13 +199,13 @@ static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
+ }
+
+ COMMAND(set, freq,
+- "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]\n"
++ "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz|320MHz]\n"
+ "<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
+ NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
+ "Set frequency/channel the hardware is using, including HT\n"
+ "configuration.");
+ COMMAND(set, freq,
+- "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]\n"
++ "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz|320MHz]\n"
+ "<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
+ NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
+
+diff --git a/util.c b/util.c
+index 93269ab..80dc301 100644
+--- a/util.c
++++ b/util.c
+@@ -583,7 +583,7 @@ static int parse_freqs(struct chandef *chandef, int argc, char **argv,
+ * <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]
+ *
+ * And if frequency is set:
+- * <freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]
++ * <freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz|320MHz]
+ * <control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]
+ *
+ * If the mode/channel width is not given the NOHT is assumed.
+@@ -1696,6 +1696,8 @@ int get_cf1(const struct chanmode *chanmode, unsigned long freq)
+ 6195, 6995 };
+ unsigned int bw160[] = { 5180, 5500, 5955, 6115, 6275, 6435,
+ 6595, 6755, 6915 };
++ /* based on 11be D2 E.1 Country information and operating classes */
++ unsigned int bw320[] = {5955, 6115, 6275, 6435, 6595, 6755};
+
+ switch (chanmode->width) {
+ case NL80211_CHAN_WIDTH_80:
+@@ -1722,6 +1724,18 @@ int get_cf1(const struct chanmode *chanmode, unsigned long freq)
+
+ cf1 = bw160[j] + 70;
+ break;
++ case NL80211_CHAN_WIDTH_320:
++ /* setup center_freq1 */
++ for (j = 0; j < ARRAY_SIZE(bw320); j++) {
++ if (freq >= bw320[j] && freq < bw320[j] + 160)
++ break;
++ }
++
++ if (j == ARRAY_SIZE(bw320))
++ break;
++
++ cf1 = bw320[j] + 150;
++ break;
+ default:
+ cf1 = freq + chanmode->freq1_diff;
+ break;
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0009-iw-scan-set-NL80211_SCAN_FLAG_COLOCATED_6GHZ-in-case.patch b/recipes-wifi/iw/patches-mlo/0009-iw-scan-set-NL80211_SCAN_FLAG_COLOCATED_6GHZ-in-case.patch
new file mode 100644
index 0000000..387088e
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0009-iw-scan-set-NL80211_SCAN_FLAG_COLOCATED_6GHZ-in-case.patch
@@ -0,0 +1,32 @@
+From 29555e143d01de7570bdb5d11d9c785f6e59e6bf Mon Sep 17 00:00:00 2001
+From: Ayala Beker <ayala.beker@intel.com>
+Date: Thu, 14 Jul 2022 09:33:36 +0300
+Subject: [PATCH 09/28] iw: scan: set NL80211_SCAN_FLAG_COLOCATED_6GHZ in case
+ of full sched scan
+
+Same as in regular scan, in case of full sched scan need to set
+NL80211_SCAN_FLAG_COLOCATED_6GHZ flag, to scan for collocated
+APs by default.
+
+Signed-off-by: Ayala Beker <ayala.beker@intel.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ scan.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/scan.c b/scan.c
+index dfc136a..4c67c87 100644
+--- a/scan.c
++++ b/scan.c
+@@ -349,6 +349,8 @@ int parse_sched_scan(struct nl_msg *msg, int *argc, char ***argv)
+ nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+ if (have_freqs)
+ nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
++ else
++ flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
+ if (have_matchset)
+ nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, matchset);
+ if (have_plans)
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0010-link-fix-some-formatting.patch b/recipes-wifi/iw/patches-mlo/0010-link-fix-some-formatting.patch
new file mode 100644
index 0000000..db9f35a
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0010-link-fix-some-formatting.patch
@@ -0,0 +1,42 @@
+From 5f64b702482d5818b8573d12ef1b58af00bbd81d Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Wed, 31 Aug 2022 21:13:11 +0200
+Subject: [PATCH 10/28] link: fix some formatting
+
+The bss flags has a spurious newline, and we don't use a
+tab for indentation after the colon in other places, fix
+that here.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ link.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/link.c b/link.c
+index 2074488..31de8b4 100644
+--- a/link.c
++++ b/link.c
+@@ -181,7 +181,7 @@ static int print_link_sta(struct nl_msg *msg, void *arg)
+ fprintf(stderr, "failed to parse nested bss parameters!\n");
+ } else {
+ char *delim = "";
+- printf("\n\tbss flags:\t");
++ printf("\tbss flags: ");
+ if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) {
+ printf("CTS-protection");
+ delim = " ";
+@@ -192,9 +192,9 @@ static int print_link_sta(struct nl_msg *msg, void *arg)
+ }
+ if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME])
+ printf("%sshort-slot-time", delim);
+- printf("\n\tdtim period:\t%d",
++ printf("\n\tdtim period: %d",
+ nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD]));
+- printf("\n\tbeacon int:\t%d",
++ printf("\n\tbeacon int: %d",
+ nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL]));
+ printf("\n");
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0011-link-update-for-MLO.patch b/recipes-wifi/iw/patches-mlo/0011-link-update-for-MLO.patch
new file mode 100644
index 0000000..94cce68
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0011-link-update-for-MLO.patch
@@ -0,0 +1,172 @@
+From 221875eb77f21d8cf1c50f046705f30708ec82bd Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Wed, 31 Aug 2022 22:35:02 +0200
+Subject: [PATCH 11/28] link: update for MLO
+
+In MLO we need to use the MLD address to get the station
+statistics (which still need work for per-link stats),
+adjust the code.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ iw.h | 2 ++
+ link.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-----------
+ scan.c | 3 ++-
+ 3 files changed, 55 insertions(+), 12 deletions(-)
+
+diff --git a/iw.h b/iw.h
+index e712c59..45e4fbe 100644
+--- a/iw.h
++++ b/iw.h
+@@ -242,6 +242,8 @@ const char *get_status_str(uint16_t status);
+ enum print_ie_type {
+ PRINT_SCAN,
+ PRINT_LINK,
++ PRINT_LINK_MLO_MLD,
++ PRINT_LINK_MLO_LINK,
+ };
+
+ #define BIT(x) (1ULL<<(x))
+diff --git a/link.c b/link.c
+index 31de8b4..a090100 100644
+--- a/link.c
++++ b/link.c
+@@ -13,9 +13,10 @@
+ #include "iw.h"
+
+ struct link_result {
+- uint8_t bssid[8];
++ uint8_t sta_addr[8];
+ bool link_found;
+ bool anything_found;
++ bool mld;
+ };
+
+ static struct link_result lr = { .link_found = false };
+@@ -37,7 +38,9 @@ static int link_bss_handler(struct nl_msg *msg, void *arg)
+ [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+ };
+ struct link_result *result = arg;
+- char mac_addr[20], dev[20];
++ char mac_addr[20], dev[20], link_addr[20];
++ int link_id = -1;
++ const char *indent = "\t";
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+@@ -62,9 +65,44 @@ static int link_bss_handler(struct nl_msg *msg, void *arg)
+ mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
+ if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+
++ if (bss[NL80211_BSS_MLO_LINK_ID])
++ link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
++
++ if (bss[NL80211_BSS_MLD_ADDR]) {
++ mac_addr_n2a(link_addr, nla_data(bss[NL80211_BSS_BSSID]));
++ indent = "\t\t";
++
++ if (result->mld) {
++ if (memcmp(result->sta_addr,
++ nla_data(bss[NL80211_BSS_MLD_ADDR]), 6)) {
++ mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_MLD_ADDR]));
++ printf("!! inconsistent MLD address information (%s)\n",
++ mac_addr);
++ }
++ } else {
++ mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_MLD_ADDR]));
++ result->mld = true;
++ memcpy(result->sta_addr,
++ nla_data(bss[NL80211_BSS_MLD_ADDR]), 6);
++ if (nla_get_u32(bss[NL80211_BSS_STATUS]) == NL80211_BSS_STATUS_ASSOCIATED) {
++ printf("Connected to %s (on %s)\n", mac_addr, dev);
++ }
++
++ if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
++ print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
++ nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
++ false, PRINT_LINK_MLO_MLD);
++ }
++ } else {
++ memcpy(result->sta_addr, nla_data(bss[NL80211_BSS_BSSID]), 6);
++ }
++
+ switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
+ case NL80211_BSS_STATUS_ASSOCIATED:
+- printf("Connected to %s (on %s)\n", mac_addr, dev);
++ if (result->mld)
++ printf("\tLink %d BSSID %s\n", link_id, link_addr);
++ else
++ printf("Connected to %s (on %s)\n", mac_addr, dev);
+ break;
+ case NL80211_BSS_STATUS_AUTHENTICATED:
+ printf("Authenticated with %s (on %s)\n", mac_addr, dev);
+@@ -81,10 +119,10 @@ static int link_bss_handler(struct nl_msg *msg, void *arg)
+ if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
+ print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+ nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+- false, PRINT_LINK);
++ false, result->mld ? PRINT_LINK_MLO_LINK : PRINT_LINK);
+
+ if (bss[NL80211_BSS_FREQUENCY])
+- printf("\tfreq: %d\n",
++ printf("%sfreq: %d\n", indent,
+ nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
+
+ if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
+@@ -92,7 +130,6 @@ static int link_bss_handler(struct nl_msg *msg, void *arg)
+
+ /* only in the assoc case do we want more info from station get */
+ result->link_found = true;
+- memcpy(result->bssid, nla_data(bss[NL80211_BSS_BSSID]), 6);
+ return NL_SKIP;
+ }
+
+@@ -250,7 +287,7 @@ static int handle_link(struct nl80211_state *state,
+ NULL,
+ NULL,
+ };
+- char bssid_buf[3*6];
++ char addr_buf[3*6];
+ int err;
+
+ link_argv[0] = argv[0];
+@@ -264,15 +301,18 @@ static int handle_link(struct nl80211_state *state,
+ return 0;
+ }
+
+- mac_addr_n2a(bssid_buf, lr.bssid);
+- bssid_buf[17] = '\0';
++ mac_addr_n2a(addr_buf, lr.sta_addr);
++ addr_buf[17] = '\0';
++
++ if (lr.mld)
++ printf("MLD %s stats:\n", addr_buf);
+
+ station_argv[0] = argv[0];
+- station_argv[3] = bssid_buf;
++ station_argv[3] = addr_buf;
+ return handle_cmd(state, id, 4, station_argv);
+ }
+ TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link,
+- "Print information about the current link, if any.");
++ "Print information about the current connection, if any.");
+ HIDDEN(link, get_sta, "<mac-addr>", NL80211_CMD_GET_STATION, 0,
+ CIB_NETDEV, handle_link_sta);
+ HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
+diff --git a/scan.c b/scan.c
+index 4c67c87..dc26787 100644
+--- a/scan.c
++++ b/scan.c
+@@ -1717,7 +1717,8 @@ static void print_ie(const struct ie_print *p, const uint8_t type, uint8_t len,
+ }
+
+ static const struct ie_print ieprinters[] = {
+- [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
++ [0] = { "SSID", print_ssid, 0, 32,
++ BIT(PRINT_SCAN) | BIT(PRINT_LINK) | BIT(PRINT_LINK_MLO_MLD), },
+ [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
+ [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
+ [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0012-interface-print-links.patch b/recipes-wifi/iw/patches-mlo/0012-interface-print-links.patch
new file mode 100644
index 0000000..9e6c1a4
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0012-interface-print-links.patch
@@ -0,0 +1,114 @@
+From 7794573f915174b5dd2cd22e123ecd63610ccf44 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 2 Sep 2022 21:59:31 +0200
+Subject: [PATCH 12/28] interface: print links
+
+Print link information in 'iw dev' and 'iw ... info'.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ interface.c | 74 ++++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 53 insertions(+), 21 deletions(-)
+
+diff --git a/interface.c b/interface.c
+index 84990c9..7e1dd58 100644
+--- a/interface.c
++++ b/interface.c
+@@ -369,6 +369,30 @@ char *channel_width_name(enum nl80211_chan_width width)
+ }
+ }
+
++static void print_channel(struct nlattr **tb)
++{
++ uint32_t freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
++
++ printf("channel %d (%d MHz)",
++ ieee80211_frequency_to_channel(freq), freq);
++
++ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
++ printf(", width: %s",
++ channel_width_name(nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])));
++ if (tb[NL80211_ATTR_CENTER_FREQ1])
++ printf(", center1: %d MHz",
++ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]));
++ if (tb[NL80211_ATTR_CENTER_FREQ2])
++ printf(", center2: %d MHz",
++ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]));
++ } else if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
++ enum nl80211_channel_type channel_type;
++
++ channel_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
++ printf(" %s", channel_type_name(channel_type));
++ }
++}
++
+ static int print_iface_handler(struct nl_msg *msg, void *arg)
+ {
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+@@ -412,27 +436,8 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
+ if (!wiphy && tb_msg[NL80211_ATTR_WIPHY])
+ printf("%s\twiphy %d\n", indent, nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]));
+ if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
+- uint32_t freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
+-
+- printf("%s\tchannel %d (%d MHz)", indent,
+- ieee80211_frequency_to_channel(freq), freq);
+-
+- if (tb_msg[NL80211_ATTR_CHANNEL_WIDTH]) {
+- printf(", width: %s",
+- channel_width_name(nla_get_u32(tb_msg[NL80211_ATTR_CHANNEL_WIDTH])));
+- if (tb_msg[NL80211_ATTR_CENTER_FREQ1])
+- printf(", center1: %d MHz",
+- nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ1]));
+- if (tb_msg[NL80211_ATTR_CENTER_FREQ2])
+- printf(", center2: %d MHz",
+- nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ2]));
+- } else if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+- enum nl80211_channel_type channel_type;
+-
+- channel_type = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+- printf(" %s", channel_type_name(channel_type));
+- }
+-
++ printf("%s\t", indent);
++ print_channel(tb_msg);
+ printf("\n");
+ }
+
+@@ -455,6 +460,33 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
+ printf("%s\t4addr: on\n", indent);
+ }
+
++ if (tb_msg[NL80211_ATTR_MLO_LINKS]) {
++ struct nlattr *link;
++ int n;
++
++ printf("%s\tMLD with links:\n", indent);
++
++ nla_for_each_nested(link, tb_msg[NL80211_ATTR_MLO_LINKS], n) {
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++
++ nla_parse_nested(tb, NL80211_ATTR_MAX, link, NULL);
++ printf("%s\t - link", indent);
++ if (tb[NL80211_ATTR_MLO_LINK_ID])
++ printf(" ID %2d", nla_get_u32(tb[NL80211_ATTR_MLO_LINK_ID]));
++ if (tb[NL80211_ATTR_MAC]) {
++ char buf[20];
++
++ mac_addr_n2a(buf, nla_data(tb[NL80211_ATTR_MAC]));
++ printf(" link addr %s", buf);
++ }
++ if (tb[NL80211_ATTR_WIPHY_FREQ]) {
++ printf("\n%s\t ", indent);
++ print_channel(tb);
++ }
++ printf("\n");
++ }
++ }
++
+ return NL_SKIP;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0013-util-don-t-print-EHT-info-if-not-present.patch b/recipes-wifi/iw/patches-mlo/0013-util-don-t-print-EHT-info-if-not-present.patch
new file mode 100644
index 0000000..105e927
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0013-util-don-t-print-EHT-info-if-not-present.patch
@@ -0,0 +1,31 @@
+From f5e3b43de3a5e3e6332106282bbcaf336e8204a5 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Wed, 15 Mar 2023 16:08:22 +0100
+Subject: [PATCH 13/28] util: don't print EHT info if not present
+
+That's just confusing, don't print it if the EHT MAC
+capabilities attribute isn't there (the kernel puts
+all the attrs together.)
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ util.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/util.c b/util.c
+index 80dc301..50e3fbc 100644
+--- a/util.c
++++ b/util.c
+@@ -1593,7 +1593,8 @@ void print_eht_info(struct nlattr *nl_iftype, int band)
+ nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
+ nla_data(nl_iftype), nla_len(nl_iftype), NULL);
+
+- if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
++ if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES] ||
++ !tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC])
+ return;
+
+ printf("\t\tEHT Iftypes: ");
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0014-iw-S1G-add-frequency-set-in-kHz-and-offset-options.patch b/recipes-wifi/iw/patches-mlo/0014-iw-S1G-add-frequency-set-in-kHz-and-offset-options.patch
new file mode 100644
index 0000000..316d754
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0014-iw-S1G-add-frequency-set-in-kHz-and-offset-options.patch
@@ -0,0 +1,361 @@
+From a32046bcffcb766b4999e76399b755887d2d5d0b Mon Sep 17 00:00:00 2001
+From: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com>
+Date: Mon, 27 Feb 2023 15:05:29 +1300
+Subject: [PATCH 14/28] iw: S1G: add frequency set in kHz and offset options
+
+This change adds support to specify the set frequency in kHz for
+the set frequency command which include an offset whenever needed.
+Also, it adds S1G bandwidth options to the selected chandef.
+
+Signed-off-by: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com>
+Link: https://lore.kernel.org/r/20230227020529.504934-1-gilad.itzkovitch@virscient.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ ap.c | 2 +-
+ ibss.c | 2 +-
+ interface.c | 2 +-
+ iw.h | 5 +++-
+ mesh.c | 2 +-
+ ocb.c | 2 +-
+ phy.c | 37 ++++++++++++++++++++++-----
+ util.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++-----
+ 8 files changed, 106 insertions(+), 18 deletions(-)
+
+diff --git a/ap.c b/ap.c
+index db9efb7..748576d 100644
+--- a/ap.c
++++ b/ap.c
+@@ -28,7 +28,7 @@ static int handle_start_ap(struct nl80211_state *state,
+ argc--;
+
+ /* chandef */
+- res = parse_freqchan(&chandef, false, argc, argv, &parsed);
++ res = parse_freqchan(&chandef, false, argc, argv, &parsed, false);
+ if (res)
+ return res;
+ argc -= parsed;
+diff --git a/ibss.c b/ibss.c
+index f6cbc4c..6e6a835 100644
+--- a/ibss.c
++++ b/ibss.c
+@@ -30,7 +30,7 @@ static int join_ibss(struct nl80211_state *state,
+ argv++;
+ argc--;
+
+- err = parse_freqchan(&chandef, false, argc, argv, &parsed);
++ err = parse_freqchan(&chandef, false, argc, argv, &parsed, false);
+ if (err)
+ return err;
+
+diff --git a/interface.c b/interface.c
+index 7e1dd58..eeef496 100644
+--- a/interface.c
++++ b/interface.c
+@@ -701,7 +701,7 @@ static int handle_chanfreq(struct nl80211_state *state, struct nl_msg *msg,
+ int parsed;
+ char *end;
+
+- res = parse_freqchan(&chandef, chan, argc, argv, &parsed);
++ res = parse_freqchan(&chandef, chan, argc, argv, &parsed, false);
+ if (res)
+ return res;
+
+diff --git a/iw.h b/iw.h
+index 45e4fbe..19c76cf 100644
+--- a/iw.h
++++ b/iw.h
+@@ -102,7 +102,9 @@ struct chandef {
+ enum nl80211_chan_width width;
+
+ unsigned int control_freq;
++ unsigned int control_freq_offset;
+ unsigned int center_freq1;
++ unsigned int center_freq1_offset;
+ unsigned int center_freq2;
+ };
+
+@@ -207,7 +209,8 @@ int parse_hex_mask(char *hexmask, unsigned char **result, size_t *result_len,
+ unsigned char *parse_hex(char *hex, size_t *outlen);
+
+ int parse_keys(struct nl_msg *msg, char **argv[], int *argc);
+-int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv, int *parsed);
++int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
++ int *parsed, bool freq_in_khz);
+ enum nl80211_chan_width str_to_bw(const char *str);
+ int parse_txq_stats(char *buf, int buflen, struct nlattr *tid_stats_attr, int header,
+ int tid, const char *indent);
+diff --git a/mesh.c b/mesh.c
+index 0fb98a3..40e5e5e 100644
+--- a/mesh.c
++++ b/mesh.c
+@@ -485,7 +485,7 @@ static int join_mesh(struct nl80211_state *state,
+ int err, parsed;
+
+ err = parse_freqchan(&chandef, false, argc - 1, argv + 1,
+- &parsed);
++ &parsed, false);
+ if (err)
+ return err;
+
+diff --git a/ocb.c b/ocb.c
+index fc9579b..ee0eedf 100644
+--- a/ocb.c
++++ b/ocb.c
+@@ -16,7 +16,7 @@ static int join_ocb(struct nl80211_state *state,
+ if (argc < 2)
+ return 1;
+
+- err = parse_freqchan(&chandef, false, argc, argv, &parsed);
++ err = parse_freqchan(&chandef, false, argc, argv, &parsed, false);
+
+ if (err)
+ return err;
+diff --git a/phy.c b/phy.c
+index 15cea32..4722125 100644
+--- a/phy.c
++++ b/phy.c
+@@ -191,7 +191,7 @@ static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
+ struct chandef chandef;
+ int res;
+
+- res = parse_freqchan(&chandef, false, argc, argv, NULL);
++ res = parse_freqchan(&chandef, false, argc, argv, NULL, false);
+ if (res)
+ return res;
+
+@@ -209,6 +209,31 @@ COMMAND(set, freq,
+ "<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
+ NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
+
++static int handle_freq_khz(struct nl80211_state *state, struct nl_msg *msg,
++ int argc, char **argv,
++ enum id_input id)
++{
++ struct chandef chandef;
++ int res;
++
++ res = parse_freqchan(&chandef, false, argc, argv, NULL, true);
++ if (res)
++ return res;
++
++ return put_chandef(msg, &chandef);
++}
++
++COMMAND(set, freq_khz,
++ "<freq> [1MHz|2MHz|4MHz|8MHz|16MHz]\n"
++ "<control freq> [1|2|4|8|16] [<center1_freq> [<center2_freq>]]",
++ NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq_khz,
++ "Set frequency in kHz the hardware is using\n"
++ "configuration.");
++COMMAND(set, freq_khz,
++ "<freq> [1MHz|2MHz|4MHz|8MHz|16MHz]\n"
++ "<control freq> [1|2|4|8|16] [<center1_freq> [<center2_freq>]]",
++ NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq_khz, NULL);
++
+ static int handle_chan(struct nl80211_state *state, struct nl_msg *msg,
+ int argc, char **argv,
+ enum id_input id)
+@@ -216,7 +241,7 @@ static int handle_chan(struct nl80211_state *state, struct nl_msg *msg,
+ struct chandef chandef;
+ int res;
+
+- res = parse_freqchan(&chandef, true, argc, argv, NULL);
++ res = parse_freqchan(&chandef, true, argc, argv, NULL, false);
+ if (res)
+ return res;
+
+@@ -288,9 +313,9 @@ static int handle_cac_trigger(struct nl80211_state *state,
+ return 1;
+
+ if (strcmp(argv[0], "channel") == 0) {
+- res = parse_freqchan(&chandef, true, argc - 1, argv + 1, NULL);
++ res = parse_freqchan(&chandef, true, argc - 1, argv + 1, NULL, false);
+ } else if (strcmp(argv[0], "freq") == 0) {
+- res = parse_freqchan(&chandef, false, argc - 1, argv + 1, NULL);
++ res = parse_freqchan(&chandef, false, argc - 1, argv + 1, NULL, false);
+ } else {
+ return 1;
+ }
+@@ -334,9 +359,9 @@ static int handle_cac(struct nl80211_state *state,
+ return 1;
+
+ if (strcmp(argv[2], "channel") == 0) {
+- err = parse_freqchan(&chandef, true, argc - 3, argv + 3, NULL);
++ err = parse_freqchan(&chandef, true, argc - 3, argv + 3, NULL, false);
+ } else if (strcmp(argv[2], "freq") == 0) {
+- err = parse_freqchan(&chandef, false, argc - 3, argv + 3, NULL);
++ err = parse_freqchan(&chandef, false, argc - 3, argv + 3, NULL, false);
+ } else {
+ err = 1;
+ }
+diff --git a/util.c b/util.c
+index 50e3fbc..eef0332 100644
+--- a/util.c
++++ b/util.c
+@@ -484,7 +484,7 @@ enum nl80211_chan_width str_to_bw(const char *str)
+ }
+
+ static int parse_freqs(struct chandef *chandef, int argc, char **argv,
+- int *parsed)
++ int *parsed, bool freq_in_khz)
+ {
+ uint32_t freq;
+ char *end;
+@@ -537,7 +537,13 @@ static int parse_freqs(struct chandef *chandef, int argc, char **argv,
+ return 1;
+ *parsed += 1;
+
+- chandef->center_freq1 = freq;
++ if (freq_in_khz) {
++ chandef->center_freq1 = freq / 1000;
++ chandef->center_freq1_offset = freq % 1000;
++ } else {
++ chandef->center_freq1 = freq;
++ chandef->center_freq1_offset = 0;
++ }
+
+ if (!need_cf2)
+ return 0;
+@@ -551,7 +557,11 @@ static int parse_freqs(struct chandef *chandef, int argc, char **argv,
+ freq = strtoul(argv[2], &end, 10);
+ if (*end)
+ return 1;
+- chandef->center_freq2 = freq;
++
++ if (freq_in_khz)
++ chandef->center_freq2 = freq / 1000;
++ else
++ chandef->center_freq2 = freq;
+
+ *parsed += 1;
+
+@@ -568,6 +578,7 @@ static int parse_freqs(struct chandef *chandef, int argc, char **argv,
+ * @argv: Array of string arguments
+ * @parsed: Pointer to return the number of used arguments, or NULL to error
+ * out if any argument is left unused.
++ * @freq_in_khz: Boolean whether to parse the frequency in kHz or default as MHz
+ *
+ * The given chandef structure will be filled in from the command line
+ * arguments. argc/argv will be updated so that further arguments from the
+@@ -591,7 +602,7 @@ static int parse_freqs(struct chandef *chandef, int argc, char **argv,
+ * Return: Number of used arguments, zero or negative error number otherwise
+ */
+ int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
+- int *parsed)
++ int *parsed, bool freq_in_khz)
+ {
+ char *end;
+ static const struct chanmode chanmode[] = {
+@@ -631,9 +642,30 @@ int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
+ .width = NL80211_CHAN_WIDTH_320,
+ .freq1_diff = 0,
+ .chantype = -1 },
++ { .name = "1MHz",
++ .width = NL80211_CHAN_WIDTH_1,
++ .freq1_diff = 0,
++ .chantype = -1 },
++ { .name = "2MHz",
++ .width = NL80211_CHAN_WIDTH_2,
++ .freq1_diff = 0,
++ .chantype = -1 },
++ { .name = "4MHz",
++ .width = NL80211_CHAN_WIDTH_4,
++ .freq1_diff = 0,
++ .chantype = -1 },
++ { .name = "8MHz",
++ .width = NL80211_CHAN_WIDTH_8,
++ .freq1_diff = 0,
++ .chantype = -1 },
++ { .name = "16MHz",
++ .width = NL80211_CHAN_WIDTH_16,
++ .freq1_diff = 0,
++ .chantype = -1 },
++
+ };
+ const struct chanmode *chanmode_selected = NULL;
+- unsigned int freq;
++ unsigned int freq, freq_offset = 0;
+ unsigned int i;
+ int _parsed = 0;
+ int res = 0;
+@@ -643,7 +675,14 @@ int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
+
+ if (!argv[0])
+ goto out;
++
+ freq = strtoul(argv[0], &end, 10);
++
++ if (freq_in_khz) {
++ freq_offset = freq % 1000;
++ freq = freq / 1000;
++ }
++
+ if (*end) {
+ res = 1;
+ goto out;
+@@ -660,8 +699,10 @@ int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
+ freq = ieee80211_channel_to_frequency(freq, band);
+ }
+ chandef->control_freq = freq;
++ chandef->control_freq_offset = freq_offset;
+ /* Assume 20MHz NOHT channel for now. */
+ chandef->center_freq1 = freq;
++ chandef->center_freq1_offset = freq_offset;
+
+ /* Try to parse HT mode definitions */
+ if (argc > 1) {
+@@ -674,9 +715,20 @@ int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
+ }
+ }
+
++ /* Set channel width's default value */
++ if (chandef->control_freq < 1000)
++ chandef->width = NL80211_CHAN_WIDTH_16;
++ else
++ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
++
+ /* channel mode given, use it and return. */
+ if (chanmode_selected) {
+ chandef->center_freq1 = get_cf1(chanmode_selected, freq);
++
++ /* For non-S1G frequency */
++ if (chandef->center_freq1 > 1000)
++ chandef->center_freq1_offset = 0;
++
+ chandef->width = chanmode_selected->width;
+ goto out;
+ }
+@@ -685,7 +737,7 @@ int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
+ if (chan)
+ goto out;
+
+- res = parse_freqs(chandef, argc - 1, argv + 1, &_parsed);
++ res = parse_freqs(chandef, argc - 1, argv + 1, &_parsed, freq_in_khz);
+
+ out:
+ /* Error out if parsed is NULL. */
+@@ -701,6 +753,9 @@ int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv,
+ int put_chandef(struct nl_msg *msg, struct chandef *chandef)
+ {
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chandef->control_freq);
++ NLA_PUT_U32(msg,
++ NL80211_ATTR_WIPHY_FREQ_OFFSET,
++ chandef->control_freq_offset);
+ NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width);
+
+ switch (chandef->width) {
+@@ -733,6 +788,11 @@ int put_chandef(struct nl_msg *msg, struct chandef *chandef)
+ NL80211_ATTR_CENTER_FREQ1,
+ chandef->center_freq1);
+
++ if (chandef->center_freq1_offset)
++ NLA_PUT_U32(msg,
++ NL80211_ATTR_CENTER_FREQ1_OFFSET,
++ chandef->center_freq1_offset);
++
+ if (chandef->center_freq2)
+ NLA_PUT_U32(msg,
+ NL80211_ATTR_CENTER_FREQ2,
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0015-iw-S1G-add-parsing-for-802.11ah-scan-IE-s.patch b/recipes-wifi/iw/patches-mlo/0015-iw-S1G-add-parsing-for-802.11ah-scan-IE-s.patch
new file mode 100644
index 0000000..79d8135
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0015-iw-S1G-add-parsing-for-802.11ah-scan-IE-s.patch
@@ -0,0 +1,425 @@
+From 1bc6ab0abbb6f26f35d826d166d06bc28ae47b6b Mon Sep 17 00:00:00 2001
+From: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com>
+Date: Tue, 28 Feb 2023 12:01:27 +1300
+Subject: [PATCH 15/28] iw: S1G: add parsing for 802.11ah scan IE's
+
+In order to support scan display for 802.11ah, this change adds
+parsing for S1G capabilities, operation, and short beacon interval
+information elements.
+
+Signed-off-by: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com>
+Link: https://lore.kernel.org/r/20230227230127.709496-1-gilad.itzkovitch@virscient.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ iw.h | 5 ++
+ scan.c | 109 ++++++++++++++++++++++++++++-
+ util.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 330 insertions(+), 2 deletions(-)
+
+diff --git a/iw.h b/iw.h
+index 19c76cf..7e9107e 100644
+--- a/iw.h
++++ b/iw.h
+@@ -224,6 +224,7 @@ void print_vht_info(__u32 capa, const __u8 *mcs);
+ void print_he_capability(const uint8_t *ie, int len);
+ void print_he_info(struct nlattr *nl_iftype);
+ void print_eht_info(struct nlattr *nl_iftype, int band);
++void print_s1g_capability(const uint8_t *caps);
+
+ char *channel_width_name(enum nl80211_chan_width width);
+ const char *iftype_name(enum nl80211_iftype iftype);
+@@ -261,6 +262,9 @@ int get_cf1(const struct chanmode *chanmode, unsigned long freq);
+
+ int parse_random_mac_addr(struct nl_msg *msg, char *addrs);
+
++char *s1g_ss_max_support(__u8 maxss);
++char *s1g_ss_min_support(__u8 minss);
++
+ #define SCHED_SCAN_OPTIONS "[interval <in_msecs> | scan_plans [<interval_secs:iterations>*] <interval_secs>] " \
+ "[delay <in_secs>] [freqs <freq>+] [matches [ssid <ssid>]+]] [active [ssid <ssid>]+|passive] " \
+ "[randomise[=<addr>/<mask>]] [coloc] [flush]"
+@@ -274,6 +278,7 @@ char *hex2bin(const char *hex, char *buf);
+ int set_bitrates(struct nl_msg *msg, int argc, char **argv,
+ enum nl80211_attrs attr);
+
++int calc_s1g_ch_center_freq(__u8 ch_index, __u8 s1g_oper_class);
+
+ /* sections */
+ DECLARE_SECTION(ap);
+diff --git a/scan.c b/scan.c
+index dc26787..7479220 100644
+--- a/scan.c
++++ b/scan.c
+@@ -1675,6 +1675,101 @@ static void print_mesh_conf(const uint8_t type, uint8_t len,
+ printf("\t\t\t Mesh Power Save Level\n");
+ }
+
++static void print_s1g_capa(const uint8_t type, uint8_t len,
++ const uint8_t *data,
++ const struct print_ies_data *ie_buffer)
++{
++ printf("\n");
++ print_s1g_capability(data);
++}
++
++static void print_short_beacon_int(const uint8_t type, uint8_t len,
++ const uint8_t *data,
++ const struct print_ies_data *ie_buffer)
++{
++ printf(" %d\n", (data[1] << 8) | data[0]);
++}
++
++static void print_s1g_oper(const uint8_t type, uint8_t len,
++ const uint8_t *data,
++ const struct print_ies_data *ie_buffer)
++{
++ int oper_ch_width, prim_ch_width;
++ int prim_ch_width_subfield = data[0] & 0x1;
++
++ prim_ch_width = 2;
++
++ /* B1-B4 BSS channel width subfield */
++ switch ((data[0] >> 1) & 0xf) {
++ case 0:
++ oper_ch_width = 1;
++ prim_ch_width = 1;
++ if (!prim_ch_width_subfield) {
++ oper_ch_width = -1;
++ prim_ch_width = -1;
++ }
++ break;
++ case 1:
++ oper_ch_width = 2;
++ if (prim_ch_width_subfield)
++ prim_ch_width = 1;
++ break;
++ case 3:
++ oper_ch_width = 4;
++ if (prim_ch_width_subfield)
++ prim_ch_width = 1;
++ break;
++ case 7:
++ oper_ch_width = 8;
++ if (prim_ch_width_subfield)
++ prim_ch_width = 1;
++ break;
++ case 15:
++ oper_ch_width = 16;
++ if (prim_ch_width_subfield)
++ prim_ch_width = 1;
++ break;
++ default:
++ oper_ch_width = -1;
++ prim_ch_width = -1;
++ break;
++ }
++
++ printf("\n");
++ printf("\t\tChannel width:\n");
++ if (oper_ch_width == -1 || prim_ch_width == -1) {
++ printf("\t\t\tBSS primary channel width: invalid\n");
++ printf("\t\t\tBSS operating channel width: invalid\n");
++ } else {
++ printf("\t\t\tBSS primary channel width: %d MHz\n", prim_ch_width);
++ printf("\t\t\tBSS operating channel width: %d MHz\n", oper_ch_width);
++ }
++ if (data[0] & BIT(5))
++ printf("\t\t\t1 MHz primary channel located at the lower side of 2 MHz\n");
++ else
++ printf("\t\t\t1 MHz primary channel located at the upper side of 2 MHz\n");
++
++ if (data[0] & BIT(7))
++ printf("\t\t\tMCS 10 not recommended\n");
++
++ printf("\t\t* operating class: %d\n", data[1]);
++ printf("\t\t* primary channel number: %d\n", data[2]);
++
++ printf("\t\t* channel index: %d\n", data[3]);
++
++ printf("\t\tMax S1G MCS Map:\n");
++ printf("\t\t\tFor 1 SS: %s\n", s1g_ss_max_support((data[4] >> 2) & 0x3));
++ printf("\t\t\tFor 2 SS: %s\n", s1g_ss_max_support((data[4] >> 6) & 0x3));
++ printf("\t\t\tFor 3 SS: %s\n", s1g_ss_max_support((data[5] >> 2) & 0x3));
++ printf("\t\t\tFor 4 SS: %s\n", s1g_ss_max_support((data[5] >> 6) & 0x3));
++
++ printf("\t\tMin S1G MCS Map:\n");
++ printf("\t\t\tFor 1 SS: %s\n", s1g_ss_min_support(data[4] & 0x3));
++ printf("\t\t\tFor 2 SS: %s\n", s1g_ss_min_support((data[4] >> 4) & 0x3));
++ printf("\t\t\tFor 3 SS: %s\n", s1g_ss_min_support(data[5] & 0x3));
++ printf("\t\t\tFor 4 SS: %s\n", s1g_ss_min_support((data[5] >> 4) & 0x3));
++}
++
+ struct ie_print {
+ const char *name;
+ void (*print)(const uint8_t type, uint8_t len, const uint8_t *data,
+@@ -1748,6 +1843,9 @@ static const struct ie_print ieprinters[] = {
+ [108] = { "802.11u Advertisement", print_11u_advert, 0, 255, BIT(PRINT_SCAN), },
+ [111] = { "802.11u Roaming Consortium", print_11u_rcon, 2, 255, BIT(PRINT_SCAN), },
+ [195] = { "Transmit Power Envelope", print_tx_power_envelope, 2, 5, BIT(PRINT_SCAN), },
++ [214] = { "Short beacon interval", print_short_beacon_int, 2, 2, BIT(PRINT_SCAN), },
++ [217] = { "S1G capabilities", print_s1g_capa, 15, 15, BIT(PRINT_SCAN), },
++ [232] = { "S1G operation", print_s1g_oper, 6, 6, BIT(PRINT_SCAN), },
+ };
+
+ static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data,
+@@ -2326,7 +2424,8 @@ void print_ies(unsigned char *ie, int ielen, bool unknown,
+ while (ielen >= 2 && ielen - 2 >= ie[1]) {
+ if (ie[0] < ARRAY_SIZE(ieprinters) &&
+ ieprinters[ie[0]].name &&
+- ieprinters[ie[0]].flags & BIT(ptype)) {
++ ieprinters[ie[0]].flags & BIT(ptype) &&
++ ie[1] > 0) {
+ print_ie(&ieprinters[ie[0]],
+ ie[0], ie[1], ie + 2, &ie_buffer);
+ } else if (ie[0] == 221 /* vendor */) {
+@@ -2419,6 +2518,7 @@ static int print_bss_handler(struct nl_msg *msg, void *arg)
+ static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
++ [NL80211_BSS_FREQUENCY_OFFSET] = { .type = NLA_U32 },
+ [NL80211_BSS_BSSID] = { },
+ [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+@@ -2491,7 +2591,12 @@ static int print_bss_handler(struct nl_msg *msg, void *arg)
+ }
+ if (bss[NL80211_BSS_FREQUENCY]) {
+ int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+- printf("\tfreq: %d\n", freq);
++ if (bss[NL80211_BSS_FREQUENCY_OFFSET])
++ printf("\tfreq: %d.%d\n", freq,
++ nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]));
++ else
++ printf("\tfreq: %d\n", freq);
++
+ if (freq > 45000)
+ is_dmg = true;
+ }
+diff --git a/util.c b/util.c
+index eef0332..18a97e1 100644
+--- a/util.c
++++ b/util.c
+@@ -1841,3 +1841,221 @@ int parse_random_mac_addr(struct nl_msg *msg, char *addrs)
+ nla_put_failure:
+ return -ENOBUFS;
+ }
++
++char *s1g_ss_max_support(__u8 maxss)
++{
++ switch (maxss) {
++ case 0: return "Max S1G-MCS 2";
++ case 1: return "Max S1G-MCS 7";
++ case 2: return "Max S1G-MCS 9";
++ case 3: return "Not supported";
++ default: return "";
++ }
++}
++
++char *s1g_ss_min_support(__u8 minss)
++{
++ switch (minss) {
++ case 0: return "no minimum restriction";
++ case 1: return "MCS 0 not recommended";
++ case 2: return "MCS 0 and 1 not recommended";
++ case 3: return "invalid";
++ default: return "";
++ }
++}
++
++void print_s1g_capability(const uint8_t *caps)
++{
++#define PRINT_S1G_CAP(_cond, _str) \
++ do { \
++ if (_cond) \
++ printf("\t\t\t" _str "\n"); \
++ } while (0)
++
++ static char buf[20];
++ int offset = 0;
++ uint8_t cap = caps[0];
++
++ /* S1G Capabilities Information subfield */
++ if (cap)
++ printf("\t\tByte[0]: 0x%02x\n", cap);
++
++ PRINT_S1G_CAP((cap & BIT(0)), "S1G PHY: S1G_LONG PPDU Format");
++
++ if ((cap >> 1) & 0x1f) {
++ offset = sprintf(buf, "SGI support:");
++ offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x1) ? " 1" : "");
++ offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x2) ? " 2" : "");
++ offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x4) ? " 4" : "");
++ offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x8) ? " 8" : "");
++ offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x10) ? " 16" : "");
++ offset += sprintf(buf + offset, " MHz");
++ printf("\t\t\t%s\n", buf);
++ }
++
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Channel width: 1, 2 MHz");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "Channel width: 1, 2, 4 MHz");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "Channel width: 1, 2, 4, 8 MHz");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3, "Channel width: 1, 2, 4, 8, 16 MHz");
++
++ cap = caps[1];
++
++ if (cap)
++ printf("\t\tByte[1]: 0x%02x\n", cap);
++
++ PRINT_S1G_CAP((cap & BIT(0)), "Rx LDPC");
++ PRINT_S1G_CAP((cap & BIT(1)), "Tx STBC");
++ PRINT_S1G_CAP((cap & BIT(2)), "Rx STBC");
++ PRINT_S1G_CAP((cap & BIT(3)), "SU Beamformer");
++ PRINT_S1G_CAP((cap & BIT(4)), "SU Beamformee");
++ if (cap & BIT(4))
++ printf("\t\t\tBeamformee STS: %d\n", (cap >> 5) + 1);
++
++ cap = caps[2];
++ printf("\t\tByte[2]: 0x%02x\n", cap);
++
++ if (caps[1] & BIT(3))
++ printf("\t\t\tSounding dimensions: %d\n", (cap & 0x7) + 1);
++
++ PRINT_S1G_CAP((cap & BIT(3)), "MU Beamformer");
++ PRINT_S1G_CAP((cap & BIT(4)), "MU Beamformee");
++ PRINT_S1G_CAP((cap & BIT(5)), "+HTC-VHT Capable");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "No support for Traveling Pilot");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "Supports 1 STS Traveling Pilot");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3, "Supports 1 and 2 STS Traveling Pilot");
++
++ cap = caps[3];
++ printf("\t\tByte[3]: 0x%02x\n", cap);
++ PRINT_S1G_CAP((cap & BIT(0)), "RD Responder");
++ /* BIT(1) in Byte 3 or BIT(25) in all capabilities is reserved */
++ PRINT_S1G_CAP(((cap & BIT(2)) == 0x0), "Max MPDU length: 3895 bytes");
++ PRINT_S1G_CAP((cap & BIT(2)), "Max MPDU length: 7991 bytes");
++
++ if (compute_ampdu_length((cap >> 2) & 0x3)) {
++ printf("\t\t\tMaximum AMPDU length: %d bytes (exponent: 0x0%02x)\n",
++ compute_ampdu_length((cap >> 2) & 0x3), (cap >> 2) & 0x3);
++ } else {
++ printf("\t\t\tMaximum AMPDU length: unrecognized bytes (exponent: %d)\n",
++ (cap >> 2) & 0x3);
++ }
++
++ printf("\t\t\tMinimum MPDU time spacing: %s (0x%02x)\n",
++ print_ampdu_space((cap >> 5) & 0x7), (cap >> 5) & 0x7);
++
++ cap = caps[4];
++ printf("\t\tByte[4]: 0x%02x\n", cap);
++ PRINT_S1G_CAP((cap & BIT(0)), "Uplink sync capable");
++ PRINT_S1G_CAP((cap & BIT(1)), "Dynamic AID");
++ PRINT_S1G_CAP((cap & BIT(2)), "BAT");
++ PRINT_S1G_CAP((cap & BIT(3)), "TIM ADE");
++ PRINT_S1G_CAP((cap & BIT(4)), "Non-TIM");
++ PRINT_S1G_CAP((cap & BIT(5)), "Group AID");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Sensor and non-sensor STAs");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "Only sensor STAs");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "Only non-sensor STAs");
++
++ cap = caps[5];
++ printf("\t\tByte[5]: 0x%02x\n", cap);
++ PRINT_S1G_CAP((cap & BIT(0)), "Centralized authentication control");
++ PRINT_S1G_CAP((cap & BIT(1)), "Distributed authentication control");
++ PRINT_S1G_CAP((cap & BIT(2)), "A-MSDU supported");
++ PRINT_S1G_CAP((cap & BIT(3)), "A-MPDU supported");
++ PRINT_S1G_CAP((cap & BIT(4)), "Asymmetric BA supported");
++ PRINT_S1G_CAP((cap & BIT(5)), "Flow control supported");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Sectorization operation not supported");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "TXOP-based sectorization operation");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "only group sectorization operation");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3, "Group and TXOP-based sectorization operations");
++
++ cap = caps[6];
++ if (cap)
++ printf("\t\tByte[6]: 0x%02x\n", cap);
++
++ PRINT_S1G_CAP((cap & BIT(0)), "OBSS mitigation");
++ PRINT_S1G_CAP((cap & BIT(1)), "Fragment BA");
++ PRINT_S1G_CAP((cap & BIT(2)), "NDP PS-Poll");
++ PRINT_S1G_CAP((cap & BIT(3)), "RAW operation");
++ PRINT_S1G_CAP((cap & BIT(4)), "Page slicing");
++ PRINT_S1G_CAP((cap & BIT(5)), "TXOP sharing smplicit Ack");
++
++ /* Only in case +HTC-VHT Capable is 0x1 */
++ if (caps[2] & BIT(5)) {
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Not provide VHT MFB (No Feedback)");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "Provides only unsolicited VHT MFB");
++ PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3,
++ "Provides both feedback and unsolicited VHT MFB");
++ }
++
++ cap = caps[7];
++ printf("\t\tByte[7]: 0x%02x\n", cap);
++ PRINT_S1G_CAP((cap & BIT(0)), "TACK support as PS-Poll response");
++ PRINT_S1G_CAP((cap & BIT(1)), "Duplicate 1 MHz");
++ PRINT_S1G_CAP((cap & BIT(2)), "MCS negotiation");
++ PRINT_S1G_CAP((cap & BIT(3)), "1 MHz control response preamble");
++ PRINT_S1G_CAP((cap & BIT(4)), "NDP beamforming report poll");
++ PRINT_S1G_CAP((cap & BIT(5)), "Unsolicited dynamic AID");
++ PRINT_S1G_CAP((cap & BIT(6)), "Sector training operation");
++ PRINT_S1G_CAP((cap & BIT(7)), "Temporary PS mode switch");
++
++ cap = caps[8];
++ if (cap)
++ printf("\t\tByte[8]: 0x%02x\n", cap);
++
++ PRINT_S1G_CAP((cap & BIT(0)), "TWT grouping");
++ PRINT_S1G_CAP((cap & BIT(1)), "BDT capable");
++ printf("\t\t\tColor: %u\n", (cap >> 2) & 0x7);
++ PRINT_S1G_CAP((cap & BIT(5)), "TWT requester");
++ PRINT_S1G_CAP((cap & BIT(6)), "TWT responder");
++ PRINT_S1G_CAP((cap & BIT(7)), "PV1 frame support");
++
++ cap = caps[9];
++ if (cap)
++ printf("\t\tByte[9]: 0x%02x\n", cap);
++
++ PRINT_S1G_CAP((cap & BIT(0)), "Link Adaptation without NDP CMAC PPDU capable");
++ /* Rest of byte 9 bits are reserved */
++
++ /* Supported S1G-MCS and NSS Set subfield */
++ /* Rx S1G-MCS Map */
++ cap = caps[10];
++ printf("\t\tMax Rx S1G MCS Map: 0x%02x\n", cap);
++ printf("\t\t\tFor 1 SS: %s\n", s1g_ss_max_support(cap & 0x3));
++ printf("\t\t\tFor 2 SS: %s\n", s1g_ss_max_support((cap >> 2) & 0x3));
++ printf("\t\t\tFor 3 SS: %s\n", s1g_ss_max_support((cap >> 4) & 0x3));
++ printf("\t\t\tFor 4 SS: %s\n", s1g_ss_max_support((cap >> 6) & 0x3));
++
++ /* Rx Long GI data rate field comprises of 9 bits */
++ cap = caps[11];
++ if (cap || caps[12] & 0x1)
++ printf("\t\t\tRx Highest Long GI Data Rate: %u Mbps\n",
++ cap + ((caps[12] & 0x1) << 8));
++
++ /* Tx S1G-MCS Map */
++ cap = caps[12];
++ printf("\t\tMax Tx S1G MCS Map: 0x%02x\n", cap);
++ printf("\t\t\tFor 1 SS: %s\n", s1g_ss_max_support((cap >> 1) & 0x3));
++ printf("\t\t\tFor 2 SS: %s\n", s1g_ss_max_support((cap >> 3) & 0x3));
++ printf("\t\t\tFor 3 SS: %s\n", s1g_ss_max_support((cap >> 5) & 0x3));
++ printf("\t\t\tFor 4 SS: %s\n", s1g_ss_max_support(((cap >> 7) & 0x1) +
++ ((caps[13] << 1) & 0x2)));
++
++ /* Tx Long GI data rate field comprises of 9 bits */
++ cap = caps[13];
++ if (((cap >> 7) & 0x7f) || (caps[14] & 0x3))
++ printf("\t\t\tTx Highest Long GI Data Rate: %u Mbps\n", ((cap >> 7) & 0x7f) +
++ ((caps[14] & 0x3) << 7));
++
++ /* Rx and Tx single spatial streams and S1G MCS Map for 1 MHz */
++ cap = (caps[15] >> 2) & 0xf;
++ PRINT_S1G_CAP((cap & 0x3) == 0x0, "Rx single SS for 1 MHz: as in Rx S1G MCS Map");
++ PRINT_S1G_CAP((cap & 0x3) == 0x1, "Rx single SS for 1 MHz: single SS and S1G-MCS 2");
++ PRINT_S1G_CAP((cap & 0x3) == 0x2, "Rx single SS for 1 MHz: single SS and S1G-MCS 7");
++ PRINT_S1G_CAP((cap & 0x3) == 0x3, "Rx single SS for 1 MHz: single SS and S1G-MCS 9");
++ cap = (cap >> 2) & 0x3;
++ PRINT_S1G_CAP((cap & 0x3) == 0x0, "Tx single SS for 1 MHz: as in Tx S1G MCS Map");
++ PRINT_S1G_CAP((cap & 0x3) == 0x1, "Tx single SS for 1 MHz: single SS and S1G-MCS 2");
++ PRINT_S1G_CAP((cap & 0x3) == 0x2, "Tx single SS for 1 MHz: single SS and S1G-MCS 7");
++ PRINT_S1G_CAP((cap & 0x3) == 0x3, "Tx single SS for 1 MHz: single SS and S1G-MCS 9");
++ /* Last 2 bits are reserved */
++#undef PRINT_S1G_CAP
++}
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0016-iw-S1G-add-list-command-support-for-802.11ah.patch b/recipes-wifi/iw/patches-mlo/0016-iw-S1G-add-list-command-support-for-802.11ah.patch
new file mode 100644
index 0000000..b67eafc
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0016-iw-S1G-add-list-command-support-for-802.11ah.patch
@@ -0,0 +1,68 @@
+From f2d9f5b52677f5414dc194be94b5916d2b080eab Mon Sep 17 00:00:00 2001
+From: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com>
+Date: Tue, 28 Feb 2023 12:11:24 +1300
+Subject: [PATCH 16/28] iw: S1G: add list command support for 802.11ah
+
+In this changeset S1G frequencies are displayed for any S1G band
+with their relevant properties.
+
+Signed-off-by: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com>
+Link: https://lore.kernel.org/r/20230227231124.711053-1-gilad.itzkovitch@virscient.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ info.c | 13 ++++++++++++-
+ util.c | 2 ++
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/info.c b/info.c
+index 9955e5e..364f9b3 100644
+--- a/info.c
++++ b/info.c
+@@ -301,6 +301,7 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
+ struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+ static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+ [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
++ [NL80211_FREQUENCY_ATTR_OFFSET] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
+ [__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
+@@ -396,12 +397,22 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
+ }
+ nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+ uint32_t freq;
++
+ nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+ nla_len(nl_freq), freq_policy);
+ if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+ continue;
+ freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+- printf("\t\t\t* %d MHz [%d]", freq, ieee80211_frequency_to_channel(freq));
++ if (tb_freq[NL80211_FREQUENCY_ATTR_OFFSET]) {
++ uint32_t offset = nla_get_u32(
++ tb_freq[NL80211_FREQUENCY_ATTR_OFFSET]);
++ printf("\t\t\t* %d.%d MHz", freq, offset);
++ } else {
++ printf("\t\t\t* %d MHz", freq);
++ }
++
++ if (ieee80211_frequency_to_channel(freq))
++ printf(" [%d]", ieee80211_frequency_to_channel(freq));
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
+ !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+diff --git a/util.c b/util.c
+index 18a97e1..dc09193 100644
+--- a/util.c
++++ b/util.c
+@@ -199,6 +199,8 @@ int ieee80211_channel_to_frequency(int chan, enum nl80211_band band)
+
+ int ieee80211_frequency_to_channel(int freq)
+ {
++ if (freq < 1000)
++ return 0;
+ /* see 802.11-2007 17.3.8.3.2 and Annex J */
+ if (freq == 2484)
+ return 14;
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0017-iw-Fix-EHT-rates-printing.patch b/recipes-wifi/iw/patches-mlo/0017-iw-Fix-EHT-rates-printing.patch
new file mode 100644
index 0000000..1da5c22
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0017-iw-Fix-EHT-rates-printing.patch
@@ -0,0 +1,76 @@
+From cf26fc9ab584833f01e27cc2dd09988179ec6a03 Mon Sep 17 00:00:00 2001
+From: Ben Greear <greearb@candelatech.com>
+Date: Tue, 16 May 2023 11:02:38 -0700
+Subject: [PATCH 17/28] iw: Fix EHT rates printing.
+
+The 20Mhz rates thing is a union with the others, so print one or
+the other. This appears to fix the output of the mcs/bw printout.
+
+Signed-off-by: Ben Greear <greearb@candelatech.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ util.c | 46 +++++++++++++++++++++-------------------------
+ 1 file changed, 21 insertions(+), 25 deletions(-)
+
+diff --git a/util.c b/util.c
+index dc09193..d36dbdc 100644
+--- a/util.c
++++ b/util.c
+@@ -1604,33 +1604,29 @@ static void __print_eht_capa(int band,
+ printf("%s\t\tEHT bw=20 MHz, max NSS for MCS %s: Rx=%u, Tx=%u\n",
+ pre, mcs[i],
+ mcs_set[i] & 0xf, mcs_set[i] >> 4);
+- }
+-
+- mcs_set += 4;
+- if (he_phy_cap[0] & (BIT(2) << 8)) {
+- for (i = 0; i < 3; i++)
+- printf("%s\t\tEHT bw <= 80 MHz, max NSS for MCS %s: Rx=%u, Tx=%u\n",
+- pre, mcs[i + 1],
+- mcs_set[i] & 0xf, mcs_set[i] >> 4);
+-
+- }
+-
+- mcs_set += 3;
+- if (he_phy_cap[0] & (BIT(3) << 8)) {
+- for (i = 0; i < 3; i++)
+- printf("%s\t\tEHT bw=160 MHz, max NSS for MCS %s: Rx=%u, Tx=%u\n",
+- pre, mcs[i + 1],
+- mcs_set[i] & 0xf, mcs_set[i] >> 4);
+-
+- }
++ } else {
++ if (he_phy_cap[0] & (BIT(2) << 8)) {
++ for (i = 0; i < 3; i++)
++ printf("%s\t\tEHT bw <= 80 MHz, max NSS for MCS %s: Rx=%u, Tx=%u\n",
++ pre, mcs[i + 1],
++ mcs_set[i] & 0xf, mcs_set[i] >> 4);
++ }
++ mcs_set += 3;
+
+- mcs_set += 3;
+- if (band == NL80211_BAND_6GHZ && (phy_cap[0] & BIT(1))) {
+- for (i = 0; i < 3; i++)
+- printf("%s\t\tEHT bw=320 MHz, max NSS for MCS %s: Rx=%u, Tx=%u\n",
+- pre, mcs[i + 1],
+- mcs_set[i] & 0xf, mcs_set[i] >> 4);
++ if (he_phy_cap[0] & (BIT(3) << 8)) {
++ for (i = 0; i < 3; i++)
++ printf("%s\t\tEHT bw=160 MHz, max NSS for MCS %s: Rx=%u, Tx=%u\n",
++ pre, mcs[i + 1],
++ mcs_set[i] & 0xf, mcs_set[i] >> 4);
++ }
+
++ mcs_set += 3;
++ if (band == NL80211_BAND_6GHZ && (phy_cap[0] & BIT(1))) {
++ for (i = 0; i < 3; i++)
++ printf("%s\t\tEHT bw=320 MHz, max NSS for MCS %s: Rx=%u, Tx=%u\n",
++ pre, mcs[i + 1],
++ mcs_set[i] & 0xf, mcs_set[i] >> 4);
++ }
+ }
+
+ if (ppet && ppet_len && (phy_cap[1] & BIT(11))) {
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0018-iw-add-more-extended-capa-bits.patch b/recipes-wifi/iw/patches-mlo/0018-iw-add-more-extended-capa-bits.patch
new file mode 100644
index 0000000..46ec818
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0018-iw-add-more-extended-capa-bits.patch
@@ -0,0 +1,37 @@
+From cb491fa6b4b5bb1e7c11788a39bcf5a7a74afafa Mon Sep 17 00:00:00 2001
+From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
+Date: Wed, 23 Aug 2023 13:10:02 +0300
+Subject: [PATCH 18/28] iw: add more extended capa bits
+
+Those were missing
+
+While at it, fix a bug in the default case, we want to print the actual
+bit offset which is bit + base.
+
+Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ scan.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/scan.c b/scan.c
+index 7479220..faf406d 100644
+--- a/scan.c
++++ b/scan.c
+@@ -1445,8 +1445,12 @@ static void print_capabilities(const uint8_t type, uint8_t len,
+ CAPA(72, "Reserved");
+ CAPA(73, "Extended Spectrum Management Capable");
+ CAPA(74, "Reserved");
++ CAPA(77, "TWT Requester Support");
++ CAPA(78, "TWT Responder Support");
++ CAPA(79, "OBSS Narrow Bandwith RU in UL OFDMA Tolerance Support");
++
+ default:
+- printf(" %d", bit);
++ printf(" %d", bit + base);
+ break;
+ }
+ #undef ADD_BIT_VAL
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0019-iw-fix-attribute-size-mismatch.patch b/recipes-wifi/iw/patches-mlo/0019-iw-fix-attribute-size-mismatch.patch
new file mode 100644
index 0000000..93ac02c
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0019-iw-fix-attribute-size-mismatch.patch
@@ -0,0 +1,32 @@
+From ea706b389fd896318bd7310b058fedf11d1b6758 Mon Sep 17 00:00:00 2001
+From: Koen Vandeputte <koen.vandeputte@citymesh.com>
+Date: Fri, 7 Jul 2023 16:48:26 +0200
+Subject: [PATCH 19/28] iw: fix attribute size mismatch
+
+NL80211_ATTR_MAX_AP_ASSOC_STA gets packed as u32 in the kernel.
+Change the receiving side to match this, or it will be wrong
+on big-endian.
+
+Signed-off-by: Koen Vandeputte <koen.vandeputte@citymesh.com>
+Link: https://lore.kernel.org/r/20230707144826.3043151-1-koen.vandeputte@citymesh.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ info.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/info.c b/info.c
+index 364f9b3..4c5f463 100644
+--- a/info.c
++++ b/info.c
+@@ -879,7 +879,7 @@ broken_combination:
+
+ if (tb_msg[NL80211_ATTR_MAX_AP_ASSOC_STA])
+ printf("\tMaximum associated stations in AP mode: %u\n",
+- nla_get_u16(tb_msg[NL80211_ATTR_MAX_AP_ASSOC_STA]));
++ nla_get_u32(tb_msg[NL80211_ATTR_MAX_AP_ASSOC_STA]));
+
+ return NL_SKIP;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0020-iw-connect-Fix-segfault-during-open-authentication.patch b/recipes-wifi/iw/patches-mlo/0020-iw-connect-Fix-segfault-during-open-authentication.patch
new file mode 100644
index 0000000..7e5da61
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0020-iw-connect-Fix-segfault-during-open-authentication.patch
@@ -0,0 +1,34 @@
+From a1c9376a115f9af4a50626d2c0fa89667afa0096 Mon Sep 17 00:00:00 2001
+From: Chaitanya Tata <chaitanya.mgit@gmail.com>
+Date: Thu, 27 Jul 2023 00:52:45 +0530
+Subject: [PATCH 20/28] iw: connect: Fix segfault during open authentication
+
+The check for remaining arguments is done after decrement effectively
+bypassing the non-zero check and causes a segfault with below command:
+
+ "connect <SSID> auth open/shared".
+
+Signed-off-by: Chaitanya Tata <Chaitanya.Tata@nordicsemi.no>
+Link: https://lore.kernel.org/r/20230726192245.100897-1-Chaitanya.Tata@nordicsemi.no
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ connect.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/connect.c b/connect.c
+index e5b177f..33e1a5f 100644
+--- a/connect.c
++++ b/connect.c
+@@ -77,6 +77,9 @@ static int iw_conn(struct nl80211_state *state,
+ if (argc && strcmp(*argv, "key") != 0 && strcmp(*argv, "keys") != 0)
+ return 1;
+
++ if (!argc)
++ return 0;
++
+ argv++;
+ argc--;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0021-update-nl80211.h.patch b/recipes-wifi/iw/patches-mlo/0021-update-nl80211.h.patch
new file mode 100644
index 0000000..0a3ac71
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0021-update-nl80211.h.patch
@@ -0,0 +1,110 @@
+From 7298198a54d81b969f688164ef33d952ddfcb81e Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Thu, 24 Aug 2023 09:19:47 +0200
+Subject: [PATCH 21/28] update nl80211.h
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ nl80211.h | 30 +++++++++++++++++++++++++++++-
+ 1 file changed, 29 insertions(+), 1 deletion(-)
+
+diff --git a/nl80211.h b/nl80211.h
+index c59fec4..88eb85c 100644
+--- a/nl80211.h
++++ b/nl80211.h
+@@ -11,7 +11,7 @@
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015-2017 Intel Deutschland GmbH
+- * Copyright (C) 2018-2022 Intel Corporation
++ * Copyright (C) 2018-2023 Intel Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+@@ -1309,6 +1309,11 @@
+ * The number of peers that HW timestamping can be enabled for concurrently
+ * is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
+ *
++ * @NL80211_CMD_LINKS_REMOVED: Notify userspace about the removal of STA MLD
++ * setup links due to AP MLD removing the corresponding affiliated APs with
++ * Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide
++ * information about the removed STA MLD setup links.
++ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+@@ -1562,6 +1567,8 @@ enum nl80211_commands {
+
+ NL80211_CMD_SET_HW_TIMESTAMP,
+
++ NL80211_CMD_LINKS_REMOVED,
++
+ /* add new commands above here */
+
+ /* used to define NL80211_CMD_MAX below */
+@@ -2805,6 +2812,9 @@ enum nl80211_commands {
+ * index. If the userspace includes more RNR elements than number of
+ * MBSSID elements then these will be added in every EMA beacon.
+ *
++ * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is
++ * disabled.
++ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3341,6 +3351,8 @@ enum nl80211_attrs {
+
+ NL80211_ATTR_EMA_RNR_ELEMS,
+
++ NL80211_ATTR_MLO_LINK_DISABLED,
++
+ /* add attributes here, update the policy in nl80211.c */
+
+ __NL80211_ATTR_AFTER_LAST,
+@@ -3667,6 +3679,13 @@ enum nl80211_eht_ru_alloc {
+ * (u8, see &enum nl80211_eht_gi)
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
+ * non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
++ * @NL80211_RATE_INFO_S1G_MCS: S1G MCS index (u8, 0-10)
++ * @NL80211_RATE_INFO_S1G_NSS: S1G NSS value (u8, 1-4)
++ * @NL80211_RATE_INFO_1_MHZ_WIDTH: 1 MHz S1G rate
++ * @NL80211_RATE_INFO_2_MHZ_WIDTH: 2 MHz S1G rate
++ * @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate
++ * @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate
++ * @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+ enum nl80211_rate_info {
+@@ -3693,6 +3712,13 @@ enum nl80211_rate_info {
+ NL80211_RATE_INFO_EHT_NSS,
+ NL80211_RATE_INFO_EHT_GI,
+ NL80211_RATE_INFO_EHT_RU_ALLOC,
++ NL80211_RATE_INFO_S1G_MCS,
++ NL80211_RATE_INFO_S1G_NSS,
++ NL80211_RATE_INFO_1_MHZ_WIDTH,
++ NL80211_RATE_INFO_2_MHZ_WIDTH,
++ NL80211_RATE_INFO_4_MHZ_WIDTH,
++ NL80211_RATE_INFO_8_MHZ_WIDTH,
++ NL80211_RATE_INFO_16_MHZ_WIDTH,
+
+ /* keep last */
+ __NL80211_RATE_INFO_AFTER_LAST,
+@@ -4424,6 +4450,7 @@ enum nl80211_sched_scan_match_attr {
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
+ * @NL80211_RRF_NO_HE: HE operation not allowed
+ * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
++ * @NL80211_RRF_NO_EHT: EHT operation not allowed
+ */
+ enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_OFDM = 1<<0,
+@@ -4443,6 +4470,7 @@ enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_160MHZ = 1<<16,
+ NL80211_RRF_NO_HE = 1<<17,
+ NL80211_RRF_NO_320MHZ = 1<<18,
++ NL80211_RRF_NO_EHT = 1<<19,
+ };
+
+ #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0022-iw-S1G-add-802.11ah-support-for-link-command-display.patch b/recipes-wifi/iw/patches-mlo/0022-iw-S1G-add-802.11ah-support-for-link-command-display.patch
new file mode 100644
index 0000000..7bb242c
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0022-iw-S1G-add-802.11ah-support-for-link-command-display.patch
@@ -0,0 +1,77 @@
+From e2224c729840cc33c6ea89ba5e91b69f79c88e85 Mon Sep 17 00:00:00 2001
+From: Bassem Dawood <bassem@morsemicro.com>
+Date: Thu, 19 Oct 2023 18:40:19 +1100
+Subject: [PATCH 22/28] iw: S1G: add 802.11ah support for link command display
+
+Amending the link command which depends on kernal changes for
+802.11ah bandwidths/MCS/NSS NL80211_RATE_INFO_ attributes.
+
+S1G frequency offset being used as well for the MHz units print.
+
+Signed-off-by: Bassem Dawood <bassem@morsemicro.com>
+Link: https://lore.kernel.org/r/20231019074019.2246629-1-bassem@morsemicro.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ link.c | 9 +++++++--
+ station.c | 10 ++++++++++
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/link.c b/link.c
+index a090100..a7ee963 100644
+--- a/link.c
++++ b/link.c
+@@ -29,6 +29,7 @@ static int link_bss_handler(struct nl_msg *msg, void *arg)
+ static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
++ [NL80211_BSS_FREQUENCY_OFFSET] = { .type = NLA_U32 },
+ [NL80211_BSS_BSSID] = { },
+ [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+@@ -41,6 +42,7 @@ static int link_bss_handler(struct nl_msg *msg, void *arg)
+ char mac_addr[20], dev[20], link_addr[20];
+ int link_id = -1;
+ const char *indent = "\t";
++ int freq_offset = 0;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+@@ -121,9 +123,12 @@ static int link_bss_handler(struct nl_msg *msg, void *arg)
+ nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+ false, result->mld ? PRINT_LINK_MLO_LINK : PRINT_LINK);
+
++ if (bss[NL80211_BSS_FREQUENCY_OFFSET])
++ freq_offset = nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]);
++
+ if (bss[NL80211_BSS_FREQUENCY])
+- printf("%sfreq: %d\n", indent,
+- nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
++ printf("%sfreq: %d.%d\n", indent,
++ nla_get_u32(bss[NL80211_BSS_FREQUENCY]), freq_offset);
+
+ if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
+ return NL_SKIP;
+diff --git a/station.c b/station.c
+index da1feae..bf7c0f5 100644
+--- a/station.c
++++ b/station.c
+@@ -241,6 +241,16 @@ void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen)
+ pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
+ if (rinfo[NL80211_RATE_INFO_320_MHZ_WIDTH])
+ pos += snprintf(pos, buflen - (pos - buf), " 320MHz");
++ if (rinfo[NL80211_RATE_INFO_1_MHZ_WIDTH])
++ pos += snprintf(pos, buflen - (pos - buf), " 1MHz");
++ if (rinfo[NL80211_RATE_INFO_2_MHZ_WIDTH])
++ pos += snprintf(pos, buflen - (pos - buf), " 2MHz");
++ if (rinfo[NL80211_RATE_INFO_4_MHZ_WIDTH])
++ pos += snprintf(pos, buflen - (pos - buf), " 4MHz");
++ if (rinfo[NL80211_RATE_INFO_8_MHZ_WIDTH])
++ pos += snprintf(pos, buflen - (pos - buf), " 8MHz");
++ if (rinfo[NL80211_RATE_INFO_16_MHZ_WIDTH])
++ pos += snprintf(pos, buflen - (pos - buf), " 16MHz");
+ if (rinfo[NL80211_RATE_INFO_SHORT_GI])
+ pos += snprintf(pos, buflen - (pos - buf), " short GI");
+ if (rinfo[NL80211_RATE_INFO_VHT_NSS])
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0023-iw-allow-extra-cflags.patch b/recipes-wifi/iw/patches-mlo/0023-iw-allow-extra-cflags.patch
new file mode 100644
index 0000000..9897062
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0023-iw-allow-extra-cflags.patch
@@ -0,0 +1,29 @@
+From 44686ac3e7b536e905c3749814e4eb0e7e210440 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 1 Sep 2023 07:50:02 +0200
+Subject: [PATCH 23/28] iw: allow extra cflags
+
+We can override the entirety of CFLAGS from the make
+command line, but not add e.g. -Werror. Append a new
+EXTRA_CFLAGS to make that possible.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ Makefile | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Makefile b/Makefile
+index 2fb8db8..17be33f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -18,6 +18,7 @@ CFLAGS += -Wall -Wextra -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-a
+ CFLAGS += -Werror-implicit-function-declaration -Wsign-compare -Wno-unused-parameter
+ CFLAGS += -Wdeclaration-after-statement
+ CFLAGS += $(CFLAGS_EVAL)
++CFLAGS += $(EXTRA_CFLAGS)
+
+ _OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
+ VERSION_OBJS := $(filter-out version.o, $(_OBJS))
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0024-update-nl80211.h.patch b/recipes-wifi/iw/patches-mlo/0024-update-nl80211.h.patch
new file mode 100644
index 0000000..c7d248a
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0024-update-nl80211.h.patch
@@ -0,0 +1,623 @@
+From ac7e46b2aaf800ff0bd1339be814754b79eed568 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Thu, 21 Dec 2023 12:47:01 +0100
+Subject: [PATCH 24/28] update nl80211.h
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ info.c | 2 +
+ nl80211.h | 199 ++++++++++++++++++++++++++++++++++++++++--------------
+ 2 files changed, 149 insertions(+), 52 deletions(-)
+
+diff --git a/info.c b/info.c
+index 4c5f463..317e7a3 100644
+--- a/info.c
++++ b/info.c
+@@ -170,6 +170,8 @@ static void ext_feat_print(enum nl80211_ext_feature_index idx)
+ ext_feat_case(PUNCT, "preamble puncturing in AP mode");
+ ext_feat_case(SECURE_NAN, "secure NAN support");
+ ext_feat_case(AUTH_AND_DEAUTH_RANDOM_TA, "random auth/deauth transmitter address");
++ ext_feat_case(OWE_OFFLOAD, "OWE DH element handling offload (client)");
++ ext_feat_case(OWE_OFFLOAD_AP, "OWE DH element handling offload (AP)");
+ }
+ }
+
+diff --git a/nl80211.h b/nl80211.h
+index 88eb85c..a682b54 100644
+--- a/nl80211.h
++++ b/nl80211.h
+@@ -72,7 +72,7 @@
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ * - a setup station entry is added, not yet authorized, without any rate
+- * or capability information, this just exists to avoid race conditions
++ * or capability information; this just exists to avoid race conditions
+ * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ * to add rate and capability information to the station and at the same
+ * time mark it authorized.
+@@ -87,7 +87,7 @@
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+- * management entities such as wpa_supplicant react to management frames
++ * management entities such as wpa_supplicant to react to management frames
+ * that are not being handled by the kernel. This includes, for example,
+ * certain classes of action frames that cannot be handled in the kernel
+ * for various reasons.
+@@ -113,7 +113,7 @@
+ *
+ * Frame transmission allows userspace to send for example the required
+ * responses to action frames. It is subject to some sanity checking,
+- * but many frames can be transmitted. When a frame was transmitted, its
++ * but many frames can be transmitted. When a frame is transmitted, its
+ * status is indicated to the sending socket.
+ *
+ * For more technical details, see the corresponding command descriptions
+@@ -123,7 +123,7 @@
+ /**
+ * DOC: Virtual interface / concurrency capabilities
+ *
+- * Some devices are able to operate with virtual MACs, they can have
++ * Some devices are able to operate with virtual MACs; they can have
+ * more than one virtual interface. The capability handling for this
+ * is a bit complex though, as there may be a number of restrictions
+ * on the types of concurrency that are supported.
+@@ -135,7 +135,7 @@
+ * Once concurrency is desired, more attributes must be observed:
+ * To start with, since some interface types are purely managed in
+ * software, like the AP-VLAN type in mac80211 for example, there's
+- * an additional list of these, they can be added at any time and
++ * an additional list of these; they can be added at any time and
+ * are only restricted by some semantic restrictions (e.g. AP-VLAN
+ * cannot be added without a corresponding AP interface). This list
+ * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+@@ -164,17 +164,17 @@
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+- * following events occur.
++ * following events occurs.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+- * b) Coalescing buffer in hardware reaches it's limit.
++ * b) Coalescing buffer in hardware reaches its limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+- * c) Condition for coalescence. pattern 'match' or 'no match'
++ * c) Condition for coalescence: pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+@@ -213,7 +213,7 @@
+ /**
+ * DOC: FILS shared key authentication offload
+ *
+- * FILS shared key authentication offload can be advertized by drivers by
++ * FILS shared key authentication offload can be advertised by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+@@ -239,7 +239,7 @@
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+- * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
++ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertised by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+@@ -290,12 +290,12 @@
+ * If the configuration needs to be applied for specific peer then the MAC
+ * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the
+ * configuration will be applied for all the connected peers in the vif except
+- * any peers that have peer specific configuration for the TID by default; if
+- * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values
++ * any peers that have peer-specific configuration for the TID by default; if
++ * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer-specific values
+ * will be overwritten.
+ *
+- * All this configuration is valid only for STA's current connection
+- * i.e. the configuration will be reset to default when the STA connects back
++ * All this configuration is valid only for STA's current connection,
++ * i.e., the configuration will be reset to default when the STA connects back
+ * after disconnection/roaming, and this configuration will be cleared when
+ * the interface goes down.
+ */
+@@ -326,7 +326,7 @@
+ /**
+ * DOC: Multi-Link Operation
+ *
+- * In Multi-Link Operation, a connection between to MLDs utilizes multiple
++ * In Multi-Link Operation, a connection between two MLDs utilizes multiple
+ * links. To use this in nl80211, various commands and responses now need
+ * to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
+ * Additionally, various commands that need to operate on a specific link
+@@ -334,6 +334,15 @@
+ * use %NL80211_CMD_START_AP or similar functions.
+ */
+
++/**
++ * DOC: OWE DH IE handling offload
++ *
++ * By setting @NL80211_EXT_FEATURE_OWE_OFFLOAD flag, drivers can indicate
++ * kernel/application space to avoid DH IE handling. When this flag is
++ * advertised, the driver/device will take care of DH IE inclusion and
++ * processing of peer DH IE to generate PMK.
++ */
++
+ /**
+ * enum nl80211_commands - supported nl80211 commands
+ *
+@@ -512,7 +521,7 @@
+ * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ * scheduled scan will run in an infinite loop with the specified interval.
+- * These attributes are mutually exculsive,
++ * These attributes are mutually exclusive,
+ * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ * NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ * If for some reason scheduled scan is aborted by the driver, all scan
+@@ -543,7 +552,7 @@
+ * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+ * is brought down while a scheduled scan was running.
+ *
+- * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
++ * @NL80211_CMD_GET_SURVEY: get survey results, e.g. channel occupation
+ * or noise level
+ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+ * NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+@@ -554,12 +563,13 @@
+ * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+- * advertized by a FILS capable AP identifying the scope of PMKSA in an
++ * advertised by a FILS capable AP identifying the scope of PMKSA in an
+ * ESS.
+ * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+ * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+- * authentication.
++ * authentication. Additionally in case of SAE offload and OWE offloads
++ * PMKSA entry can be deleted using %NL80211_ATTR_SSID.
+ * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+ *
+ * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+@@ -598,7 +608,7 @@
+ * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+ * the SSID (mainly for association, but is included in authentication
+ * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ +
+- * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the
++ * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequency of the
+ * channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the
+ * authentication type. %NL80211_ATTR_IE is used to define IEs
+ * (VendorSpecificInfo, but also including RSN IE and FT IEs) to be added
+@@ -807,7 +817,7 @@
+ * reached.
+ * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ * and the attributes determining channel width) the given interface
+- * (identifed by %NL80211_ATTR_IFINDEX) shall operate on.
++ * (identified by %NL80211_ATTR_IFINDEX) shall operate on.
+ * In case multiple channels are supported by the device, the mechanism
+ * with which it switches channels is implementation-defined.
+ * When a monitor interface is given, it can only switch channel while
+@@ -879,7 +889,7 @@
+ * inform userspace of the new replay counter.
+ *
+ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+- * of PMKSA caching dandidates.
++ * of PMKSA caching candidates.
+ *
+ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+ * In addition, this can be used as an event to request userspace to take
+@@ -915,7 +925,7 @@
+ *
+ * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
+ * by sending a null data frame to it and reporting when the frame is
+- * acknowleged. This is used to allow timing out inactive clients. Uses
++ * acknowledged. This is used to allow timing out inactive clients. Uses
+ * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
+ * direct reply with an %NL80211_ATTR_COOKIE that is later used to match
+ * up the event with the request. The event includes the same data and
+@@ -1126,11 +1136,15 @@
+ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+ * configured PMK for the authenticator address identified by
+ * %NL80211_ATTR_MAC.
+- * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was
+- * completed successfully. Drivers that support 4 way handshake offload
+- * should send this event after indicating 802.1X FT assocation with
+- * %NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT
+- * should be indicated instead.
++ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates port is authorized and
++ * open for regular data traffic. For STA/P2P-client, this event is sent
++ * with AP MAC address and for AP/P2P-GO, the event carries the STA/P2P-
++ * client MAC address.
++ * Drivers that support 4 way handshake offload should send this event for
++ * STA/P2P-client after successful 4-way HS or after 802.1X FT following
++ * NL80211_CMD_CONNECT or NL80211_CMD_ROAM. Drivers using AP/P2P-GO 4-way
++ * handshake offload should send this event on successful completion of
++ * 4-way handshake with the peer (STA/P2P-client).
+ * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
+ * and RX notification. This command is used both as a request to transmit
+ * a control port frame and as a notification that a control port frame
+@@ -1314,6 +1328,11 @@
+ * Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide
+ * information about the removed STA MLD setup links.
+ *
++ * @NL80211_CMD_SET_TID_TO_LINK_MAPPING: Set the TID to Link Mapping for a
++ * non-AP MLD station. The %NL80211_ATTR_MLO_TTLM_DLINK and
++ * %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the
++ * TID to Link mapping for downlink/uplink traffic.
++ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+@@ -1569,6 +1588,8 @@ enum nl80211_commands {
+
+ NL80211_CMD_LINKS_REMOVED,
+
++ NL80211_CMD_SET_TID_TO_LINK_MAPPING,
++
+ /* add new commands above here */
+
+ /* used to define NL80211_CMD_MAX below */
+@@ -1826,7 +1847,7 @@ enum nl80211_commands {
+ * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is
+ * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
+ * flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth
+- * frames are not forwared over the control port.
++ * frames are not forwarded over the control port.
+ *
+ * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
+ * We recommend using nested, driver-specific attributes within this.
+@@ -1963,10 +1984,10 @@ enum nl80211_commands {
+ * bit. Depending on which antennas are selected in the bitmap, 802.11n
+ * drivers can derive which chainmasks to use (if all antennas belonging to
+ * a particular chain are disabled this chain should be disabled) and if
+- * a chain has diversity antennas wether diversity should be used or not.
++ * a chain has diversity antennas whether diversity should be used or not.
+ * HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+ * derived from the available chains after applying the antenna mask.
+- * Non-802.11n drivers can derive wether to use diversity or not.
++ * Non-802.11n drivers can derive whether to use diversity or not.
+ * Drivers may reject configurations or RX/TX mask combinations they cannot
+ * support by returning -EINVAL.
+ *
+@@ -2536,7 +2557,7 @@ enum nl80211_commands {
+ * from successful FILS authentication and is used with
+ * %NL80211_CMD_CONNECT.
+ *
+- * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
++ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertised by a FILS AP
+ * identifying the scope of PMKSAs. This is used with
+ * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+@@ -2690,11 +2711,13 @@ enum nl80211_commands {
+ *
+ * @NL80211_ATTR_FILS_DISCOVERY: Optional parameter to configure FILS
+ * discovery. It is a nested attribute, see
+- * &enum nl80211_fils_discovery_attributes.
++ * &enum nl80211_fils_discovery_attributes. Userspace should pass an empty
++ * nested attribute to disable this feature and delete the templates.
+ *
+ * @NL80211_ATTR_UNSOL_BCAST_PROBE_RESP: Optional parameter to configure
+ * unsolicited broadcast probe response. It is a nested attribute, see
+- * &enum nl80211_unsol_bcast_probe_resp_attributes.
++ * &enum nl80211_unsol_bcast_probe_resp_attributes. Userspace should pass an empty
++ * nested attribute to disable this feature and delete the templates.
+ *
+ * @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION)
+@@ -2815,6 +2838,19 @@ enum nl80211_commands {
+ * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is
+ * disabled.
+ *
++ * @NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA: Include BSS usage data, i.e.
++ * include BSSes that can only be used in restricted scenarios and/or
++ * cannot be used at all.
++ *
++ * @NL80211_ATTR_MLO_TTLM_DLINK: Binary attribute specifying the downlink TID to
++ * link mapping. The length is 8 * sizeof(u16). For each TID the link
++ * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
++ * in Draft P802.11be_D4.0.
++ * @NL80211_ATTR_MLO_TTLM_ULINK: Binary attribute specifying the uplink TID to
++ * link mapping. The length is 8 * sizeof(u16). For each TID the link
++ * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
++ * in Draft P802.11be_D4.0.
++ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3353,6 +3389,11 @@ enum nl80211_attrs {
+
+ NL80211_ATTR_MLO_LINK_DISABLED,
+
++ NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA,
++
++ NL80211_ATTR_MLO_TTLM_DLINK,
++ NL80211_ATTR_MLO_TTLM_ULINK,
++
+ /* add attributes here, update the policy in nl80211.c */
+
+ __NL80211_ATTR_AFTER_LAST,
+@@ -4159,7 +4200,7 @@ enum nl80211_wmm_rule {
+ * (100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+ * (enum nl80211_dfs_state)
+- * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
++ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in milliseconds for how long
+ * this channel is in this DFS state.
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+ * channel as the control channel
+@@ -4213,6 +4254,8 @@ enum nl80211_wmm_rule {
+ * as the primary or any of the secondary channels isn't possible
+ * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
+ * in current regulatory domain.
++ * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that
++ * is allowed on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ * currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+@@ -4251,6 +4294,7 @@ enum nl80211_frequency_attr {
+ NL80211_FREQUENCY_ATTR_16MHZ,
+ NL80211_FREQUENCY_ATTR_NO_320MHZ,
+ NL80211_FREQUENCY_ATTR_NO_EHT,
++ NL80211_FREQUENCY_ATTR_PSD,
+
+ /* keep last */
+ __NL80211_FREQUENCY_ATTR_AFTER_LAST,
+@@ -4351,6 +4395,8 @@ enum nl80211_reg_type {
+ * a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * If not present or 0 default CAC time will be used.
++ * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm).
++ * This could be negative.
+ * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
+ * currently defined
+ * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
+@@ -4368,6 +4414,8 @@ enum nl80211_reg_rule_attr {
+
+ NL80211_ATTR_DFS_CAC_TIME,
+
++ NL80211_ATTR_POWER_RULE_PSD,
++
+ /* keep last */
+ __NL80211_REG_RULE_ATTR_AFTER_LAST,
+ NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
+@@ -4451,6 +4499,7 @@ enum nl80211_sched_scan_match_attr {
+ * @NL80211_RRF_NO_HE: HE operation not allowed
+ * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
+ * @NL80211_RRF_NO_EHT: EHT operation not allowed
++ * @NL80211_RRF_PSD: Ruleset has power spectral density value
+ */
+ enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_OFDM = 1<<0,
+@@ -4471,6 +4520,7 @@ enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_HE = 1<<17,
+ NL80211_RRF_NO_320MHZ = 1<<18,
+ NL80211_RRF_NO_EHT = 1<<19,
++ NL80211_RRF_PSD = 1<<20,
+ };
+
+ #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
+@@ -5007,6 +5057,30 @@ enum nl80211_bss_scan_width {
+ NL80211_BSS_CHAN_WIDTH_2,
+ };
+
++/**
++ * enum nl80211_bss_use_for - bitmap indicating possible BSS use
++ * @NL80211_BSS_USE_FOR_NORMAL: Use this BSS for normal "connection",
++ * including IBSS/MBSS depending on the type.
++ * @NL80211_BSS_USE_FOR_MLD_LINK: This BSS can be used as a link in an
++ * MLO connection. Note that for an MLO connection, all links including
++ * the assoc link must have this flag set, and the assoc link must
++ * additionally have %NL80211_BSS_USE_FOR_NORMAL set.
++ */
++enum nl80211_bss_use_for {
++ NL80211_BSS_USE_FOR_NORMAL = 1 << 0,
++ NL80211_BSS_USE_FOR_MLD_LINK = 1 << 1,
++};
++
++/**
++ * enum nl80211_bss_cannot_use_reasons - reason(s) connection to a
++ * BSS isn't possible
++ * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't
++ * supported by the device, and this BSS entry represents one.
++ */
++enum nl80211_bss_cannot_use_reasons {
++ NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 1 << 0,
++};
++
+ /**
+ * enum nl80211_bss - netlink attributes for a BSS
+ *
+@@ -5038,7 +5112,7 @@ enum nl80211_bss_scan_width {
+ * elements from a Beacon frame (bin); not present if no Beacon frame has
+ * yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+- * (u32, enum nl80211_bss_scan_width)
++ * (u32, enum nl80211_bss_scan_width) - No longer used!
+ * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64)
+ * (not present if no beacon frame has been received yet)
+ * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
+@@ -5059,6 +5133,14 @@ enum nl80211_bss_scan_width {
+ * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
+ * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
++ * @NL80211_BSS_USE_FOR: u32 bitmap attribute indicating what the BSS can be
++ * used for, see &enum nl80211_bss_use_for.
++ * @NL80211_BSS_CANNOT_USE_REASONS: Indicates the reason that this BSS cannot
++ * be used for all or some of the possible uses by the device reporting it,
++ * even though its presence was detected.
++ * This is a u64 attribute containing a bitmap of values from
++ * &enum nl80211_cannot_use_reasons, note that the attribute may be missing
++ * if no reasons are specified.
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+@@ -5086,6 +5168,8 @@ enum nl80211_bss {
+ NL80211_BSS_FREQUENCY_OFFSET,
+ NL80211_BSS_MLO_LINK_ID,
+ NL80211_BSS_MLD_ADDR,
++ NL80211_BSS_USE_FOR,
++ NL80211_BSS_CANNOT_USE_REASONS,
+
+ /* keep last */
+ __NL80211_BSS_AFTER_LAST,
+@@ -5434,7 +5518,7 @@ enum nl80211_tx_rate_setting {
+ * (%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
+ * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
+ * per peer instead.
+- * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
++ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if set indicates
+ * that the new configuration overrides all previous peer
+ * configurations, otherwise previous peer specific configurations
+ * should be left untouched.
+@@ -5817,7 +5901,7 @@ enum nl80211_attr_coalesce_rule {
+
+ /**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+- * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
++ * @NL80211_COALESCE_CONDITION_MATCH: coalesce Rx packets when patterns
+ * in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ * in a rule are not matched.
+@@ -5916,7 +6000,7 @@ enum nl80211_if_combination_attrs {
+ * enum nl80211_plink_state - state of a mesh peer link finite state machine
+ *
+ * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+- * state of non existent mesh peer links
++ * state of non-existent mesh peer links
+ * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+ * this mesh peer
+ * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+@@ -6209,7 +6293,7 @@ enum nl80211_feature_flags {
+ * request to use RRM (see %NL80211_ATTR_USE_RRM) with
+ * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+ * the ASSOC_REQ_USE_RRM flag in the association request even if
+- * NL80211_FEATURE_QUIET is not advertized.
++ * NL80211_FEATURE_QUIET is not advertised.
+ * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+ * sniffer which means that it can be configured to hear packets from
+ * certain groups which can be configured by the
+@@ -6221,13 +6305,15 @@ enum nl80211_feature_flags {
+ * the BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+- * time the last beacon/probe was received. The time is the TSF of the
+- * BSS that the interface that requested the scan is connected to
+- * (if available).
++ * time the last beacon/probe was received. For a non-MLO connection, the
++ * time is the TSF of the BSS that the interface that requested the scan is
++ * connected to (if available). For an MLO connection, the time is the TSF
++ * of the BSS corresponding with link ID specified in the scan request (if
++ * specified).
+ * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+ * channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+- * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
++ * configuration (AP/mesh), supporting a legacy (non-HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ * configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+@@ -6400,6 +6486,12 @@ enum nl80211_feature_flags {
+ * in authentication and deauthentication frames sent to unassociated peer
+ * using @NL80211_CMD_FRAME.
+ *
++ * @NL80211_EXT_FEATURE_OWE_OFFLOAD: Driver/Device wants to do OWE DH IE
++ * handling in station mode.
++ *
++ * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE
++ * handling in AP mode.
++ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+@@ -6471,6 +6563,8 @@ enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_PUNCT,
+ NL80211_EXT_FEATURE_SECURE_NAN,
+ NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
++ NL80211_EXT_FEATURE_OWE_OFFLOAD,
++ NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+@@ -6555,7 +6649,7 @@ enum nl80211_timeout_reason {
+ * request parameters IE in the probe request
+ * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
+- * rate of at least 5.5M. In case non OCE AP is discovered in the channel,
++ * rate of at least 5.5M. In case non-OCE AP is discovered in the channel,
+ * only the first probe req in the channel will be sent in high rate.
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
+ * tx deferral (dot11FILSProbeDelay shall be set to 15ms)
+@@ -6591,7 +6685,7 @@ enum nl80211_timeout_reason {
+ * received on the 2.4/5 GHz channels to actively scan only the 6GHz
+ * channels on which APs are expected to be found. Note that when not set,
+ * the scan logic would scan all 6GHz channels, but since transmission of
+- * probe requests on non PSC channels is limited, it is highly likely that
++ * probe requests on non-PSC channels is limited, it is highly likely that
+ * these channels would passively be scanned. Also note that when the flag
+ * is set, in addition to the colocated APs, PSC channels would also be
+ * scanned if the user space has asked for it.
+@@ -6923,7 +7017,7 @@ enum nl80211_nan_func_term_reason {
+ * The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ * is follow up. This is a u8.
+- * The requestor instance ID for the follow up Service Discovery Frame.
++ * The requester instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ * follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+@@ -7313,7 +7407,7 @@ enum nl80211_peer_measurement_attrs {
+ * @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if
+ * trigger based ranging measurement is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating
+- * if non trigger based ranging measurement is supported
++ * if non-trigger-based ranging measurement is supported
+ *
+ * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
+@@ -7367,7 +7461,7 @@ enum nl80211_peer_measurement_ftm_capa {
+ * if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
+ * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
+ * ranging will be used.
+- * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based
++ * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non-trigger-based
+ * ranging measurement (flag)
+ * This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are
+ * mutually exclusive.
+@@ -7445,7 +7539,7 @@ enum nl80211_peer_measurement_ftm_failure_reasons {
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
+ * transmitted (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
+- * that were acknowleged (u32, optional)
++ * that were acknowledged (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
+ * busy peer (u32, seconds)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
+@@ -7606,7 +7700,7 @@ enum nl80211_iftype_akm_attributes {
+ * @NL80211_FILS_DISCOVERY_ATTR_INT_MIN: Minimum packet interval (u32, TU).
+ * Allowed range: 0..10000 (TU = Time Unit)
+ * @NL80211_FILS_DISCOVERY_ATTR_INT_MAX: Maximum packet interval (u32, TU).
+- * Allowed range: 0..10000 (TU = Time Unit)
++ * Allowed range: 0..10000 (TU = Time Unit). If set to 0, the feature is disabled.
+ * @NL80211_FILS_DISCOVERY_ATTR_TMPL: Template data for FILS discovery action
+ * frame including the headers.
+ *
+@@ -7639,7 +7733,8 @@ enum nl80211_fils_discovery_attributes {
+ *
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT: Maximum packet interval (u32, TU).
+ * Allowed range: 0..20 (TU = Time Unit). IEEE P802.11ax/D6.0
+- * 26.17.2.3.2 (AP behavior for fast passive scanning).
++ * 26.17.2.3.2 (AP behavior for fast passive scanning). If set to 0, the feature is
++ * disabled.
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL: Unsolicited broadcast probe response
+ * frame template (binary).
+ *
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0025-bump-version-to-6.7.patch b/recipes-wifi/iw/patches-mlo/0025-bump-version-to-6.7.patch
new file mode 100644
index 0000000..89cdbb7
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0025-bump-version-to-6.7.patch
@@ -0,0 +1,29 @@
+From 52ae9650b3a09306608cc266bd2ca5b4d789d453 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Thu, 21 Dec 2023 12:51:14 +0100
+Subject: [PATCH 25/28] bump version to 6.7
+
+This already has some "will be 6.8" content, but
+that's not entirely closed yet.
+
+Change-Id: I16beefdf8b5e37fc72948d8a874772dc8c97e7b2
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ version.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/version.sh b/version.sh
+index 9895056..3091e1e 100755
+--- a/version.sh
++++ b/version.sh
+@@ -1,6 +1,6 @@
+ #!/bin/sh
+
+-VERSION="5.19"
++VERSION="6.7"
+ OUT="$1"
+
+ # get the absolute path for the OUT file
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0026-update-nl80211.h.patch b/recipes-wifi/iw/patches-mlo/0026-update-nl80211.h.patch
new file mode 100644
index 0000000..6c12bd8
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0026-update-nl80211.h.patch
@@ -0,0 +1,111 @@
+From c49eb9ee6847953b24ba265add49fa4ec587c7a8 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Thu, 21 Dec 2023 20:42:17 +0100
+Subject: [PATCH 26/28] update nl80211.h
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ info.c | 1 +
+ nl80211.h | 29 +++++++++++++++++++++++++++++
+ 2 files changed, 30 insertions(+)
+
+diff --git a/info.c b/info.c
+index 317e7a3..40dcc81 100644
+--- a/info.c
++++ b/info.c
+@@ -172,6 +172,7 @@ static void ext_feat_print(enum nl80211_ext_feature_index idx)
+ ext_feat_case(AUTH_AND_DEAUTH_RANDOM_TA, "random auth/deauth transmitter address");
+ ext_feat_case(OWE_OFFLOAD, "OWE DH element handling offload (client)");
+ ext_feat_case(OWE_OFFLOAD_AP, "OWE DH element handling offload (AP)");
++ ext_feat_case(DFS_CONCURRENT, "DFS channel use under concurrent DFS master");
+ }
+ }
+
+diff --git a/nl80211.h b/nl80211.h
+index a682b54..1ccdcae 100644
+--- a/nl80211.h
++++ b/nl80211.h
+@@ -4256,6 +4256,14 @@ enum nl80211_wmm_rule {
+ * in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that
+ * is allowed on this channel in current regulatory domain.
++ * @NL80211_FREQUENCY_ATTR_DFS_CONCURRENT: Operation on this channel is
++ * allowed for peer-to-peer or adhoc communication under the control
++ * of a DFS master which operates on the same channel (FCC-594280 D01
++ * Section B.3). Should be used together with %NL80211_RRF_DFS only.
++ * @NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT: Client connection to VLP AP
++ * not allowed using this channel
++ * @NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT: Client connection to AFC AP
++ * not allowed using this channel
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ * currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+@@ -4295,6 +4303,9 @@ enum nl80211_frequency_attr {
+ NL80211_FREQUENCY_ATTR_NO_320MHZ,
+ NL80211_FREQUENCY_ATTR_NO_EHT,
+ NL80211_FREQUENCY_ATTR_PSD,
++ NL80211_FREQUENCY_ATTR_DFS_CONCURRENT,
++ NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT,
++ NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT,
+
+ /* keep last */
+ __NL80211_FREQUENCY_ATTR_AFTER_LAST,
+@@ -4500,6 +4511,12 @@ enum nl80211_sched_scan_match_attr {
+ * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
+ * @NL80211_RRF_NO_EHT: EHT operation not allowed
+ * @NL80211_RRF_PSD: Ruleset has power spectral density value
++ * @NL80211_RRF_DFS_CONCURRENT: Operation on this channel is allowed for
++ peer-to-peer or adhoc communication under the control of a DFS master
++ which operates on the same channel (FCC-594280 D01 Section B.3).
++ Should be used together with %NL80211_RRF_DFS only.
++ * @NL80211_RRF_NO_UHB_VLP_CLIENT: Client connection to VLP AP not allowed
++ * @NL80211_RRF_NO_UHB_AFC_CLIENT: Client connection to AFC AP not allowed
+ */
+ enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_OFDM = 1<<0,
+@@ -4521,6 +4538,9 @@ enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_320MHZ = 1<<18,
+ NL80211_RRF_NO_EHT = 1<<19,
+ NL80211_RRF_PSD = 1<<20,
++ NL80211_RRF_DFS_CONCURRENT = 1<<21,
++ NL80211_RRF_NO_UHB_VLP_CLIENT = 1<<22,
++ NL80211_RRF_NO_UHB_AFC_CLIENT = 1<<23,
+ };
+
+ #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
+@@ -5076,9 +5096,12 @@ enum nl80211_bss_use_for {
+ * BSS isn't possible
+ * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't
+ * supported by the device, and this BSS entry represents one.
++ * @NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH: STA is not supporting
++ * the AP power type (SP, VLP, AP) that the AP uses.
+ */
+ enum nl80211_bss_cannot_use_reasons {
+ NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 1 << 0,
++ NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH = 1 << 1,
+ };
+
+ /**
+@@ -6492,6 +6515,11 @@ enum nl80211_feature_flags {
+ * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE
+ * handling in AP mode.
+ *
++ * @NL80211_EXT_FEATURE_DFS_CONCURRENT: The device supports peer-to-peer or
++ * ad hoc operation on DFS channels under the control of a concurrent
++ * DFS master on the same channel as described in FCC-594280 D01
++ * (Section B.3). This, for example, allows P2P GO and P2P clients to
++ * operate on DFS channels as long as there's a concurrent BSS connection.
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+@@ -6565,6 +6593,7 @@ enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
++ NL80211_EXT_FEATURE_DFS_CONCURRENT,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0027-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch b/recipes-wifi/iw/patches-mlo/0027-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch
new file mode 100644
index 0000000..743e415
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0027-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch
@@ -0,0 +1,73 @@
+From 3bcf45100b9512270c674099a21162c6f0c901dc Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 21 Nov 2021 00:02:57 +0100
+Subject: [PATCH 27/28] Revert "iw: allow specifying CFLAGS/LIBS externally"
+
+This reverts commit 1325244b77d56fd7a16d1e35fdae0efc151920b1.
+
+The OpenWrt build system provides the CFLAGS and LIBS names from the
+package Makefile to overwrite them for libnl-tiny. This is not possible
+after this upstream change which we revert here any more
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ Makefile | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 17be33f..2482b93 100644
+--- a/Makefile
++++ b/Makefile
+@@ -46,30 +46,30 @@ NLLIBNAME = libnl-1
+ endif
+
+ ifeq ($(NL2FOUND),Y)
+-override CFLAGS += -DCONFIG_LIBNL20
+-override LIBS += -lnl-genl
++CFLAGS += -DCONFIG_LIBNL20
++LIBS += -lnl-genl
+ NLLIBNAME = libnl-2.0
+ endif
+
+ ifeq ($(NL3xFOUND),Y)
+ # libnl 3.2 might be found as 3.2 and 3.0
+ NL3FOUND = N
+-override CFLAGS += -DCONFIG_LIBNL30
+-override LIBS += -lnl-genl-3
++CFLAGS += -DCONFIG_LIBNL30
++LIBS += -lnl-genl-3
+ NLLIBNAME = libnl-3.0
+ endif
+
+ ifeq ($(NL3FOUND),Y)
+-override CFLAGS += -DCONFIG_LIBNL30
+-override LIBS += -lnl-genl
++CFLAGS += -DCONFIG_LIBNL30
++LIBS += -lnl-genl
+ NLLIBNAME = libnl-3.0
+ endif
+
+ # nl-3.1 has a broken libnl-gnl-3.1.pc file
+ # as show by pkg-config --debug --libs --cflags --exact-version=3.1 libnl-genl-3.1;echo $?
+ ifeq ($(NL31FOUND),Y)
+-override CFLAGS += -DCONFIG_LIBNL30
+-override LIBS += -lnl-genl
++CFLAGS += -DCONFIG_LIBNL30
++LIBS += -lnl-genl
+ NLLIBNAME = libnl-3.1
+ endif
+
+@@ -77,8 +77,8 @@ ifeq ($(NLLIBNAME),)
+ $(error Cannot find development files for any supported version of libnl)
+ endif
+
+-override LIBS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME))
+-override CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME))
++LIBS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME))
++CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME))
+ endif # NO_PKG_CONFIG
+
+ ifeq ($(V),1)
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/0028-survey-bss-rx-time.patch b/recipes-wifi/iw/patches-mlo/0028-survey-bss-rx-time.patch
new file mode 100644
index 0000000..3869b00
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0028-survey-bss-rx-time.patch
@@ -0,0 +1,26 @@
+From a059a337750198bf2481073194cc55022bb71c76 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 25 Dec 2023 11:24:01 +0800
+Subject: [PATCH 28/28] survey bss rx time
+
+---
+ survey.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/survey.c b/survey.c
+index 7f5385e..53cec9d 100644
+--- a/survey.c
++++ b/survey.c
+@@ -60,6 +60,9 @@ static int print_survey_handler(struct nl_msg *msg, void *arg)
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX])
+ printf("\tchannel receive time:\t\t%llu ms\n",
+ (unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]));
++ if (sinfo[NL80211_SURVEY_INFO_TIME_BSS_RX])
++ printf("\tchannel BSS receive time:\t%llu ms\n",
++ (unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_BSS_RX]));
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX])
+ printf("\tchannel transmit time:\t\t%llu ms\n",
+ (unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]));
+--
+2.39.2
+
diff --git a/recipes-wifi/iw/patches-mlo/patches.inc b/recipes-wifi/iw/patches-mlo/patches.inc
new file mode 100644
index 0000000..09d5579
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/patches.inc
@@ -0,0 +1,31 @@
+#patch patches (come from openwrt/lede/target/linux/mediatek)
+SRC_URI_append = " \
+ file://0001-iw-info-print-PMSR-capabilities.patch \
+ file://0002-iw-add-cac-background-command.patch \
+ file://0003-iw-info-fix-bug-reading-preambles-and-bandwidths.patch \
+ file://0004-iw-add-support-for-retrieving-keys.patch \
+ file://0005-iw-event-fix-printf-format-error.patch \
+ file://0006-update-nl80211.h.patch \
+ file://0007-util-add-support-for-320Mhz-bandwidth.patch \
+ file://0008-util-add-support-for-320MHz-bandwidth-without-cf1.patch \
+ file://0009-iw-scan-set-NL80211_SCAN_FLAG_COLOCATED_6GHZ-in-case.patch \
+ file://0010-link-fix-some-formatting.patch \
+ file://0011-link-update-for-MLO.patch \
+ file://0012-interface-print-links.patch \
+ file://0013-util-don-t-print-EHT-info-if-not-present.patch \
+ file://0014-iw-S1G-add-frequency-set-in-kHz-and-offset-options.patch \
+ file://0015-iw-S1G-add-parsing-for-802.11ah-scan-IE-s.patch \
+ file://0016-iw-S1G-add-list-command-support-for-802.11ah.patch \
+ file://0017-iw-Fix-EHT-rates-printing.patch \
+ file://0018-iw-add-more-extended-capa-bits.patch \
+ file://0019-iw-fix-attribute-size-mismatch.patch \
+ file://0020-iw-connect-Fix-segfault-during-open-authentication.patch \
+ file://0021-update-nl80211.h.patch \
+ file://0022-iw-S1G-add-802.11ah-support-for-link-command-display.patch \
+ file://0023-iw-allow-extra-cflags.patch \
+ file://0024-update-nl80211.h.patch \
+ file://0025-bump-version-to-6.7.patch \
+ file://0026-update-nl80211.h.patch \
+ file://0027-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch \
+ file://0028-survey-bss-rx-time.patch \
+ "
diff --git a/recipes-wifi/libubox/libubox_git.bbappend b/recipes-wifi/libubox/libubox_git.bbappend
index ed6a08c..2422b30 100644
--- a/recipes-wifi/libubox/libubox_git.bbappend
+++ b/recipes-wifi/libubox/libubox_git.bbappend
@@ -1,6 +1,6 @@
SRC_URI_remove = "file://0001-blobmsg-fix-array-out-of-bounds-GCC-10-warning.patch"
wifi6_ver = "b14c4688612c05c78ce984d7bde633bce8703b1e"
-wifi7_ver = "c1be505732e6d254464973bdeacb955214c76c46"
+wifi7_ver = "eb9bcb64185ac155c02cc1a604692c4b00368324"
SRCREV = "${@bb.utils.contains('DISTRO_FEATURES', 'wifi_eht', '${wifi7_ver}', '${wifi6_ver}', d)}"
diff --git a/recipes-wifi/linux-mac80211/files/backports-2024-04-03.tar.xz b/recipes-wifi/linux-mac80211/files/backports-2024-04-03.tar.xz
new file mode 100644
index 0000000..a6715ac
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/backports-2024-04-03.tar.xz
Binary files differ
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/000-fix_kconfig.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/000-fix_kconfig.patch
deleted file mode 100644
index 3987aae..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/000-fix_kconfig.patch
+++ /dev/null
@@ -1,14 +0,0 @@
---- a/kconf/Makefile
-+++ b/kconf/Makefile
-@@ -1,9 +1,9 @@
--CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
-+CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -DKBUILD_NO_NLS
-
- LXDIALOG := lxdialog/checklist.o lxdialog/inputbox.o lxdialog/menubox.o lxdialog/textbox.o lxdialog/util.o lxdialog/yesno.o
-
- conf: conf.o zconf.tab.o
--mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) -DLOCALE
-+mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags)
- mconf_LDFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC))
- mconf: CFLAGS += $(mconf_CFLAGS)
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/001-fix_build.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/001-fix_build.patch
deleted file mode 100644
index 8f63d36..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/001-fix_build.patch
+++ /dev/null
@@ -1,169 +0,0 @@
---- a/Makefile
-+++ b/Makefile
-@@ -5,7 +5,7 @@
- ifeq ($(KERNELRELEASE),)
-
- MAKEFLAGS += --no-print-directory
--SHELL := /bin/bash
-+SHELL := /usr/bin/env bash
- BACKPORT_DIR := $(shell pwd)
-
- KMODDIR ?= updates
-@@ -19,6 +19,7 @@ KLIB_BUILD ?= $(KLIB)/build/
- KERNEL_CONFIG := $(KLIB_BUILD)/.config
- KERNEL_MAKEFILE := $(KLIB_BUILD)/Makefile
- CONFIG_MD5 := $(shell md5sum $(KERNEL_CONFIG) 2>/dev/null | sed 's/\s.*//')
-+STAMP_KERNEL_CONFIG := .kernel_config_md5_$(CONFIG_MD5)
-
- export KLIB KLIB_BUILD BACKPORT_DIR KMODDIR KMODPATH_ARG
-
-@@ -36,7 +37,8 @@ mrproper:
- @rm -f .kernel_config_md5 Kconfig.versions Kconfig.kernel
- @rm -f backport-include/backport/autoconf.h
-
--.DEFAULT:
-+.SILENT: $(STAMP_KERNEL_CONFIG)
-+$(STAMP_KERNEL_CONFIG):
- @set -e ; test -f local-symbols || ( \
- echo "/--------------" ;\
- echo "| You shouldn't run make in the backports tree, but only in" ;\
-@@ -60,58 +62,62 @@ mrproper:
- echo "| (that isn't currently running.)" ;\
- echo "\\--" ;\
- false)
-- @set -e ; if [ "$$(cat .kernel_config_md5 2>/dev/null)" != "$(CONFIG_MD5)" ] ;\
-- then \
-- echo -n "Generating local configuration database from kernel ..." ;\
-- grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | ( \
-- while read l ; do \
-- if [ "$${l:0:7}" != "CONFIG_" ] ; then \
-- continue ;\
-- fi ;\
-- l=$${l:7} ;\
-- n=$${l%%=*} ;\
-- v=$${l#*=} ;\
-- if [ "$$v" = "m" ] ; then \
-- echo config $$n ;\
-- echo ' tristate' ;\
-- elif [ "$$v" = "y" ] ; then \
-- echo config $$n ;\
-- echo ' bool' ;\
-- else \
-- continue ;\
-- fi ;\
-- echo " default $$v" ;\
-- echo "" ;\
-- done \
-- ) > Kconfig.kernel ;\
-- kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR) \
-- kernelversion | sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
-- test "$$kver" != "" || echo "Kernel version parse failed!" ;\
-- test "$$kver" != "" ;\
-- kvers="$$(seq 14 39 | sed 's/^/2.6./')" ;\
-- kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\
-- kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')" ;\
-- kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')" ;\
-- print=0 ;\
-- for v in $$kvers ; do \
-- if [ "$$print" = "1" ] ; then \
-- echo config KERNEL_$$(echo $$v | tr . _) ;\
-- echo " def_bool y" ;\
-- fi ;\
-- if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\
-- done > Kconfig.versions ;\
-- # RHEL as well, sadly we need to grep for it ;\
-- RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | \
-- sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
-- RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | \
-- sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
-- for v in $$(seq 0 $$RHEL_MINOR) ; do \
-- echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v ;\
-- echo " def_bool y" ;\
-- done >> Kconfig.versions ;\
-- echo " done." ;\
-- fi ;\
-- echo "$(CONFIG_MD5)" > .kernel_config_md5
-+ @rm -f .kernel_config_md5_*
-+ @touch $@
-+
-+Kconfig.kernel: $(STAMP_KERNEL_CONFIG) local-symbols
-+ @printf "Generating local configuration database from kernel ..."
-+ @grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | ( \
-+ while read l ; do \
-+ if [ "$${l:0:7}" != "CONFIG_" ] ; then \
-+ continue ;\
-+ fi ;\
-+ l=$${l:7} ;\
-+ n=$${l%%=*} ;\
-+ v=$${l#*=} ;\
-+ if [ "$$v" = "m" ] ; then \
-+ echo config $$n ;\
-+ echo ' tristate' ;\
-+ elif [ "$$v" = "y" ] ; then \
-+ echo config $$n ;\
-+ echo ' bool' ;\
-+ else \
-+ continue ;\
-+ fi ;\
-+ echo " default $$v" ;\
-+ echo "" ;\
-+ done \
-+ ) > $@
-+ @echo " done."
-+
-+Kconfig.versions: Kconfig.kernel
-+ @kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR) \
-+ kernelversion | sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
-+ test "$$kver" != "" || echo "Kernel version parse failed!" ;\
-+ test "$$kver" != "" ;\
-+ kvers="$$(seq 14 39 | sed 's/^/2.6./')" ;\
-+ kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\
-+ kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')" ;\
-+ kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')" ;\
-+ print=0 ;\
-+ for v in $$kvers ; do \
-+ if [ "$$print" = "1" ] ; then \
-+ echo config KERNEL_$$(echo $$v | tr . _) ;\
-+ echo " def_bool y" ;\
-+ fi ;\
-+ if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\
-+ done > $@
-+ @RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | \
-+ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
-+ RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | \
-+ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
-+ for v in $$(seq 0 $$RHEL_MINOR) ; do \
-+ echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v ;\
-+ echo " def_bool y" ;\
-+ done >> $@
-+
-+.DEFAULT:
-+ @$(MAKE) Kconfig.versions
- @$(MAKE) -f Makefile.real "$@"
-
- .PHONY: defconfig-help
---- a/Makefile.real
-+++ b/Makefile.real
-@@ -59,7 +59,7 @@ defconfig-%::
-
- backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
- @$(MAKE) oldconfig
-- @echo -n "Building backport-include/backport/autoconf.h ..."
-+ @printf "Building backport-include/backport/autoconf.h ..."
- @grep -f local-symbols .config | ( \
- echo "#ifndef COMPAT_AUTOCONF_INCLUDED" ;\
- echo "#define COMPAT_AUTOCONF_INCLUDED" ;\
-@@ -80,7 +80,12 @@ backport-include/backport/autoconf.h: .c
- esac ;\
- done ;\
- echo "#endif /* COMPAT_AUTOCONF_INCLUDED */" ;\
-- ) > backport-include/backport/autoconf.h
-+ ) > $@.new
-+ @if cmp -s $@ $@.new; then \
-+ rm -f $@.new; \
-+ else \
-+ mv $@.new $@; \
-+ fi
- @echo " done."
-
- .PHONY: modules
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/002-change_allconfig.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/002-change_allconfig.patch
deleted file mode 100644
index 368725d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/002-change_allconfig.patch
+++ /dev/null
@@ -1,64 +0,0 @@
---- a/kconf/conf.c
-+++ b/kconf/conf.c
-@@ -598,40 +598,12 @@ int main(int ac, char **av)
- case oldconfig:
- case listnewconfig:
- case olddefconfig:
-- conf_read(NULL);
-- break;
- case allnoconfig:
- case allyesconfig:
- case allmodconfig:
- case alldefconfig:
- case randconfig:
-- name = getenv("KCONFIG_ALLCONFIG");
-- if (!name)
-- break;
-- if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
-- if (conf_read_simple(name, S_DEF_USER)) {
-- fprintf(stderr,
-- _("*** Can't read seed configuration \"%s\"!\n"),
-- name);
-- exit(1);
-- }
-- break;
-- }
-- switch (input_mode) {
-- case allnoconfig: name = "allno.config"; break;
-- case allyesconfig: name = "allyes.config"; break;
-- case allmodconfig: name = "allmod.config"; break;
-- case alldefconfig: name = "alldef.config"; break;
-- case randconfig: name = "allrandom.config"; break;
-- default: break;
-- }
-- if (conf_read_simple(name, S_DEF_USER) &&
-- conf_read_simple("all.config", S_DEF_USER)) {
-- fprintf(stderr,
-- _("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"),
-- name);
-- exit(1);
-- }
-+ conf_read(NULL);
- break;
- default:
- break;
---- a/kconf/confdata.c
-+++ b/kconf/confdata.c
-@@ -1170,6 +1170,8 @@ bool conf_set_all_new_symbols(enum conf_
- }
- bool has_changed = false;
-
-+ sym_clear_all_valid();
-+
- for_all_symbols(i, sym) {
- if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
- continue;
-@@ -1213,8 +1215,6 @@ bool conf_set_all_new_symbols(enum conf_
-
- }
-
-- sym_clear_all_valid();
--
- /*
- * We have different type of choice blocks.
- * If curr.tri equals to mod then we can select several
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/003-remove_bogus_modparams.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/003-remove_bogus_modparams.patch
deleted file mode 100644
index aa26c8c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/003-remove_bogus_modparams.patch
+++ /dev/null
@@ -1,34 +0,0 @@
---- a/compat/main.c
-+++ b/compat/main.c
-@@ -19,31 +19,6 @@ MODULE_LICENSE("GPL");
- #error "You need a CPTCFG_VERSION"
- #endif
-
--static char *backported_kernel_name = CPTCFG_KERNEL_NAME;
--
--module_param(backported_kernel_name, charp, 0400);
--MODULE_PARM_DESC(backported_kernel_name,
-- "The kernel tree name that was used for this backport (" CPTCFG_KERNEL_NAME ")");
--
--#ifdef BACKPORTS_GIT_TRACKED
--static char *backports_tracker_id = BACKPORTS_GIT_TRACKED;
--module_param(backports_tracker_id, charp, 0400);
--MODULE_PARM_DESC(backports_tracker_id,
-- "The version of the tree containing this backport (" BACKPORTS_GIT_TRACKED ")");
--#else
--static char *backported_kernel_version = CPTCFG_KERNEL_VERSION;
--static char *backports_version = CPTCFG_VERSION;
--
--module_param(backported_kernel_version, charp, 0400);
--MODULE_PARM_DESC(backported_kernel_version,
-- "The kernel version that was used for this backport (" CPTCFG_KERNEL_VERSION ")");
--
--module_param(backports_version, charp, 0400);
--MODULE_PARM_DESC(backports_version,
-- "The git version of the backports tree used to generate this backport (" CPTCFG_VERSION ")");
--
--#endif
--
- void backport_dependency_symbol(void)
- {
- }
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/004-fix-kconf-compiling.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/004-fix-kconf-compiling.patch
deleted file mode 100644
index 8bae836..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/004-fix-kconf-compiling.patch
+++ /dev/null
@@ -1,47 +0,0 @@
---- a/Makefile.real
-+++ b/Makefile.real
-@@ -6,6 +6,18 @@ else
- export BACKPORTS_GIT_TRACKER_DEF=
- endif
-
-+ifneq ($(LLVM),)
-+ifneq ($(filter %/,$(LLVM)),)
-+LLVM_PREFIX := $(LLVM)
-+else ifneq ($(filter -%,$(LLVM)),)
-+LLVM_SUFFIX := $(LLVM)
-+endif
-+
-+HOSTCC = $(LLVM_PREFIX)clang$(LLVM_SUFFIX)
-+else
-+HOSTCC = gcc
-+endif
-+
- # disable built-in rules for this file
- .SUFFIXES:
-
-@@ -24,21 +36,21 @@ listnewconfig oldaskconfig oldconfig \
- silentoldconfig olddefconfig oldnoconfig \
- allnoconfig allyesconfig allmodconfig \
- alldefconfig randconfig:
-- @$(MAKE) -C kconf conf
-+ @$(MAKE) -C kconf CC=$(HOSTCC) conf
- @./kconf/conf --$@ Kconfig
-
- .PHONY: usedefconfig
- usedefconfig:
-- @$(MAKE) -C kconf conf
-+ @$(MAKE) -C kconf CC=$(HOSTCC) conf
- @./kconf/conf --defconfig=defconfig Kconfig
-
- .PHONY: savedefconfig
- savedefconfig:
-- @$(MAKE) -C kconf conf
-+ @$(MAKE) -C kconf CC=$(HOSTCC) conf
- @./kconf/conf --savedefconfig=defconfig Kconfig
-
- defconfig-%::
-- @$(MAKE) -C kconf conf
-+ @$(MAKE) -C kconf CC=$(HOSTCC) conf
- @./kconf/conf --defconfig=defconfigs/$(@:defconfig-%=%) Kconfig
-
- .config:
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/012-kernel_build_check.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/012-kernel_build_check.patch
deleted file mode 100644
index d225ba1..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/012-kernel_build_check.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/Makefile
-+++ b/Makefile
-@@ -2,7 +2,7 @@
- # Makefile for the output source package
- #
-
--ifeq ($(KERNELRELEASE),)
-+ifeq ($(KERNELVERSION),)
-
- MAKEFLAGS += --no-print-directory
- SHELL := /usr/bin/env bash
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/060-no_local_ssb_bcma.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/060-no_local_ssb_bcma.patch
deleted file mode 100644
index 2a0f9ed..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/060-no_local_ssb_bcma.patch
+++ /dev/null
@@ -1,314 +0,0 @@
---- a/local-symbols
-+++ b/local-symbols
-@@ -493,43 +493,6 @@ USB_VL600=
- USB_NET_CH9200=
- USB_NET_AQC111=
- USB_RTL8153_ECM=
--SSB_POSSIBLE=
--SSB=
--SSB_SPROM=
--SSB_BLOCKIO=
--SSB_PCIHOST_POSSIBLE=
--SSB_PCIHOST=
--SSB_B43_PCI_BRIDGE=
--SSB_PCMCIAHOST_POSSIBLE=
--SSB_PCMCIAHOST=
--SSB_SDIOHOST_POSSIBLE=
--SSB_SDIOHOST=
--SSB_HOST_SOC=
--SSB_SERIAL=
--SSB_DRIVER_PCICORE_POSSIBLE=
--SSB_DRIVER_PCICORE=
--SSB_PCICORE_HOSTMODE=
--SSB_DRIVER_MIPS=
--SSB_SFLASH=
--SSB_EMBEDDED=
--SSB_DRIVER_EXTIF=
--SSB_DRIVER_GIGE=
--SSB_DRIVER_GPIO=
--BCMA_POSSIBLE=
--BCMA=
--BCMA_BLOCKIO=
--BCMA_HOST_PCI_POSSIBLE=
--BCMA_HOST_PCI=
--BCMA_HOST_SOC=
--BCMA_DRIVER_PCI=
--BCMA_DRIVER_PCI_HOSTMODE=
--BCMA_DRIVER_MIPS=
--BCMA_PFLASH=
--BCMA_SFLASH=
--BCMA_NFLASH=
--BCMA_DRIVER_GMAC_CMN=
--BCMA_DRIVER_GPIO=
--BCMA_DEBUG=
- USB_ACM=
- USB_PRINTER=
- USB_WDM=
---- a/drivers/net/wireless/broadcom/b43/Kconfig
-+++ b/drivers/net/wireless/broadcom/b43/Kconfig
-@@ -63,21 +63,21 @@ endchoice
- config B43_PCI_AUTOSELECT
- bool
- depends on B43 && SSB_PCIHOST_POSSIBLE
-- select SSB_PCIHOST
-- select SSB_B43_PCI_BRIDGE
-+ depends on SSB_PCIHOST
-+ depends on SSB_B43_PCI_BRIDGE
- default y
-
- # Auto-select SSB PCICORE driver, if possible
- config B43_PCICORE_AUTOSELECT
- bool
- depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE
-- select SSB_DRIVER_PCICORE
-+ depends on SSB_DRIVER_PCICORE
- default y
-
- config B43_SDIO
- bool "Broadcom 43xx SDIO device support"
- depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE
-- select SSB_SDIOHOST
-+ depends on SSB_SDIOHOST
- help
- Broadcom 43xx device support for Soft-MAC SDIO devices.
-
-@@ -96,13 +96,13 @@ config B43_SDIO
- config B43_BCMA_PIO
- bool
- depends on B43 && B43_BCMA
-- select BCMA_BLOCKIO
-+ depends on BCMA_BLOCKIO
- default y
-
- config B43_PIO
- bool
- depends on B43 && B43_SSB
-- select SSB_BLOCKIO
-+ depends on SSB_BLOCKIO
- default y
-
- config B43_PHY_G
---- a/drivers/net/wireless/broadcom/b43/main.c
-+++ b/drivers/net/wireless/broadcom/b43/main.c
-@@ -2853,7 +2853,7 @@ static struct ssb_device *b43_ssb_gpio_d
- {
- struct ssb_bus *bus = dev->dev->sdev->bus;
-
--#ifdef CPTCFG_SSB_DRIVER_PCICORE
-+#ifdef CONFIG_SSB_DRIVER_PCICORE
- return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev);
- #else
- return bus->chipco.dev;
-@@ -4871,7 +4871,7 @@ static int b43_wireless_core_init(struct
- }
- if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
- hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
--#if defined(CPTCFG_B43_SSB) && defined(CPTCFG_SSB_DRIVER_PCICORE)
-+#if defined(CPTCFG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE)
- if (dev->dev->bus_type == B43_BUS_SSB &&
- dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
- dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
---- a/drivers/net/wireless/broadcom/b43legacy/Kconfig
-+++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig
-@@ -3,7 +3,7 @@ config B43LEGACY
- tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)"
- depends on m
- depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
-- select SSB
-+ depends on SSB
- depends on FW_LOADER
- help
- b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and
-@@ -25,15 +25,15 @@ config B43LEGACY
- config B43LEGACY_PCI_AUTOSELECT
- bool
- depends on B43LEGACY && SSB_PCIHOST_POSSIBLE
-- select SSB_PCIHOST
-- select SSB_B43_PCI_BRIDGE
-+ depends on SSB_PCIHOST
-+ depends on SSB_B43_PCI_BRIDGE
- default y
-
- # Auto-select SSB PCICORE driver, if possible
- config B43LEGACY_PCICORE_AUTOSELECT
- bool
- depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE
-- select SSB_DRIVER_PCICORE
-+ depends on SSB_DRIVER_PCICORE
- default y
-
- # LED support
---- a/drivers/net/wireless/broadcom/b43legacy/main.c
-+++ b/drivers/net/wireless/broadcom/b43legacy/main.c
-@@ -1907,7 +1907,7 @@ static int b43legacy_gpio_init(struct b4
- if (dev->dev->id.revision >= 2)
- mask |= 0x0010; /* FIXME: This is redundant. */
-
--#ifdef CPTCFG_SSB_DRIVER_PCICORE
-+#ifdef CONFIG_SSB_DRIVER_PCICORE
- pcidev = bus->pcicore.dev;
- #endif
- gpiodev = bus->chipco.dev ? : pcidev;
-@@ -1926,7 +1926,7 @@ static void b43legacy_gpio_cleanup(struc
- struct ssb_bus *bus = dev->dev->bus;
- struct ssb_device *gpiodev, *pcidev = NULL;
-
--#ifdef CPTCFG_SSB_DRIVER_PCICORE
-+#ifdef CONFIG_SSB_DRIVER_PCICORE
- pcidev = bus->pcicore.dev;
- #endif
- gpiodev = bus->chipco.dev ? : pcidev;
---- a/drivers/net/wireless/broadcom/brcm80211/Kconfig
-+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
-@@ -8,7 +8,7 @@ config BRCMSMAC
- depends on m
- depends on MAC80211
- depends on BCMA_POSSIBLE
-- select BCMA
-+ depends on BCMA
- select BRCMUTIL
- depends on FW_LOADER
- depends on CORDIC
---- a/Kconfig.local
-+++ b/Kconfig.local
-@@ -1483,117 +1483,6 @@ config BACKPORTED_USB_NET_AQC111
- config BACKPORTED_USB_RTL8153_ECM
- tristate
- default USB_RTL8153_ECM
--config BACKPORTED_SSB_POSSIBLE
-- tristate
-- default SSB_POSSIBLE
--config BACKPORTED_SSB
-- tristate
-- default SSB
--config BACKPORTED_SSB_SPROM
-- tristate
-- default SSB_SPROM
--config BACKPORTED_SSB_BLOCKIO
-- tristate
-- default SSB_BLOCKIO
--config BACKPORTED_SSB_PCIHOST_POSSIBLE
-- tristate
-- default SSB_PCIHOST_POSSIBLE
--config BACKPORTED_SSB_PCIHOST
-- tristate
-- default SSB_PCIHOST
--config BACKPORTED_SSB_B43_PCI_BRIDGE
-- tristate
-- default SSB_B43_PCI_BRIDGE
--config BACKPORTED_SSB_PCMCIAHOST_POSSIBLE
-- tristate
-- default SSB_PCMCIAHOST_POSSIBLE
--config BACKPORTED_SSB_PCMCIAHOST
-- tristate
-- default SSB_PCMCIAHOST
--config BACKPORTED_SSB_SDIOHOST_POSSIBLE
-- tristate
-- default SSB_SDIOHOST_POSSIBLE
--config BACKPORTED_SSB_SDIOHOST
-- tristate
-- default SSB_SDIOHOST
--config BACKPORTED_SSB_HOST_SOC
-- tristate
-- default SSB_HOST_SOC
--config BACKPORTED_SSB_SERIAL
-- tristate
-- default SSB_SERIAL
--config BACKPORTED_SSB_DRIVER_PCICORE_POSSIBLE
-- tristate
-- default SSB_DRIVER_PCICORE_POSSIBLE
--config BACKPORTED_SSB_DRIVER_PCICORE
-- tristate
-- default SSB_DRIVER_PCICORE
--config BACKPORTED_SSB_PCICORE_HOSTMODE
-- tristate
-- default SSB_PCICORE_HOSTMODE
--config BACKPORTED_SSB_DRIVER_MIPS
-- tristate
-- default SSB_DRIVER_MIPS
--config BACKPORTED_SSB_SFLASH
-- tristate
-- default SSB_SFLASH
--config BACKPORTED_SSB_EMBEDDED
-- tristate
-- default SSB_EMBEDDED
--config BACKPORTED_SSB_DRIVER_EXTIF
-- tristate
-- default SSB_DRIVER_EXTIF
--config BACKPORTED_SSB_DRIVER_GIGE
-- tristate
-- default SSB_DRIVER_GIGE
--config BACKPORTED_SSB_DRIVER_GPIO
-- tristate
-- default SSB_DRIVER_GPIO
--config BACKPORTED_BCMA_POSSIBLE
-- tristate
-- default BCMA_POSSIBLE
--config BACKPORTED_BCMA
-- tristate
-- default BCMA
--config BACKPORTED_BCMA_BLOCKIO
-- tristate
-- default BCMA_BLOCKIO
--config BACKPORTED_BCMA_HOST_PCI_POSSIBLE
-- tristate
-- default BCMA_HOST_PCI_POSSIBLE
--config BACKPORTED_BCMA_HOST_PCI
-- tristate
-- default BCMA_HOST_PCI
--config BACKPORTED_BCMA_HOST_SOC
-- tristate
-- default BCMA_HOST_SOC
--config BACKPORTED_BCMA_DRIVER_PCI
-- tristate
-- default BCMA_DRIVER_PCI
--config BACKPORTED_BCMA_DRIVER_PCI_HOSTMODE
-- tristate
-- default BCMA_DRIVER_PCI_HOSTMODE
--config BACKPORTED_BCMA_DRIVER_MIPS
-- tristate
-- default BCMA_DRIVER_MIPS
--config BACKPORTED_BCMA_PFLASH
-- tristate
-- default BCMA_PFLASH
--config BACKPORTED_BCMA_SFLASH
-- tristate
-- default BCMA_SFLASH
--config BACKPORTED_BCMA_NFLASH
-- tristate
-- default BCMA_NFLASH
--config BACKPORTED_BCMA_DRIVER_GMAC_CMN
-- tristate
-- default BCMA_DRIVER_GMAC_CMN
--config BACKPORTED_BCMA_DRIVER_GPIO
-- tristate
-- default BCMA_DRIVER_GPIO
--config BACKPORTED_BCMA_DEBUG
-- tristate
-- default BCMA_DEBUG
- config BACKPORTED_USB_ACM
- tristate
- default USB_ACM
---- a/Kconfig.sources
-+++ b/Kconfig.sources
-@@ -10,9 +10,6 @@ source "$BACKPORT_DIR/drivers/soc/qcom/K
- source "$BACKPORT_DIR/drivers/net/wireless/Kconfig"
- source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
-
--source "$BACKPORT_DIR/drivers/ssb/Kconfig"
--source "$BACKPORT_DIR/drivers/bcma/Kconfig"
--
- source "$BACKPORT_DIR/drivers/usb/class/Kconfig"
-
- source "$BACKPORT_DIR/drivers/staging/Kconfig"
---- a/Makefile.kernel
-+++ b/Makefile.kernel
-@@ -43,8 +43,6 @@ obj-$(CPTCFG_QRTR) += net/qrtr/
- obj-$(CPTCFG_QCOM_QMI_HELPERS) += drivers/soc/qcom/
- obj-$(CPTCFG_MHI_BUS) += drivers/bus/mhi/
- obj-$(CPTCFG_WLAN) += drivers/net/wireless/
--obj-$(CPTCFG_SSB) += drivers/ssb/
--obj-$(CPTCFG_BCMA) += drivers/bcma/
- obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/
-
- obj-$(CPTCFG_USB_WDM) += drivers/usb/class/
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/070-remove-broken-wext-select.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/070-remove-broken-wext-select.patch
deleted file mode 100644
index 121b7fa..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/070-remove-broken-wext-select.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- a/drivers/staging/rtl8723bs/Kconfig
-+++ b/drivers/staging/rtl8723bs/Kconfig
-@@ -5,7 +5,6 @@ config RTL8723BS
- depends on m
- depends on WLAN && MMC && CFG80211
- depends on m
-- select CFG80211_WEXT
- depends on CRYPTO
- select BPAUTO_CRYPTO_LIB_ARC4
- help
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/080-resv_start_op.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/080-resv_start_op.patch
deleted file mode 100644
index bbd9018..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/080-resv_start_op.patch
+++ /dev/null
@@ -1,24 +0,0 @@
---- a/drivers/net/wireless/virtual/mac80211_hwsim.c
-+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
-@@ -6179,7 +6179,9 @@ static struct genl_family hwsim_genl_fam
- .module = THIS_MODULE,
- .small_ops = hwsim_ops,
- .n_small_ops = ARRAY_SIZE(hwsim_ops),
-+#if LINUX_VERSION_IS_GEQ(6,1,0)
- .resv_start_op = HWSIM_CMD_REPORT_PMSR + 1, // match with __HWSIM_CMD_MAX
-+#endif
- .mcgrps = hwsim_mcgrps,
- .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
- };
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -17551,7 +17551,9 @@ static struct genl_family nl80211_fam __
- .n_ops = ARRAY_SIZE(nl80211_ops),
- .small_ops = nl80211_small_ops,
- .n_small_ops = ARRAY_SIZE(nl80211_small_ops),
-+#if LINUX_VERSION_IS_GEQ(6,1,0)
- .resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1,
-+#endif
- .mcgrps = nl80211_mcgrps,
- .n_mcgrps = ARRAY_SIZE(nl80211_mcgrps),
- .parallel_ops = true,
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/090-bcma-otp.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/090-bcma-otp.patch
deleted file mode 100644
index 3974776..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/090-bcma-otp.patch
+++ /dev/null
@@ -1,13 +0,0 @@
---- /dev/null
-+++ b/backport-include/linux/bcma/bcma_driver_chipcommon.h
-@@ -0,0 +1,10 @@
-+#ifndef __BACKPORT_BCMA_DRIVER_CHIPCOMMON_H
-+#define __BACKPORT_BCMA_DRIVER_CHIPCOMMON_H
-+
-+#include_next <linux/bcma/bcma_driver_chipcommon.h>
-+
-+#ifndef BCMA_CC_SROM_CONTROL_OTP_PRESENT
-+#define BCMA_CC_SROM_CONTROL_OTP_PRESENT 0x00000020
-+#endif
-+
-+#endif
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/100-backports-drop-QRTR-and-MHI.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/100-backports-drop-QRTR-and-MHI.patch
deleted file mode 100644
index b017a0c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/100-backports-drop-QRTR-and-MHI.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-From 54e0f9aaf340377fb76acdffee9ec7372c4b70ae Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Mon, 17 Oct 2022 11:35:36 +0200
-Subject: [PATCH] backports: drop QRTR and MHI
-
-Backports currently include QRTR and MHI due to ath11k-pci requiring them,
-however this at the same time prevents us from adding ath11k-ahb as it
-also requires QRTR however its AHB variant from the kernel will conflict
-with the core provided by backports.
-
-Since MHI also conflicts with existing OpenWrt kmods providing MHI drop
-both from backports and use the ones provided by OpenWrt kernel.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
----
- Kconfig.sources | 2 --
- Makefile.kernel | 2 --
- drivers/net/wireless/ath/ath11k/Kconfig | 6 +++---
- local-symbols | 8 --------
- 4 files changed, 3 insertions(+), 15 deletions(-)
-
---- a/Kconfig.sources
-+++ b/Kconfig.sources
-@@ -4,8 +4,6 @@ source "$BACKPORT_DIR/compat/Kconfig"
- # these are copied from the kernel
- source "$BACKPORT_DIR/net/wireless/Kconfig"
- source "$BACKPORT_DIR/net/mac80211/Kconfig"
--source "$BACKPORT_DIR/net/qrtr/Kconfig"
--source "$BACKPORT_DIR/drivers/bus/mhi/Kconfig"
- source "$BACKPORT_DIR/drivers/soc/qcom/Kconfig"
- source "$BACKPORT_DIR/drivers/net/wireless/Kconfig"
- source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
---- a/Makefile.kernel
-+++ b/Makefile.kernel
-@@ -39,9 +39,7 @@ obj-y += compat/
-
- obj-$(CPTCFG_CFG80211) += net/wireless/
- obj-$(CPTCFG_MAC80211) += net/mac80211/
--obj-$(CPTCFG_QRTR) += net/qrtr/
- obj-$(CPTCFG_QCOM_QMI_HELPERS) += drivers/soc/qcom/
--obj-$(CPTCFG_MHI_BUS) += drivers/bus/mhi/
- obj-$(CPTCFG_WLAN) += drivers/net/wireless/
- obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/
-
---- a/drivers/net/wireless/ath/ath11k/Kconfig
-+++ b/drivers/net/wireless/ath/ath11k/Kconfig
-@@ -25,9 +25,9 @@ config ATH11K_PCI
- tristate "Atheros ath11k PCI support"
- depends on m
- depends on ATH11K && PCI
-- select MHI_BUS
-- select QRTR
-- select QRTR_MHI
-+ depends on MHI_BUS
-+ depends on QRTR
-+ depends on QRTR_MHI
- help
- This module adds support for PCIE bus
-
---- a/local-symbols
-+++ b/local-symbols
-@@ -65,14 +65,6 @@ MAC80211_MESH_PS_DEBUG=
- MAC80211_TDLS_DEBUG=
- MAC80211_DEBUG_COUNTERS=
- MAC80211_STA_HASH_MAX_SIZE=
--QRTR=
--QRTR_SMD=
--QRTR_TUN=
--QRTR_MHI=
--MHI_BUS=
--MHI_BUS_DEBUG=
--MHI_BUS_PCI_GENERIC=
--MHI_BUS_EP=
- QCOM_AOSS_QMP=
- QCOM_COMMAND_DB=
- QCOM_CPR=
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/110-backport_namepace_const.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/110-backport_namepace_const.patch
deleted file mode 100644
index 6dca708..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/110-backport_namepace_const.patch
+++ /dev/null
@@ -1,14 +0,0 @@
---- a/net/wireless/sysfs.c
-+++ b/net/wireless/sysfs.c
-@@ -154,7 +154,11 @@ static SIMPLE_DEV_PM_OPS(wiphy_pm_ops, w
- #define WIPHY_PM_OPS NULL
- #endif
-
-+#if LINUX_VERSION_IS_GEQ(6,2,0)
- static const void *wiphy_namespace(const struct device *d)
-+#else
-+static const void *wiphy_namespace(struct device *d)
-+#endif
- {
- struct wiphy *wiphy = container_of(d, struct wiphy, dev);
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/120-headers_version_fix.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/120-headers_version_fix.patch
deleted file mode 100644
index 9a8c474..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/120-headers_version_fix.patch
+++ /dev/null
@@ -1,25 +0,0 @@
---- a/backport-include/linux/random.h
-+++ b/backport-include/linux/random.h
-@@ -23,7 +23,7 @@ static inline u16 get_random_u16(void)
- }
- #endif
-
--#if LINUX_VERSION_IS_LESS(6,2,0)
-+#if LINUX_VERSION_IS_LESS(6,1,4)
- static inline u32 __get_random_u32_below(u32 ceil)
- {
- /*
---- a/backport-include/net/dropreason.h
-+++ b/backport-include/net/dropreason.h
-@@ -3,10 +3,9 @@
-
- #include <linux/version.h>
-
-+#include <net/dropreason-core.h>
- #if LINUX_VERSION_IS_GEQ(6,0,0)
- #include_next <net/dropreason.h>
--#else
--#include <net/dropreason-core.h>
- #endif
-
- #if LINUX_VERSION_IS_LESS(6,4,0)
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/130-iommu_backport.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/130-iommu_backport.patch
deleted file mode 100644
index 2d3ef88..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/130-iommu_backport.patch
+++ /dev/null
@@ -1,26 +0,0 @@
---- /dev/null
-+++ b/backport-include/linux/iommu.h
-@@ -0,0 +1,23 @@
-+#ifndef __BACKPORT_LINUX_IOMMU_H
-+#define __BACKPORT_LINUX_IOMMU_H
-+
-+#include_next <linux/iommu.h>
-+#include <linux/version.h>
-+
-+#if LINUX_VERSION_IS_LESS(6,3,0)
-+
-+static inline int LINUX_BACKPORT(iommu_map)(struct iommu_domain *domain,
-+ unsigned long iova,
-+ phys_addr_t paddr, size_t size,
-+ int prot, gfp_t gfp)
-+{
-+ if (gfp == GFP_ATOMIC)
-+ return iommu_map_atomic(domain, iova, paddr, size, prot);
-+
-+ return iommu_map(domain, iova, paddr, size, prot);
-+}
-+#define iommu_map LINUX_BACKPORT(iommu_map)
-+
-+#endif /* < 6.3 */
-+
-+#endif
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/200-Revert-wifi-iwlwifi-Use-generic-thermal_zone_get_tri.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/200-Revert-wifi-iwlwifi-Use-generic-thermal_zone_get_tri.patch
deleted file mode 100644
index 3a5285d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/200-Revert-wifi-iwlwifi-Use-generic-thermal_zone_get_tri.patch
+++ /dev/null
@@ -1,159 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Apr 2023 19:42:38 +0200
-Subject: [PATCH] Revert "wifi: iwlwifi: Use generic thermal_zone_get_trip()
- function"
-
-This reverts commit 3d2f20ad46f83b333025f5e8e4afc34be8f13c4c.
----
-
---- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
-+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
-@@ -531,7 +531,7 @@ struct iwl_mvm_tt_mgmt {
- * @tzone: thermal zone device data
- */
- struct iwl_mvm_thermal_device {
-- struct thermal_trip trips[IWL_MAX_DTS_TRIPS];
-+ s16 temp_trips[IWL_MAX_DTS_TRIPS];
- u8 fw_trips_index[IWL_MAX_DTS_TRIPS];
- struct thermal_zone_device *tzone;
- };
---- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
-+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
-@@ -573,11 +573,11 @@ int iwl_mvm_send_temp_report_ths_cmd(str
- * and uncompressed, the FW should get it compressed and sorted
- */
-
-- /* compress trips to cmd array, remove uninitialized values*/
-+ /* compress temp_trips to cmd array, remove uninitialized values*/
- for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
-- if (mvm->tz_device.trips[i].temperature != INT_MIN) {
-+ if (mvm->tz_device.temp_trips[i] != S16_MIN) {
- cmd.thresholds[idx++] =
-- cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000));
-+ cpu_to_le16(mvm->tz_device.temp_trips[i]);
- }
- }
- cmd.num_temps = cpu_to_le32(idx);
-@@ -593,8 +593,8 @@ int iwl_mvm_send_temp_report_ths_cmd(str
- */
- for (i = 0; i < idx; i++) {
- for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
-- if ((int)(le16_to_cpu(cmd.thresholds[i]) * 1000) ==
-- mvm->tz_device.trips[j].temperature)
-+ if (le16_to_cpu(cmd.thresholds[i]) ==
-+ mvm->tz_device.temp_trips[j])
- mvm->tz_device.fw_trips_index[i] = j;
- }
- }
-@@ -638,12 +638,37 @@ out:
- return ret;
- }
-
-+static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
-+ int trip, int *temp)
-+{
-+ struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
-+
-+ if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
-+ return -EINVAL;
-+
-+ *temp = mvm->tz_device.temp_trips[trip] * 1000;
-+
-+ return 0;
-+}
-+
-+static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
-+ int trip, enum thermal_trip_type *type)
-+{
-+ if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
-+ return -EINVAL;
-+
-+ *type = THERMAL_TRIP_PASSIVE;
-+
-+ return 0;
-+}
-+
- static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
- int trip, int temp)
- {
- struct iwl_mvm *mvm = thermal_zone_device_priv(device);
- struct iwl_mvm_thermal_device *tzone;
-- int ret;
-+ int i, ret;
-+ s16 temperature;
-
- mutex_lock(&mvm->mutex);
-
-@@ -653,17 +678,40 @@ static int iwl_mvm_tzone_set_trip_temp(s
- goto out;
- }
-
-+ if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
-+ ret = -EINVAL;
-+ goto out;
-+ }
-+
- if ((temp / 1000) > S16_MAX) {
- ret = -EINVAL;
- goto out;
- }
-
-+ temperature = (s16)(temp / 1000);
- tzone = &mvm->tz_device;
-+
- if (!tzone) {
- ret = -EIO;
- goto out;
- }
-
-+ /* no updates*/
-+ if (tzone->temp_trips[trip] == temperature) {
-+ ret = 0;
-+ goto out;
-+ }
-+
-+ /* already existing temperature */
-+ for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
-+ if (tzone->temp_trips[i] == temperature) {
-+ ret = -EINVAL;
-+ goto out;
-+ }
-+ }
-+
-+ tzone->temp_trips[trip] = temperature;
-+
- ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
- out:
- mutex_unlock(&mvm->mutex);
-@@ -672,6 +720,8 @@ out:
-
- static struct thermal_zone_device_ops tzone_ops = {
- .get_temp = iwl_mvm_tzone_get_temp,
-+ .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
-+ .get_trip_type = iwl_mvm_tzone_get_trip_type,
- .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
- };
-
-@@ -693,8 +743,7 @@ static void iwl_mvm_thermal_zone_registe
- BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
-
- sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
-- mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
-- mvm->tz_device.trips,
-+ mvm->tz_device.tzone = thermal_zone_device_register(name,
- IWL_MAX_DTS_TRIPS,
- IWL_WRITABLE_TRIPS_MSK,
- mvm, &tzone_ops,
-@@ -717,10 +766,8 @@ static void iwl_mvm_thermal_zone_registe
- /* 0 is a valid temperature,
- * so initialize the array with S16_MIN which invalid temperature
- */
-- for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {
-- mvm->tz_device.trips[i].temperature = INT_MIN;
-- mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;
-- }
-+ for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
-+ mvm->tz_device.temp_trips[i] = S16_MIN;
- }
-
- static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/210-revert-split-op.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/210-revert-split-op.patch
deleted file mode 100644
index 4f1f8a7..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/210-revert-split-op.patch
+++ /dev/null
@@ -1,22 +0,0 @@
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -16442,8 +16442,7 @@ static u32 nl80211_internal_flags[] = {
- #undef SELECTOR
- };
-
--static int nl80211_pre_doit(const struct genl_split_ops *ops,
-- struct sk_buff *skb,
-+static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- struct cfg80211_registered_device *rdev = NULL;
-@@ -16544,8 +16543,7 @@ out_unlock:
- return err;
- }
-
--static void nl80211_post_doit(const struct genl_split_ops *ops,
-- struct sk_buff *skb,
-+static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- u32 internal_flags = nl80211_internal_flags[ops->internal_flags];
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/220-list-don-t-backport-list_count_nodes.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/220-list-don-t-backport-list_count_nodes.patch
deleted file mode 100644
index 6d30002..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/220-list-don-t-backport-list_count_nodes.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Fri, 15 Dec 2023 10:17:21 +0100
-Subject: [PATCH] list: don't backport list_count_nodes()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It's redundant in OpenWrt as it backports it on its own. This fixes:
-backport-include/linux/list.h:11:22: error: redefinition of 'list_count_nodes'
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
----
- backport-include/linux/list.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/backport-include/linux/list.h
-+++ b/backport-include/linux/list.h
-@@ -3,7 +3,7 @@
- #include_next <linux/list.h>
- #include <linux/version.h>
-
--#if LINUX_VERSION_IS_LESS(6,3,0)
-+#if 0 /* OpenWrt backports list_count_nodes() on its own */
- /**
- * list_count_nodes - count nodes in the list
- * @head: the head for your list.
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/230-backport_genl_info_userhdr.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/230-backport_genl_info_userhdr.patch
deleted file mode 100644
index 38f86dc..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/230-backport_genl_info_userhdr.patch
+++ /dev/null
@@ -1,32 +0,0 @@
---- a/backport-include/net/genetlink.h
-+++ b/backport-include/net/genetlink.h
-@@ -3,6 +3,7 @@
- #include_next <net/genetlink.h>
- #include <linux/version.h>
-
-+#if LINUX_VERSION_IS_LESS(4,12,0)
- static inline void __bp_genl_info_userhdr_set(struct genl_info *info,
- void *userhdr)
- {
-@@ -14,7 +15,6 @@ static inline void *__bp_genl_info_userh
- return info->userhdr;
- }
-
--#if LINUX_VERSION_IS_LESS(4,12,0)
- #define GENL_SET_ERR_MSG(info, msg) NL_SET_ERR_MSG(genl_info_extack(info), msg)
-
- static inline int genl_err_attr(struct genl_info *info, int err,
-@@ -44,11 +44,13 @@ static inline struct netlink_ext_ack *ge
- #endif
- }
-
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- /* this gets put in place of info->userhdr, since we use that above */
- static inline void *genl_info_userhdr(struct genl_info *info)
- {
- return (u8 *)info->genlhdr + GENL_HDRLEN;
- }
-+#endif
-
- #if LINUX_VERSION_IS_LESS(4,10,0)
- #define __genl_ro_after_init
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/240-backport_genl_split_ops.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/240-backport_genl_split_ops.patch
deleted file mode 100644
index b22804c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/240-backport_genl_split_ops.patch
+++ /dev/null
@@ -1,32 +0,0 @@
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -16442,8 +16442,14 @@ static u32 nl80211_internal_flags[] = {
- #undef SELECTOR
- };
-
-+#if LINUX_VERSION_IS_LESS(6,2,0)
- static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
-+#else
-+static int nl80211_pre_doit(const struct genl_split_ops *ops,
-+ struct sk_buff *skb,
-+ struct genl_info *info)
-+#endif
- {
- struct cfg80211_registered_device *rdev = NULL;
- struct wireless_dev *wdev = NULL;
-@@ -16543,8 +16549,14 @@ out_unlock:
- return err;
- }
-
-+#if LINUX_VERSION_IS_LESS(6,2,0)
- static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
-+#else
-+static void nl80211_post_doit(const struct genl_split_ops *ops,
-+ struct sk_buff *skb,
-+ struct genl_info *info)
-+#endif
- {
- u32 internal_flags = nl80211_internal_flags[ops->internal_flags];
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/250-backport_iwlwifi_thermal.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/build/250-backport_iwlwifi_thermal.patch
deleted file mode 100644
index 631fdd7..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/250-backport_iwlwifi_thermal.patch
+++ /dev/null
@@ -1,160 +0,0 @@
---- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
-+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
-@@ -531,7 +531,11 @@ struct iwl_mvm_tt_mgmt {
- * @tzone: thermal zone device data
- */
- struct iwl_mvm_thermal_device {
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- s16 temp_trips[IWL_MAX_DTS_TRIPS];
-+#else
-+ struct thermal_trip trips[IWL_MAX_DTS_TRIPS];
-+#endif
- u8 fw_trips_index[IWL_MAX_DTS_TRIPS];
- struct thermal_zone_device *tzone;
- };
---- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
-+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
-@@ -573,6 +573,7 @@ int iwl_mvm_send_temp_report_ths_cmd(str
- * and uncompressed, the FW should get it compressed and sorted
- */
-
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- /* compress temp_trips to cmd array, remove uninitialized values*/
- for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
- if (mvm->tz_device.temp_trips[i] != S16_MIN) {
-@@ -580,6 +581,15 @@ int iwl_mvm_send_temp_report_ths_cmd(str
- cpu_to_le16(mvm->tz_device.temp_trips[i]);
- }
- }
-+#else
-+ /* compress trips to cmd array, remove uninitialized values*/
-+ for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
-+ if (mvm->tz_device.trips[i].temperature != INT_MIN) {
-+ cmd.thresholds[idx++] =
-+ cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000));
-+ }
-+ }
-+#endif
- cmd.num_temps = cpu_to_le32(idx);
-
- if (!idx)
-@@ -593,8 +603,13 @@ int iwl_mvm_send_temp_report_ths_cmd(str
- */
- for (i = 0; i < idx; i++) {
- for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- if (le16_to_cpu(cmd.thresholds[i]) ==
- mvm->tz_device.temp_trips[j])
-+#else
-+ if ((int)(le16_to_cpu(cmd.thresholds[i]) * 1000) ==
-+ mvm->tz_device.trips[j].temperature)
-+#endif
- mvm->tz_device.fw_trips_index[i] = j;
- }
- }
-@@ -638,6 +653,7 @@ out:
- return ret;
- }
-
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
- int trip, int *temp)
- {
-@@ -661,14 +677,19 @@ static int iwl_mvm_tzone_get_trip_type(s
-
- return 0;
- }
-+#endif
-
- static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
- int trip, int temp)
- {
- struct iwl_mvm *mvm = thermal_zone_device_priv(device);
- struct iwl_mvm_thermal_device *tzone;
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- int i, ret;
- s16 temperature;
-+#else
-+ int ret;
-+#endif
-
- mutex_lock(&mvm->mutex);
-
-@@ -678,17 +699,21 @@ static int iwl_mvm_tzone_set_trip_temp(s
- goto out;
- }
-
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
- ret = -EINVAL;
- goto out;
- }
-+#endif
-
- if ((temp / 1000) > S16_MAX) {
- ret = -EINVAL;
- goto out;
- }
-
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- temperature = (s16)(temp / 1000);
-+#endif
- tzone = &mvm->tz_device;
-
- if (!tzone) {
-@@ -696,6 +721,7 @@ static int iwl_mvm_tzone_set_trip_temp(s
- goto out;
- }
-
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- /* no updates*/
- if (tzone->temp_trips[trip] == temperature) {
- ret = 0;
-@@ -711,6 +737,7 @@ static int iwl_mvm_tzone_set_trip_temp(s
- }
-
- tzone->temp_trips[trip] = temperature;
-+#endif
-
- ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
- out:
-@@ -720,8 +747,10 @@ out:
-
- static struct thermal_zone_device_ops tzone_ops = {
- .get_temp = iwl_mvm_tzone_get_temp,
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
- .get_trip_type = iwl_mvm_tzone_get_trip_type,
-+#endif
- .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
- };
-
-@@ -743,7 +772,12 @@ static void iwl_mvm_thermal_zone_registe
- BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
-
- sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- mvm->tz_device.tzone = thermal_zone_device_register(name,
-+#else
-+ mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
-+ mvm->tz_device.trips,
-+#endif
- IWL_MAX_DTS_TRIPS,
- IWL_WRITABLE_TRIPS_MSK,
- mvm, &tzone_ops,
-@@ -766,8 +800,15 @@ static void iwl_mvm_thermal_zone_registe
- /* 0 is a valid temperature,
- * so initialize the array with S16_MIN which invalid temperature
- */
-+#if LINUX_VERSION_IS_LESS(6,6,0)
- for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
- mvm->tz_device.temp_trips[i] = S16_MIN;
-+#else
-+ for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {
-+ mvm->tz_device.trips[i].temperature = INT_MIN;
-+ mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;
-+ }
-+#endif
- }
-
- static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/build/build.inc b/recipes-wifi/linux-mac80211/files/patches-6.x/build/build.inc
deleted file mode 100644
index 40fef68..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/build/build.inc
+++ /dev/null
@@ -1,23 +0,0 @@
-#patch build (come from openwrt/lede/target/linux/mediatek)
-SRC_URI_append = " \
- file://000-fix_kconfig.patch \
- file://001-fix_build.patch \
- file://002-change_allconfig.patch \
- file://003-remove_bogus_modparams.patch \
- file://004-fix-kconf-compiling.patch \
- file://012-kernel_build_check.patch \
- file://060-no_local_ssb_bcma.patch \
- file://070-remove-broken-wext-select.patch \
- file://080-resv_start_op.patch \
- file://090-bcma-otp.patch \
- file://100-backports-drop-QRTR-and-MHI.patch \
- file://110-backport_namepace_const.patch \
- file://120-headers_version_fix.patch \
- file://130-iommu_backport.patch \
- file://200-Revert-wifi-iwlwifi-Use-generic-thermal_zone_get_tri.patch \
- file://210-revert-split-op.patch \
- file://220-list-don-t-backport-list_count_nodes.patch \
- file://230-backport_genl_info_userhdr.patch \
- file://240-backport_genl_split_ops.patch \
- file://250-backport_iwlwifi_thermal.patch \
- "
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-sync-backports-patches-build.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-sync-backports-patches-build.patch
new file mode 100644
index 0000000..e926f0b
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-sync-backports-patches-build.patch
@@ -0,0 +1,815 @@
+From 45a2c927c790c9f71a81983f6d59b15cfb6b05ac Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 12 Mar 2024 11:25:41 +0800
+Subject: [PATCH 01/61] sync backports patches/build
+
+---
+ Kconfig.local | 111 -----------------
+ Kconfig.sources | 5 -
+ Makefile | 116 +++++++++---------
+ Makefile.real | 29 ++++-
+ compat/main.c | 25 ----
+ drivers/net/wireless/ath/ath11k/Kconfig | 6 +-
+ drivers/net/wireless/broadcom/b43/Kconfig | 12 +-
+ drivers/net/wireless/broadcom/b43/main.c | 4 +-
+ .../net/wireless/broadcom/b43legacy/Kconfig | 8 +-
+ .../net/wireless/broadcom/b43legacy/main.c | 4 +-
+ .../net/wireless/broadcom/brcm80211/Kconfig | 2 +-
+ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +-
+ drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 21 ++--
+ drivers/staging/rtl8723bs/Kconfig | 1 -
+ kconf/Makefile | 4 +-
+ kconf/conf.c | 30 +----
+ kconf/confdata.c | 4 +-
+ local-symbols | 45 -------
+ 18 files changed, 118 insertions(+), 311 deletions(-)
+
+diff --git a/Kconfig.local b/Kconfig.local
+index d9495b1..547f8ad 100644
+--- a/Kconfig.local
++++ b/Kconfig.local
+@@ -1420,117 +1420,6 @@ config BACKPORTED_USB_NET_AQC111
+ config BACKPORTED_USB_RTL8153_ECM
+ tristate
+ default USB_RTL8153_ECM
+-config BACKPORTED_SSB_POSSIBLE
+- tristate
+- default SSB_POSSIBLE
+-config BACKPORTED_SSB
+- tristate
+- default SSB
+-config BACKPORTED_SSB_SPROM
+- tristate
+- default SSB_SPROM
+-config BACKPORTED_SSB_BLOCKIO
+- tristate
+- default SSB_BLOCKIO
+-config BACKPORTED_SSB_PCIHOST_POSSIBLE
+- tristate
+- default SSB_PCIHOST_POSSIBLE
+-config BACKPORTED_SSB_PCIHOST
+- tristate
+- default SSB_PCIHOST
+-config BACKPORTED_SSB_B43_PCI_BRIDGE
+- tristate
+- default SSB_B43_PCI_BRIDGE
+-config BACKPORTED_SSB_PCMCIAHOST_POSSIBLE
+- tristate
+- default SSB_PCMCIAHOST_POSSIBLE
+-config BACKPORTED_SSB_PCMCIAHOST
+- tristate
+- default SSB_PCMCIAHOST
+-config BACKPORTED_SSB_SDIOHOST_POSSIBLE
+- tristate
+- default SSB_SDIOHOST_POSSIBLE
+-config BACKPORTED_SSB_SDIOHOST
+- tristate
+- default SSB_SDIOHOST
+-config BACKPORTED_SSB_HOST_SOC
+- tristate
+- default SSB_HOST_SOC
+-config BACKPORTED_SSB_SERIAL
+- tristate
+- default SSB_SERIAL
+-config BACKPORTED_SSB_DRIVER_PCICORE_POSSIBLE
+- tristate
+- default SSB_DRIVER_PCICORE_POSSIBLE
+-config BACKPORTED_SSB_DRIVER_PCICORE
+- tristate
+- default SSB_DRIVER_PCICORE
+-config BACKPORTED_SSB_PCICORE_HOSTMODE
+- tristate
+- default SSB_PCICORE_HOSTMODE
+-config BACKPORTED_SSB_DRIVER_MIPS
+- tristate
+- default SSB_DRIVER_MIPS
+-config BACKPORTED_SSB_SFLASH
+- tristate
+- default SSB_SFLASH
+-config BACKPORTED_SSB_EMBEDDED
+- tristate
+- default SSB_EMBEDDED
+-config BACKPORTED_SSB_DRIVER_EXTIF
+- tristate
+- default SSB_DRIVER_EXTIF
+-config BACKPORTED_SSB_DRIVER_GIGE
+- tristate
+- default SSB_DRIVER_GIGE
+-config BACKPORTED_SSB_DRIVER_GPIO
+- tristate
+- default SSB_DRIVER_GPIO
+-config BACKPORTED_BCMA_POSSIBLE
+- tristate
+- default BCMA_POSSIBLE
+-config BACKPORTED_BCMA
+- tristate
+- default BCMA
+-config BACKPORTED_BCMA_BLOCKIO
+- tristate
+- default BCMA_BLOCKIO
+-config BACKPORTED_BCMA_HOST_PCI_POSSIBLE
+- tristate
+- default BCMA_HOST_PCI_POSSIBLE
+-config BACKPORTED_BCMA_HOST_PCI
+- tristate
+- default BCMA_HOST_PCI
+-config BACKPORTED_BCMA_HOST_SOC
+- tristate
+- default BCMA_HOST_SOC
+-config BACKPORTED_BCMA_DRIVER_PCI
+- tristate
+- default BCMA_DRIVER_PCI
+-config BACKPORTED_BCMA_DRIVER_PCI_HOSTMODE
+- tristate
+- default BCMA_DRIVER_PCI_HOSTMODE
+-config BACKPORTED_BCMA_DRIVER_MIPS
+- tristate
+- default BCMA_DRIVER_MIPS
+-config BACKPORTED_BCMA_PFLASH
+- tristate
+- default BCMA_PFLASH
+-config BACKPORTED_BCMA_SFLASH
+- tristate
+- default BCMA_SFLASH
+-config BACKPORTED_BCMA_NFLASH
+- tristate
+- default BCMA_NFLASH
+-config BACKPORTED_BCMA_DRIVER_GMAC_CMN
+- tristate
+- default BCMA_DRIVER_GMAC_CMN
+-config BACKPORTED_BCMA_DRIVER_GPIO
+- tristate
+- default BCMA_DRIVER_GPIO
+-config BACKPORTED_BCMA_DEBUG
+- tristate
+- default BCMA_DEBUG
+ config BACKPORTED_USB_ACM
+ tristate
+ default USB_ACM
+diff --git a/Kconfig.sources b/Kconfig.sources
+index 2ea4d8a..d74affd 100644
+--- a/Kconfig.sources
++++ b/Kconfig.sources
+@@ -4,15 +4,10 @@ source "$BACKPORT_DIR/compat/Kconfig"
+ # these are copied from the kernel
+ source "$BACKPORT_DIR/net/wireless/Kconfig"
+ source "$BACKPORT_DIR/net/mac80211/Kconfig"
+-source "$BACKPORT_DIR/net/qrtr/Kconfig"
+-source "$BACKPORT_DIR/drivers/bus/mhi/Kconfig"
+ source "$BACKPORT_DIR/drivers/soc/qcom/Kconfig"
+ source "$BACKPORT_DIR/drivers/net/wireless/Kconfig"
+ source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
+
+-source "$BACKPORT_DIR/drivers/ssb/Kconfig"
+-source "$BACKPORT_DIR/drivers/bcma/Kconfig"
+-
+ source "$BACKPORT_DIR/drivers/usb/class/Kconfig"
+
+ source "$BACKPORT_DIR/drivers/staging/Kconfig"
+diff --git a/Makefile b/Makefile
+index 77c2670..c431b71 100644
+--- a/Makefile
++++ b/Makefile
+@@ -2,10 +2,10 @@
+ # Makefile for the output source package
+ #
+
+-ifeq ($(KERNELRELEASE),)
++ifeq ($(KERNELVERSION),)
+
+ MAKEFLAGS += --no-print-directory
+-SHELL := /bin/bash
++SHELL := /usr/bin/env bash
+ BACKPORT_DIR := $(shell pwd)
+
+ KMODDIR ?= updates
+@@ -19,6 +19,7 @@ KLIB_BUILD ?= $(KLIB)/build/
+ KERNEL_CONFIG := $(KLIB_BUILD)/.config
+ KERNEL_MAKEFILE := $(KLIB_BUILD)/Makefile
+ CONFIG_MD5 := $(shell md5sum $(KERNEL_CONFIG) 2>/dev/null | sed 's/\s.*//')
++STAMP_KERNEL_CONFIG := .kernel_config_md5_$(CONFIG_MD5)
+
+ export KLIB KLIB_BUILD BACKPORT_DIR KMODDIR KMODPATH_ARG
+
+@@ -36,7 +37,8 @@ mrproper:
+ @rm -f .kernel_config_md5 Kconfig.versions Kconfig.kernel
+ @rm -f backport-include/backport/autoconf.h
+
+-.DEFAULT:
++.SILENT: $(STAMP_KERNEL_CONFIG)
++$(STAMP_KERNEL_CONFIG):
+ @set -e ; test -f local-symbols || ( \
+ echo "/--------------" ;\
+ echo "| You shouldn't run make in the backports tree, but only in" ;\
+@@ -60,58 +62,62 @@ mrproper:
+ echo "| (that isn't currently running.)" ;\
+ echo "\\--" ;\
+ false)
+- @set -e ; if [ "$$(cat .kernel_config_md5 2>/dev/null)" != "$(CONFIG_MD5)" ] ;\
+- then \
+- echo -n "Generating local configuration database from kernel ..." ;\
+- grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | ( \
+- while read l ; do \
+- if [ "$${l:0:7}" != "CONFIG_" ] ; then \
+- continue ;\
+- fi ;\
+- l=$${l:7} ;\
+- n=$${l%%=*} ;\
+- v=$${l#*=} ;\
+- if [ "$$v" = "m" ] ; then \
+- echo config $$n ;\
+- echo ' tristate' ;\
+- elif [ "$$v" = "y" ] ; then \
+- echo config $$n ;\
+- echo ' bool' ;\
+- else \
+- continue ;\
+- fi ;\
+- echo " default $$v" ;\
+- echo "" ;\
+- done \
+- ) > Kconfig.kernel ;\
+- kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR) \
+- kernelversion | sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
+- test "$$kver" != "" || echo "Kernel version parse failed!" ;\
+- test "$$kver" != "" ;\
+- kvers="$$(seq 14 39 | sed 's/^/2.6./')" ;\
+- kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\
+- kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')" ;\
+- kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')" ;\
+- print=0 ;\
+- for v in $$kvers ; do \
+- if [ "$$print" = "1" ] ; then \
+- echo config KERNEL_$$(echo $$v | tr . _) ;\
+- echo " def_bool y" ;\
+- fi ;\
+- if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\
+- done > Kconfig.versions ;\
+- # RHEL as well, sadly we need to grep for it ;\
+- RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | \
+- sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
+- RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | \
+- sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
+- for v in $$(seq 0 $$RHEL_MINOR) ; do \
+- echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v ;\
+- echo " def_bool y" ;\
+- done >> Kconfig.versions ;\
+- echo " done." ;\
+- fi ;\
+- echo "$(CONFIG_MD5)" > .kernel_config_md5
++ @rm -f .kernel_config_md5_*
++ @touch $@
++
++Kconfig.kernel: $(STAMP_KERNEL_CONFIG) local-symbols
++ @printf "Generating local configuration database from kernel ..."
++ @grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | ( \
++ while read l ; do \
++ if [ "$${l:0:7}" != "CONFIG_" ] ; then \
++ continue ;\
++ fi ;\
++ l=$${l:7} ;\
++ n=$${l%%=*} ;\
++ v=$${l#*=} ;\
++ if [ "$$v" = "m" ] ; then \
++ echo config $$n ;\
++ echo ' tristate' ;\
++ elif [ "$$v" = "y" ] ; then \
++ echo config $$n ;\
++ echo ' bool' ;\
++ else \
++ continue ;\
++ fi ;\
++ echo " default $$v" ;\
++ echo "" ;\
++ done \
++ ) > $@
++ @echo " done."
++
++Kconfig.versions: Kconfig.kernel
++ @kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR) \
++ kernelversion | sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
++ test "$$kver" != "" || echo "Kernel version parse failed!" ;\
++ test "$$kver" != "" ;\
++ kvers="$$(seq 14 39 | sed 's/^/2.6./')" ;\
++ kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\
++ kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')" ;\
++ kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')" ;\
++ print=0 ;\
++ for v in $$kvers ; do \
++ if [ "$$print" = "1" ] ; then \
++ echo config KERNEL_$$(echo $$v | tr . _) ;\
++ echo " def_bool y" ;\
++ fi ;\
++ if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\
++ done > $@
++ @RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | \
++ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
++ RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | \
++ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
++ for v in $$(seq 0 $$RHEL_MINOR) ; do \
++ echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v ;\
++ echo " def_bool y" ;\
++ done >> $@
++
++.DEFAULT:
++ @$(MAKE) Kconfig.versions
+ @$(MAKE) -f Makefile.real "$@"
+
+ .PHONY: defconfig-help
+diff --git a/Makefile.real b/Makefile.real
+index 6550802..971a543 100644
+--- a/Makefile.real
++++ b/Makefile.real
+@@ -6,6 +6,18 @@ else
+ export BACKPORTS_GIT_TRACKER_DEF=
+ endif
+
++ifneq ($(LLVM),)
++ifneq ($(filter %/,$(LLVM)),)
++LLVM_PREFIX := $(LLVM)
++else ifneq ($(filter -%,$(LLVM)),)
++LLVM_SUFFIX := $(LLVM)
++endif
++
++HOSTCC = $(LLVM_PREFIX)clang$(LLVM_SUFFIX)
++else
++HOSTCC = gcc
++endif
++
+ # disable built-in rules for this file
+ .SUFFIXES:
+
+@@ -24,21 +36,21 @@ listnewconfig oldaskconfig oldconfig \
+ silentoldconfig olddefconfig oldnoconfig \
+ allnoconfig allyesconfig allmodconfig \
+ alldefconfig randconfig:
+- @$(MAKE) -C kconf conf
++ @$(MAKE) -C kconf CC=$(HOSTCC) conf
+ @./kconf/conf --$@ Kconfig
+
+ .PHONY: usedefconfig
+ usedefconfig:
+- @$(MAKE) -C kconf conf
++ @$(MAKE) -C kconf CC=$(HOSTCC) conf
+ @./kconf/conf --defconfig=defconfig Kconfig
+
+ .PHONY: savedefconfig
+ savedefconfig:
+- @$(MAKE) -C kconf conf
++ @$(MAKE) -C kconf CC=$(HOSTCC) conf
+ @./kconf/conf --savedefconfig=defconfig Kconfig
+
+ defconfig-%::
+- @$(MAKE) -C kconf conf
++ @$(MAKE) -C kconf CC=$(HOSTCC) conf
+ @./kconf/conf --defconfig=defconfigs/$(@:defconfig-%=%) Kconfig
+
+ .config:
+@@ -59,7 +71,7 @@ defconfig-%::
+
+ backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
+ @$(MAKE) oldconfig
+- @echo -n "Building backport-include/backport/autoconf.h ..."
++ @printf "Building backport-include/backport/autoconf.h ..."
+ @grep -f local-symbols .config | ( \
+ echo "#ifndef COMPAT_AUTOCONF_INCLUDED" ;\
+ echo "#define COMPAT_AUTOCONF_INCLUDED" ;\
+@@ -80,7 +92,12 @@ backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
+ esac ;\
+ done ;\
+ echo "#endif /* COMPAT_AUTOCONF_INCLUDED */" ;\
+- ) > backport-include/backport/autoconf.h
++ ) > $@.new
++ @if cmp -s $@ $@.new; then \
++ rm -f $@.new; \
++ else \
++ mv $@.new $@; \
++ fi
+ @echo " done."
+
+ .PHONY: modules
+diff --git a/compat/main.c b/compat/main.c
+index d4f3340..651ab63 100644
+--- a/compat/main.c
++++ b/compat/main.c
+@@ -19,31 +19,6 @@ MODULE_LICENSE("GPL");
+ #error "You need a CPTCFG_VERSION"
+ #endif
+
+-static char *backported_kernel_name = CPTCFG_KERNEL_NAME;
+-
+-module_param(backported_kernel_name, charp, 0400);
+-MODULE_PARM_DESC(backported_kernel_name,
+- "The kernel tree name that was used for this backport (" CPTCFG_KERNEL_NAME ")");
+-
+-#ifdef BACKPORTS_GIT_TRACKED
+-static char *backports_tracker_id = BACKPORTS_GIT_TRACKED;
+-module_param(backports_tracker_id, charp, 0400);
+-MODULE_PARM_DESC(backports_tracker_id,
+- "The version of the tree containing this backport (" BACKPORTS_GIT_TRACKED ")");
+-#else
+-static char *backported_kernel_version = CPTCFG_KERNEL_VERSION;
+-static char *backports_version = CPTCFG_VERSION;
+-
+-module_param(backported_kernel_version, charp, 0400);
+-MODULE_PARM_DESC(backported_kernel_version,
+- "The kernel version that was used for this backport (" CPTCFG_KERNEL_VERSION ")");
+-
+-module_param(backports_version, charp, 0400);
+-MODULE_PARM_DESC(backports_version,
+- "The git version of the backports tree used to generate this backport (" CPTCFG_VERSION ")");
+-
+-#endif
+-
+ void backport_dependency_symbol(void)
+ {
+ }
+diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig
+index 7430aaf..f5f58a5 100644
+--- a/drivers/net/wireless/ath/ath11k/Kconfig
++++ b/drivers/net/wireless/ath/ath11k/Kconfig
+@@ -25,9 +25,9 @@ config ATH11K_PCI
+ tristate "Atheros ath11k PCI support"
+ depends on m
+ depends on ATH11K && PCI
+- select MHI_BUS
+- select QRTR
+- select QRTR_MHI
++ depends on MHI_BUS
++ depends on QRTR
++ depends on QRTR_MHI
+ help
+ This module adds support for PCIE bus
+
+diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig
+index 2e196b5..84cbe38 100644
+--- a/drivers/net/wireless/broadcom/b43/Kconfig
++++ b/drivers/net/wireless/broadcom/b43/Kconfig
+@@ -63,21 +63,21 @@ endchoice
+ config B43_PCI_AUTOSELECT
+ bool
+ depends on B43 && SSB_PCIHOST_POSSIBLE
+- select SSB_PCIHOST
+- select SSB_B43_PCI_BRIDGE
++ depends on SSB_PCIHOST
++ depends on SSB_B43_PCI_BRIDGE
+ default y
+
+ # Auto-select SSB PCICORE driver, if possible
+ config B43_PCICORE_AUTOSELECT
+ bool
+ depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE
+- select SSB_DRIVER_PCICORE
++ depends on SSB_DRIVER_PCICORE
+ default y
+
+ config B43_SDIO
+ bool "Broadcom 43xx SDIO device support"
+ depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE
+- select SSB_SDIOHOST
++ depends on SSB_SDIOHOST
+ help
+ Broadcom 43xx device support for Soft-MAC SDIO devices.
+
+@@ -96,13 +96,13 @@ config B43_SDIO
+ config B43_BCMA_PIO
+ bool
+ depends on B43 && B43_BCMA
+- select BCMA_BLOCKIO
++ depends on BCMA_BLOCKIO
+ default y
+
+ config B43_PIO
+ bool
+ depends on B43 && B43_SSB
+- select SSB_BLOCKIO
++ depends on SSB_BLOCKIO
+ default y
+
+ config B43_PHY_G
+diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
+index c2e0a05..bf2be0a 100644
+--- a/drivers/net/wireless/broadcom/b43/main.c
++++ b/drivers/net/wireless/broadcom/b43/main.c
+@@ -2854,7 +2854,7 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
+ {
+ struct ssb_bus *bus = dev->dev->sdev->bus;
+
+-#ifdef CPTCFG_SSB_DRIVER_PCICORE
++#ifdef CONFIG_SSB_DRIVER_PCICORE
+ return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev);
+ #else
+ return bus->chipco.dev;
+@@ -4873,7 +4873,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
+ }
+ if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
+ hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
+-#if defined(CPTCFG_B43_SSB) && defined(CPTCFG_SSB_DRIVER_PCICORE)
++#if defined(CPTCFG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE)
+ if (dev->dev->bus_type == B43_BUS_SSB &&
+ dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
+ dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
+diff --git a/drivers/net/wireless/broadcom/b43legacy/Kconfig b/drivers/net/wireless/broadcom/b43legacy/Kconfig
+index 6ba7eb7..b924f63 100644
+--- a/drivers/net/wireless/broadcom/b43legacy/Kconfig
++++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig
+@@ -3,7 +3,7 @@ config B43LEGACY
+ tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)"
+ depends on m
+ depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
+- select SSB
++ depends on SSB
+ depends on FW_LOADER
+ help
+ b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and
+@@ -25,15 +25,15 @@ config B43LEGACY
+ config B43LEGACY_PCI_AUTOSELECT
+ bool
+ depends on B43LEGACY && SSB_PCIHOST_POSSIBLE
+- select SSB_PCIHOST
+- select SSB_B43_PCI_BRIDGE
++ depends on SSB_PCIHOST
++ depends on SSB_B43_PCI_BRIDGE
+ default y
+
+ # Auto-select SSB PCICORE driver, if possible
+ config B43LEGACY_PCICORE_AUTOSELECT
+ bool
+ depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE
+- select SSB_DRIVER_PCICORE
++ depends on SSB_DRIVER_PCICORE
+ default y
+
+ # LED support
+diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c
+index edd91ff..0222f63 100644
+--- a/drivers/net/wireless/broadcom/b43legacy/main.c
++++ b/drivers/net/wireless/broadcom/b43legacy/main.c
+@@ -1907,7 +1907,7 @@ static int b43legacy_gpio_init(struct b43legacy_wldev *dev)
+ if (dev->dev->id.revision >= 2)
+ mask |= 0x0010; /* FIXME: This is redundant. */
+
+-#ifdef CPTCFG_SSB_DRIVER_PCICORE
++#ifdef CONFIG_SSB_DRIVER_PCICORE
+ pcidev = bus->pcicore.dev;
+ #endif
+ gpiodev = bus->chipco.dev ? : pcidev;
+@@ -1926,7 +1926,7 @@ static void b43legacy_gpio_cleanup(struct b43legacy_wldev *dev)
+ struct ssb_bus *bus = dev->dev->bus;
+ struct ssb_device *gpiodev, *pcidev = NULL;
+
+-#ifdef CPTCFG_SSB_DRIVER_PCICORE
++#ifdef CONFIG_SSB_DRIVER_PCICORE
+ pcidev = bus->pcicore.dev;
+ #endif
+ gpiodev = bus->chipco.dev ? : pcidev;
+diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig
+index 400dc88..b2d97b8 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/Kconfig
++++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
+@@ -8,7 +8,7 @@ config BRCMSMAC
+ depends on m
+ depends on MAC80211
+ depends on BCMA_POSSIBLE
+- select BCMA
++ depends on BCMA
+ select BRCMUTIL
+ depends on FW_LOADER
+ depends on CORDIC
+diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+index b1a1ce5..66f9281 100644
+--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
++++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+@@ -555,7 +555,7 @@ struct iwl_mvm_tt_mgmt {
+ * @tzone: thermal zone device data
+ */
+ struct iwl_mvm_thermal_device {
+- struct thermal_trip trips[IWL_MAX_DTS_TRIPS];
++ s16 temp_trips[IWL_MAX_DTS_TRIPS];
+ u8 fw_trips_index[IWL_MAX_DTS_TRIPS];
+ struct thermal_zone_device *tzone;
+ };
+diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+index dee9c36..1fb364d 100644
+--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
++++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+@@ -573,11 +573,11 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
+ * and uncompressed, the FW should get it compressed and sorted
+ */
+
+- /* compress trips to cmd array, remove uninitialized values*/
++ /* compress temp_trips to cmd array, remove uninitialized values*/
+ for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
+- if (mvm->tz_device.trips[i].temperature != INT_MIN) {
++ if (mvm->tz_device.temp_trips[i] != S16_MIN) {
+ cmd.thresholds[idx++] =
+- cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000));
++ cpu_to_le16(mvm->tz_device.temp_trips[i]);
+ }
+ }
+ cmd.num_temps = cpu_to_le32(idx);
+@@ -593,8 +593,8 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
+ */
+ for (i = 0; i < idx; i++) {
+ for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
+- if ((int)(le16_to_cpu(cmd.thresholds[i]) * 1000) ==
+- mvm->tz_device.trips[j].temperature)
++ if (le16_to_cpu(cmd.thresholds[i]) ==
++ mvm->tz_device.temp_trips[j])
+ mvm->tz_device.fw_trips_index[i] = j;
+ }
+ }
+@@ -665,6 +665,8 @@ out:
+
+ static struct thermal_zone_device_ops tzone_ops = {
+ .get_temp = iwl_mvm_tzone_get_temp,
++ .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
++ .get_trip_type = iwl_mvm_tzone_get_trip_type,
+ .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
+ };
+
+@@ -686,8 +688,7 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
+ BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
+
+ sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
+- mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
+- mvm->tz_device.trips,
++ mvm->tz_device.tzone = thermal_zone_device_register(name,
+ IWL_MAX_DTS_TRIPS,
+ IWL_WRITABLE_TRIPS_MSK,
+ mvm, &tzone_ops,
+@@ -710,10 +711,8 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
+ /* 0 is a valid temperature,
+ * so initialize the array with S16_MIN which invalid temperature
+ */
+- for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {
+- mvm->tz_device.trips[i].temperature = INT_MIN;
+- mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;
+- }
++ for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
++ mvm->tz_device.temp_trips[i] = S16_MIN;
+ }
+
+ static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
+diff --git a/drivers/staging/rtl8723bs/Kconfig b/drivers/staging/rtl8723bs/Kconfig
+index b51916c..b46ff98 100644
+--- a/drivers/staging/rtl8723bs/Kconfig
++++ b/drivers/staging/rtl8723bs/Kconfig
+@@ -5,7 +5,6 @@ config RTL8723BS
+ depends on m
+ depends on WLAN && MMC && CFG80211
+ depends on m
+- select CFG80211_WEXT
+ depends on CRYPTO
+ select BPAUTO_CRYPTO_LIB_ARC4
+ help
+diff --git a/kconf/Makefile b/kconf/Makefile
+index 2004c44..a2790b1 100644
+--- a/kconf/Makefile
++++ b/kconf/Makefile
+@@ -1,9 +1,9 @@
+-CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
++CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -DKBUILD_NO_NLS
+
+ LXDIALOG := lxdialog/checklist.o lxdialog/inputbox.o lxdialog/menubox.o lxdialog/textbox.o lxdialog/util.o lxdialog/yesno.o
+
+ conf: conf.o zconf.tab.o
+-mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) -DLOCALE
++mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags)
+ mconf_LDFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC))
+ mconf: CFLAGS += $(mconf_CFLAGS)
+
+diff --git a/kconf/conf.c b/kconf/conf.c
+index 283eeed..1707f05 100644
+--- a/kconf/conf.c
++++ b/kconf/conf.c
+@@ -598,40 +598,12 @@ int main(int ac, char **av)
+ case oldconfig:
+ case listnewconfig:
+ case olddefconfig:
+- conf_read(NULL);
+- break;
+ case allnoconfig:
+ case allyesconfig:
+ case allmodconfig:
+ case alldefconfig:
+ case randconfig:
+- name = getenv("KCONFIG_ALLCONFIG");
+- if (!name)
+- break;
+- if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
+- if (conf_read_simple(name, S_DEF_USER)) {
+- fprintf(stderr,
+- _("*** Can't read seed configuration \"%s\"!\n"),
+- name);
+- exit(1);
+- }
+- break;
+- }
+- switch (input_mode) {
+- case allnoconfig: name = "allno.config"; break;
+- case allyesconfig: name = "allyes.config"; break;
+- case allmodconfig: name = "allmod.config"; break;
+- case alldefconfig: name = "alldef.config"; break;
+- case randconfig: name = "allrandom.config"; break;
+- default: break;
+- }
+- if (conf_read_simple(name, S_DEF_USER) &&
+- conf_read_simple("all.config", S_DEF_USER)) {
+- fprintf(stderr,
+- _("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"),
+- name);
+- exit(1);
+- }
++ conf_read(NULL);
+ break;
+ default:
+ break;
+diff --git a/kconf/confdata.c b/kconf/confdata.c
+index df26c7b..1038c30 100644
+--- a/kconf/confdata.c
++++ b/kconf/confdata.c
+@@ -1170,6 +1170,8 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode)
+ }
+ bool has_changed = false;
+
++ sym_clear_all_valid();
++
+ for_all_symbols(i, sym) {
+ if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
+ continue;
+@@ -1213,8 +1215,6 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode)
+
+ }
+
+- sym_clear_all_valid();
+-
+ /*
+ * We have different type of choice blocks.
+ * If curr.tri equals to mod then we can select several
+diff --git a/local-symbols b/local-symbols
+index 078fe8b..0743888 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -67,14 +67,6 @@ MAC80211_MESH_PS_DEBUG=
+ MAC80211_TDLS_DEBUG=
+ MAC80211_DEBUG_COUNTERS=
+ MAC80211_STA_HASH_MAX_SIZE=
+-QRTR=
+-QRTR_SMD=
+-QRTR_TUN=
+-QRTR_MHI=
+-MHI_BUS=
+-MHI_BUS_DEBUG=
+-MHI_BUS_PCI_GENERIC=
+-MHI_BUS_EP=
+ QCOM_AOSS_QMP=
+ QCOM_COMMAND_DB=
+ QCOM_GENI_SE=
+@@ -472,43 +464,6 @@ USB_VL600=
+ USB_NET_CH9200=
+ USB_NET_AQC111=
+ USB_RTL8153_ECM=
+-SSB_POSSIBLE=
+-SSB=
+-SSB_SPROM=
+-SSB_BLOCKIO=
+-SSB_PCIHOST_POSSIBLE=
+-SSB_PCIHOST=
+-SSB_B43_PCI_BRIDGE=
+-SSB_PCMCIAHOST_POSSIBLE=
+-SSB_PCMCIAHOST=
+-SSB_SDIOHOST_POSSIBLE=
+-SSB_SDIOHOST=
+-SSB_HOST_SOC=
+-SSB_SERIAL=
+-SSB_DRIVER_PCICORE_POSSIBLE=
+-SSB_DRIVER_PCICORE=
+-SSB_PCICORE_HOSTMODE=
+-SSB_DRIVER_MIPS=
+-SSB_SFLASH=
+-SSB_EMBEDDED=
+-SSB_DRIVER_EXTIF=
+-SSB_DRIVER_GIGE=
+-SSB_DRIVER_GPIO=
+-BCMA_POSSIBLE=
+-BCMA=
+-BCMA_BLOCKIO=
+-BCMA_HOST_PCI_POSSIBLE=
+-BCMA_HOST_PCI=
+-BCMA_HOST_SOC=
+-BCMA_DRIVER_PCI=
+-BCMA_DRIVER_PCI_HOSTMODE=
+-BCMA_DRIVER_MIPS=
+-BCMA_PFLASH=
+-BCMA_SFLASH=
+-BCMA_NFLASH=
+-BCMA_DRIVER_GMAC_CMN=
+-BCMA_DRIVER_GPIO=
+-BCMA_DEBUG=
+ USB_ACM=
+ USB_PRINTER=
+ USB_WDM=
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-sync-backports-patches-ath.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-sync-backports-patches-ath.patch
new file mode 100644
index 0000000..f47e6f5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-sync-backports-patches-ath.patch
@@ -0,0 +1,482 @@
+From 429745cbf1435d31a656d4d912b7ea1cdd271be2 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 12 Mar 2024 11:26:07 +0800
+Subject: [PATCH 02/61] sync backports patches/ath
+
+---
+ drivers/net/wireless/ath/Kconfig | 5 +-
+ drivers/net/wireless/ath/Makefile | 2 +-
+ drivers/net/wireless/ath/ath.h | 17 ++---
+ drivers/net/wireless/ath/ath5k/ani.c | 2 +-
+ drivers/net/wireless/ath/ath5k/base.c | 4 +-
+ drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +-
+ drivers/net/wireless/ath/ath5k/pci.c | 26 ++++++-
+ drivers/net/wireless/ath/ath9k/link.c | 2 +-
+ drivers/net/wireless/ath/ath9k/main.c | 4 +-
+ drivers/net/wireless/ath/hw.c | 2 +-
+ drivers/net/wireless/ath/regd.c | 72 +++++++++++++------
+ drivers/net/wireless/ath/regd_common.h | 3 +
+ local-symbols | 1 +
+ net/wireless/reg.c | 3 +
+ 14 files changed, 102 insertions(+), 43 deletions(-)
+
+diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
+index 9ef8d46..bd576b4 100644
+--- a/drivers/net/wireless/ath/Kconfig
++++ b/drivers/net/wireless/ath/Kconfig
+@@ -1,6 +1,6 @@
+ # SPDX-License-Identifier: ISC
+ config ATH_COMMON
+- tristate
++ tristate "ath.ko"
+ depends on m
+
+ config WLAN_VENDOR_ATH
+@@ -24,6 +24,9 @@ config WLAN_VENDOR_ATH
+
+ if WLAN_VENDOR_ATH
+
++config ATH_USER_REGD
++ bool "Do not enforce EEPROM regulatory restrictions"
++
+ config ATH_DEBUG
+ bool "Atheros wireless debugging"
+ help
+diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
+index 7432d87..adea25c 100644
+--- a/drivers/net/wireless/ath/Makefile
++++ b/drivers/net/wireless/ath/Makefile
+@@ -16,10 +16,10 @@ ath-objs := main.o \
+ regd.o \
+ hw.o \
+ key.o \
++ debug.o \
+ dfs_pattern_detector.o \
+ dfs_pri_detector.o
+
+-ath-$(CPTCFG_ATH_DEBUG) += debug.o
+ ath-$(CPTCFG_ATH_TRACEPOINTS) += trace.o
+
+ CFLAGS_trace.o := -I$(src)
+diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
+index e9d95c7..888c5e2 100644
+--- a/drivers/net/wireless/ath/ath.h
++++ b/drivers/net/wireless/ath/ath.h
+@@ -43,10 +43,12 @@ struct ath_ani {
+ };
+
+ struct ath_cycle_counters {
+- u32 cycles;
+- u32 rx_busy;
+- u32 rx_frame;
+- u32 tx_frame;
++ struct_group(cnts,
++ u32 cycles;
++ u32 rx_busy;
++ u32 rx_frame;
++ u32 tx_frame;
++ );
+ };
+
+ enum ath_device_state {
+@@ -319,14 +321,7 @@ void _ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask,
+ #endif /* CPTCFG_ATH_DEBUG */
+
+ /** Returns string describing opmode, or NULL if unknown mode. */
+-#ifdef CPTCFG_ATH_DEBUG
+ const char *ath_opmode_to_string(enum nl80211_iftype opmode);
+-#else
+-static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode)
+-{
+- return "UNKNOWN";
+-}
+-#endif
+
+ extern const char *ath_bus_type_strings[];
+ static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype)
+diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c
+index 11fd1ca..897cfd9 100644
+--- a/drivers/net/wireless/ath/ath5k/ani.c
++++ b/drivers/net/wireless/ath/ath5k/ani.c
+@@ -379,7 +379,7 @@ ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
+ spin_lock_bh(&common->cc_lock);
+
+ ath_hw_cycle_counters_update(common);
+- memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
++ memcpy(&as->last_cc.cnts, &common->cc_ani.cnts, sizeof(as->last_cc.cnts));
+
+ /* clears common->cc_ani */
+ listen = ath_hw_get_listen_time(common);
+diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
+index 7ec5d9a..bad83fd 100644
+--- a/drivers/net/wireless/ath/ath5k/base.c
++++ b/drivers/net/wireless/ath/ath5k/base.c
+@@ -2985,8 +2985,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
+ memset(&ah->survey, 0, sizeof(ah->survey));
+ spin_lock_bh(&common->cc_lock);
+ ath_hw_cycle_counters_update(common);
+- memset(&common->cc_survey, 0, sizeof(common->cc_survey));
+- memset(&common->cc_ani, 0, sizeof(common->cc_ani));
++ memset(&common->cc_survey.cnts, 0, sizeof(common->cc_survey.cnts));
++ memset(&common->cc_ani.cnts, 0, sizeof(common->cc_ani.cnts));
+ spin_unlock_bh(&common->cc_lock);
+
+ /*
+diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+index eea4bda..da9dc62 100644
+--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
++++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+@@ -664,7 +664,7 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
+ ah->survey.time_rx += cc->rx_frame / div;
+ ah->survey.time_tx += cc->tx_frame / div;
+ }
+- memset(cc, 0, sizeof(*cc));
++ memset(&cc->cnts, 0, sizeof(cc->cnts));
+ spin_unlock_bh(&common->cc_lock);
+
+ memcpy(survey, &ah->survey, sizeof(*survey));
+diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
+index b51fce5..c4315dd 100644
+--- a/drivers/net/wireless/ath/ath5k/pci.c
++++ b/drivers/net/wireless/ath/ath5k/pci.c
+@@ -20,6 +20,7 @@
+ #include <linux/pci.h>
+ #include <linux/etherdevice.h>
+ #include <linux/module.h>
++#include <linux/ath5k_platform.h>
+ #include "../ath.h"
+ #include "ath5k.h"
+ #include "debug.h"
+@@ -71,7 +72,7 @@ static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz)
+ }
+
+ /*
+- * Read from eeprom
++ * Read from eeprom or platform_data
+ */
+ static bool
+ ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
+@@ -79,6 +80,19 @@ ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
+ struct ath5k_hw *ah = common->ah;
+ u32 status, timeout;
+
++ struct ath5k_platform_data *pdata = NULL;
++
++ if (ah->pdev)
++ pdata = ah->pdev->dev.platform_data;
++
++ if (pdata && pdata->eeprom_data && pdata->eeprom_data[61] == AR5K_EEPROM_MAGIC_VALUE) {
++ if (offset >= ATH5K_PLAT_EEP_MAX_WORDS)
++ return false;
++
++ *data = pdata->eeprom_data[offset];
++ return true;
++ }
++
+ /*
+ * Initialize EEPROM access
+ */
+@@ -122,6 +136,16 @@ static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
+ u16 data;
+ int octet;
+
++ struct ath5k_platform_data *pdata = NULL;
++
++ if (ah->pdev)
++ pdata = ah->pdev->dev.platform_data;
++
++ if (pdata && pdata->macaddr) {
++ memcpy(mac, pdata->macaddr, ETH_ALEN);
++ return 0;
++ }
++
+ AR5K_EEPROM_READ(0x20, data);
+
+ for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
+diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
+index d1e5767..7f18d71 100644
+--- a/drivers/net/wireless/ath/ath9k/link.c
++++ b/drivers/net/wireless/ath/ath9k/link.c
+@@ -536,7 +536,7 @@ int ath_update_survey_stats(struct ath_softc *sc)
+ if (cc->cycles > 0)
+ ret = cc->rx_busy * 100 / cc->cycles;
+
+- memset(cc, 0, sizeof(*cc));
++ memset(&cc->cnts, 0, sizeof(cc->cnts));
+
+ ath_update_survey_nf(sc, pos);
+
+diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
+index 34d11b0..6d120df 100644
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -135,8 +135,8 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
+ if (power_mode != ATH9K_PM_AWAKE) {
+ spin_lock(&common->cc_lock);
+ ath_hw_cycle_counters_update(common);
+- memset(&common->cc_survey, 0, sizeof(common->cc_survey));
+- memset(&common->cc_ani, 0, sizeof(common->cc_ani));
++ memset(&common->cc_survey.cnts, 0, sizeof(common->cc_survey.cnts));
++ memset(&common->cc_ani.cnts, 0, sizeof(common->cc_ani.cnts));
+ spin_unlock(&common->cc_lock);
+ }
+
+diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c
+index 8595557..be8bfed 100644
+--- a/drivers/net/wireless/ath/hw.c
++++ b/drivers/net/wireless/ath/hw.c
+@@ -183,7 +183,7 @@ int32_t ath_hw_get_listen_time(struct ath_common *common)
+ listen_time = (cc->cycles - cc->rx_frame - cc->tx_frame) /
+ (common->clockrate * 1000);
+
+- memset(cc, 0, sizeof(*cc));
++ memset(&cc->cnts, 0, sizeof(cc->cnts));
+
+ return listen_time;
+ }
+diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
+index 2afdebf..3ba9fc3 100644
+--- a/drivers/net/wireless/ath/regd.c
++++ b/drivers/net/wireless/ath/regd.c
+@@ -24,6 +24,7 @@
+ #include "regd_common.h"
+
+ static int __ath_regd_init(struct ath_regulatory *reg);
++static struct reg_dmn_pair_mapping *ath_get_regpair(int regdmn);
+
+ /*
+ * This is a set of common rules used by our world regulatory domains.
+@@ -43,7 +44,8 @@ static int __ath_regd_init(struct ath_regulatory *reg);
+ NL80211_RRF_NO_OFDM)
+
+ /* We allow IBSS on these on a case by case basis by regulatory domain */
+-#define ATH_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\
++#define ATH_5GHZ_5150_5350 REG_RULE(5150-10, 5240+10, 80, 0, 30, 0),\
++ REG_RULE(5260-10, 5350+10, 80, 0, 30,\
+ NL80211_RRF_NO_IR)
+ #define ATH_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\
+ NL80211_RRF_NO_IR)
+@@ -61,64 +63,79 @@ static int __ath_regd_init(struct ath_regulatory *reg);
+ #define ATH_5GHZ_NO_MIDBAND ATH_5GHZ_5150_5350, \
+ ATH_5GHZ_5725_5850
+
++#define REGD_RULES(...) \
++ .reg_rules = { __VA_ARGS__ }, \
++ .n_reg_rules = ARRAY_SIZE(((struct ieee80211_reg_rule[]) { __VA_ARGS__ }))
++
+ /* Can be used for:
+ * 0x60, 0x61, 0x62 */
+ static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
+- .n_reg_rules = 5,
+ .alpha2 = "99",
+- .reg_rules = {
++ REGD_RULES(
+ ATH_2GHZ_ALL,
+ ATH_5GHZ_ALL,
+- }
++ )
+ };
+
+ /* Can be used by 0x63 and 0x65 */
+ static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
+- .n_reg_rules = 4,
+ .alpha2 = "99",
+- .reg_rules = {
++ REGD_RULES(
+ ATH_2GHZ_CH01_11,
+ ATH_2GHZ_CH12_13,
+ ATH_5GHZ_NO_MIDBAND,
+- }
++ )
+ };
+
+ /* Can be used by 0x64 only */
+ static const struct ieee80211_regdomain ath_world_regdom_64 = {
+- .n_reg_rules = 3,
+ .alpha2 = "99",
+- .reg_rules = {
++ REGD_RULES(
+ ATH_2GHZ_CH01_11,
+ ATH_5GHZ_NO_MIDBAND,
+- }
++ )
+ };
+
+ /* Can be used by 0x66 and 0x69 */
+ static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
+- .n_reg_rules = 3,
+ .alpha2 = "99",
+- .reg_rules = {
++ REGD_RULES(
+ ATH_2GHZ_CH01_11,
+ ATH_5GHZ_ALL,
+- }
++ )
+ };
+
+ /* Can be used by 0x67, 0x68, 0x6A and 0x6C */
+ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
+- .n_reg_rules = 4,
+ .alpha2 = "99",
+- .reg_rules = {
++ REGD_RULES(
+ ATH_2GHZ_CH01_11,
+ ATH_2GHZ_CH12_13,
+ ATH_5GHZ_ALL,
+- }
++ )
+ };
+
++static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
++{
++ return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
++}
++
++static bool is_default_regd(struct ath_regulatory *reg)
++{
++ return ath_regd_get_eepromRD(reg) == CTRY_DEFAULT;
++}
++
+ static bool dynamic_country_user_possible(struct ath_regulatory *reg)
+ {
++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++ return true;
++
+ if (IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
+ return true;
+
++ if (is_default_regd(reg))
++ return true;
++
+ switch (reg->country_code) {
+ case CTRY_UNITED_STATES:
+ case CTRY_JAPAN1:
+@@ -188,6 +205,8 @@ static bool dynamic_country_user_possible(struct ath_regulatory *reg)
+
+ static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg)
+ {
++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++ return true;
+ if (!IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_REG_HINTS))
+ return false;
+ if (!dynamic_country_user_possible(reg))
+@@ -202,11 +221,6 @@ static inline bool is_wwr_sku(u16 regd)
+ (regd == WORLD));
+ }
+
+-static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
+-{
+- return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
+-}
+-
+ bool ath_is_world_regd(struct ath_regulatory *reg)
+ {
+ return is_wwr_sku(ath_regd_get_eepromRD(reg));
+@@ -345,6 +359,9 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
+ struct ieee80211_channel *ch;
+ unsigned int i;
+
++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++ return;
++
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ if (!wiphy->bands[band])
+ continue;
+@@ -379,6 +396,9 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy,
+ {
+ struct ieee80211_supported_band *sband;
+
++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++ return;
++
+ sband = wiphy->bands[NL80211_BAND_2GHZ];
+ if (!sband)
+ return;
+@@ -408,6 +428,9 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy,
+ struct ieee80211_channel *ch;
+ unsigned int i;
+
++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++ return;
++
+ if (!wiphy->bands[NL80211_BAND_5GHZ])
+ return;
+
+@@ -640,6 +663,13 @@ ath_regd_init_wiphy(struct ath_regulatory *reg,
+ const struct ieee80211_regdomain *regd;
+
+ wiphy->reg_notifier = reg_notifier;
++
++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++ return 0;
++
++ if (is_default_regd(reg))
++ return 0;
++
+ wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
+ REGULATORY_CUSTOM_REG;
+
+diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h
+index cdb1e9a..2574f80 100644
+--- a/drivers/net/wireless/ath/regd_common.h
++++ b/drivers/net/wireless/ath/regd_common.h
+@@ -32,6 +32,7 @@ enum EnumRd {
+ FCC2_WORLD = 0x21,
+ FCC2_ETSIC = 0x22,
+ FCC6_WORLD = 0x23,
++ FCC3_FCCA_2 = 0x2A,
+ FRANCE_RES = 0x31,
+ FCC3_FCCA = 0x3A,
+ FCC3_WORLD = 0x3B,
+@@ -173,6 +174,7 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = {
+ {FCC2_WORLD, CTL_FCC, CTL_ETSI},
+ {FCC2_ETSIC, CTL_FCC, CTL_ETSI},
+ {FCC3_FCCA, CTL_FCC, CTL_FCC},
++ {FCC3_FCCA_2, CTL_FCC, CTL_FCC},
+ {FCC3_WORLD, CTL_FCC, CTL_ETSI},
+ {FCC3_ETSIC, CTL_FCC, CTL_ETSI},
+ {FCC4_FCCA, CTL_FCC, CTL_FCC},
+@@ -486,6 +488,7 @@ static struct country_code_to_enum_rd allCountries[] = {
+ {CTRY_UAE, NULL1_WORLD, "AE"},
+ {CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"},
+ {CTRY_UNITED_STATES, FCC3_FCCA, "US"},
++ {CTRY_UNITED_STATES, FCC3_FCCA_2, "US"},
+ {CTRY_UNITED_STATES2, FCC3_FCCA, "US"},
+ {CTRY_UNITED_STATES3, FCC3_FCCA, "US"},
+ /* This "PS" is for US public safety actually... to support this we
+diff --git a/local-symbols b/local-symbols
+index 0743888..b200b00 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -101,6 +101,7 @@ ADM8211=
+ ATH_COMMON=
+ WLAN_VENDOR_ATH=
+ ATH_DEBUG=
++ATH_USER_REGD=
+ ATH_TRACEPOINTS=
+ ATH_REG_DYNAMIC_USER_REG_HINTS=
+ ATH_REG_DYNAMIC_USER_CERT_TESTING=
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 233ebab..2c0c1f1 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -3365,6 +3365,8 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band,
+ enum environment_cap env = ENVIRON_ANY;
+ struct regulatory_request *request = NULL, *lr;
+
++ return;
++
+ /* IE len must be evenly divisible by 2 */
+ if (country_ie_len & 0x01)
+ return;
+@@ -3616,6 +3618,7 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag)
+
+ void regulatory_hint_disconnect(void)
+ {
++ return;
+ /* Restore of regulatory settings is not required when wiphy(s)
+ * ignore IE from connected access point but clearance of beacon hints
+ * is required when wiphy(s) supports beacon hints.
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-sync-backports-patches-ath5k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-sync-backports-patches-ath5k.patch
new file mode 100644
index 0000000..e0051b5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-sync-backports-patches-ath5k.patch
@@ -0,0 +1,315 @@
+From 410e3c08cd5f7bd85e3ca3965fcbf79b9eb6d1e4 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 12 Mar 2024 11:26:26 +0800
+Subject: [PATCH 03/61] sync backports patches/ath5k
+
+---
+ drivers/net/wireless/ath/ath5k/ath5k.h | 1 +
+ drivers/net/wireless/ath/ath5k/base.c | 8 +-
+ drivers/net/wireless/ath/ath5k/debug.c | 93 +++++++++++++++++++
+ drivers/net/wireless/ath/ath5k/dma.c | 8 ++
+ drivers/net/wireless/ath/ath5k/initvals.c | 6 ++
+ drivers/net/wireless/ath/ath5k/mac80211-ops.c | 9 +-
+ drivers/net/wireless/ath/ath5k/pci.c | 2 +
+ drivers/net/wireless/ath/ath5k/reset.c | 2 +
+ include/linux/ath5k_platform.h | 30 ++++++
+ 9 files changed, 150 insertions(+), 9 deletions(-)
+ create mode 100644 include/linux/ath5k_platform.h
+
+diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
+index 308a429..0e6d184 100644
+--- a/drivers/net/wireless/ath/ath5k/ath5k.h
++++ b/drivers/net/wireless/ath/ath5k/ath5k.h
+@@ -1372,6 +1372,7 @@ struct ath5k_hw {
+ u8 ah_coverage_class;
+ bool ah_ack_bitrate_high;
+ u8 ah_bwmode;
++ u8 ah_bwmode_debug;
+ bool ah_short_slot;
+
+ /* Antenna Control */
+diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
+index bad83fd..fb0366c 100644
+--- a/drivers/net/wireless/ath/ath5k/base.c
++++ b/drivers/net/wireless/ath/ath5k/base.c
+@@ -465,6 +465,9 @@ ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef)
+ return -EINVAL;
+ }
+
++ if (ah->ah_bwmode_debug != AR5K_BWMODE_DEFAULT)
++ ah->ah_bwmode = ah->ah_bwmode_debug;
++
+ /*
+ * To switch channels clear any pending DMA operations;
+ * wait long enough for the RX fifo to drain, reset the
+@@ -2009,7 +2012,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
+ }
+
+ if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs +
+- ah->num_mesh_vifs > 1) ||
++ ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) ||
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ u64 tsf = ath5k_hw_get_tsf64(ah);
+ u32 tsftu = TSF_TO_TU(tsf);
+@@ -2095,7 +2098,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
+
+ intval = ah->bintval & AR5K_BEACON_PERIOD;
+ if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs
+- + ah->num_mesh_vifs > 1) {
++ + ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) {
+ intval /= ATH_BCBUF; /* staggered multi-bss beacons */
+ if (intval < 15)
+ ATH5K_WARN(ah, "intval %u is too low, min 15\n",
+@@ -2561,6 +2564,7 @@ static const struct ieee80211_iface_limit if_limits[] = {
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+ #endif
+ BIT(NL80211_IFTYPE_AP) },
++ { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
+ };
+
+ static const struct ieee80211_iface_combination if_comb = {
+diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
+index ec13051..239d789 100644
+--- a/drivers/net/wireless/ath/ath5k/debug.c
++++ b/drivers/net/wireless/ath/ath5k/debug.c
+@@ -803,6 +803,97 @@ static const struct file_operations fops_ani = {
+ .llseek = default_llseek,
+ };
+
++/* debugfs: bwmode */
++
++static ssize_t read_file_bwmode(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath5k_hw *ah = file->private_data;
++ char buf[15];
++ unsigned int len = 0;
++
++ int cur_ah_bwmode = ah->ah_bwmode_debug;
++
++#define print_selected(MODE, LABEL) \
++ if (cur_ah_bwmode == MODE) \
++ len += snprintf(buf+len, sizeof(buf)-len, "[%s]", LABEL); \
++ else \
++ len += snprintf(buf+len, sizeof(buf)-len, "%s", LABEL); \
++ len += snprintf(buf+len, sizeof(buf)-len, " ");
++
++ print_selected(AR5K_BWMODE_5MHZ, "5");
++ print_selected(AR5K_BWMODE_10MHZ, "10");
++ print_selected(AR5K_BWMODE_DEFAULT, "20");
++ print_selected(AR5K_BWMODE_40MHZ, "40");
++#undef print_selected
++
++ len += snprintf(buf+len, sizeof(buf)-len, "\n");
++
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_bwmode(struct file *file,
++ const char __user *userbuf,
++ size_t count, loff_t *ppos)
++{
++ struct ath5k_hw *ah = file->private_data;
++ char buf[3];
++ int bw = 20;
++ int tobwmode = AR5K_BWMODE_DEFAULT;
++
++ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
++ return -EFAULT;
++
++ /* TODO: Add check for active interface */
++
++ if(strncmp(buf, "5", 1) == 0 ) {
++ tobwmode = AR5K_BWMODE_5MHZ;
++ bw = 5;
++ } else if ( strncmp(buf, "10", 2) == 0 ) {
++ tobwmode = AR5K_BWMODE_10MHZ;
++ bw = 10;
++ } else if ( strncmp(buf, "20", 2) == 0 ) {
++ tobwmode = AR5K_BWMODE_DEFAULT;
++ bw = 20;
++ } else if ( strncmp(buf, "40", 2) == 0 ) {
++ tobwmode = AR5K_BWMODE_40MHZ;
++ bw = 40;
++ } else
++ return -EINVAL;
++
++ ATH5K_INFO(ah, "Changing to %imhz channel width[%i]\n",
++ bw, tobwmode);
++
++ switch (ah->ah_radio) {
++ /* TODO: only define radios that actually support 5/10mhz channels */
++ case AR5K_RF5413:
++ case AR5K_RF5110:
++ case AR5K_RF5111:
++ case AR5K_RF5112:
++ case AR5K_RF2413:
++ case AR5K_RF2316:
++ case AR5K_RF2317:
++ case AR5K_RF2425:
++ if(ah->ah_bwmode_debug != tobwmode) {
++ mutex_lock(&ah->lock);
++ ah->ah_bwmode = tobwmode;
++ ah->ah_bwmode_debug = tobwmode;
++ mutex_unlock(&ah->lock);
++ }
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++ return count;
++}
++
++static const struct file_operations fops_bwmode = {
++ .read = read_file_bwmode,
++ .write = write_file_bwmode,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
+
+ /* debugfs: queues etc */
+
+@@ -995,6 +1086,8 @@ ath5k_debug_init_device(struct ath5k_hw *ah)
+ debugfs_create_file("queue", 0600, phydir, ah, &fops_queue);
+ debugfs_create_bool("32khz_clock", 0600, phydir,
+ &ah->ah_use_32khz_clock);
++ debugfs_create_file("bwmode", S_IWUSR | S_IRUSR, phydir, ah,
++ &fops_bwmode);
+ }
+
+ /* functions used in other places */
+diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
+index d9e376e..db06ff8 100644
+--- a/drivers/net/wireless/ath/ath5k/dma.c
++++ b/drivers/net/wireless/ath/ath5k/dma.c
+@@ -854,10 +854,18 @@ ath5k_hw_dma_init(struct ath5k_hw *ah)
+ * guess we can tweak it and see how it goes ;-)
+ */
+ if (ah->ah_version != AR5K_AR5210) {
++#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79)
+ AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+ AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
+ AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
+ AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
++#else
++ /* WAR for AR71xx PCI bug */
++ AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
++ AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
++ AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
++ AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_4B);
++#endif
+ }
+
+ /* Pre-enable interrupts on 5211/5212*/
+diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c
+index ee1c2fa..122fe1c 100644
+--- a/drivers/net/wireless/ath/ath5k/initvals.c
++++ b/drivers/net/wireless/ath/ath5k/initvals.c
+@@ -62,8 +62,14 @@ static const struct ath5k_ini ar5210_ini[] = {
+ { AR5K_IMR, 0 },
+ { AR5K_IER, AR5K_IER_DISABLE },
+ { AR5K_BSR, 0, AR5K_INI_READ },
++#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79)
+ { AR5K_TXCFG, AR5K_DMASIZE_128B },
+ { AR5K_RXCFG, AR5K_DMASIZE_128B },
++#else
++ /* WAR for AR71xx PCI bug */
++ { AR5K_TXCFG, AR5K_DMASIZE_128B },
++ { AR5K_RXCFG, AR5K_DMASIZE_4B },
++#endif
+ { AR5K_CFG, AR5K_INIT_CFG },
+ { AR5K_TOPS, 8 },
+ { AR5K_RXNOFRM, 8 },
+diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+index da9dc62..2fb37d3 100644
+--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
++++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+@@ -86,13 +86,8 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ goto end;
+ }
+
+- /* Don't allow other interfaces if one ad-hoc is configured.
+- * TODO: Fix the problems with ad-hoc and multiple other interfaces.
+- * We would need to operate the HW in ad-hoc mode to allow TSF updates
+- * for the IBSS, but this breaks with additional AP or STA interfaces
+- * at the moment. */
+- if (ah->num_adhoc_vifs ||
+- (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
++ /* Don't allow more than one ad-hoc interface */
++ if (ah->num_adhoc_vifs && vif->type == NL80211_IFTYPE_ADHOC) {
+ ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n");
+ ret = -ELNRNG;
+ goto end;
+diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
+index c4315dd..2aae4de 100644
+--- a/drivers/net/wireless/ath/ath5k/pci.c
++++ b/drivers/net/wireless/ath/ath5k/pci.c
+@@ -47,6 +47,8 @@ static const struct pci_device_id ath5k_pci_id_table[] = {
+ { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
+ { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
+ { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
++ { PCI_VDEVICE(ATHEROS, 0xff16) }, /* 2413,2414 sx76x on lantiq_danube */
++ { PCI_VDEVICE(ATHEROS, 0xff1a) }, /* 2417 arv45xx on lantiq_danube */
+ { PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */
+ { 0 }
+ };
+diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
+index 9fdb528..eabf225 100644
+--- a/drivers/net/wireless/ath/ath5k/reset.c
++++ b/drivers/net/wireless/ath/ath5k/reset.c
+@@ -1154,6 +1154,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ tsf_lo = 0;
+ mode = 0;
+
++#if 0
+ /*
+ * Sanity check for fast flag
+ * Fast channel change only available
+@@ -1161,6 +1162,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ */
+ if (fast && (ah->ah_radio != AR5K_RF2413) &&
+ (ah->ah_radio != AR5K_RF5413))
++#endif
+ fast = false;
+
+ /* Disable sleep clock operation
+diff --git a/include/linux/ath5k_platform.h b/include/linux/ath5k_platform.h
+new file mode 100644
+index 0000000..ec85224
+--- /dev/null
++++ b/include/linux/ath5k_platform.h
+@@ -0,0 +1,30 @@
++/*
++ * Copyright (c) 2008 Atheros Communications Inc.
++ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
++ * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef _LINUX_ATH5K_PLATFORM_H
++#define _LINUX_ATH5K_PLATFORM_H
++
++#define ATH5K_PLAT_EEP_MAX_WORDS 2048
++
++struct ath5k_platform_data {
++ u16 *eeprom_data;
++ u8 *macaddr;
++};
++
++#endif /* _LINUX_ATH5K_PLATFORM_H */
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-sync-backports-patches-ath9k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-sync-backports-patches-ath9k.patch
new file mode 100644
index 0000000..93b4572
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-sync-backports-patches-ath9k.patch
@@ -0,0 +1,2508 @@
+From 58736b0bd1ae576889c23c0639aa36f29ab6a74f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 12 Mar 2024 11:26:51 +0800
+Subject: [PATCH 04/61] sync backports patches/ath9k
+
+---
+ drivers/net/wireless/ath/ath.h | 2 +
+ drivers/net/wireless/ath/ath9k/Kconfig | 13 +
+ drivers/net/wireless/ath/ath9k/Makefile | 1 +
+ drivers/net/wireless/ath/ath9k/ahb.c | 260 ++++++++++++-
+ drivers/net/wireless/ath/ath9k/ani.h | 2 +-
+ drivers/net/wireless/ath/ath9k/ar5008_phy.c | 72 ++--
+ drivers/net/wireless/ath/ath9k/ar9002_phy.h | 11 +
+ drivers/net/wireless/ath/ath9k/ar9003_phy.c | 95 +----
+ drivers/net/wireless/ath/ath9k/ath9k.h | 34 +-
+ drivers/net/wireless/ath/ath9k/channel.c | 7 +
+ drivers/net/wireless/ath/ath9k/common-debug.c | 107 +++++
+ drivers/net/wireless/ath/ath9k/common-debug.h | 4 +
+ drivers/net/wireless/ath/ath9k/common.c | 21 +-
+ drivers/net/wireless/ath/ath9k/debug.c | 107 +++++
+ drivers/net/wireless/ath/ath9k/gpio.c | 365 ++++++++++++++++--
+ drivers/net/wireless/ath/ath9k/hsr.c | 247 ++++++++++++
+ drivers/net/wireless/ath/ath9k/hsr.h | 48 +++
+ .../net/wireless/ath/ath9k/htc_drv_debug.c | 2 +
+ drivers/net/wireless/ath/ath9k/htc_drv_init.c | 7 +-
+ drivers/net/wireless/ath/ath9k/hw-ops.h | 6 +
+ drivers/net/wireless/ath/ath9k/hw.c | 154 ++++++--
+ drivers/net/wireless/ath/ath9k/hw.h | 12 +
+ drivers/net/wireless/ath/ath9k/init.c | 48 ++-
+ drivers/net/wireless/ath/ath9k/mac.c | 9 +-
+ drivers/net/wireless/ath/ath9k/main.c | 12 +
+ drivers/net/wireless/ath/ath9k/pci.c | 1 +
+ drivers/net/wireless/ath/ath9k/phy.h | 3 +
+ include/linux/ath9k_platform.h | 7 +
+ local-symbols | 1 +
+ 29 files changed, 1438 insertions(+), 220 deletions(-)
+ create mode 100644 drivers/net/wireless/ath/ath9k/hsr.c
+ create mode 100644 drivers/net/wireless/ath/ath9k/hsr.h
+
+diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
+index 888c5e2..f7e2e89 100644
+--- a/drivers/net/wireless/ath/ath.h
++++ b/drivers/net/wireless/ath/ath.h
+@@ -153,6 +153,7 @@ struct ath_common {
+ int debug_mask;
+ enum ath_device_state state;
+ unsigned long op_flags;
++ u32 chan_bw;
+
+ struct ath_ani ani;
+
+@@ -181,6 +182,7 @@ struct ath_common {
+ const struct ath_ops *ops;
+ const struct ath_bus_ops *bus_ops;
+ const struct ath_ps_ops *ps_ops;
++ const struct ieee80211_ops *ieee_ops;
+
+ bool btcoex_enabled;
+ bool disable_ani;
+diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
+index b97598f..d3e72a8 100644
+--- a/drivers/net/wireless/ath/ath9k/Kconfig
++++ b/drivers/net/wireless/ath/ath9k/Kconfig
+@@ -58,6 +58,19 @@ config ATH9K_AHB
+ Say Y, if you have a SoC with a compatible built-in
+ wireless MAC. Say N if unsure.
+
++config ATH9K_UBNTHSR
++ bool "Ubiquiti UniFi Outdoor Plus HSR support"
++ depends on ATH9K
++ ---help---
++ This options enables code to control the HSR RF
++ filter in the receive path of the Ubiquiti UniFi
++ Outdoor Plus access point.
++
++ Say Y if you want to use the access point. The
++ code will only be used if the device is detected,
++ so it does not harm other setup other than occupying
++ a bit of memory.
++
+ config ATH9K_DEBUGFS
+ bool "Atheros ath9k debugging"
+ depends on ATH9K && DEBUG_FS && MAC80211_DEBUGFS
+diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
+index 847c8a8..6427bc6 100644
+--- a/drivers/net/wireless/ath/ath9k/Makefile
++++ b/drivers/net/wireless/ath/ath9k/Makefile
+@@ -17,6 +17,7 @@ ath9k-$(CPTCFG_ATH9K_DFS_CERTIFIED) += dfs.o
+ ath9k-$(CPTCFG_ATH9K_TX99) += tx99.o
+ ath9k-$(CPTCFG_ATH9K_WOW) += wow.o
+ ath9k-$(CPTCFG_ATH9K_HWRNG) += rng.o
++ath9k-$(CPTCFG_ATH9K_UBNTHSR) += hsr.o
+
+ ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o
+
+diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
+index 1a6697b..f9a76e7 100644
+--- a/drivers/net/wireless/ath/ath9k/ahb.c
++++ b/drivers/net/wireless/ath/ath9k/ahb.c
+@@ -20,7 +20,15 @@
+ #include <linux/platform_device.h>
+ #include <linux/module.h>
+ #include <linux/mod_devicetable.h>
++#include <linux/of_device.h>
+ #include "ath9k.h"
++#include <linux/ath9k_platform.h>
++
++#ifdef CONFIG_OF
++#include <asm/mach-ath79/ath79.h>
++#include <asm/mach-ath79/ar71xx_regs.h>
++#include <linux/mtd/mtd.h>
++#endif
+
+ static const struct platform_device_id ath9k_platform_id_table[] = {
+ {
+@@ -69,6 +77,236 @@ static const struct ath_bus_ops ath_ahb_bus_ops = {
+ .eeprom_read = ath_ahb_eeprom_read,
+ };
+
++#ifdef CONFIG_OF
++
++#define QCA955X_DDR_CTL_CONFIG 0x108
++#define QCA955X_DDR_CTL_CONFIG_ACT_WMAC BIT(23)
++
++static int of_get_wifi_cal(struct device_node *np, struct ath9k_platform_data *pdata)
++{
++#ifdef CONFIG_MTD
++ struct device_node *mtd_np = NULL;
++ size_t retlen;
++ int size, ret;
++ struct mtd_info *mtd;
++ const char *part;
++ const __be32 *list;
++ phandle phandle;
++
++ list = of_get_property(np, "mtd-cal-data", &size);
++ if (!list)
++ return 0;
++
++ if (size != (2 * sizeof(*list)))
++ return 1;
++
++ phandle = be32_to_cpup(list++);
++ if (phandle)
++ mtd_np = of_find_node_by_phandle(phandle);
++
++ if (!mtd_np)
++ return 1;
++
++ part = of_get_property(mtd_np, "label", NULL);
++ if (!part)
++ part = mtd_np->name;
++
++ mtd = get_mtd_device_nm(part);
++ if (IS_ERR(mtd))
++ return 1;
++
++ ret = mtd_read(mtd, be32_to_cpup(list), sizeof(pdata->eeprom_data),
++ &retlen, (u8*)pdata->eeprom_data);
++ put_mtd_device(mtd);
++
++#endif
++ return 0;
++}
++
++static int ar913x_wmac_reset(void)
++{
++ ath79_device_reset_set(AR913X_RESET_AMBA2WMAC);
++ mdelay(10);
++
++ ath79_device_reset_clear(AR913X_RESET_AMBA2WMAC);
++ mdelay(10);
++
++ return 0;
++}
++
++static int ar933x_wmac_reset(void)
++{
++ int retries = 20;
++
++ ath79_device_reset_set(AR933X_RESET_WMAC);
++ ath79_device_reset_clear(AR933X_RESET_WMAC);
++
++ while (1) {
++ u32 bootstrap;
++
++ bootstrap = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
++ if ((bootstrap & AR933X_BOOTSTRAP_EEPBUSY) == 0)
++ return 0;
++
++ if (retries-- == 0)
++ break;
++
++ udelay(10000);
++ }
++
++ pr_err("ar933x: WMAC reset timed out");
++ return -ETIMEDOUT;
++}
++
++static int qca955x_wmac_reset(void)
++{
++ int i;
++
++ /* Try to wait for WMAC DDR activity to stop */
++ for (i = 0; i < 10; i++) {
++ if (!(__raw_readl(ath79_ddr_base + QCA955X_DDR_CTL_CONFIG) &
++ QCA955X_DDR_CTL_CONFIG_ACT_WMAC))
++ break;
++
++ udelay(10);
++ }
++
++ ath79_device_reset_set(QCA955X_RESET_RTC);
++ udelay(10);
++ ath79_device_reset_clear(QCA955X_RESET_RTC);
++ udelay(10);
++
++ return 0;
++}
++
++enum {
++ AR913X_WMAC = 0,
++ AR933X_WMAC,
++ AR934X_WMAC,
++ QCA953X_WMAC,
++ QCA955X_WMAC,
++ QCA956X_WMAC,
++};
++
++static int ar9330_get_soc_revision(void)
++{
++ if (ath79_soc_rev == 1)
++ return ath79_soc_rev;
++
++ return 0;
++}
++
++static int ath79_get_soc_revision(void)
++{
++ return ath79_soc_rev;
++}
++
++static const struct of_ath_ahb_data {
++ u16 dev_id;
++ u32 bootstrap_reg;
++ u32 bootstrap_ref;
++
++ int (*soc_revision)(void);
++ int (*wmac_reset)(void);
++} of_ath_ahb_data[] = {
++ [AR913X_WMAC] = {
++ .dev_id = AR5416_AR9100_DEVID,
++ .wmac_reset = ar913x_wmac_reset,
++
++ },
++ [AR933X_WMAC] = {
++ .dev_id = AR9300_DEVID_AR9330,
++ .bootstrap_reg = AR933X_RESET_REG_BOOTSTRAP,
++ .bootstrap_ref = AR933X_BOOTSTRAP_REF_CLK_40,
++ .soc_revision = ar9330_get_soc_revision,
++ .wmac_reset = ar933x_wmac_reset,
++ },
++ [AR934X_WMAC] = {
++ .dev_id = AR9300_DEVID_AR9340,
++ .bootstrap_reg = AR934X_RESET_REG_BOOTSTRAP,
++ .bootstrap_ref = AR934X_BOOTSTRAP_REF_CLK_40,
++ .soc_revision = ath79_get_soc_revision,
++ },
++ [QCA953X_WMAC] = {
++ .dev_id = AR9300_DEVID_AR953X,
++ .bootstrap_reg = QCA953X_RESET_REG_BOOTSTRAP,
++ .bootstrap_ref = QCA953X_BOOTSTRAP_REF_CLK_40,
++ .soc_revision = ath79_get_soc_revision,
++ },
++ [QCA955X_WMAC] = {
++ .dev_id = AR9300_DEVID_QCA955X,
++ .bootstrap_reg = QCA955X_RESET_REG_BOOTSTRAP,
++ .bootstrap_ref = QCA955X_BOOTSTRAP_REF_CLK_40,
++ .wmac_reset = qca955x_wmac_reset,
++ },
++ [QCA956X_WMAC] = {
++ .dev_id = AR9300_DEVID_QCA956X,
++ .bootstrap_reg = QCA956X_RESET_REG_BOOTSTRAP,
++ .bootstrap_ref = QCA956X_BOOTSTRAP_REF_CLK_40,
++ .soc_revision = ath79_get_soc_revision,
++ },
++};
++
++const struct of_device_id of_ath_ahb_match[] = {
++ { .compatible = "qca,ar9130-wmac", .data = &of_ath_ahb_data[AR913X_WMAC] },
++ { .compatible = "qca,ar9330-wmac", .data = &of_ath_ahb_data[AR933X_WMAC] },
++ { .compatible = "qca,ar9340-wmac", .data = &of_ath_ahb_data[AR934X_WMAC] },
++ { .compatible = "qca,qca9530-wmac", .data = &of_ath_ahb_data[QCA953X_WMAC] },
++ { .compatible = "qca,qca9550-wmac", .data = &of_ath_ahb_data[QCA955X_WMAC] },
++ { .compatible = "qca,qca9560-wmac", .data = &of_ath_ahb_data[QCA956X_WMAC] },
++ {},
++};
++MODULE_DEVICE_TABLE(of, of_ath_ahb_match);
++
++static int of_ath_ahb_probe(struct platform_device *pdev)
++{
++ struct ath9k_platform_data *pdata;
++ const struct of_device_id *match;
++ const struct of_ath_ahb_data *data;
++ u8 led_pin;
++
++ match = of_match_device(of_ath_ahb_match, &pdev->dev);
++ data = (const struct of_ath_ahb_data *)match->data;
++
++ pdata = dev_get_platdata(&pdev->dev);
++
++ if (!of_property_read_u8(pdev->dev.of_node, "qca,led-pin", &led_pin))
++ pdata->led_pin = led_pin;
++ else
++ pdata->led_pin = -1;
++
++ if (of_property_read_bool(pdev->dev.of_node, "qca,tx-gain-buffalo"))
++ pdata->tx_gain_buffalo = true;
++
++ if (data->wmac_reset) {
++ data->wmac_reset();
++ pdata->external_reset = data->wmac_reset;
++ }
++
++ if (data->dev_id == AR9300_DEVID_AR953X) {
++ /*
++ * QCA953x only supports 25MHz refclk.
++ * Some vendors have an invalid bootstrap option
++ * set, which would break the WMAC here.
++ */
++ pdata->is_clk_25mhz = true;
++ } else if (data->bootstrap_reg && data->bootstrap_ref) {
++ u32 t = ath79_reset_rr(data->bootstrap_reg);
++ if (t & data->bootstrap_ref)
++ pdata->is_clk_25mhz = false;
++ else
++ pdata->is_clk_25mhz = true;
++ }
++
++ pdata->get_mac_revision = data->soc_revision;
++
++ if (of_get_wifi_cal(pdev->dev.of_node, pdata))
++ dev_err(&pdev->dev, "failed to load calibration data from mtd device\n");
++
++ return data->dev_id;
++}
++#endif
++
+ static int ath_ahb_probe(struct platform_device *pdev)
+ {
+ void __iomem *mem;
+@@ -80,6 +318,17 @@ static int ath_ahb_probe(struct platform_device *pdev)
+ int ret = 0;
+ struct ath_hw *ah;
+ char hw_name[64];
++ u16 dev_id;
++
++ if (id)
++ dev_id = id->driver_data;
++
++#ifdef CONFIG_OF
++ if (pdev->dev.of_node)
++ pdev->dev.platform_data = devm_kzalloc(&pdev->dev,
++ sizeof(struct ath9k_platform_data),
++ GFP_KERNEL);
++#endif
+
+ if (!dev_get_platdata(&pdev->dev)) {
+ dev_err(&pdev->dev, "no platform data specified\n");
+@@ -118,17 +367,23 @@ static int ath_ahb_probe(struct platform_device *pdev)
+ sc->mem = mem;
+ sc->irq = irq;
+
++#ifdef CONFIG_OF
++ dev_id = of_ath_ahb_probe(pdev);
++#endif
+ ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto err_free_hw;
+ }
+
+- ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops);
++ ret = ath9k_init_device(dev_id, sc, &ath_ahb_bus_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize device\n");
+ goto err_irq;
+ }
++#ifdef CONFIG_OF
++ pdev->dev.platform_data = NULL;
++#endif
+
+ ah = sc->sc_ah;
+ ath9k_hw_name(ah, hw_name, sizeof(hw_name));
+@@ -162,6 +417,9 @@ static struct platform_driver ath_ahb_driver = {
+ .remove_new = ath_ahb_remove,
+ .driver = {
+ .name = "ath9k",
++#ifdef CONFIG_OF
++ .of_match_table = of_ath_ahb_match,
++#endif
+ },
+ .id_table = ath9k_platform_id_table,
+ };
+diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
+index c40965b..f66f7ed 100644
+--- a/drivers/net/wireless/ath/ath9k/ani.h
++++ b/drivers/net/wireless/ath/ath9k/ani.h
+@@ -42,7 +42,7 @@
+ #define ATH9K_ANI_PERIOD 300
+
+ /* in ms */
+-#define ATH9K_ANI_POLLINTERVAL 1000
++#define ATH9K_ANI_POLLINTERVAL 300
+
+ #define ATH9K_SIG_FIRSTEP_SETTING_MIN 0
+ #define ATH9K_SIG_FIRSTEP_SETTING_MAX 20
+diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+index 7a45f5f..3f0ca1d 100644
+--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+@@ -969,55 +969,6 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
+ * on == 0 means more noise imm
+ */
+ u32 on = param ? 1 : 0;
+- /*
+- * make register setting for default
+- * (weak sig detect ON) come from INI file
+- */
+- int m1ThreshLow = on ?
+- aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+- int m2ThreshLow = on ?
+- aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+- int m1Thresh = on ?
+- aniState->iniDef.m1Thresh : m1Thresh_off;
+- int m2Thresh = on ?
+- aniState->iniDef.m2Thresh : m2Thresh_off;
+- int m2CountThr = on ?
+- aniState->iniDef.m2CountThr : m2CountThr_off;
+- int m2CountThrLow = on ?
+- aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+- int m1ThreshLowExt = on ?
+- aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+- int m2ThreshLowExt = on ?
+- aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+- int m1ThreshExt = on ?
+- aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+- int m2ThreshExt = on ?
+- aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+-
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+- AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+- m1ThreshLow);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+- AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+- m2ThreshLow);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+- AR_PHY_SFCORR_M1_THRESH, m1Thresh);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+- AR_PHY_SFCORR_M2_THRESH, m2Thresh);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+- AR_PHY_SFCORR_M2COUNT_THR, m2CountThr);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+- AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+- m2CountThrLow);
+-
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt);
+
+ if (on)
+ REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+@@ -1340,9 +1291,30 @@ void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
+ }
+ }
+
++static void ar5008_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
++{
++ int i, j;
++
++ REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
++ REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
++ REG_RMW_FIELD(ah, AR_PHY_TEST2, AR_PHY_TEST2_RX_OBS_SEL, 0);
++
++ memset(buf, 0, len);
++ for (i = 0; i < len; i++) {
++ for (j = 0; j < 4; j++) {
++ u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
++
++ buf[i] <<= 2;
++ buf[i] |= (regval & 1) | ((regval & BIT(9)) >> 8);
++ udelay(1);
++ }
++ }
++}
++
+ int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
+ {
+ struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
++ struct ath_hw_ops *ops = ath9k_hw_ops(ah);
+ static const u32 ar5416_cca_regs[6] = {
+ AR_PHY_CCA,
+ AR_PHY_CH1_CCA,
+@@ -1357,6 +1329,8 @@ int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
+ if (ret)
+ return ret;
+
++ ops->get_adc_entropy = ar5008_hw_get_adc_entropy;
++
+ priv_ops->rf_set_freq = ar5008_hw_set_channel;
+ priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate;
+
+diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+index 2b58245..d20a936 100644
+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+@@ -20,6 +20,12 @@
+ #define PHY_AGC_CLR 0x10000000
+ #define RFSILENT_BB 0x00002000
+
++#define AR_PHY_TEST_BBB_OBS_SEL 0x780000
++#define AR_PHY_TEST_BBB_OBS_SEL_S 19
++
++#define AR_PHY_TEST_RX_OBS_SEL_BIT5_S 23
++#define AR_PHY_TEST_RX_OBS_SEL_BIT5 (1 << AR_PHY_TEST_RX_OBS_SEL_BIT5_S)
++
+ #define AR_PHY_TURBO 0x9804
+ #define AR_PHY_FC_TURBO_MODE 0x00000001
+ #define AR_PHY_FC_TURBO_SHORT 0x00000002
+@@ -36,6 +42,9 @@
+
+ #define AR_PHY_TEST2 0x9808
+
++#define AR_PHY_TEST2_RX_OBS_SEL 0x3C00
++#define AR_PHY_TEST2_RX_OBS_SEL_S 10
++
+ #define AR_PHY_TIMING2 0x9810
+ #define AR_PHY_TIMING3 0x9814
+ #define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000
+@@ -393,6 +402,8 @@
+ #define AR_PHY_RFBUS_GRANT 0x9C20
+ #define AR_PHY_RFBUS_GRANT_EN 0x00000001
+
++#define AR_PHY_TST_ADC 0x9C24
++
+ #define AR_PHY_CHAN_INFO_GAIN_DIFF 0x9CF4
+ #define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320
+
+diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+index f715149..0246ad0 100644
+--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+@@ -42,20 +42,6 @@ static const int cycpwrThr1_table[] =
+ /* level: 0 1 2 3 4 5 6 7 8 */
+ { -6, -4, -2, 0, 2, 4, 6, 8 }; /* lvl 0-7, default 3 */
+
+-/*
+- * register values to turn OFDM weak signal detection OFF
+- */
+-static const int m1ThreshLow_off = 127;
+-static const int m2ThreshLow_off = 127;
+-static const int m1Thresh_off = 127;
+-static const int m2Thresh_off = 127;
+-static const int m2CountThr_off = 31;
+-static const int m2CountThrLow_off = 63;
+-static const int m1ThreshLowExt_off = 127;
+-static const int m2ThreshLowExt_off = 127;
+-static const int m1ThreshExt_off = 127;
+-static const int m2ThreshExt_off = 127;
+-
+ static const u8 ofdm2pwr[] = {
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+@@ -1065,11 +1051,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_channel *chan = ah->curchan;
+ struct ar5416AniState *aniState = &ah->ani;
+- int m1ThreshLow, m2ThreshLow;
+- int m1Thresh, m2Thresh;
+- int m2CountThr, m2CountThrLow;
+- int m1ThreshLowExt, m2ThreshLowExt;
+- int m1ThreshExt, m2ThreshExt;
+ s32 value, value2;
+
+ switch (cmd & ah->ani_function) {
+@@ -1083,61 +1064,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
+ */
+ u32 on = param ? 1 : 0;
+
+- if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+- goto skip_ws_det;
+-
+- m1ThreshLow = on ?
+- aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+- m2ThreshLow = on ?
+- aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+- m1Thresh = on ?
+- aniState->iniDef.m1Thresh : m1Thresh_off;
+- m2Thresh = on ?
+- aniState->iniDef.m2Thresh : m2Thresh_off;
+- m2CountThr = on ?
+- aniState->iniDef.m2CountThr : m2CountThr_off;
+- m2CountThrLow = on ?
+- aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+- m1ThreshLowExt = on ?
+- aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+- m2ThreshLowExt = on ?
+- aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+- m1ThreshExt = on ?
+- aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+- m2ThreshExt = on ?
+- aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+-
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+- AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+- m1ThreshLow);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+- AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+- m2ThreshLow);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+- AR_PHY_SFCORR_M1_THRESH,
+- m1Thresh);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+- AR_PHY_SFCORR_M2_THRESH,
+- m2Thresh);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+- AR_PHY_SFCORR_M2COUNT_THR,
+- m2CountThr);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+- AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+- m2CountThrLow);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+- m1ThreshLowExt);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+- m2ThreshLowExt);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M1_THRESH,
+- m1ThreshExt);
+- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+- AR_PHY_SFCORR_EXT_M2_THRESH,
+- m2ThreshExt);
+-skip_ws_det:
+ if (on)
+ REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+ AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+@@ -1915,6 +1841,26 @@ void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ }
+ }
+
++static void ar9003_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
++{
++ int i, j;
++
++ REG_RMW_FIELD(ah, AR_PHY_TEST(ah), AR_PHY_TEST_BBB_OBS_SEL, 1);
++ REG_CLR_BIT(ah, AR_PHY_TEST(ah), AR_PHY_TEST_RX_OBS_SEL_BIT5);
++ REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS(ah), AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
++
++ memset(buf, 0, len);
++ for (i = 0; i < len; i++) {
++ for (j = 0; j < 4; j++) {
++ u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
++
++ buf[i] <<= 2;
++ buf[i] |= (regval & 1) | ((regval & BIT(10)) >> 9);
++ udelay(1);
++ }
++ }
++}
++
+ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
+ {
+ struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+@@ -1951,6 +1897,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
+ priv_ops->set_radar_params = ar9003_hw_set_radar_params;
+ priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;
+
++ ops->get_adc_entropy = ar9003_hw_get_adc_entropy;
+ ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
+ ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
+ ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
+diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
+index 97f4710..94efbdb 100644
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -25,6 +25,8 @@
+ #include <linux/completion.h>
+ #include <linux/time.h>
+ #include <linux/hw_random.h>
++#include <linux/gpio/driver.h>
++#include <linux/reset.h>
+
+ #include "common.h"
+ #include "debug.h"
+@@ -90,7 +92,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
+ (_l) &= ((_sz) - 1); \
+ } while (0)
+
+-#define ATH_RXBUF 512
++#define ATH_RXBUF 256
+ #define ATH_TXBUF 512
+ #define ATH_TXBUF_RESERVE 5
+ #define ATH_TXMAXTRY 13
+@@ -845,6 +847,9 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
+ #ifdef CPTCFG_MAC80211_LEDS
+ void ath_init_leds(struct ath_softc *sc);
+ void ath_deinit_leds(struct ath_softc *sc);
++int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name,
++ const char *trigger, bool active_low);
++
+ #else
+ static inline void ath_init_leds(struct ath_softc *sc)
+ {
+@@ -981,6 +986,21 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
+
+ #define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */
+
++struct ath_led {
++ struct list_head list;
++ struct ath_softc *sc;
++ const struct gpio_led *gpio;
++ struct led_classdev cdev;
++};
++
++#ifdef CONFIG_GPIOLIB
++struct ath9k_gpio_chip {
++ struct ath_softc *sc;
++ char label[32];
++ struct gpio_chip gchip;
++};
++#endif
++
+ struct ath_softc {
+ struct ieee80211_hw *hw;
+ struct device *dev;
+@@ -994,6 +1014,9 @@ struct ath_softc {
+ struct ath_hw *sc_ah;
+ void __iomem *mem;
+ int irq;
++#ifdef CONFIG_OF
++ struct reset_control *reset;
++#endif
+ spinlock_t sc_serial_rw;
+ spinlock_t sc_pm_lock;
+ spinlock_t sc_pcu_lock;
+@@ -1034,9 +1057,12 @@ struct ath_softc {
+ spinlock_t chan_lock;
+
+ #ifdef CPTCFG_MAC80211_LEDS
+- bool led_registered;
+- char led_name[32];
+- struct led_classdev led_cdev;
++ const char *led_default_trigger;
++ struct list_head leds;
++#ifdef CONFIG_GPIOLIB
++ struct ath9k_gpio_chip *gpiochip;
++ struct platform_device *btnpdev; /* gpio-keys-polled */
++#endif
+ #endif
+
+ #ifdef CPTCFG_ATH9K_DEBUGFS
+diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
+index 86c59bd..49cf036 100644
+--- a/drivers/net/wireless/ath/ath9k/channel.c
++++ b/drivers/net/wireless/ath/ath9k/channel.c
+@@ -15,6 +15,7 @@
+ */
+
+ #include "ath9k.h"
++#include "hsr.h"
+
+ /* Set/change channels. If the channel is really being changed, it's done
+ * by reseting the chip. To accomplish this we must first cleanup any pending
+@@ -22,6 +23,7 @@
+ */
+ static int ath_set_channel(struct ath_softc *sc)
+ {
++ struct device_node *np = sc->dev->of_node;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_hw *hw = sc->hw;
+@@ -42,6 +44,11 @@ static int ath_set_channel(struct ath_softc *sc)
+ ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+ chan->center_freq, chandef->width);
+
++ if (of_property_read_bool(np, "ubnt,hsr")) {
++ ath9k_hsr_enable(ah, chandef->width, chan->center_freq);
++ ath9k_hsr_status(ah);
++ }
++
+ /* update survey stats for the old channel before switching */
+ spin_lock_irqsave(&common->cc_lock, flags);
+ ath_update_survey_stats(sc);
+diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c
+index 7aefb79..944bbe0 100644
+--- a/drivers/net/wireless/ath/ath9k/common-debug.c
++++ b/drivers/net/wireless/ath/ath9k/common-debug.c
+@@ -260,3 +260,110 @@ void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
+ &fops_phy_err);
+ }
+ EXPORT_SYMBOL(ath9k_cmn_debug_phy_err);
++
++static ssize_t read_file_eeprom(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_hw *ah = file->private_data;
++ struct ath_common *common = ath9k_hw_common(ah);
++ int bytes = 0;
++ int pos = *ppos;
++ int size = 4096;
++ u16 val;
++ int i;
++
++ if (AR_SREV_9300_20_OR_LATER(ah))
++ size = 16384;
++
++ if (*ppos < 0)
++ return -EINVAL;
++
++ if (count > size - *ppos)
++ count = size - *ppos;
++
++ for (i = *ppos / 2; count > 0; count -= bytes, *ppos += bytes, i++) {
++ void *from = &val;
++
++ if (!common->bus_ops->eeprom_read(common, i, &val))
++ val = 0xffff;
++
++ if (*ppos % 2) {
++ from++;
++ bytes = 1;
++ } else if (count == 1) {
++ bytes = 1;
++ } else {
++ bytes = 2;
++ }
++ if (copy_to_user(user_buf, from, bytes))
++ return -EFAULT;
++ user_buf += bytes;
++ }
++ return *ppos - pos;
++}
++
++static const struct file_operations fops_eeprom = {
++ .read = read_file_eeprom,
++ .open = simple_open,
++ .owner = THIS_MODULE
++};
++
++void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy,
++ struct ath_hw *ah)
++{
++ debugfs_create_file("eeprom", S_IRUSR, debugfs_phy, ah,
++ &fops_eeprom);
++}
++EXPORT_SYMBOL(ath9k_cmn_debug_eeprom);
++
++static ssize_t read_file_chan_bw(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_hw *ah = file->private_data;
++ struct ath_common *common = ath9k_hw_common(ah);
++ char buf[32];
++ unsigned int len;
++
++ len = sprintf(buf, "0x%08x\n", common->chan_bw);
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_chan_bw(struct file *file, const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_hw *ah = file->private_data;
++ struct ath_common *common = ath9k_hw_common(ah);
++ unsigned long chan_bw;
++ char buf[32];
++ ssize_t len;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++ if (kstrtoul(buf, 0, &chan_bw))
++ return -EINVAL;
++
++ common->chan_bw = chan_bw;
++ if (!test_bit(ATH_OP_INVALID, &common->op_flags))
++ common->ieee_ops->config(ah->hw, IEEE80211_CONF_CHANGE_CHANNEL);
++
++ return count;
++}
++
++static const struct file_operations fops_chanbw = {
++ .read = read_file_chan_bw,
++ .write = write_file_chan_bw,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy,
++ struct ath_hw *ah)
++{
++ debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, debugfs_phy, ah,
++ &fops_chanbw);
++}
++EXPORT_SYMBOL(ath9k_cmn_debug_chanbw);
+diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h
+index 54f4c42..7a8b7ed 100644
+--- a/drivers/net/wireless/ath/ath9k/common-debug.h
++++ b/drivers/net/wireless/ath/ath9k/common-debug.h
+@@ -69,6 +69,10 @@ void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
+ struct ath_hw *ah);
+ void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
+ struct ath_hw *ah);
++void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy,
++ struct ath_hw *ah);
++void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy,
++ struct ath_hw *ah);
+ void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats,
+ struct ath_rx_status *rs);
+ void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
+diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
+index 099f3d4..86d4a50 100644
+--- a/drivers/net/wireless/ath/ath9k/common.c
++++ b/drivers/net/wireless/ath/ath9k/common.c
+@@ -297,11 +297,13 @@ EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
+ /*
+ * Update internal channel flags.
+ */
+-static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
++static void ath9k_cmn_update_ichannel(struct ath_common *common,
++ struct ath9k_channel *ichan,
+ struct cfg80211_chan_def *chandef)
+ {
+ struct ieee80211_channel *chan = chandef->chan;
+ u16 flags = 0;
++ int width;
+
+ ichan->channel = chan->center_freq;
+ ichan->chan = chan;
+@@ -309,7 +311,19 @@ static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
+ if (chan->band == NL80211_BAND_5GHZ)
+ flags |= CHANNEL_5GHZ;
+
+- switch (chandef->width) {
++ switch (common->chan_bw) {
++ case 5:
++ width = NL80211_CHAN_WIDTH_5;
++ break;
++ case 10:
++ width = NL80211_CHAN_WIDTH_10;
++ break;
++ default:
++ width = chandef->width;
++ break;
++ }
++
++ switch (width) {
+ case NL80211_CHAN_WIDTH_5:
+ flags |= CHANNEL_QUARTER;
+ break;
+@@ -342,10 +356,11 @@ struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
+ struct cfg80211_chan_def *chandef)
+ {
+ struct ieee80211_channel *curchan = chandef->chan;
++ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_channel *channel;
+
+ channel = &ah->channels[curchan->hw_value];
+- ath9k_cmn_update_ichannel(channel, chandef);
++ ath9k_cmn_update_ichannel(common, channel, chandef);
+
+ return channel;
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
+index 87ffcb4..f081d1d 100644
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -123,6 +123,61 @@ static const struct file_operations fops_debug = {
+
+ #define DMA_BUF_LEN 1024
+
++#ifdef CONFIG_MAC80211_LEDS
++
++static ssize_t write_file_gpio_led(struct file *file, const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_softc *sc = file->private_data;
++ char buf[32], *str, *name, *c;
++ ssize_t len;
++ unsigned int gpio;
++ bool active_low = false;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, ubuf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++ name = strchr(buf, ',');
++ if (!name)
++ return -EINVAL;
++
++ *(name++) = 0;
++ if (!*name)
++ return -EINVAL;
++
++ c = strchr(name, '\n');
++ if (c)
++ *c = 0;
++
++ str = buf;
++ if (*str == '!') {
++ str++;
++ active_low = true;
++ }
++
++ if (kstrtouint(str, 0, &gpio) < 0)
++ return -EINVAL;
++
++ if (gpio >= sc->sc_ah->caps.num_gpio_pins)
++ return -EINVAL;
++
++ if (ath_create_gpio_led(sc, gpio, name, NULL, active_low) < 0)
++ return -EINVAL;
++
++ return count;
++}
++
++static const struct file_operations fops_gpio_led = {
++ .write = write_file_gpio_led,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++#endif
++
+
+ static ssize_t read_file_ani(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+@@ -1373,6 +1428,50 @@ void ath9k_deinit_debug(struct ath_softc *sc)
+ ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
+ }
+
++static ssize_t read_file_diag(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_softc *sc = file->private_data;
++ struct ath_hw *ah = sc->sc_ah;
++ char buf[32];
++ unsigned int len;
++
++ len = sprintf(buf, "0x%08lx\n", ah->diag);
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_diag(struct file *file, const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_softc *sc = file->private_data;
++ struct ath_hw *ah = sc->sc_ah;
++ unsigned long diag;
++ char buf[32];
++ ssize_t len;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++ if (kstrtoul(buf, 0, &diag))
++ return -EINVAL;
++
++ ah->diag = diag;
++ ath9k_hw_update_diag(ah);
++
++ return count;
++}
++
++static const struct file_operations fops_diag = {
++ .read = read_file_diag,
++ .write = write_file_diag,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++
+ int ath9k_init_debug(struct ath_hw *ah)
+ {
+ struct ath_common *common = ath9k_hw_common(ah);
+@@ -1392,6 +1491,12 @@ int ath9k_init_debug(struct ath_hw *ah)
+ ath9k_tx99_init_debug(sc);
+ ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
+
++#ifdef CONFIG_MAC80211_LEDS
++ debugfs_create_file("gpio_led", S_IWUSR,
++ sc->debug.debugfs_phy, sc, &fops_gpio_led);
++#endif
++ debugfs_create_file("diag", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
++ sc, &fops_diag);
+ debugfs_create_devm_seqfile(sc->dev, "dma", sc->debug.debugfs_phy,
+ read_file_dma);
+ debugfs_create_devm_seqfile(sc->dev, "interrupt", sc->debug.debugfs_phy,
+@@ -1431,6 +1536,8 @@ int ath9k_init_debug(struct ath_hw *ah)
+
+ ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
+ ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
++ ath9k_cmn_debug_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
++ ath9k_cmn_debug_chanbw(sc->debug.debugfs_phy, sc->sc_ah);
+
+ debugfs_create_u32("gpio_mask", 0600,
+ sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
+diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
+index a8101c9..e1449d7 100644
+--- a/drivers/net/wireless/ath/ath9k/gpio.c
++++ b/drivers/net/wireless/ath/ath9k/gpio.c
+@@ -15,13 +15,211 @@
+ */
+
+ #include "ath9k.h"
++#include <linux/ath9k_platform.h>
++#include <linux/gpio.h>
++#include <linux/platform_device.h>
++#include <linux/gpio_keys.h>
++
++#ifdef CPTCFG_MAC80211_LEDS
++
++#ifdef CONFIG_GPIOLIB
++
++/***************/
++/* GPIO Chip */
++/***************/
++
++/* gpio_chip handler : set GPIO to input */
++static int ath9k_gpio_pin_cfg_input(struct gpio_chip *chip, unsigned offset)
++{
++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++ gchip);
++
++ ath9k_hw_gpio_request_in(gc->sc->sc_ah, offset, "ath9k-gpio");
++
++ return 0;
++}
++
++/* gpio_chip handler : set GPIO to output */
++static int ath9k_gpio_pin_cfg_output(struct gpio_chip *chip, unsigned offset,
++ int value)
++{
++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++ gchip);
++
++ ath9k_hw_gpio_request_out(gc->sc->sc_ah, offset, "ath9k-gpio",
++ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++ ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
++
++ return 0;
++}
++
++/* gpio_chip handler : query GPIO direction (0=out, 1=in) */
++static int ath9k_gpio_pin_get_dir(struct gpio_chip *chip, unsigned offset)
++{
++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++ gchip);
++ struct ath_hw *ah = gc->sc->sc_ah;
++
++ return !((REG_READ(ah, AR_GPIO_OE_OUT(ah)) >> (offset * 2)) & 3);
++}
++
++/* gpio_chip handler : get GPIO pin value */
++static int ath9k_gpio_pin_get(struct gpio_chip *chip, unsigned offset)
++{
++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++ gchip);
++
++ return ath9k_hw_gpio_get(gc->sc->sc_ah, offset);
++}
++
++/* gpio_chip handler : set GPIO pin to value */
++static void ath9k_gpio_pin_set(struct gpio_chip *chip, unsigned offset,
++ int value)
++{
++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++ gchip);
++
++ ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
++}
++
++/* register GPIO chip */
++static void ath9k_register_gpio_chip(struct ath_softc *sc)
++{
++ struct ath9k_gpio_chip *gc;
++ struct ath_hw *ah = sc->sc_ah;
++
++ gc = kzalloc(sizeof(struct ath9k_gpio_chip), GFP_KERNEL);
++ if (!gc)
++ return;
++
++ gc->sc = sc;
++ snprintf(gc->label, sizeof(gc->label), "ath9k-%s",
++ wiphy_name(sc->hw->wiphy));
++#ifdef CONFIG_OF
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
++ gc->gchip.parent = sc->dev;
++#else
++ gc->gchip.dev = sc->dev;
++#endif
++#endif
++ gc->gchip.label = gc->label;
++ gc->gchip.base = -1; /* determine base automatically */
++ gc->gchip.ngpio = ah->caps.num_gpio_pins;
++ gc->gchip.direction_input = ath9k_gpio_pin_cfg_input;
++ gc->gchip.direction_output = ath9k_gpio_pin_cfg_output;
++ gc->gchip.get_direction = ath9k_gpio_pin_get_dir;
++ gc->gchip.get = ath9k_gpio_pin_get;
++ gc->gchip.set = ath9k_gpio_pin_set;
++
++ if (gpiochip_add(&gc->gchip)) {
++ kfree(gc);
++ return;
++ }
++
++#ifdef CONFIG_OF
++ gc->gchip.owner = NULL;
++#endif
++ sc->gpiochip = gc;
++}
++
++/* remove GPIO chip */
++static void ath9k_unregister_gpio_chip(struct ath_softc *sc)
++{
++ struct ath9k_gpio_chip *gc = sc->gpiochip;
++
++ if (!gc)
++ return;
++
++ gpiochip_remove(&gc->gchip);
++ kfree(gc);
++ sc->gpiochip = NULL;
++}
++
++/******************/
++/* GPIO Buttons */
++/******************/
++
++/* add GPIO buttons */
++static void ath9k_init_buttons(struct ath_softc *sc)
++{
++ struct ath9k_platform_data *pdata = sc->dev->platform_data;
++ struct platform_device *pdev;
++ struct gpio_keys_platform_data gkpdata;
++ struct gpio_keys_button *bt;
++ int i;
++
++ if (!sc->gpiochip)
++ return;
++
++ if (!pdata || !pdata->btns || !pdata->num_btns)
++ return;
++
++ bt = devm_kmemdup(sc->dev, pdata->btns,
++ pdata->num_btns * sizeof(struct gpio_keys_button),
++ GFP_KERNEL);
++ if (!bt)
++ return;
++
++ for (i = 0; i < pdata->num_btns; i++) {
++ if (pdata->btns[i].gpio == sc->sc_ah->led_pin)
++ sc->sc_ah->led_pin = -1;
++
++ ath9k_hw_gpio_request_in(sc->sc_ah, pdata->btns[i].gpio,
++ "ath9k-gpio");
++ bt[i].gpio = sc->gpiochip->gchip.base + pdata->btns[i].gpio;
++ }
++
++ memset(&gkpdata, 0, sizeof(struct gpio_keys_platform_data));
++ gkpdata.buttons = bt;
++ gkpdata.nbuttons = pdata->num_btns;
++ gkpdata.poll_interval = pdata->btn_poll_interval;
++
++ pdev = platform_device_register_data(sc->dev, "gpio-keys-polled",
++ PLATFORM_DEVID_AUTO, &gkpdata,
++ sizeof(gkpdata));
++ if (!IS_ERR_OR_NULL(pdev))
++ sc->btnpdev = pdev;
++ else {
++ sc->btnpdev = NULL;
++ devm_kfree(sc->dev, bt);
++ }
++}
++
++/* remove GPIO buttons */
++static void ath9k_deinit_buttons(struct ath_softc *sc)
++{
++ if (!sc->gpiochip || !sc->btnpdev)
++ return;
++
++ platform_device_unregister(sc->btnpdev);
++
++ sc->btnpdev = NULL;
++}
++
++#else /* CONFIG_GPIOLIB */
++
++static inline void ath9k_register_gpio_chip(struct ath_softc *sc)
++{
++}
++
++static inline void ath9k_unregister_gpio_chip(struct ath_softc *sc)
++{
++}
++
++static inline void ath9k_init_buttons(struct ath_softc *sc)
++{
++}
++
++static inline void ath9k_deinit_buttons(struct ath_softc *sc)
++{
++}
++
++#endif /* CONFIG_GPIOLIB */
+
+ /********************************/
+ /* LED functions */
+ /********************************/
+
+-#ifdef CPTCFG_MAC80211_LEDS
+-
+ static void ath_fill_led_pin(struct ath_softc *sc)
+ {
+ struct ath_hw *ah = sc->sc_ah;
+@@ -39,62 +237,171 @@ static void ath_fill_led_pin(struct ath_softc *sc)
+ else
+ ah->led_pin = ATH_LED_PIN_DEF;
+ }
++}
++
++static void ath_led_brightness(struct led_classdev *led_cdev,
++ enum led_brightness brightness)
++{
++ struct ath_led *led = container_of(led_cdev, struct ath_led, cdev);
++ struct ath_softc *sc = led->sc;
++
++ ath9k_ps_wakeup(sc);
++ ath9k_hw_set_gpio(sc->sc_ah, led->gpio->gpio,
++ (brightness != LED_OFF) ^ led->gpio->active_low);
++ ath9k_ps_restore(sc);
++}
++
++static int ath_add_led(struct ath_softc *sc, struct ath_led *led)
++{
++ const struct gpio_led *gpio = led->gpio;
++ int ret;
++
++ led->cdev.name = gpio->name;
++ led->cdev.default_trigger = gpio->default_trigger;
++ led->cdev.brightness_set = ath_led_brightness;
++
++ ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->cdev);
++ if (ret < 0)
++ return ret;
++
++ led->sc = sc;
++ list_add(&led->list, &sc->leds);
+
+ /* Configure gpio for output */
+- ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
++ ath9k_hw_gpio_request_out(sc->sc_ah, gpio->gpio, gpio->name,
+ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+
+- /* LED off, active low */
+- ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
++ /* Set default LED state */
++ if (gpio->default_state == LEDS_GPIO_DEFSTATE_ON)
++ ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, !gpio->active_low);
++ else
++ ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
++
++#ifdef CONFIG_GPIOLIB
++ /* If there is GPIO chip configured, reserve LED pin */
++ if (sc->gpiochip)
++ gpio_request(sc->gpiochip->gchip.base + gpio->gpio, gpio->name);
++#endif
++
++ return 0;
+ }
+
+-static void ath_led_brightness(struct led_classdev *led_cdev,
+- enum led_brightness brightness)
++int ath_create_gpio_led(struct ath_softc *sc, int gpio_num, const char *name,
++ const char *trigger, bool active_low)
+ {
+- struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
+- u32 val = (brightness == LED_OFF);
++ struct ath_led *led;
++ struct gpio_led *gpio;
++ char *_name;
++ int ret;
++
++ led = kzalloc(sizeof(*led) + sizeof(*gpio) + strlen(name) + 1,
++ GFP_KERNEL);
++ if (!led)
++ return -ENOMEM;
+
+- if (sc->sc_ah->config.led_active_high)
+- val = !val;
++ led->gpio = gpio = (struct gpio_led *) (led + 1);
++ _name = (char *) (led->gpio + 1);
+
+- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
++ strcpy(_name, name);
++ gpio->name = _name;
++ gpio->gpio = gpio_num;
++ gpio->active_low = active_low;
++ gpio->default_trigger = trigger;
++
++ ret = ath_add_led(sc, led);
++ if (unlikely(ret < 0))
++ kfree(led);
++
++ return ret;
+ }
+
+-void ath_deinit_leds(struct ath_softc *sc)
++static int ath_create_platform_led(struct ath_softc *sc,
++ const struct gpio_led *gpio)
+ {
+- if (!sc->led_registered)
+- return;
++ struct ath_led *led;
++ int ret;
+
+- ath_led_brightness(&sc->led_cdev, LED_OFF);
+- led_classdev_unregister(&sc->led_cdev);
++ led = kzalloc(sizeof(*led), GFP_KERNEL);
++ if (!led)
++ return -ENOMEM;
+
+- ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
++ led->gpio = gpio;
++ ret = ath_add_led(sc, led);
++ if (ret < 0)
++ kfree(led);
++
++ return ret;
++}
++
++void ath_deinit_leds(struct ath_softc *sc)
++{
++ struct ath_led *led;
++
++ ath9k_deinit_buttons(sc);
++ while (!list_empty(&sc->leds)) {
++ led = list_first_entry(&sc->leds, struct ath_led, list);
++#ifdef CONFIG_GPIOLIB
++ /* If there is GPIO chip configured, free LED pin */
++ if (sc->gpiochip)
++ gpio_free(sc->gpiochip->gchip.base + led->gpio->gpio);
++#endif
++ list_del(&led->list);
++ ath_led_brightness(&led->cdev, LED_OFF);
++ led_classdev_unregister(&led->cdev);
++ ath9k_hw_gpio_free(sc->sc_ah, led->gpio->gpio);
++ kfree(led);
++ }
++ ath9k_unregister_gpio_chip(sc);
+ }
+
+ void ath_init_leds(struct ath_softc *sc)
+ {
+- int ret;
++ struct ath9k_platform_data *pdata = sc->dev->platform_data;
++ struct device_node *np = sc->dev->of_node;
++ char led_name[32];
++ const char *trigger;
++ int i;
++
++ INIT_LIST_HEAD(&sc->leds);
+
+ if (AR_SREV_9100(sc->sc_ah))
+ return;
+
++ if (!np)
++ ath9k_register_gpio_chip(sc);
++
++ /* setup gpio controller only if requested and skip the led_pin setup */
++ if (of_property_read_bool(np, "gpio-controller")) {
++ ath9k_register_gpio_chip(sc);
++ return;
++ }
++
+ ath_fill_led_pin(sc);
++ ath9k_init_buttons(sc);
+
+- if (!ath9k_led_blink)
+- sc->led_cdev.default_trigger =
+- ieee80211_get_radio_led_name(sc->hw);
++ if (pdata && pdata->leds && pdata->num_leds)
++ for (i = 0; i < pdata->num_leds; i++) {
++ if (pdata->leds[i].gpio == sc->sc_ah->led_pin)
++ sc->sc_ah->led_pin = -1;
+
+- snprintf(sc->led_name, sizeof(sc->led_name),
+- "ath9k-%s", wiphy_name(sc->hw->wiphy));
+- sc->led_cdev.name = sc->led_name;
+- sc->led_cdev.brightness_set = ath_led_brightness;
++ ath_create_platform_led(sc, &pdata->leds[i]);
++ }
+
+- ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
+- if (ret < 0)
++ if (sc->sc_ah->led_pin < 0)
+ return;
+
+- sc->led_registered = true;
++ snprintf(led_name, sizeof(led_name), "ath9k-%s",
++ wiphy_name(sc->hw->wiphy));
++
++ if (ath9k_led_blink)
++ trigger = sc->led_default_trigger;
++ else
++ trigger = ieee80211_get_radio_led_name(sc->hw);
++
++ ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger,
++ !sc->sc_ah->config.led_active_high);
+ }
++
+ #endif
+
+ /*******************/
+diff --git a/drivers/net/wireless/ath/ath9k/hsr.c b/drivers/net/wireless/ath/ath9k/hsr.c
+new file mode 100644
+index 0000000..7d12d91
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/hsr.c
+@@ -0,0 +1,247 @@
++/*
++ *
++ * The MIT License (MIT)
++ *
++ * Copyright (c) 2015 Kirill Berezin
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ */
++
++#include <linux/io.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/time.h>
++#include <linux/bitops.h>
++#include <linux/etherdevice.h>
++#include <linux/rtnetlink.h>
++#include <asm/unaligned.h>
++
++#include "hw.h"
++#include "ath9k.h"
++
++#define HSR_GPIO_CSN 8
++#define HSR_GPIO_CLK 6
++#define HSR_GPIO_DOUT 7
++#define HSR_GPIO_DIN 5
++
++/* delays are in useconds */
++#define HSR_DELAY_HALF_TICK 100
++#define HSR_DELAY_PRE_WRITE 75
++#define HSR_DELAY_FINAL 20000
++#define HSR_DELAY_TRAILING 200
++
++void ath9k_hsr_init(struct ath_hw *ah)
++{
++ ath9k_hw_gpio_request_in(ah, HSR_GPIO_DIN, NULL);
++ ath9k_hw_gpio_request_out(ah, HSR_GPIO_CSN, NULL,
++ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++ ath9k_hw_gpio_request_out(ah, HSR_GPIO_CLK, NULL,
++ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++ ath9k_hw_gpio_request_out(ah, HSR_GPIO_DOUT, NULL,
++ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++
++ ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
++ ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, 0);
++
++ udelay(HSR_DELAY_TRAILING);
++}
++
++static u32 ath9k_hsr_write_byte(struct ath_hw *ah, int delay, u32 value)
++{
++ struct ath_common *common = ath9k_hw_common(ah);
++ int i;
++ u32 rval = 0;
++
++ udelay(delay);
++
++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
++ udelay(HSR_DELAY_HALF_TICK);
++
++ ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 0);
++ udelay(HSR_DELAY_HALF_TICK);
++
++ for (i = 0; i < 8; ++i) {
++ rval = rval << 1;
++
++ /* pattern is left to right, that is 7-th bit runs first */
++ ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, (value >> (7 - i)) & 0x1);
++ udelay(HSR_DELAY_HALF_TICK);
++
++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 1);
++ udelay(HSR_DELAY_HALF_TICK);
++
++ rval |= ath9k_hw_gpio_get(ah, HSR_GPIO_DIN);
++
++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
++ udelay(HSR_DELAY_HALF_TICK);
++ }
++
++ ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
++ udelay(HSR_DELAY_HALF_TICK);
++
++ ath_dbg(common, CONFIG, "ath9k_hsr_write_byte: write byte %d return value is %d %c\n",
++ value, rval, rval > 32 ? rval : '-');
++
++ return rval & 0xff;
++}
++
++static int ath9k_hsr_write_a_chain(struct ath_hw *ah, char *chain, int items)
++{
++ int status = 0;
++ int i = 0;
++ int err;
++
++ /* a preamble */
++ ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++ status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++
++ /* clear HSR's reply buffer */
++ if (status) {
++ int loop = 0;
++
++ for (loop = 0; (loop < 42) && status; ++loop)
++ status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE,
++ 0);
++
++ if (loop >= 42) {
++ ATH_DBG_WARN(1,
++ "ath9k_hsr_write_a_chain: can't clear an output buffer after a 42 cycles.\n");
++ return -1;
++ }
++ }
++
++ for (i = 0; (i < items) && (chain[i] != 0); ++i)
++ ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, (u32)chain[i]);
++
++ ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++ mdelay(HSR_DELAY_FINAL / 1000);
++
++ /* reply */
++ memset(chain, 0, items);
++
++ ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++ udelay(HSR_DELAY_TRAILING);
++
++ for (i = 0; i < (items - 1); ++i) {
++ u32 ret;
++
++ ret = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++ if (ret != 0)
++ chain[i] = (char)ret;
++ else
++ break;
++
++ udelay(HSR_DELAY_TRAILING);
++ }
++
++ if (i <= 1)
++ return 0;
++
++ err = kstrtoint(chain + 1, 10, &i);
++ if (err)
++ return err;
++
++ return i;
++}
++
++int ath9k_hsr_disable(struct ath_hw *ah)
++{
++ char cmd[10] = {'b', '4', '0', 0, 0, 0, 0, 0, 0, 0};
++ int ret;
++
++ ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++ if ((ret > 0) && (*cmd == 'B'))
++ return 0;
++
++ return -1;
++}
++
++int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
++{
++ char cmd[10];
++ int ret;
++
++ /* Bandwidth argument is 0 sometimes. Assume default 802.11bgn
++ * 20MHz on invalid values
++ */
++ if ((bw != 5) && (bw != 10) && (bw != 20) && (bw != 40))
++ bw = 20;
++
++ memset(cmd, 0, sizeof(cmd));
++ *cmd = 'b';
++ snprintf(cmd + 1, 3, "%02d", bw);
++
++ ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++ if ((*cmd != 'B') || (ret != bw)) {
++ ATH_DBG_WARN(1,
++ "ath9k_hsr_enable: failed changing bandwidth -> set (%d,%d) reply (%d, %d)\n",
++ 'b', bw, *cmd, ret);
++ return -1;
++ }
++
++ memset(cmd, 0, sizeof(cmd));
++ *cmd = 'x';
++ ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++ if (*cmd != 'X') {
++ ATH_DBG_WARN(1,
++ "ath9k_hsr_enable: failed 'x' command -> reply (%d, %d)\n",
++ *cmd, ret);
++ return -1;
++ }
++
++ memset(cmd, 0, sizeof(cmd));
++ *cmd = 'm';
++ ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++ if (*cmd != 'M') {
++ ATH_DBG_WARN(1,
++ "ath9k_hsr_enable: failed 'm' command -> reply (%d, %d)\n",
++ *cmd, ret);
++ return -1;
++ }
++
++ memset(cmd, 0, sizeof(cmd));
++ *cmd = 'f';
++ snprintf(cmd + 1, 6, "%05d", fq);
++ ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++ if ((*cmd != 'F') && (ret != fq)) {
++ ATH_DBG_WARN(1,
++ "ath9k_hsr_enable: failed set frequency -> reply (%d, %d)\n",
++ *cmd, ret);
++ return -1;
++ }
++
++ return 0;
++}
++
++int ath9k_hsr_status(struct ath_hw *ah)
++{
++ char cmd[10] = {'s', 0, 0, 0, 0, 0, 0, 0, 0, 0};
++ int ret;
++
++ ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++ if (*cmd != 'S') {
++ ATH_DBG_WARN(1, "ath9k_hsr_status: returned %d,%d\n", *cmd,
++ ret);
++ return -1;
++ }
++
++ return 0;
++}
+diff --git a/drivers/net/wireless/ath/ath9k/hsr.h b/drivers/net/wireless/ath/ath9k/hsr.h
+new file mode 100644
+index 0000000..78af444
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/hsr.h
+@@ -0,0 +1,48 @@
++/*
++ * The MIT License (MIT)
++ *
++ * Copyright (c) 2015 Kirill Berezin
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#ifndef HSR_H
++#define HSR_H
++
++#ifdef CPTCFG_ATH9K_UBNTHSR
++
++void ath9k_hsr_init(struct ath_hw *ah);
++int ath9k_hsr_disable(struct ath_hw *ah);
++int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq);
++int ath9k_hsr_status(struct ath_hw *ah);
++
++#else
++static inline void ath9k_hsr_init(struct ath_hw *ah) {}
++
++static inline int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
++{
++ return 0;
++}
++
++static inline int ath9k_hsr_disable(struct ath_hw *ah) { return 0; }
++static inline int ath9k_hsr_status(struct ath_hw *ah) { return 0; }
++
++#endif
++
++#endif /* HSR_H */
+diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+index f7c6d9b..5c015ac 100644
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+@@ -514,6 +514,8 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
+
+ ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah);
+ ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah);
++ ath9k_cmn_debug_eeprom(priv->debug.debugfs_phy, priv->ah);
++ ath9k_cmn_debug_chanbw(priv->debug.debugfs_phy, priv->ah);
+
+ return 0;
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+index fa02d9a..53e49b6 100644
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+@@ -631,6 +631,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
+ priv->ah = ah;
+
+ common = ath9k_hw_common(ah);
++ common->ieee_ops = &ath9k_htc_ops;
+ common->ops = &ah->reg_ops;
+ common->ps_ops = &ath9k_htc_ps_ops;
+ common->bus_ops = &ath9k_usb_bus_ops;
+@@ -746,9 +747,9 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
+
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+- WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+-
+- hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
++ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
++ WIPHY_FLAG_SUPPORTS_5_10_MHZ |
++ WIPHY_FLAG_SUPPORTS_TDLS;
+
+ hw->queues = 4;
+ hw->max_listen_interval = 1;
+diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
+index 174d716..605abe1 100644
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -100,6 +100,12 @@ static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power)
+ ath9k_hw_ops(ah)->tx99_set_txpower(ah, power);
+ }
+
++static inline void ath9k_hw_get_adc_entropy(struct ath_hw *ah,
++ u8 *buf, size_t len)
++{
++ ath9k_hw_ops(ah)->get_adc_entropy(ah, buf, len);
++}
++
+ #ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
+
+ static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
+diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
+index fafe1f0..f53964b 100644
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -247,6 +247,19 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
+ centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT);
+ }
+
++static inline void ath9k_hw_disable_pll_lock_detect(struct ath_hw *ah)
++{
++ /* On AR9330 and AR9340 devices, some PHY registers must be
++ * tuned to gain better stability/performance. These registers
++ * might be changed while doing wlan reset so the registers must
++ * be reprogrammed after each reset.
++ */
++ REG_CLR_BIT(ah, AR_PHY_USB_CTRL1, BIT(20));
++ REG_RMW(ah, AR_PHY_USB_CTRL2,
++ (1 << 21) | (0xf << 22),
++ (1 << 21) | (0x3 << 22));
++}
++
+ /******************/
+ /* Chip Revisions */
+ /******************/
+@@ -402,13 +415,8 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
+
+ ah->config.rx_intr_mitigation = true;
+
+- if (AR_SREV_9300_20_OR_LATER(ah)) {
+- ah->config.rimt_last = 500;
+- ah->config.rimt_first = 2000;
+- } else {
+- ah->config.rimt_last = 250;
+- ah->config.rimt_first = 700;
+- }
++ ah->config.rimt_last = 250;
++ ah->config.rimt_first = 500;
+
+ if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+ ah->config.pll_pwrsave = 7;
+@@ -667,6 +675,7 @@ int ath9k_hw_init(struct ath_hw *ah)
+
+ /* These are all the AR5008/AR9001/AR9002/AR9003 hardware family of chipsets */
+ switch (ah->hw_version.devid) {
++ case AR9300_DEVID_INVALID:
+ case AR5416_DEVID_PCI:
+ case AR5416_DEVID_PCIE:
+ case AR5416_AR9100_DEVID:
+@@ -1311,39 +1320,56 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
+ *coef_exponent = coef_exp - 16;
+ }
+
+-/* AR9330 WAR:
+- * call external reset function to reset WMAC if:
+- * - doing a cold reset
+- * - we have pending frames in the TX queues.
+- */
+-static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type)
++static bool ath9k_hw_need_external_reset(struct ath_hw *ah, int type)
+ {
+- int i, npend = 0;
++ int i;
+
+- for (i = 0; i < AR_NUM_QCU; i++) {
+- npend = ath9k_hw_numtxpending(ah, i);
+- if (npend)
+- break;
++ if (type == ATH9K_RESET_COLD)
++ return true;
++
++ if (AR_SREV_9550(ah))
++ return true;
++
++ /* AR9330 WAR:
++ * call external reset function to reset WMAC if:
++ * - doing a cold reset
++ * - we have pending frames in the TX queues.
++ */
++ if (AR_SREV_9330(ah)) {
++ for (i = 0; i < AR_NUM_QCU; i++) {
++ if (ath9k_hw_numtxpending(ah, i))
++ return true;
++ }
+ }
+
+- if (ah->external_reset &&
+- (npend || type == ATH9K_RESET_COLD)) {
+- int reset_err = 0;
++ return false;
++}
+
+- ath_dbg(ath9k_hw_common(ah), RESET,
+- "reset MAC via external reset\n");
++static bool ath9k_hw_external_reset(struct ath_hw *ah, int type)
++{
++ int err;
+
+- reset_err = ah->external_reset();
+- if (reset_err) {
+- ath_err(ath9k_hw_common(ah),
+- "External reset failed, err=%d\n",
+- reset_err);
+- return false;
+- }
++ if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type))
++ return true;
+
+- REG_WRITE(ah, AR_RTC_RESET(ah), 1);
++ ath_dbg(ath9k_hw_common(ah), RESET,
++ "reset MAC via external reset\n");
++
++ err = ah->external_reset();
++ if (err) {
++ ath_err(ath9k_hw_common(ah),
++ "External reset failed, err=%d\n", err);
++ return false;
+ }
+
++ if (AR_SREV_9550(ah)) {
++ REG_WRITE(ah, AR_RTC_RESET(ah), 0);
++ udelay(10);
++ }
++
++ REG_WRITE(ah, AR_RTC_RESET(ah), 1);
++ udelay(10);
++
+ return true;
+ }
+
+@@ -1396,24 +1422,24 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
+ rst_flags |= AR_RTC_RC_MAC_COLD;
+ }
+
+- if (AR_SREV_9330(ah)) {
+- if (!ath9k_hw_ar9330_reset_war(ah, type))
+- return false;
+- }
+-
+ if (ath9k_hw_mci_is_enabled(ah))
+ ar9003_mci_check_gpm_offset(ah);
+
+ /* DMA HALT added to resolve ar9300 and ar9580 bus error during
+- * RTC_RC reg read
++ * RTC_RC reg read. Also needed for AR9550 external reset
+ */
+- if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) {
++ if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
+ REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+ ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK,
+ 20 * AH_WAIT_TIMEOUT);
+- REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+ }
+
++ if (!AR_SREV_9100(ah))
++ ath9k_hw_external_reset(ah, type);
++
++ if (AR_SREV_9300(ah) || AR_SREV_9580(ah))
++ REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
++
+ REG_WRITE(ah, AR_RTC_RC(ah), rst_flags);
+
+ REGWRITE_BUFFER_FLUSH(ah);
+@@ -1434,8 +1460,15 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
+ if (!AR_SREV_9100(ah))
+ REG_WRITE(ah, AR_RC, 0);
+
+- if (AR_SREV_9100(ah))
++ if (AR_SREV_9100(ah)) {
++ /* Reset the AHB-WMAC interface */
++ if (ah->external_reset)
++ ah->external_reset();
+ udelay(50);
++ }
++
++ if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++ ath9k_hw_disable_pll_lock_detect(ah);
+
+ return true;
+ }
+@@ -1536,6 +1569,9 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
+ ar9003_hw_internal_regulator_apply(ah);
+ ath9k_hw_init_pll(ah, chan);
+
++ if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++ ath9k_hw_disable_pll_lock_detect(ah);
++
+ return true;
+ }
+
+@@ -1842,8 +1878,14 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
+ if (AR_SREV_9271(ah))
+ ar9002_hw_load_ani_reg(ah, chan);
+
++ if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++ ath9k_hw_disable_pll_lock_detect(ah);
++
+ return 0;
+ fail:
++ if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++ ath9k_hw_disable_pll_lock_detect(ah);
++
+ return -EINVAL;
+ }
+
+@@ -1864,6 +1906,20 @@ u32 ath9k_hw_get_tsf_offset(struct timespec64 *last, struct timespec64 *cur)
+ }
+ EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
+
++void ath9k_hw_update_diag(struct ath_hw *ah)
++{
++ if (test_bit(ATH_DIAG_DISABLE_RX, &ah->diag))
++ REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
++ else
++ REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
++
++ if (test_bit(ATH_DIAG_DISABLE_TX, &ah->diag))
++ REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
++ else
++ REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
++}
++EXPORT_SYMBOL(ath9k_hw_update_diag);
++
+ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
+ struct ath9k_hw_cal_data *caldata, bool fastcc)
+ {
+@@ -2072,6 +2128,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
+ ar9003_hw_disable_phy_restart(ah);
+
+ ath9k_hw_apply_gpio_override(ah);
++ ath9k_hw_update_diag(ah);
+
+ if (AR_SREV_9565(ah) && common->bt_ant_diversity)
+ REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
+@@ -2082,6 +2139,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
+ ath9k_hw_set_radar_params(ah);
+ }
+
++ if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++ ath9k_hw_disable_pll_lock_detect(ah);
++
+ return 0;
+ }
+ EXPORT_SYMBOL(ath9k_hw_reset);
+@@ -2956,7 +3016,8 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
+ {
+ struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
+ struct ieee80211_channel *channel;
+- int chan_pwr, new_pwr;
++ int chan_pwr, new_pwr, max_gain;
++ int ant_gain, ant_reduction = 0;
+ u16 ctl = NO_CTL;
+
+ if (!chan)
+@@ -2968,9 +3029,18 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
+ channel = chan->chan;
+ chan_pwr = min_t(int, channel->max_power * 2, MAX_COMBINED_POWER);
+ new_pwr = min_t(int, chan_pwr, reg->power_limit);
++ max_gain = chan_pwr - new_pwr + channel->max_antenna_gain * 2;
++
++ ant_gain = get_antenna_gain(ah, chan);
++ if (ant_gain > max_gain)
++ ant_reduction = ant_gain - max_gain;
++
++ /* FCC allows maximum antenna gain of 6 dBi */
++ if (reg->region == NL80211_DFS_FCC)
++ ant_reduction = max_t(int, ant_reduction - 12, 0);
+
+ ah->eep_ops->set_txpower(ah, chan, ctl,
+- get_antenna_gain(ah, chan), new_pwr, test);
++ ant_reduction, new_pwr, test);
+ }
+
+ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test)
+diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
+index 6243626..8d756dc 100644
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -36,6 +36,7 @@
+
+ #define ATHEROS_VENDOR_ID 0x168c
+
++#define AR9300_DEVID_INVALID 0xabcd
+ #define AR5416_DEVID_PCI 0x0023
+ #define AR5416_DEVID_PCIE 0x0024
+ #define AR9160_DEVID_PCI 0x0027
+@@ -521,6 +522,12 @@ enum {
+ ATH9K_RESET_COLD,
+ };
+
++enum {
++ ATH_DIAG_DISABLE_RX,
++ ATH_DIAG_DISABLE_TX,
++ ATH_DIAG_TRIGGER_ERROR,
++};
++
+ struct ath9k_hw_version {
+ u32 magic;
+ u16 devid;
+@@ -716,6 +723,7 @@ struct ath_spec_scan {
+ * @config_pci_powersave:
+ * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC
+ *
++ * @get_adc_entropy: get entropy from the raw ADC I/Q output
+ * @spectral_scan_config: set parameters for spectral scan and enable/disable it
+ * @spectral_scan_trigger: trigger a spectral scan run
+ * @spectral_scan_wait: wait for a spectral scan run to finish
+@@ -738,6 +746,7 @@ struct ath_hw_ops {
+ struct ath_hw_antcomb_conf *antconf);
+ void (*antdiv_comb_conf_set)(struct ath_hw *ah,
+ struct ath_hw_antcomb_conf *antconf);
++ void (*get_adc_entropy)(struct ath_hw *ah, u8 *buf, size_t len);
+ void (*spectral_scan_config)(struct ath_hw *ah,
+ struct ath_spec_scan *param);
+ void (*spectral_scan_trigger)(struct ath_hw *ah);
+@@ -809,6 +818,8 @@ struct ath_hw {
+ u32 ah_flags;
+ s16 nf_override;
+
++ unsigned long diag;
++
+ bool reset_power_on;
+ bool htc_reset_init;
+
+@@ -1078,6 +1089,7 @@ void ath9k_hw_check_nav(struct ath_hw *ah);
+ bool ath9k_hw_check_alive(struct ath_hw *ah);
+
+ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
++void ath9k_hw_update_diag(struct ath_hw *ah);
+
+ /* Generic hw timer primitives */
+ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
+diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
+index 2458387..2f5266c 100644
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -48,7 +48,7 @@ int ath9k_modparam_nohwcrypt;
+ module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444);
+ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
+
+-int ath9k_led_blink;
++int ath9k_led_blink = 1;
+ module_param_named(blink, ath9k_led_blink, int, 0444);
+ MODULE_PARM_DESC(blink, "Enable LED blink on activity");
+
+@@ -696,6 +696,12 @@ static int ath9k_of_init(struct ath_softc *sc)
+ return 0;
+ }
+
++static void ath9k_of_gpio_mask(struct ath_softc *sc)
++{
++ of_property_read_u32(sc->dev->of_node, "qca,gpio-mask",
++ &sc->sc_ah->caps.gpio_mask);
++}
++
+ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
+ const struct ath_bus_ops *bus_ops)
+ {
+@@ -733,6 +739,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
+ if (!ath9k_is_chanctx_enabled())
+ sc->cur_chan->hw_queue_base = 0;
+
++ common->ieee_ops = &ath9k_ops;
+ common->ops = &ah->reg_ops;
+ common->bus_ops = bus_ops;
+ common->ps_ops = &ath9k_ps_ops;
+@@ -803,6 +810,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
+ if (ret)
+ goto err_hw;
+
++ /* GPIO mask quirk */
++ ath9k_of_gpio_mask(sc);
++
+ ret = ath9k_init_queues(sc);
+ if (ret)
+ goto err_queues;
+@@ -870,7 +880,8 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
+
+- ah->curchan = curchan;
++ if (curchan)
++ ah->curchan = curchan;
+ }
+
+ static const struct ieee80211_iface_limit if_limits[] = {
+@@ -882,6 +893,7 @@ static const struct ieee80211_iface_limit if_limits[] = {
+ BIT(NL80211_IFTYPE_AP) },
+ { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) },
++ { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
+ };
+
+ #ifdef CPTCFG_ATH9K_CHANNEL_CONTEXT
+@@ -962,6 +974,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+ ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
++ ieee80211_hw_set(hw, MFP_CAPABLE);
+
+ if (ath9k_ps_enable)
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+@@ -974,9 +987,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ IEEE80211_RADIOTAP_MCS_HAVE_STBC;
+ }
+
+- if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
+- ieee80211_hw_set(hw, MFP_CAPABLE);
+-
+ hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+ NL80211_FEATURE_P2P_GO_CTWIN;
+@@ -1049,6 +1059,18 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ }
+
++static void ath_get_initial_entropy(struct ath_softc *sc)
++{
++ struct ath_hw *ah = sc->sc_ah;
++ char buf[256];
++
++ /* reuse last channel initialized by the tx power test */
++ ath9k_hw_reset(ah, ah->curchan, NULL, false);
++
++ ath9k_hw_get_adc_entropy(ah, buf, sizeof(buf));
++ add_device_randomness(buf, sizeof(buf));
++}
++
+ int ath9k_init_device(u16 devid, struct ath_softc *sc,
+ const struct ath_bus_ops *bus_ops)
+ {
+@@ -1089,13 +1111,15 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
+
+ #ifdef CPTCFG_MAC80211_LEDS
+ /* must be initialized before ieee80211_register_hw */
+- sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
++ sc->led_default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
+ IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink,
+ ARRAY_SIZE(ath9k_tpt_blink));
+ #endif
+
+ wiphy_read_of_freq_limits(hw->wiphy);
+
++ ath_get_initial_entropy(sc);
++
+ /* Register with mac80211 */
+ error = ieee80211_register_hw(hw);
+ if (error)
+@@ -1179,25 +1203,25 @@ static int __init ath9k_init(void)
+ {
+ int error;
+
+- error = ath_pci_init();
++ error = ath_ahb_init();
+ if (error < 0) {
+- pr_err("No PCI devices found, driver not installed\n");
+ error = -ENODEV;
+ goto err_out;
+ }
+
+- error = ath_ahb_init();
++ error = ath_pci_init();
+ if (error < 0) {
++ pr_err("No PCI devices found, driver not installed\n");
+ error = -ENODEV;
+- goto err_pci_exit;
++ goto err_ahb_exit;
+ }
+
+ dmi_check_system(ath9k_quirks);
+
+ return 0;
+
+- err_pci_exit:
+- ath_pci_exit();
++ err_ahb_exit:
++ ath_ahb_exit();
+ err_out:
+ return error;
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
+index b070403..d8d14a5 100644
+--- a/drivers/net/wireless/ath/ath9k/mac.c
++++ b/drivers/net/wireless/ath/ath9k/mac.c
+@@ -678,13 +678,18 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning)
+
+ ath9k_ani_reset(ah, is_scanning);
+
+- REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
++ REG_CLR_BIT(ah, AR_DIAG_SW,
++ AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
+ }
+ EXPORT_SYMBOL(ath9k_hw_startpcureceive);
+
+ void ath9k_hw_abortpcurecv(struct ath_hw *ah)
+ {
+- REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
++ u32 reg = AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT;
++
++ if (!IS_ENABLED(CPTCFG_ATH9K_TX99))
++ reg |= AR_DIAG_FORCE_RX_CLEAR;
++ REG_SET_BIT(ah, AR_DIAG_SW, reg);
+
+ ath9k_hw_disable_mib_counters(ah);
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
+index 6d120df..2807d36 100644
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -18,6 +18,7 @@
+ #include <linux/delay.h>
+ #include "ath9k.h"
+ #include "btcoex.h"
++#include "hsr.h"
+
+ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop);
+@@ -538,6 +539,11 @@ irqreturn_t ath_isr(int irq, void *dev)
+ return IRQ_HANDLED;
+ }
+
++ if (test_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag)) {
++ status |= ATH9K_INT_FATAL;
++ clear_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag);
++ }
++
+ /*
+ * If there are no status bits set, then this interrupt was not
+ * for me (should have been caught above).
+@@ -654,6 +660,7 @@ void ath_reset_work(struct work_struct *work)
+ static int ath9k_start(struct ieee80211_hw *hw)
+ {
+ struct ath_softc *sc = hw->priv;
++ struct device_node *np = sc->dev->of_node;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
+@@ -732,6 +739,11 @@ static int ath9k_start(struct ieee80211_hw *hw)
+ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+ }
+
++ if (of_property_read_bool(np, "ubnt,hsr")) {
++ ath9k_hsr_init(ah);
++ ath9k_hsr_disable(ah);
++ }
++
+ /*
+ * Reset key cache to sane defaults (all entries cleared) instead of
+ * semi-random values after suspend/resume.
+diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
+index 6421e49..20c7095 100644
+--- a/drivers/net/wireless/ath/ath9k/pci.c
++++ b/drivers/net/wireless/ath/ath9k/pci.c
+@@ -772,6 +772,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ #endif
+
++ { PCI_VDEVICE(ATHEROS, 0xabcd) }, /* PCI-E internal chip default ID */
+ { 0 }
+ };
+
+diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h
+index 4a1b992..af667a3 100644
+--- a/drivers/net/wireless/ath/ath9k/phy.h
++++ b/drivers/net/wireless/ath/ath9k/phy.h
+@@ -48,6 +48,9 @@
+ #define AR_PHY_PLL_CONTROL 0x16180
+ #define AR_PHY_PLL_MODE 0x16184
+
++#define AR_PHY_USB_CTRL1 0x16c84
++#define AR_PHY_USB_CTRL2 0x16c88
++
+ enum ath9k_ant_div_comb_lna_conf {
+ ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
+ ATH_ANT_DIV_COMB_LNA2,
+diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h
+index 76860a4..e210108 100644
+--- a/include/linux/ath9k_platform.h
++++ b/include/linux/ath9k_platform.h
+@@ -46,6 +46,13 @@ struct ath9k_platform_data {
+ int (*external_reset)(void);
+
+ bool use_eeprom;
++
++ int num_leds;
++ const struct gpio_led *leds;
++
++ unsigned num_btns;
++ const struct gpio_keys_button *btns;
++ unsigned btn_poll_interval;
+ };
+
+ #endif /* _LINUX_ATH9K_PLATFORM_H */
+diff --git a/local-symbols b/local-symbols
+index b200b00..b5745bf 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -128,6 +128,7 @@ ATH9K_WOW=
+ ATH9K_RFKILL=
+ ATH9K_CHANNEL_CONTEXT=
+ ATH9K_PCOEM=
++ATH9K_UBNTHSR=
+ ATH9K_PCI_NO_EEPROM=
+ ATH9K_HTC=
+ ATH9K_HTC_DEBUGFS=
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-sync-backports-patches-ath10k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-sync-backports-patches-ath10k.patch
new file mode 100644
index 0000000..feebf11
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-sync-backports-patches-ath10k.patch
@@ -0,0 +1,835 @@
+From 38cce019a8791ff3d295bd936362432cc592e061 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 12 Mar 2024 11:27:11 +0800
+Subject: [PATCH 05/61] sync backports patches/ath10k
+
+---
+ drivers/net/wireless/ath/ath10k/Kconfig | 16 ++++
+ drivers/net/wireless/ath/ath10k/Makefile | 3 +-
+ drivers/net/wireless/ath/ath10k/core.c | 34 ++++++++
+ drivers/net/wireless/ath/ath10k/core.h | 12 +++
+ drivers/net/wireless/ath/ath10k/htt.h | 4 +
+ drivers/net/wireless/ath/ath10k/hw.h | 1 +
+ drivers/net/wireless/ath/ath10k/leds.c | 101 ++++++++++++++++++++++
+ drivers/net/wireless/ath/ath10k/leds.h | 41 +++++++++
+ drivers/net/wireless/ath/ath10k/mac.c | 66 +++++++++++++-
+ drivers/net/wireless/ath/ath10k/pci.c | 16 ++++
+ drivers/net/wireless/ath/ath10k/thermal.h | 2 +-
+ drivers/net/wireless/ath/ath10k/wmi-ops.h | 32 +++++++
+ drivers/net/wireless/ath/ath10k/wmi-tlv.c | 2 +
+ drivers/net/wireless/ath/ath10k/wmi.c | 54 ++++++++++++
+ drivers/net/wireless/ath/ath10k/wmi.h | 35 ++++++++
+ local-symbols | 2 +
+ 16 files changed, 415 insertions(+), 6 deletions(-)
+ create mode 100644 drivers/net/wireless/ath/ath10k/leds.c
+ create mode 100644 drivers/net/wireless/ath/ath10k/leds.h
+
+diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
+index d10c65d..5cabe0e 100644
+--- a/drivers/net/wireless/ath/ath10k/Kconfig
++++ b/drivers/net/wireless/ath/ath10k/Kconfig
+@@ -72,6 +72,16 @@ config ATH10K_DEBUGFS
+
+ If unsure, say Y to make it easier to debug problems.
+
++config ATH10K_LEDS
++ bool "Atheros ath10k LED support"
++ depends on ATH10K
++ select MAC80211_LEDS
++ select LEDS_CLASS
++ select NEW_LEDS
++ default y
++ ---help---
++ This option is necessary, if you want LED support for chipset connected led pins. If unsure, say N.
++
+ config ATH10K_SPECTRAL
+ bool "Atheros ath10k spectral scan support"
+ depends on ATH10K_DEBUGFS
+@@ -87,6 +97,12 @@ config ATH10K_TRACING
+ help
+ Select this to ath10k use tracing infrastructure.
+
++config ATH10K_THERMAL
++ bool "Atheros ath10k thermal monitoring support"
++ depends on THERMAL
++ ---help---
++ Select this to ath10k use hwmon for thermal measurement.
++
+ config ATH10K_DFS_CERTIFIED
+ bool "Atheros DFS support for certified platforms"
+ depends on ATH10K && CFG80211_CERTIFICATION_ONUS
+diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
+index 24d846a..e040d84 100644
+--- a/drivers/net/wireless/ath/ath10k/Makefile
++++ b/drivers/net/wireless/ath/ath10k/Makefile
+@@ -18,7 +18,8 @@ ath10k_core-y += mac.o \
+ ath10k_core-$(CPTCFG_ATH10K_SPECTRAL) += spectral.o
+ ath10k_core-$(CPTCFG_NL80211_TESTMODE) += testmode.o
+ ath10k_core-$(CPTCFG_ATH10K_TRACING) += trace.o
+-ath10k_core-$(CONFIG_THERMAL) += thermal.o
++ath10k_core-$(CPTCFG_ATH10K_THERMAL) += thermal.o
++ath10k_core-$(CPTCFG_ATH10K_LEDS) += leds.o
+ ath10k_core-$(CPTCFG_MAC80211_DEBUGFS) += debugfs_sta.o
+ ath10k_core-$(CONFIG_PM) += wow.o
+ ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o
+diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
+index 9ce6f49..a168fdf 100644
+--- a/drivers/net/wireless/ath/ath10k/core.c
++++ b/drivers/net/wireless/ath/ath10k/core.c
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/firmware.h>
+ #include <linux/of.h>
++#include <linux/of_net.h>
+ #include <linux/property.h>
+ #include <linux/dmi.h>
+ #include <linux/ctype.h>
+@@ -27,6 +28,7 @@
+ #include "testmode.h"
+ #include "wmi-ops.h"
+ #include "coredump.h"
++#include "leds.h"
+
+ unsigned int ath10k_debug_mask;
+ EXPORT_SYMBOL(ath10k_debug_mask);
+@@ -66,6 +68,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+ .dev_id = QCA988X_2_0_DEVICE_ID,
+ .bus = ATH10K_BUS_PCI,
+ .name = "qca988x hw2.0",
++ .led_pin = 1,
+ .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
+ .uart_pin = 7,
+ .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
+@@ -149,6 +152,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+ .dev_id = QCA9887_1_0_DEVICE_ID,
+ .bus = ATH10K_BUS_PCI,
+ .name = "qca9887 hw1.0",
++ .led_pin = 1,
+ .patch_load_addr = QCA9887_HW_1_0_PATCH_LOAD_ADDR,
+ .uart_pin = 7,
+ .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
+@@ -396,6 +400,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+ .dev_id = QCA99X0_2_0_DEVICE_ID,
+ .bus = ATH10K_BUS_PCI,
+ .name = "qca99x0 hw2.0",
++ .led_pin = 17,
+ .patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR,
+ .uart_pin = 7,
+ .otp_exe_param = 0x00000700,
+@@ -443,6 +448,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+ .dev_id = QCA9984_1_0_DEVICE_ID,
+ .bus = ATH10K_BUS_PCI,
+ .name = "qca9984/qca9994 hw1.0",
++ .led_pin = 17,
+ .patch_load_addr = QCA9984_HW_1_0_PATCH_LOAD_ADDR,
+ .uart_pin = 7,
+ .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
+@@ -497,6 +503,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+ .dev_id = QCA9888_2_0_DEVICE_ID,
+ .bus = ATH10K_BUS_PCI,
+ .name = "qca9888 hw2.0",
++ .led_pin = 17,
+ .patch_load_addr = QCA9888_HW_2_0_PATCH_LOAD_ADDR,
+ .uart_pin = 7,
+ .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
+@@ -3239,6 +3246,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
+ goto err_hif_stop;
+ }
+
++ status = ath10k_leds_start(ar);
++ if (status)
++ goto err_hif_stop;
++
+ return 0;
+
+ err_hif_stop:
+@@ -3405,6 +3416,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
+
+ device_get_mac_address(ar->dev, ar->mac_addr);
+
++ of_get_mac_address(ar->dev->of_node, ar->mac_addr);
++
+ ret = ath10k_core_init_firmware_features(ar);
+ if (ret) {
+ ath10k_err(ar, "fatal problem with firmware features: %d\n",
+@@ -3497,9 +3510,18 @@ static void ath10k_core_register_work(struct work_struct *work)
+ goto err_spectral_destroy;
+ }
+
++ status = ath10k_leds_register(ar);
++ if (status) {
++ ath10k_err(ar, "could not register leds: %d\n",
++ status);
++ goto err_thermal_unregister;
++ }
++
+ set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags);
+ return;
+
++err_thermal_unregister:
++ ath10k_thermal_unregister(ar);
+ err_spectral_destroy:
+ ath10k_spectral_destroy(ar);
+ err_debug_destroy:
+@@ -3524,6 +3546,16 @@ int ath10k_core_register(struct ath10k *ar,
+
+ queue_work(ar->workqueue, &ar->register_work);
+
++ /* OpenWrt requires all PHYs to be initialized to create the
++ * configuration files during bootup. ath10k violates this
++ * because it delays the creation of the PHY to a not well defined
++ * point in the future.
++ *
++ * Forcing the work to be done immediately works around this problem
++ * but may also delay the boot when firmware images cannot be found.
++ */
++ flush_workqueue(ar->workqueue);
++
+ return 0;
+ }
+ EXPORT_SYMBOL(ath10k_core_register);
+@@ -3535,6 +3567,8 @@ void ath10k_core_unregister(struct ath10k *ar)
+ if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags))
+ return;
+
++ ath10k_leds_unregister(ar);
++
+ ath10k_thermal_unregister(ar);
+ /* Stop spectral before unregistering from mac80211 to remove the
+ * relayfs debugfs file cleanly. Otherwise the parent debugfs tree
+diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
+index 2435291..8546f2f 100644
+--- a/drivers/net/wireless/ath/ath10k/core.h
++++ b/drivers/net/wireless/ath/ath10k/core.h
+@@ -15,6 +15,7 @@
+ #include <linux/pci.h>
+ #include <linux/uuid.h>
+ #include <linux/time.h>
++#include <linux/leds.h>
+
+ #include "htt.h"
+ #include "htc.h"
+@@ -1256,6 +1257,13 @@ struct ath10k {
+ bool utf_monitor;
+ } testmode;
+
++ struct {
++ struct gpio_led wifi_led;
++ struct led_classdev cdev;
++ char label[48];
++ u32 gpio_state_pin;
++ } leds;
++
+ struct {
+ /* protected by data_lock */
+ u32 rx_crc_err_drop;
+@@ -1305,6 +1313,10 @@ struct ath10k {
+ s32 tx_power_2g_limit;
+ s32 tx_power_5g_limit;
+
++#ifdef CPTCFG_MAC80211_LEDS
++ const char *led_default_trigger;
++#endif
++
+ /* must be last */
+ u8 drv_priv[] __aligned(sizeof(void *));
+ };
+diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
+index 603f6de..bea1d16 100644
+--- a/drivers/net/wireless/ath/ath10k/htt.h
++++ b/drivers/net/wireless/ath/ath10k/htt.h
+@@ -236,7 +236,11 @@ enum htt_rx_ring_flags {
+ };
+
+ #define HTT_RX_RING_SIZE_MIN 128
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ #define HTT_RX_RING_SIZE_MAX 2048
++#else
++#define HTT_RX_RING_SIZE_MAX 512
++#endif
+ #define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX
+ #define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1)
+ #define HTT_RX_RING_FILL_LEVEL_DUAL_MAC (HTT_RX_RING_SIZE - 1)
+diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
+index 3b26994..aeada6c 100644
+--- a/drivers/net/wireless/ath/ath10k/hw.h
++++ b/drivers/net/wireless/ath/ath10k/hw.h
+@@ -520,6 +520,7 @@ struct ath10k_hw_params {
+ const char *name;
+ u32 patch_load_addr;
+ int uart_pin;
++ int led_pin;
+ u32 otp_exe_param;
+
+ /* Type of hw cycle counter wraparound logic, for more info
+diff --git a/drivers/net/wireless/ath/ath10k/leds.c b/drivers/net/wireless/ath/ath10k/leds.c
+new file mode 100644
+index 0000000..be8f255
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/leds.c
+@@ -0,0 +1,101 @@
++/*
++ * Copyright (c) 2005-2011 Atheros Communications Inc.
++ * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
++ * Copyright (c) 2018 Sebastian Gottschall <s.gottschall@dd-wrt.com>
++ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/leds.h>
++
++#include "core.h"
++#include "wmi.h"
++#include "wmi-ops.h"
++
++#include "leds.h"
++
++static int ath10k_leds_set_brightness_blocking(struct led_classdev *led_cdev,
++ enum led_brightness brightness)
++{
++ struct ath10k *ar = container_of(led_cdev, struct ath10k,
++ leds.cdev);
++ struct gpio_led *led = &ar->leds.wifi_led;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_ON)
++ goto out;
++
++ ar->leds.gpio_state_pin = (brightness != LED_OFF) ^ led->active_low;
++ ath10k_wmi_gpio_output(ar, led->gpio, ar->leds.gpio_state_pin);
++
++out:
++ mutex_unlock(&ar->conf_mutex);
++
++ return 0;
++}
++
++int ath10k_leds_start(struct ath10k *ar)
++{
++ if (ar->hw_params.led_pin == 0)
++ /* leds not supported */
++ return 0;
++
++ /* under some circumstances, the gpio pin gets reconfigured
++ * to default state by the firmware, so we need to
++ * reconfigure it this behaviour has only ben seen on
++ * QCA9984 and QCA99XX devices so far
++ */
++ ath10k_wmi_gpio_config(ar, ar->hw_params.led_pin, 0,
++ WMI_GPIO_PULL_NONE, WMI_GPIO_INTTYPE_DISABLE);
++ ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, 1);
++
++ return 0;
++}
++
++int ath10k_leds_register(struct ath10k *ar)
++{
++ int ret;
++
++ if (ar->hw_params.led_pin == 0)
++ /* leds not supported */
++ return 0;
++
++ snprintf(ar->leds.label, sizeof(ar->leds.label), "ath10k-%s",
++ wiphy_name(ar->hw->wiphy));
++ ar->leds.wifi_led.active_low = 1;
++ ar->leds.wifi_led.gpio = ar->hw_params.led_pin;
++ ar->leds.wifi_led.name = ar->leds.label;
++ ar->leds.wifi_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
++
++ ar->leds.cdev.name = ar->leds.label;
++ ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking;
++ ar->leds.cdev.default_trigger = ar->led_default_trigger;
++
++ ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++void ath10k_leds_unregister(struct ath10k *ar)
++{
++ if (ar->hw_params.led_pin == 0)
++ /* leds not supported */
++ return;
++
++ led_classdev_unregister(&ar->leds.cdev);
++}
++
+diff --git a/drivers/net/wireless/ath/ath10k/leds.h b/drivers/net/wireless/ath/ath10k/leds.h
+new file mode 100644
+index 0000000..a0f5c84
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/leds.h
+@@ -0,0 +1,41 @@
++/*
++ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++#ifndef _LEDS_H_
++#define _LEDS_H_
++
++#include "core.h"
++
++#ifdef CPTCFG_ATH10K_LEDS
++void ath10k_leds_unregister(struct ath10k *ar);
++int ath10k_leds_start(struct ath10k *ar);
++int ath10k_leds_register(struct ath10k *ar);
++#else
++static inline void ath10k_leds_unregister(struct ath10k *ar)
++{
++}
++
++static inline int ath10k_leds_start(struct ath10k *ar)
++{
++ return 0;
++}
++
++static inline int ath10k_leds_register(struct ath10k *ar)
++{
++ return 0;
++}
++
++#endif
++#endif /* _LEDS_H_ */
+diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
+index ec43216..f179dff 100644
+--- a/drivers/net/wireless/ath/ath10k/mac.c
++++ b/drivers/net/wireless/ath/ath10k/mac.c
+@@ -25,6 +25,7 @@
+ #include "wmi-tlv.h"
+ #include "wmi-ops.h"
+ #include "wow.h"
++#include "leds.h"
+
+ /*********/
+ /* Rates */
+@@ -1021,6 +1022,40 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
+ return ar->last_wmi_vdev_start_status;
+ }
+
++static u32 ath10k_get_max_antenna_gain(struct ath10k *ar,
++ u32 ch_max_antenna_gain)
++{
++ u32 max_antenna_gain;
++
++ if (ar->dfs_detector && ar->dfs_detector->region == NL80211_DFS_FCC) {
++ /* FCC allows maximum antenna gain of 6 dBi. 15.247(b)(4):
++ *
++ * > (4) The conducted output power limit
++ * > specified in paragraph (b) of this section
++ * > is based on the use of antennas
++ * > with directional gains that do not exceed
++ * > 6 dBi. Except as shown in paragraph
++ * > (c) of this section, if transmitting
++ * > antennas of directional gain greater
++ * > than 6 dBi are used, the conducted
++ * > output power from the intentional radiator
++ * > shall be reduced below the stated
++ * > values in paragraphs (b)(1), (b)(2),
++ * > and (b)(3) of this section, as appropriate,
++ * > by the amount in dB that the
++ * > directional gain of the antenna exceeds
++ * > 6 dBi.
++ *
++ * https://www.gpo.gov/fdsys/pkg/CFR-2013-title47-vol1/pdf/CFR-2013-title47-vol1-sec15-247.pdf
++ */
++ max_antenna_gain = 6;
++ } else {
++ max_antenna_gain = 0;
++ }
++
++ return max(ch_max_antenna_gain, max_antenna_gain);
++}
++
+ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
+ {
+ struct cfg80211_chan_def *chandef = NULL;
+@@ -1053,7 +1088,8 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
+ arg.channel.min_power = 0;
+ arg.channel.max_power = channel->max_power * 2;
+ arg.channel.max_reg_power = channel->max_reg_power * 2;
+- arg.channel.max_antenna_gain = channel->max_antenna_gain;
++ arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
++ channel->max_antenna_gain);
+
+ reinit_completion(&ar->vdev_setup_done);
+ reinit_completion(&ar->vdev_delete_done);
+@@ -1499,7 +1535,8 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
+ arg.channel.min_power = 0;
+ arg.channel.max_power = chandef->chan->max_power * 2;
+ arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+- arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain;
++ arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
++ chandef->chan->max_antenna_gain);
+
+ if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+ arg.ssid = arvif->u.ap.ssid;
+@@ -3430,7 +3467,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
+ ch->min_power = 0;
+ ch->max_power = channel->max_power * 2;
+ ch->max_reg_power = channel->max_reg_power * 2;
+- ch->max_antenna_gain = channel->max_antenna_gain;
++ ch->max_antenna_gain = ath10k_get_max_antenna_gain(ar,
++ channel->max_antenna_gain);
+ ch->reg_class_id = 0; /* FIXME */
+
+ /* FIXME: why use only legacy modes, why not any
+@@ -9918,6 +9956,21 @@ static int ath10k_mac_init_rd(struct ath10k *ar)
+ return 0;
+ }
+
++#ifdef CPTCFG_MAC80211_LEDS
++static const struct ieee80211_tpt_blink ath10k_tpt_blink[] = {
++ { .throughput = 0 * 1024, .blink_time = 334 },
++ { .throughput = 1 * 1024, .blink_time = 260 },
++ { .throughput = 2 * 1024, .blink_time = 220 },
++ { .throughput = 5 * 1024, .blink_time = 190 },
++ { .throughput = 10 * 1024, .blink_time = 170 },
++ { .throughput = 25 * 1024, .blink_time = 150 },
++ { .throughput = 54 * 1024, .blink_time = 130 },
++ { .throughput = 120 * 1024, .blink_time = 110 },
++ { .throughput = 265 * 1024, .blink_time = 80 },
++ { .throughput = 586 * 1024, .blink_time = 50 },
++};
++#endif
++
+ int ath10k_mac_register(struct ath10k *ar)
+ {
+ static const u32 cipher_suites[] = {
+@@ -10036,7 +10089,6 @@ int ath10k_mac_register(struct ath10k *ar)
+ ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA);
+ ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
+ ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
+- ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
+
+ if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
+ ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL);
+@@ -10280,6 +10332,12 @@ int ath10k_mac_register(struct ath10k *ar)
+
+ ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
+
++#ifdef CPTCFG_MAC80211_LEDS
++ ar->led_default_trigger = ieee80211_create_tpt_led_trigger(ar->hw,
++ IEEE80211_TPT_LEDTRIG_FL_RADIO, ath10k_tpt_blink,
++ ARRAY_SIZE(ath10k_tpt_blink));
++#endif
++
+ ret = ieee80211_register_hw(ar->hw);
+ if (ret) {
+ ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
+diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
+index d7ac5ae..c7e8e90 100644
+--- a/drivers/net/wireless/ath/ath10k/pci.c
++++ b/drivers/net/wireless/ath/ath10k/pci.c
+@@ -132,7 +132,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 2048,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ .dest_nentries = 512,
++#else
++ .dest_nentries = 128,
++#endif
+ .recv_cb = ath10k_pci_htt_htc_rx_cb,
+ },
+
+@@ -141,7 +145,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 2048,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ .dest_nentries = 128,
++#else
++ .dest_nentries = 64,
++#endif
+ .recv_cb = ath10k_pci_htc_rx_cb,
+ },
+
+@@ -168,7 +176,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 512,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ .dest_nentries = 512,
++#else
++ .dest_nentries = 128,
++#endif
+ .recv_cb = ath10k_pci_htt_rx_cb,
+ },
+
+@@ -193,7 +205,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 2048,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ .dest_nentries = 128,
++#else
++ .dest_nentries = 96,
++#endif
+ .recv_cb = ath10k_pci_pktlog_rx_cb,
+ },
+
+diff --git a/drivers/net/wireless/ath/ath10k/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h
+index 1f4de9f..fcfa3c2 100644
+--- a/drivers/net/wireless/ath/ath10k/thermal.h
++++ b/drivers/net/wireless/ath/ath10k/thermal.h
+@@ -25,7 +25,7 @@ struct ath10k_thermal {
+ int temperature;
+ };
+
+-#if IS_REACHABLE(CONFIG_THERMAL)
++#if IS_REACHABLE(CPTCFG_ATH10K_THERMAL)
+ int ath10k_thermal_register(struct ath10k *ar);
+ void ath10k_thermal_unregister(struct ath10k *ar);
+ void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
+diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
+index aa57d80..f3f6b59 100644
+--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
++++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
+@@ -226,7 +226,10 @@ struct wmi_ops {
+ const struct wmi_bb_timing_cfg_arg *arg);
+ struct sk_buff *(*gen_per_peer_per_tid_cfg)(struct ath10k *ar,
+ const struct wmi_per_peer_per_tid_cfg_arg *arg);
++ struct sk_buff *(*gen_gpio_config)(struct ath10k *ar, u32 gpio_num,
++ u32 input, u32 pull_type, u32 intr_mode);
+
++ struct sk_buff *(*gen_gpio_output)(struct ath10k *ar, u32 gpio_num, u32 set);
+ };
+
+ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
+@@ -1122,6 +1125,35 @@ ath10k_wmi_force_fw_hang(struct ath10k *ar,
+ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
+ }
+
++static inline int ath10k_wmi_gpio_config(struct ath10k *ar, u32 gpio_num,
++ u32 input, u32 pull_type, u32 intr_mode)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_gpio_config)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_gpio_config(ar, gpio_num, input, pull_type, intr_mode);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->gpio_config_cmdid);
++}
++
++static inline int ath10k_wmi_gpio_output(struct ath10k *ar, u32 gpio_num, u32 set)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_gpio_config)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_gpio_output(ar, gpio_num, set);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->gpio_output_cmdid);
++}
++
+ static inline int
+ ath10k_wmi_dbglog_cfg(struct ath10k *ar, u64 module_enable, u32 log_level)
+ {
+diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+index aed97fd..dbaf26d 100644
+--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
++++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+@@ -4606,6 +4606,8 @@ static const struct wmi_ops wmi_tlv_ops = {
+ .gen_echo = ath10k_wmi_tlv_op_gen_echo,
+ .gen_vdev_spectral_conf = ath10k_wmi_tlv_op_gen_vdev_spectral_conf,
+ .gen_vdev_spectral_enable = ath10k_wmi_tlv_op_gen_vdev_spectral_enable,
++ /* .gen_gpio_config not implemented */
++ /* .gen_gpio_output not implemented */
+ };
+
+ static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = {
+diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
+index 5d80988..6b428b4 100644
+--- a/drivers/net/wireless/ath/ath10k/wmi.c
++++ b/drivers/net/wireless/ath/ath10k/wmi.c
+@@ -7493,6 +7493,49 @@ ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id,
+ return skb;
+ }
+
++static struct sk_buff *ath10k_wmi_op_gen_gpio_config(struct ath10k *ar,
++ u32 gpio_num, u32 input,
++ u32 pull_type, u32 intr_mode)
++{
++ struct wmi_gpio_config_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_gpio_config_cmd *)skb->data;
++ cmd->pull_type = __cpu_to_le32(pull_type);
++ cmd->gpio_num = __cpu_to_le32(gpio_num);
++ cmd->input = __cpu_to_le32(input);
++ cmd->intr_mode = __cpu_to_le32(intr_mode);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_config gpio_num 0x%08x input 0x%08x pull_type 0x%08x intr_mode 0x%08x\n",
++ gpio_num, input, pull_type, intr_mode);
++
++ return skb;
++}
++
++static struct sk_buff *ath10k_wmi_op_gen_gpio_output(struct ath10k *ar,
++ u32 gpio_num, u32 set)
++{
++ struct wmi_gpio_output_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_gpio_output_cmd *)skb->data;
++ cmd->gpio_num = __cpu_to_le32(gpio_num);
++ cmd->set = __cpu_to_le32(set);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_output gpio_num 0x%08x set 0x%08x\n",
++ gpio_num, set);
++
++ return skb;
++}
++
+ static struct sk_buff *
+ ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
+ enum wmi_sta_ps_mode psmode)
+@@ -9157,6 +9200,9 @@ static const struct wmi_ops wmi_ops = {
+ .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
+ .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
+ .gen_echo = ath10k_wmi_op_gen_echo,
++ .gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
++ .gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
++
+ /* .gen_bcn_tmpl not implemented */
+ /* .gen_prb_tmpl not implemented */
+ /* .gen_p2p_go_bcn_ie not implemented */
+@@ -9227,6 +9273,8 @@ static const struct wmi_ops wmi_10_1_ops = {
+ .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
+ .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
+ .gen_echo = ath10k_wmi_op_gen_echo,
++ .gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
++ .gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
+ /* .gen_bcn_tmpl not implemented */
+ /* .gen_prb_tmpl not implemented */
+ /* .gen_p2p_go_bcn_ie not implemented */
+@@ -9299,6 +9347,8 @@ static const struct wmi_ops wmi_10_2_ops = {
+ .gen_delba_send = ath10k_wmi_op_gen_delba_send,
+ .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
+ .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
++ .gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
++ .gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
+ /* .gen_pdev_enable_adaptive_cca not implemented */
+ };
+
+@@ -9370,6 +9420,8 @@ static const struct wmi_ops wmi_10_2_4_ops = {
+ ath10k_wmi_op_gen_pdev_enable_adaptive_cca,
+ .get_vdev_subtype = ath10k_wmi_10_2_4_op_get_vdev_subtype,
+ .gen_bb_timing = ath10k_wmi_10_2_4_op_gen_bb_timing,
++ .gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
++ .gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
+ /* .gen_bcn_tmpl not implemented */
+ /* .gen_prb_tmpl not implemented */
+ /* .gen_p2p_go_bcn_ie not implemented */
+@@ -9451,6 +9503,8 @@ static const struct wmi_ops wmi_10_4_ops = {
+ .gen_pdev_bss_chan_info_req = ath10k_wmi_10_2_op_gen_pdev_bss_chan_info,
+ .gen_echo = ath10k_wmi_op_gen_echo,
+ .gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config,
++ .gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
++ .gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
+ };
+
+ int ath10k_wmi_attach(struct ath10k *ar)
+diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
+index 2379501..0faefc0 100644
+--- a/drivers/net/wireless/ath/ath10k/wmi.h
++++ b/drivers/net/wireless/ath/ath10k/wmi.h
+@@ -3034,6 +3034,41 @@ enum wmi_10_4_feature_mask {
+
+ };
+
++/* WMI_GPIO_CONFIG_CMDID */
++enum {
++ WMI_GPIO_PULL_NONE,
++ WMI_GPIO_PULL_UP,
++ WMI_GPIO_PULL_DOWN,
++};
++
++enum {
++ WMI_GPIO_INTTYPE_DISABLE,
++ WMI_GPIO_INTTYPE_RISING_EDGE,
++ WMI_GPIO_INTTYPE_FALLING_EDGE,
++ WMI_GPIO_INTTYPE_BOTH_EDGE,
++ WMI_GPIO_INTTYPE_LEVEL_LOW,
++ WMI_GPIO_INTTYPE_LEVEL_HIGH
++};
++
++/* WMI_GPIO_CONFIG_CMDID */
++struct wmi_gpio_config_cmd {
++ __le32 gpio_num; /* GPIO number to be setup */
++ __le32 input; /* 0 - Output/ 1 - Input */
++ __le32 pull_type; /* Pull type defined above */
++ __le32 intr_mode; /* Interrupt mode defined above (Input) */
++} __packed;
++
++/* WMI_GPIO_OUTPUT_CMDID */
++struct wmi_gpio_output_cmd {
++ __le32 gpio_num; /* GPIO number to be setup */
++ __le32 set; /* Set the GPIO pin*/
++} __packed;
++
++/* WMI_GPIO_INPUT_EVENTID */
++struct wmi_gpio_input_event {
++ __le32 gpio_num; /* GPIO number which changed state */
++} __packed;
++
+ struct wmi_ext_resource_config_10_4_cmd {
+ /* contains enum wmi_host_platform_type */
+ __le32 host_platform_config;
+diff --git a/local-symbols b/local-symbols
+index b5745bf..3d81ba5 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -160,6 +160,8 @@ ATH10K_SNOC=
+ ATH10K_DEBUG=
+ ATH10K_DEBUGFS=
+ ATH10K_SPECTRAL=
++ATH10K_THERMAL=
++ATH10K_LEDS=
+ ATH10K_TRACING=
+ ATH10K_DFS_CERTIFIED=
+ WCN36XX=
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-sync-backports-patches-rt2x00.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-sync-backports-patches-rt2x00.patch
new file mode 100644
index 0000000..39975b3
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-sync-backports-patches-rt2x00.patch
@@ -0,0 +1,1263 @@
+From a08aa9e789ade7bb35eb442afa6566368d5a318f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 12 Mar 2024 11:27:40 +0800
+Subject: [PATCH 06/61] sync backports patches/rt2x00
+
+---
+ drivers/net/wireless/ralink/rt2x00/Kconfig | 23 +-
+ drivers/net/wireless/ralink/rt2x00/Makefile | 1 +
+ drivers/net/wireless/ralink/rt2x00/rt2800.h | 5 +
+ .../net/wireless/ralink/rt2x00/rt2800lib.c | 373 +++++++++++-------
+ .../net/wireless/ralink/rt2x00/rt2800lib.h | 24 ++
+ .../net/wireless/ralink/rt2x00/rt2800pci.c | 7 +
+ .../net/wireless/ralink/rt2x00/rt2800soc.c | 52 ++-
+ .../net/wireless/ralink/rt2x00/rt2800usb.c | 7 +
+ drivers/net/wireless/ralink/rt2x00/rt2x00.h | 15 +
+ .../net/wireless/ralink/rt2x00/rt2x00dev.c | 34 +-
+ .../net/wireless/ralink/rt2x00/rt2x00eeprom.c | 208 ++++++++++
+ .../net/wireless/ralink/rt2x00/rt2x00leds.c | 3 +
+ .../net/wireless/ralink/rt2x00/rt2x00soc.c | 16 +
+ .../net/wireless/ralink/rt2x00/rt2x00soc.h | 9 +
+ include/linux/rt2x00_platform.h | 23 ++
+ local-symbols | 1 +
+ 16 files changed, 635 insertions(+), 166 deletions(-)
+ create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
+ create mode 100644 include/linux/rt2x00_platform.h
+
+diff --git a/drivers/net/wireless/ralink/rt2x00/Kconfig b/drivers/net/wireless/ralink/rt2x00/Kconfig
+index 8f6e3d2..abaa51b 100644
+--- a/drivers/net/wireless/ralink/rt2x00/Kconfig
++++ b/drivers/net/wireless/ralink/rt2x00/Kconfig
+@@ -70,6 +70,7 @@ config RT2800PCI
+ select RT2X00_LIB_MMIO
+ select RT2X00_LIB_PCI
+ select RT2X00_LIB_FIRMWARE
++ select RT2X00_LIB_EEPROM
+ select RT2X00_LIB_CRYPTO
+ depends on CRC_CCITT
+ depends on EEPROM_93CX6
+@@ -211,13 +212,15 @@ endif
+ config RT2800SOC
+ tristate "Ralink WiSoC support"
+ depends on m
+- depends on SOC_RT288X || SOC_RT305X || SOC_MT7620
++ depends on SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620
+ select RT2X00_LIB_SOC
+ select RT2X00_LIB_MMIO
+ select RT2X00_LIB_CRYPTO
+ select RT2X00_LIB_FIRMWARE
++ select RT2X00_LIB_EEPROM
+ select RT2800_LIB
+ select RT2800_LIB_MMIO
++ select MTD if SOC_RT288X || SOC_RT305X
+ help
+ This adds support for Ralink WiSoC devices.
+ Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352.
+@@ -226,36 +229,37 @@ config RT2800SOC
+
+
+ config RT2800_LIB
+- tristate
++ tristate "RT2800 USB/PCI support"
+ depends on m
+
+ config RT2800_LIB_MMIO
+- tristate
++ tristate "RT2800 MMIO support"
+ depends on m
+ select RT2X00_LIB_MMIO
+ select RT2800_LIB
+
+ config RT2X00_LIB_MMIO
+- tristate
++ tristate "RT2x00 MMIO support"
+ depends on m
+
+ config RT2X00_LIB_PCI
+- tristate
++ tristate "RT2x00 PCI support"
+ depends on m
+ select RT2X00_LIB
+
+ config RT2X00_LIB_SOC
+- tristate
++ tristate "RT2x00 SoC support"
++ depends on SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620
+ depends on m
+ select RT2X00_LIB
+
+ config RT2X00_LIB_USB
+- tristate
++ tristate "RT2x00 USB support"
+ depends on m
+ select RT2X00_LIB
+
+ config RT2X00_LIB
+- tristate
++ tristate "RT2x00 support"
+ depends on m
+
+ config RT2X00_LIB_FIRMWARE
+@@ -265,6 +269,9 @@ config RT2X00_LIB_FIRMWARE
+ config RT2X00_LIB_CRYPTO
+ bool
+
++config RT2X00_LIB_EEPROM
++ bool
++
+ config RT2X00_LIB_LEDS
+ bool
+ default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n)
+diff --git a/drivers/net/wireless/ralink/rt2x00/Makefile b/drivers/net/wireless/ralink/rt2x00/Makefile
+index 4a2156b..94335ec 100644
+--- a/drivers/net/wireless/ralink/rt2x00/Makefile
++++ b/drivers/net/wireless/ralink/rt2x00/Makefile
+@@ -8,6 +8,7 @@ rt2x00lib-$(CPTCFG_RT2X00_LIB_DEBUGFS) += rt2x00debug.o
+ rt2x00lib-$(CPTCFG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o
+ rt2x00lib-$(CPTCFG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o
+ rt2x00lib-$(CPTCFG_RT2X00_LIB_LEDS) += rt2x00leds.o
++rt2x00lib-$(CPTCFG_RT2X00_LIB_EEPROM) += rt2x00eeprom.o
+
+ obj-$(CPTCFG_RT2X00_LIB) += rt2x00lib.o
+ obj-$(CPTCFG_RT2X00_LIB_MMIO) += rt2x00mmio.o
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
+index 8930589..cbfa680 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
+@@ -1056,6 +1056,11 @@
+ #define MIMO_PS_CFG_RX_STBY_POL FIELD32(0x00000010)
+ #define MIMO_PS_CFG_RX_RX_STBY0 FIELD32(0x00000020)
+
++#define BB_PA_MODE_CFG0 0x1214
++#define BB_PA_MODE_CFG1 0x1218
++#define RF_PA_MODE_CFG0 0x121C
++#define RF_PA_MODE_CFG1 0x1220
++
+ /*
+ * EDCA_AC0_CFG:
+ */
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+index d2ab374..7461d2e 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+@@ -25,6 +25,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
++#include <linux/of.h>
+
+ #include "rt2x00.h"
+ #include "rt2800lib.h"
+@@ -304,6 +305,24 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
+ mutex_unlock(&rt2x00dev->csr_mutex);
+ }
+
++void rt6352_enable_pa_pin(struct rt2x00_dev *rt2x00dev, int enable)
++{
++ if (!rt2x00dev->pinctrl)
++ return;
++
++ if (enable) {
++ if (!rt2x00dev->pins_default)
++ return;
++
++ pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_default);
++ } else {
++ if (!rt2x00dev->pins_pa_gpio)
++ return;
++
++ pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_pa_gpio);
++ }
++}
++
+ static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = {
+ [EEPROM_CHIP_ID] = 0x0000,
+ [EEPROM_VERSION] = 0x0001,
+@@ -3817,14 +3836,16 @@ static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev,
+ rt2x00_set_field8(&rfcsr, RFCSR19_K, rf->rf4);
+ rt2800_rfcsr_write(rt2x00dev, 19, rfcsr);
+
+- /* Default: XO=20MHz , SDM mode */
+- rfcsr = rt2800_rfcsr_read(rt2x00dev, 16);
+- rt2x00_set_field8(&rfcsr, RFCSR16_SDM_MODE_MT7620, 0x80);
+- rt2800_rfcsr_write(rt2x00dev, 16, rfcsr);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++ /* Default: XO=20MHz , SDM mode */
++ rfcsr = rt2800_rfcsr_read(rt2x00dev, 16);
++ rt2x00_set_field8(&rfcsr, RFCSR16_SDM_MODE_MT7620, 0x80);
++ rt2800_rfcsr_write(rt2x00dev, 16, rfcsr);
+
+- rfcsr = rt2800_rfcsr_read(rt2x00dev, 21);
+- rt2x00_set_field8(&rfcsr, RFCSR21_BIT8, 1);
+- rt2800_rfcsr_write(rt2x00dev, 21, rfcsr);
++ rfcsr = rt2800_rfcsr_read(rt2x00dev, 21);
++ rt2x00_set_field8(&rfcsr, RFCSR21_BIT8, 1);
++ rt2800_rfcsr_write(rt2x00dev, 21, rfcsr);
++ }
+
+ rfcsr = rt2800_rfcsr_read(rt2x00dev, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX2_EN_MT7620,
+@@ -3858,18 +3879,23 @@ static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev,
+ rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x20);
+ }
+
+- if (conf_is_ht40(conf)) {
+- rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x08);
+- rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x08);
+- } else {
+- rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x28);
+- rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x28);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++ if (conf_is_ht40(conf)) {
++ rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x08);
++ rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x08);
++ } else {
++ rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x28);
++ rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x28);
++ }
+ }
+
+- rfcsr = rt2800_rfcsr_read(rt2x00dev, 28);
+- rt2x00_set_field8(&rfcsr, RFCSR28_CH11_HT40,
+- conf_is_ht40(conf) && (rf->channel == 11));
+- rt2800_rfcsr_write(rt2x00dev, 28, rfcsr);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++ rt2800_hw_get_chipeco(rt2x00dev) == 2) {
++ rfcsr = rt2800_rfcsr_read(rt2x00dev, 28);
++ rt2x00_set_field8(&rfcsr, RFCSR28_CH11_HT40,
++ conf_is_ht40(conf) && (rf->channel == 11));
++ rt2800_rfcsr_write(rt2x00dev, 28, rfcsr);
++ }
+
+ if (!test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) {
+ if (conf_is_ht40(conf)) {
+@@ -3983,25 +4009,29 @@ static void rt2800_config_alc_rt6352(struct rt2x00_dev *rt2x00dev,
+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY)))
+ rt2x00_warn(rt2x00dev, "RF busy while configuring ALC\n");
+
+- if (chan->center_freq > 2457) {
+- bbp = rt2800_bbp_read(rt2x00dev, 30);
+- bbp = 0x40;
+- rt2800_bbp_write(rt2x00dev, 30, bbp);
+- rt2800_rfcsr_write(rt2x00dev, 39, 0);
+- if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
+- rt2800_rfcsr_write(rt2x00dev, 42, 0xfb);
+- else
+- rt2800_rfcsr_write(rt2x00dev, 42, 0x7b);
+- } else {
+- bbp = rt2800_bbp_read(rt2x00dev, 30);
+- bbp = 0x1f;
+- rt2800_bbp_write(rt2x00dev, 30, bbp);
+- rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
+- if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
+- rt2800_rfcsr_write(rt2x00dev, 42, 0xdb);
+- else
+- rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++ rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++ if (chan->center_freq > 2457) {
++ bbp = rt2800_bbp_read(rt2x00dev, 30);
++ bbp = 0x40;
++ rt2800_bbp_write(rt2x00dev, 30, bbp);
++ rt2800_rfcsr_write(rt2x00dev, 39, 0);
++ if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
++ rt2800_rfcsr_write(rt2x00dev, 42, 0xfb);
++ else
++ rt2800_rfcsr_write(rt2x00dev, 42, 0x7b);
++ } else {
++ bbp = rt2800_bbp_read(rt2x00dev, 30);
++ bbp = 0x1f;
++ rt2800_bbp_write(rt2x00dev, 30, bbp);
++ rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
++ if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
++ rt2800_rfcsr_write(rt2x00dev, 42, 0xdb);
++ else
++ rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
++ }
+ }
++
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, mac_sys_ctrl);
+
+ rt2800_vco_calibration(rt2x00dev);
+@@ -4494,7 +4524,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
+ if (rt2x00_rt(rt2x00dev, RT6352)) {
+ /* BBP for GLRT BW */
+ bbp = conf_is_ht40(conf) ?
+- 0x10 : rt2x00_has_cap_external_lna_bg(rt2x00dev) ?
++ 0x10 : !rt2x00_has_cap_external_lna_bg(rt2x00dev) ?
++ 0x1a : rt2800_hw_get_chippkg(rt2x00dev) == 1 ?
+ 0x15 : 0x1a;
+ rt2800_bbp_glrt_write(rt2x00dev, 141, bbp);
+
+@@ -5998,18 +6029,33 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
+ } else if (rt2x00_rt(rt2x00dev, RT5350)) {
+ rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404);
+ } else if (rt2x00_rt(rt2x00dev, RT6352)) {
+- rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401);
+- rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001);
+- rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
+- rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000);
+- rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0);
+- rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0);
+- rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN, 0x6C6C666C);
+- rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN, 0x6C6C666C);
+- rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT,
+- 0x3630363A);
+- rt2800_register_write(rt2x00dev, TX1_RF_GAIN_CORRECT,
+- 0x3630363A);
++ if (rt2800_hw_get_chipver(rt2x00dev) <= 1) {
++ rt2800_register_write(rt2x00dev, TX_ALC_VGA3,
++ 0x00000000);
++ rt2800_register_write(rt2x00dev, BB_PA_MODE_CFG0,
++ 0x000055FF);
++ rt2800_register_write(rt2x00dev, BB_PA_MODE_CFG1,
++ 0x00550055);
++ rt2800_register_write(rt2x00dev, RF_PA_MODE_CFG0,
++ 0x000055FF);
++ rt2800_register_write(rt2x00dev, RF_PA_MODE_CFG1,
++ 0x00550055);
++ } else {
++ rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401);
++ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001);
++ rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
++ rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000);
++ rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0);
++ rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0);
++ rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN,
++ 0x6C6C666C);
++ rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN,
++ 0x6C6C666C);
++ rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT,
++ 0x3630363A);
++ rt2800_register_write(rt2x00dev, TX1_RF_GAIN_CORRECT,
++ 0x3630363A);
++ }
+ reg = rt2800_register_read(rt2x00dev, TX_ALC_CFG_1);
+ rt2x00_set_field32(®, TX_ALC_CFG_1_ROS_BUSY_EN, 0);
+ rt2800_register_write(rt2x00dev, TX_ALC_CFG_1, reg);
+@@ -7122,14 +7168,16 @@ static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev)
+ rt2800_bbp_write(rt2x00dev, 188, 0x00);
+ rt2800_bbp_write(rt2x00dev, 189, 0x00);
+
+- rt2800_bbp_write(rt2x00dev, 91, 0x06);
+- rt2800_bbp_write(rt2x00dev, 92, 0x04);
+- rt2800_bbp_write(rt2x00dev, 93, 0x54);
+- rt2800_bbp_write(rt2x00dev, 99, 0x50);
+- rt2800_bbp_write(rt2x00dev, 148, 0x84);
+- rt2800_bbp_write(rt2x00dev, 167, 0x80);
+- rt2800_bbp_write(rt2x00dev, 178, 0xFF);
+- rt2800_bbp_write(rt2x00dev, 106, 0x13);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++ rt2800_bbp_write(rt2x00dev, 91, 0x06);
++ rt2800_bbp_write(rt2x00dev, 92, 0x04);
++ rt2800_bbp_write(rt2x00dev, 93, 0x54);
++ rt2800_bbp_write(rt2x00dev, 99, 0x50);
++ rt2800_bbp_write(rt2x00dev, 148, 0x84);
++ rt2800_bbp_write(rt2x00dev, 167, 0x80);
++ rt2800_bbp_write(rt2x00dev, 178, 0xFF);
++ rt2800_bbp_write(rt2x00dev, 106, 0x13);
++ }
+
+ /* BBP for G band GLRT function (BBP_128 ~ BBP_221) */
+ rt2800_bbp_glrt_write(rt2x00dev, 0, 0x00);
+@@ -10359,6 +10407,9 @@ static void rt2800_restore_rf_bbp_rt6352(struct rt2x00_dev *rt2x00dev)
+ rt2800_register_write(rt2x00dev, RF_BYPASS3, 0x0);
+ }
+
++ if (rt2800_hw_get_chippkg(rt2x00dev) != 1)
++ return;
++
+ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
+ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
+ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
+@@ -10403,8 +10454,10 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
+ u32 reg;
+
+ if (rt2x00_has_cap_external_pa(rt2x00dev) ||
+- rt2x00_has_cap_external_lna_bg(rt2x00dev))
++ rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
++ rt6352_enable_pa_pin(rt2x00dev, 0);
+ rt2800_restore_rf_bbp_rt6352(rt2x00dev);
++ }
+
+ rt2800_r_calibration(rt2x00dev);
+ rt2800_rf_self_txdc_cal(rt2x00dev);
+@@ -10422,6 +10475,8 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
+ !rt2x00_has_cap_external_lna_bg(rt2x00dev))
+ return;
+
++ rt6352_enable_pa_pin(rt2x00dev, 1);
++
+ if (rt2x00_has_cap_external_pa(rt2x00dev)) {
+ reg = rt2800_register_read(rt2x00dev, RF_CONTROL3);
+ reg |= 0x00000101;
+@@ -10432,6 +10487,9 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
+ rt2800_register_write(rt2x00dev, RF_BYPASS3, reg);
+ }
+
++ if (rt2800_hw_get_chippkg(rt2x00dev) != 1)
++ return;
++
+ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
+ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66);
+ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20);
+@@ -10522,31 +10580,36 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
+ rt2800_rfcsr_write(rt2x00dev, 42, 0x5B);
+ rt2800_rfcsr_write(rt2x00dev, 43, 0x00);
+
+- rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+- if (rt2800_clk_is_20mhz(rt2x00dev))
+- rt2800_rfcsr_write(rt2x00dev, 13, 0x03);
+- else
+- rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
+- rt2800_rfcsr_write(rt2x00dev, 14, 0x7C);
+- rt2800_rfcsr_write(rt2x00dev, 16, 0x80);
+- rt2800_rfcsr_write(rt2x00dev, 17, 0x99);
+- rt2800_rfcsr_write(rt2x00dev, 18, 0x99);
+- rt2800_rfcsr_write(rt2x00dev, 19, 0x09);
+- rt2800_rfcsr_write(rt2x00dev, 20, 0x50);
+- rt2800_rfcsr_write(rt2x00dev, 21, 0xB0);
+- rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+- rt2800_rfcsr_write(rt2x00dev, 23, 0x06);
+- rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
+- rt2800_rfcsr_write(rt2x00dev, 25, 0x00);
+- rt2800_rfcsr_write(rt2x00dev, 26, 0x5D);
+- rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
+- rt2800_rfcsr_write(rt2x00dev, 28, 0x61);
+- rt2800_rfcsr_write(rt2x00dev, 29, 0xB5);
+- rt2800_rfcsr_write(rt2x00dev, 43, 0x02);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++ rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
++ if (rt2800_clk_is_20mhz(rt2x00dev))
++ rt2800_rfcsr_write(rt2x00dev, 13, 0x03);
++ else
++ rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
++ rt2800_rfcsr_write(rt2x00dev, 14, 0x7C);
++ rt2800_rfcsr_write(rt2x00dev, 16, 0x80);
++ rt2800_rfcsr_write(rt2x00dev, 17, 0x99);
++ rt2800_rfcsr_write(rt2x00dev, 18, 0x99);
++ rt2800_rfcsr_write(rt2x00dev, 19, 0x09);
++ rt2800_rfcsr_write(rt2x00dev, 20, 0x50);
++ rt2800_rfcsr_write(rt2x00dev, 21, 0xB0);
++ rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
++ rt2800_rfcsr_write(rt2x00dev, 23, 0x06);
++ rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
++ rt2800_rfcsr_write(rt2x00dev, 25, 0x00);
++ rt2800_rfcsr_write(rt2x00dev, 26, 0x5D);
++ rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
++ rt2800_rfcsr_write(rt2x00dev, 28, 0x61);
++ rt2800_rfcsr_write(rt2x00dev, 29, 0xB5);
++ rt2800_rfcsr_write(rt2x00dev, 43, 0x02);
++ }
+
+- rt2800_rfcsr_write(rt2x00dev, 28, 0x62);
+- rt2800_rfcsr_write(rt2x00dev, 29, 0xAD);
+- rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++ rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++ rt2800_rfcsr_write(rt2x00dev, 28, 0x62);
++ rt2800_rfcsr_write(rt2x00dev, 29, 0xAD);
++ rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
++ }
+
+ /* Initialize RF channel register to default value */
+ rt2800_rfcsr_write_chanreg(rt2x00dev, 0, 0x03);
+@@ -10612,63 +10675,71 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
+
+ rt2800_rfcsr_write_bank(rt2x00dev, 6, 45, 0xC5);
+
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x47);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x71);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x33);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x0E);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA4);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x02);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x12);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x1C);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0xEB);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x7D);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xD6);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x08);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB4);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27);
+- rt2800_rfcsr_write_bank(rt2x00dev, 4, 47, 0x67);
+- rt2800_rfcsr_write_bank(rt2x00dev, 6, 47, 0x69);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF);
+- rt2800_rfcsr_write_bank(rt2x00dev, 4, 54, 0x27);
+- rt2800_rfcsr_write_bank(rt2x00dev, 6, 54, 0x20);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x20);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xF7);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x09);
+-
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x51);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x2C);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x64);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x51);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x36);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
+-
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x6C);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFC);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1F);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
+-
+- /* Initialize RF channel register for DRQFN */
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xE3);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xE5);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x28);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x68);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xF7);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x02);
+- rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xC7);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x47);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x71);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x33);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x0E);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA4);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x02);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x12);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x1C);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0xEB);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x7D);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xD6);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x08);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB4);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27);
++ rt2800_rfcsr_write_bank(rt2x00dev, 4, 47, 0x67);
++ rt2800_rfcsr_write_bank(rt2x00dev, 6, 47, 0x69);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF);
++ rt2800_rfcsr_write_bank(rt2x00dev, 4, 54, 0x27);
++ rt2800_rfcsr_write_bank(rt2x00dev, 6, 54, 0x20);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x20);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xF7);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x09);
++ }
++
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++ rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x51);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x2C);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x64);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x51);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x36);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
++
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x6C);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFC);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1F);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
++ }
++
++ if (rt2800_hw_get_chippkg(rt2x00dev) == 0 &&
++ rt2800_hw_get_chipver(rt2x00dev) == 1) {
++ /* Initialize RF channel register for DRQFN */
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xE3);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xE5);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x28);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x68);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xF7);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x02);
++ rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xC7);
++ }
+
+ /* Initialize RF DC calibration register to default value */
+ rt2800_rfcsr_write_dccal(rt2x00dev, 0, 0x47);
+@@ -10731,12 +10802,17 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
+ rt2800_rfcsr_write_dccal(rt2x00dev, 62, 0x00);
+ rt2800_rfcsr_write_dccal(rt2x00dev, 63, 0x00);
+
+- rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x08);
+- rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x04);
+- rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x20);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x08);
++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x04);
++ rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x20);
++ }
+
+- rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
+- rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
++ if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++ rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++ rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
++ rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
++ }
+
+ /* Do calibration and init PA/LNA */
+ rt2800_calibration_rt6352(rt2x00dev);
+@@ -11282,6 +11358,17 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
+ rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC);
+ rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY);
+
++ {
++ struct device_node *np = rt2x00dev->dev->of_node;
++ unsigned int led_polarity;
++
++ /* Allow overriding polarity from OF */
++ if (!of_property_read_u32(np, "ralink,led-polarity",
++ &led_polarity))
++ rt2x00_set_field16(&eeprom, EEPROM_FREQ_LED_POLARITY,
++ led_polarity);
++ }
++
+ rt2x00dev->led_mcu_reg = eeprom;
+ #endif /* CPTCFG_RT2X00_LIB_LEDS */
+
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+index 194de67..a18140c 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+@@ -76,6 +76,9 @@ struct rt2800_ops {
+ int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev);
+ __le32 *(*drv_get_txwi)(struct queue_entry *entry);
+ unsigned int (*drv_get_dma_done)(struct data_queue *queue);
++ int (*hw_get_chippkg)(void);
++ int (*hw_get_chipver)(void);
++ int (*hw_get_chipeco)(void);
+ };
+
+ static inline u32 rt2800_register_read(struct rt2x00_dev *rt2x00dev,
+@@ -184,6 +187,27 @@ static inline unsigned int rt2800_drv_get_dma_done(struct data_queue *queue)
+ return rt2800ops->drv_get_dma_done(queue);
+ }
+
++static inline int rt2800_hw_get_chippkg(struct rt2x00_dev *rt2x00dev)
++{
++ const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
++
++ return rt2800ops->hw_get_chippkg();
++}
++
++static inline int rt2800_hw_get_chipver(struct rt2x00_dev *rt2x00dev)
++{
++ const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
++
++ return rt2800ops->hw_get_chipver();
++}
++
++static inline int rt2800_hw_get_chipeco(struct rt2x00_dev *rt2x00dev)
++{
++ const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
++
++ return rt2800ops->hw_get_chipeco();
++}
++
+ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
+ const u8 command, const u8 token,
+ const u8 arg0, const u8 arg1);
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+index c891043..b041952 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+@@ -286,6 +286,10 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
+ return retval;
+ }
+
++static int rt2800pci_get_chippkg(void) { return 0; }
++static int rt2800pci_get_chipver(void) { return 0; }
++static int rt2800pci_get_chipeco(void) { return 0; }
++
+ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
+ .add_chanctx = ieee80211_emulate_add_chanctx,
+ .remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -333,6 +337,9 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
+ .drv_init_registers = rt2800mmio_init_registers,
+ .drv_get_txwi = rt2800mmio_get_txwi,
+ .drv_get_dma_done = rt2800mmio_get_dma_done,
++ .hw_get_chippkg = rt2800pci_get_chippkg,
++ .hw_get_chipver = rt2800pci_get_chipver,
++ .hw_get_chipeco = rt2800pci_get_chipeco,
+ };
+
+ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+index 787dbf0..e0d7893 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+@@ -27,6 +27,12 @@
+ #include "rt2800lib.h"
+ #include "rt2800mmio.h"
+
++/* Needed to probe CHIP_VER register on MT7620 */
++#ifdef CONFIG_SOC_MT7620
++#include <asm/mach-ralink/ralink_regs.h>
++#include <asm/mach-ralink/mt7620.h>
++#endif
++
+ /* Allow hardware encryption to be disabled. */
+ static bool modparam_nohwcrypt;
+ module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
+@@ -90,19 +96,6 @@ static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev,
+ return retval;
+ }
+
+-static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev)
+-{
+- void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
+-
+- if (!base_addr)
+- return -ENOMEM;
+-
+- memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
+-
+- iounmap(base_addr);
+- return 0;
+-}
+-
+ /* Firmware functions */
+ static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev)
+ {
+@@ -131,6 +124,27 @@ static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev,
+ return 0;
+ }
+
++#ifdef CONFIG_SOC_MT7620
++static int rt2800soc_get_chippkg(void)
++{
++ return mt7620_get_pkg();
++}
++
++static int rt2800soc_get_chipver(void)
++{
++ return mt7620_get_chipver();
++}
++
++static int rt2800soc_get_chipeco(void)
++{
++ return mt7620_get_eco();
++}
++#else
++static int rt2800soc_get_chippkg(void) { return 0; }
++static int rt2800soc_get_chipver(void) { return 0; }
++static int rt2800soc_get_chipeco(void) { return 0; }
++#endif
++
+ static const struct ieee80211_ops rt2800soc_mac80211_ops = {
+ .add_chanctx = ieee80211_emulate_add_chanctx,
+ .remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -172,12 +186,15 @@ static const struct rt2800_ops rt2800soc_rt2800_ops = {
+ .register_multiread = rt2x00mmio_register_multiread,
+ .register_multiwrite = rt2x00mmio_register_multiwrite,
+ .regbusy_read = rt2x00mmio_regbusy_read,
+- .read_eeprom = rt2800soc_read_eeprom,
++ .read_eeprom = rt2x00lib_read_eeprom,
+ .hwcrypt_disabled = rt2800soc_hwcrypt_disabled,
+ .drv_write_firmware = rt2800soc_write_firmware,
+ .drv_init_registers = rt2800mmio_init_registers,
+ .drv_get_txwi = rt2800mmio_get_txwi,
+ .drv_get_dma_done = rt2800mmio_get_dma_done,
++ .hw_get_chippkg = rt2800soc_get_chippkg,
++ .hw_get_chipver = rt2800soc_get_chipver,
++ .hw_get_chipeco = rt2800soc_get_chipeco,
+ };
+
+ static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = {
+@@ -243,10 +260,17 @@ static int rt2800soc_probe(struct platform_device *pdev)
+ return rt2x00soc_probe(pdev, &rt2800soc_ops);
+ }
+
++static const struct of_device_id rt2880_wmac_match[] = {
++ { .compatible = "ralink,rt2880-wmac" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, rt2880_wmac_match);
++
+ static struct platform_driver rt2800soc_driver = {
+ .driver = {
+ .name = "rt2800_wmac",
+ .mod_name = KBUILD_MODNAME,
++ .of_match_table = rt2880_wmac_match,
+ },
+ .probe = rt2800soc_probe,
+ .remove = rt2x00soc_remove,
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+index a37f8ea..2663447 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+@@ -628,6 +628,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
+ return 0;
+ }
+
++static int rt2800usb_get_chippkg(void) { return 0; }
++static int rt2800usb_get_chipver(void) { return 0; }
++static int rt2800usb_get_chipeco(void) { return 0; }
++
+ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
+ .add_chanctx = ieee80211_emulate_add_chanctx,
+ .remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -676,6 +680,9 @@ static const struct rt2800_ops rt2800usb_rt2800_ops = {
+ .drv_init_registers = rt2800usb_init_registers,
+ .drv_get_txwi = rt2800usb_get_txwi,
+ .drv_get_dma_done = rt2800usb_get_dma_done,
++ .hw_get_chippkg = rt2800usb_get_chippkg,
++ .hw_get_chipver = rt2800usb_get_chipver,
++ .hw_get_chipeco = rt2800usb_get_chipeco,
+ };
+
+ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+index 0ac4ae9..27d283b 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+@@ -28,6 +28,8 @@
+ #include <linux/average.h>
+ #include <linux/usb.h>
+ #include <linux/clk.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/rt2x00_platform.h>
+
+ #include <net/mac80211.h>
+
+@@ -407,6 +409,7 @@ struct hw_mode_spec {
+ unsigned int supported_bands;
+ #define SUPPORT_BAND_2GHZ 0x00000001
+ #define SUPPORT_BAND_5GHZ 0x00000002
++#define SUPPORT_BAND_BOTH (SUPPORT_BAND_2GHZ | SUPPORT_BAND_5GHZ)
+
+ unsigned int supported_rates;
+ #define SUPPORT_RATE_CCK 0x00000001
+@@ -702,6 +705,7 @@ enum rt2x00_capability_flags {
+ REQUIRE_HT_TX_DESC,
+ REQUIRE_PS_AUTOWAKE,
+ REQUIRE_DELAYED_RFKILL,
++ REQUIRE_EEPROM_FILE,
+
+ /*
+ * Capabilities
+@@ -1024,6 +1028,11 @@ struct rt2x00_dev {
+
+ /* Clock for System On Chip devices. */
+ struct clk *clk;
++
++ /* pinctrl and states for System On Chip devices with PA/LNA. */
++ struct pinctrl *pinctrl;
++ struct pinctrl_state *pins_default;
++ struct pinctrl_state *pins_pa_gpio;
+ };
+
+ struct rt2x00_bar_list_entry {
+@@ -1271,6 +1280,12 @@ rt2x00_has_cap_external_pa(struct rt2x00_dev *rt2x00dev)
+ return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_PA_TX0);
+ }
+
++static inline bool
++rt2x00_has_cap_external_pa(struct rt2x00_dev *rt2x00dev)
++{
++ return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_PA_TX0);
++}
++
+ static inline bool
+ rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev)
+ {
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+index 274524e..69f8d5a 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+@@ -990,6 +990,12 @@ static void rt2x00lib_rate(struct ieee80211_rate *entry,
+
+ void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
+ {
++ struct rt2x00_platform_data *pdata;
++
++ pdata = rt2x00dev->dev->platform_data;
++ if (pdata && pdata->mac_address)
++ ether_addr_copy(eeprom_mac_addr, pdata->mac_address);
++
+ of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
+
+ if (!is_valid_ether_addr(eeprom_mac_addr)) {
+@@ -1007,6 +1013,32 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_rate *rates;
+ unsigned int num_rates;
+ unsigned int i;
++#ifdef CONFIG_OF
++ struct device_node *np = rt2x00dev->dev->of_node;
++ unsigned int enabled;
++ if (!of_property_read_u32(np, "ralink,2ghz",
++ &enabled) && !enabled)
++ spec->supported_bands &= ~SUPPORT_BAND_2GHZ;
++ if (!of_property_read_u32(np, "ralink,5ghz",
++ &enabled) && !enabled)
++ spec->supported_bands &= ~SUPPORT_BAND_5GHZ;
++#endif /* CONFIG_OF */
++
++ if (rt2x00dev->dev->platform_data) {
++ struct rt2x00_platform_data *pdata;
++
++ pdata = rt2x00dev->dev->platform_data;
++ if (pdata->disable_2ghz)
++ spec->supported_bands &= ~SUPPORT_BAND_2GHZ;
++ if (pdata->disable_5ghz)
++ spec->supported_bands &= ~SUPPORT_BAND_5GHZ;
++ }
++
++ if ((spec->supported_bands & SUPPORT_BAND_BOTH) == 0) {
++ rt2x00_err(rt2x00dev, "No supported bands\n");
++ return -EINVAL;
++ }
++
+
+ num_rates = 0;
+ if (spec->supported_rates & SUPPORT_RATE_CCK)
+@@ -1330,7 +1362,7 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
+ */
+ if_limit = &rt2x00dev->if_limits_ap;
+ if_limit->max = rt2x00dev->ops->max_ap_intf;
+- if_limit->types = BIT(NL80211_IFTYPE_AP);
++ if_limit->types = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION);
+ #ifdef CPTCFG_MAC80211_MESH
+ if_limit->types |= BIT(NL80211_IFTYPE_MESH_POINT);
+ #endif
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
+new file mode 100644
+index 0000000..15c4e0b
+--- /dev/null
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
+@@ -0,0 +1,208 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/* Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
++ * Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
++ * <http://rt2x00.serialmonkey.com>
++ */
++
++/* Module: rt2x00lib
++ * Abstract: rt2x00 eeprom file loading routines.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#if IS_ENABLED(CONFIG_MTD)
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#endif
++#include <linux/nvmem-consumer.h>
++#include <linux/of.h>
++
++#include "rt2x00.h"
++#include "rt2x00soc.h"
++
++static void rt2800lib_eeprom_swap(struct rt2x00_dev *rt2x00dev)
++{
++ struct device_node *np = rt2x00dev->dev->of_node;
++ size_t len = rt2x00dev->ops->eeprom_size;
++ int i;
++
++ if (!of_find_property(np, "ralink,eeprom-swap", NULL))
++ return;
++
++ for (i = 0; i < len / sizeof(u16); i++)
++ rt2x00dev->eeprom[i] = swab16(rt2x00dev->eeprom[i]);
++}
++
++#if IS_ENABLED(CONFIG_MTD)
++static int rt2800lib_read_eeprom_mtd(struct rt2x00_dev *rt2x00dev)
++{
++ int ret = -EINVAL;
++#ifdef CONFIG_OF
++ struct device_node *np = rt2x00dev->dev->of_node, *mtd_np = NULL;
++ int size, offset = 0;
++ struct mtd_info *mtd;
++ const char *part;
++ const __be32 *list;
++ phandle phandle;
++ size_t retlen;
++
++ list = of_get_property(np, "ralink,mtd-eeprom", &size);
++ if (!list)
++ return -ENOENT;
++
++ phandle = be32_to_cpup(list++);
++ if (phandle)
++ mtd_np = of_find_node_by_phandle(phandle);
++ if (!mtd_np) {
++ dev_err(rt2x00dev->dev, "failed to load mtd phandle\n");
++ return -EINVAL;
++ }
++
++ part = of_get_property(mtd_np, "label", NULL);
++ if (!part)
++ part = mtd_np->name;
++
++ mtd = get_mtd_device_nm(part);
++ if (IS_ERR(mtd)) {
++ dev_err(rt2x00dev->dev, "failed to get mtd device \"%s\"\n", part);
++ return PTR_ERR(mtd);
++ }
++
++ if (size > sizeof(*list))
++ offset = be32_to_cpup(list);
++
++ ret = mtd_read(mtd, offset, rt2x00dev->ops->eeprom_size,
++ &retlen, (u_char *)rt2x00dev->eeprom);
++ put_mtd_device(mtd);
++
++ if (retlen != rt2x00dev->ops->eeprom_size || ret) {
++ dev_err(rt2x00dev->dev, "failed to load eeprom from device \"%s\"\n", part);
++ return ret;
++ }
++
++ rt2800lib_eeprom_swap(rt2x00dev);
++
++ dev_info(rt2x00dev->dev, "loaded eeprom from mtd device \"%s\"\n", part);
++#endif
++
++ return ret;
++}
++#endif
++
++static int rt2800lib_read_eeprom_nvmem(struct rt2x00_dev *rt2x00dev)
++{
++ struct device_node *np = rt2x00dev->dev->of_node;
++ unsigned int len = rt2x00dev->ops->eeprom_size;
++ struct nvmem_cell *cell;
++ const void *data;
++ size_t retlen;
++ int ret = 0;
++
++ cell = of_nvmem_cell_get(np, "eeprom");
++ if (IS_ERR(cell))
++ return PTR_ERR(cell);
++
++ data = nvmem_cell_read(cell, &retlen);
++ nvmem_cell_put(cell);
++
++ if (IS_ERR(data))
++ return PTR_ERR(data);
++
++ if (retlen != len) {
++ dev_err(rt2x00dev->dev, "invalid eeprom size, required: 0x%04x\n", len);
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ memcpy(rt2x00dev->eeprom, data, len);
++
++ rt2800lib_eeprom_swap(rt2x00dev);
++
++exit:
++ kfree(data);
++ return ret;
++}
++
++static const char *
++rt2x00lib_get_eeprom_file_name(struct rt2x00_dev *rt2x00dev)
++{
++ struct rt2x00_platform_data *pdata = rt2x00dev->dev->platform_data;
++#ifdef CONFIG_OF
++ struct device_node *np;
++ const char *eep;
++#endif
++
++ if (pdata && pdata->eeprom_file_name)
++ return pdata->eeprom_file_name;
++
++#ifdef CONFIG_OF
++ np = rt2x00dev->dev->of_node;
++ if (np && !of_property_read_string(np, "ralink,eeprom", &eep))
++ return eep;
++#endif
++
++ return NULL;
++}
++
++static int rt2x00lib_read_eeprom_file(struct rt2x00_dev *rt2x00dev)
++{
++ const struct firmware *ee;
++ const char *ee_name;
++ int retval;
++
++ ee_name = rt2x00lib_get_eeprom_file_name(rt2x00dev);
++ if (!ee_name && test_bit(REQUIRE_EEPROM_FILE, &rt2x00dev->cap_flags)) {
++ rt2x00_err(rt2x00dev, "Required EEPROM name is missing.");
++ return -EINVAL;
++ }
++
++ if (!ee_name)
++ return 0;
++
++ rt2x00_info(rt2x00dev, "Loading EEPROM data from '%s'.\n", ee_name);
++
++ retval = request_firmware(&ee, ee_name, rt2x00dev->dev);
++ if (retval) {
++ rt2x00_err(rt2x00dev, "Failed to request EEPROM.\n");
++ return retval;
++ }
++
++ if (!ee || !ee->size || !ee->data) {
++ rt2x00_err(rt2x00dev, "Failed to read EEPROM file.\n");
++ retval = -ENOENT;
++ goto err_exit;
++ }
++
++ if (ee->size != rt2x00dev->ops->eeprom_size) {
++ rt2x00_err(rt2x00dev,
++ "EEPROM file size is invalid, it should be %d bytes\n",
++ rt2x00dev->ops->eeprom_size);
++ retval = -EINVAL;
++ goto err_release_ee;
++ }
++
++ memcpy(rt2x00dev->eeprom, ee->data, rt2x00dev->ops->eeprom_size);
++
++err_release_ee:
++ release_firmware(ee);
++err_exit:
++ return retval;
++}
++
++int rt2x00lib_read_eeprom(struct rt2x00_dev *rt2x00dev)
++{
++ int ret;
++
++#if IS_ENABLED(CONFIG_MTD)
++ ret = rt2800lib_read_eeprom_mtd(rt2x00dev);
++ if (!ret)
++ return 0;
++#endif
++
++ ret = rt2800lib_read_eeprom_nvmem(rt2x00dev);
++ if (!ret)
++ return 0;
++
++ return rt2x00lib_read_eeprom_file(rt2x00dev);
++}
++EXPORT_SYMBOL_GPL(rt2x00lib_read_eeprom);
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
+index f5361d5..bad5ce2 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
+@@ -98,6 +98,9 @@ static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
+ led->led_dev.name = name;
+ led->led_dev.brightness = LED_OFF;
+
++ if (rt2x00_is_soc(rt2x00dev))
++ led->led_dev.brightness_set(&led->led_dev, LED_OFF);
++
+ retval = led_classdev_register(device, &led->led_dev);
+ if (retval) {
+ rt2x00_err(rt2x00dev, "Failed to register led handler\n");
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
+index eface61..541b718 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
+@@ -86,6 +86,7 @@ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops)
+ if (IS_ERR(rt2x00dev->clk))
+ rt2x00dev->clk = NULL;
+
++ set_bit(REQUIRE_EEPROM_FILE, &rt2x00dev->cap_flags);
+ rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
+
+ retval = rt2x00soc_alloc_reg(rt2x00dev);
+@@ -96,6 +97,21 @@ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops)
+ if (retval)
+ goto exit_free_reg;
+
++ rt2x00dev->pinctrl = devm_pinctrl_get(&pdev->dev);
++ if (IS_ERR(rt2x00dev->pinctrl)) {
++ rt2x00dev->pinctrl = NULL;
++ rt2x00dev->pins_default = NULL;
++ rt2x00dev->pins_pa_gpio = NULL;
++ } else {
++ rt2x00dev->pins_default = pinctrl_lookup_state(rt2x00dev->pinctrl, "default");
++ if (IS_ERR(rt2x00dev->pins_default))
++ rt2x00dev->pins_default = NULL;
++
++ rt2x00dev->pins_pa_gpio = pinctrl_lookup_state(rt2x00dev->pinctrl, "pa_gpio");
++ if (IS_ERR(rt2x00dev->pins_pa_gpio))
++ rt2x00dev->pins_pa_gpio = NULL;
++ }
++
+ return 0;
+
+ exit_free_reg:
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
+index 021fd06..21cd951 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
+@@ -26,4 +26,13 @@ int rt2x00soc_resume(struct platform_device *pdev);
+ #define rt2x00soc_resume NULL
+ #endif /* CONFIG_PM */
+
++/*
++ * EEPROM file handlers.
++ */
++#ifdef CPTCFG_RT2X00_LIB_EEPROM
++int rt2x00lib_read_eeprom(struct rt2x00_dev *rt2x00dev);
++#else
++#define rt2x00lib_read_eeprom NULL
++#endif /* CPTCFG_RT2X00_LIB_EEPROM */
++
+ #endif /* RT2X00SOC_H */
+diff --git a/include/linux/rt2x00_platform.h b/include/linux/rt2x00_platform.h
+new file mode 100644
+index 0000000..e10377e
+--- /dev/null
++++ b/include/linux/rt2x00_platform.h
+@@ -0,0 +1,23 @@
++/*
++ * Platform data definition for the rt2x00 driver
++ *
++ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation.
++ *
++ */
++
++#ifndef _RT2X00_PLATFORM_H
++#define _RT2X00_PLATFORM_H
++
++struct rt2x00_platform_data {
++ char *eeprom_file_name;
++ const u8 *mac_address;
++
++ int disable_2ghz;
++ int disable_5ghz;
++};
++
++#endif /* _RT2X00_PLATFORM_H */
+diff --git a/local-symbols b/local-symbols
+index 3d81ba5..beafad9 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -331,6 +331,7 @@ RT2X00_LIB_FIRMWARE=
+ RT2X00_LIB_CRYPTO=
+ RT2X00_LIB_LEDS=
+ RT2X00_LIB_DEBUGFS=
++RT2X00_LIB_EEPROM=
+ RT2X00_DEBUG=
+ WLAN_VENDOR_REALTEK=
+ RTL8180=
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-sync-backports-patches-subsys.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-sync-backports-patches-subsys.patch
new file mode 100644
index 0000000..2ab7252
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-sync-backports-patches-subsys.patch
@@ -0,0 +1,941 @@
+From 3a7f4236d9d089c749bbc4ff537f8ff2437acf2e Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 12 Mar 2024 11:29:55 +0800
+Subject: [PATCH 07/61] sync backports patches/subsys
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ include/net/cfg80211.h | 28 +++++++++++
+ net/mac80211/cfg.c | 2 +
+ net/mac80211/debugfs.c | 13 +++++-
+ net/mac80211/ieee80211_i.h | 4 ++
+ net/mac80211/main.c | 19 +-------
+ net/mac80211/mesh.c | 8 +++-
+ net/mac80211/mesh.h | 31 ++++++++++--
+ net/mac80211/mesh_pathtbl.c | 19 +++++---
+ net/mac80211/rc80211_minstrel_ht.c | 75 +++++++-----------------------
+ net/mac80211/rc80211_minstrel_ht.h | 2 +-
+ net/mac80211/rx.c | 13 ++++--
+ net/mac80211/sta_info.c | 29 +++++++-----
+ net/mac80211/tx.c | 46 +++++++++---------
+ net/wireless/ap.c | 6 +--
+ net/wireless/chan.c | 45 ++++++++++++++++++
+ net/wireless/core.c | 15 ------
+ net/wireless/core.h | 2 +
+ net/wireless/mlme.c | 7 +--
+ net/wireless/sysfs.c | 27 +++++++++--
+ 19 files changed, 240 insertions(+), 151 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 0b5799f..4c5daf9 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -189,6 +189,8 @@ enum ieee80211_channel_flags {
+ * @dfs_state: current state of this channel. Only relevant if radar is required
+ * on this channel.
+ * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
++ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
++ * channel was available.
+ * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
+ * @psd: power spectral density (in dBm)
+ */
+@@ -206,6 +208,7 @@ struct ieee80211_channel {
+ int orig_mag, orig_mpwr;
+ enum nl80211_dfs_state dfs_state;
+ unsigned long dfs_state_entered;
++ unsigned long dfs_state_last_available;
+ unsigned int dfs_cac_ms;
+ s8 psd;
+ };
+@@ -1075,6 +1078,30 @@ int cfg80211_chandef_primary(const struct cfg80211_chan_def *chandef,
+ enum nl80211_chan_width primary_chan_width,
+ u16 *punctured);
+
++/**
++ * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable and we
++ * can/need start CAC on such channel
++ * @wiphy: the wiphy to validate against
++ * @chandef: the channel definition to check
++ *
++ * Return: true if all channels available and at least
++ * one channel requires CAC (NL80211_DFS_USABLE)
++ */
++bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef);
++
++/**
++ * cfg80211_chandef_dfs_cac_time - get the DFS CAC time (in ms) for given
++ * channel definition
++ * @wiphy: the wiphy to validate against
++ * @chandef: the channel definition to check
++ *
++ * Returns: DFS CAC time (in ms) which applies for this channel definition
++ */
++unsigned int
++cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef);
++
+ /**
+ * nl80211_send_chandef - sends the channel definition.
+ * @msg: the msg to send channel definition
+@@ -3413,6 +3440,7 @@ enum wiphy_params_flags {
+ /* The per TXQ device queue limit in airtime */
+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000
+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000
++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC 50000
+
+ /* The per interface airtime threshold to switch to lower queue limit */
+ #define IEEE80211_AQL_THRESHOLD 24000
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index db71792..72e64be 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -2851,6 +2851,8 @@ static int ieee80211_scan(struct wiphy *wiphy,
+ */
+ fallthrough;
+ case NL80211_IFTYPE_AP:
++ /* skip check */
++ break;
+ /*
+ * If the scan has been forced (and the driver supports
+ * forcing), don't care about being beaconing already.
+diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
+index c660138..f9c5ed8 100644
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct file *file,
+ "VI %u us\n"
+ "BE %u us\n"
+ "BK %u us\n"
++ "BC/MC %u us\n"
+ "total %u us\n",
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
++ atomic_read(&local->aql_bc_pending_airtime),
+ atomic_read(&local->aql_total_pending_airtime));
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, len);
+@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct file *file,
+ "VO %u %u\n"
+ "VI %u %u\n"
+ "BE %u %u\n"
+- "BK %u %u\n",
++ "BK %u %u\n"
++ "BC/MC %u\n",
+ local->aql_txq_limit_low[IEEE80211_AC_VO],
+ local->aql_txq_limit_high[IEEE80211_AC_VO],
+ local->aql_txq_limit_low[IEEE80211_AC_VI],
+@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct file *file,
+ local->aql_txq_limit_low[IEEE80211_AC_BE],
+ local->aql_txq_limit_high[IEEE80211_AC_BE],
+ local->aql_txq_limit_low[IEEE80211_AC_BK],
+- local->aql_txq_limit_high[IEEE80211_AC_BK]);
++ local->aql_txq_limit_high[IEEE80211_AC_BK],
++ local->aql_txq_limit_bc);
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, len);
+ }
+@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struct file *file,
+ else
+ buf[count] = '\0';
+
++ if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
++ local->aql_txq_limit_bc = q_limit_low;
++ return count;
++ }
++
+ if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
+ return -EINVAL;
+
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index 4827825..c5781c3 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -102,6 +102,8 @@ ieee80211_sta_keep_active(struct sta_info *sta, u8 ac)
+ return time_before_eq(jiffies, sta->airtime[ac].last_active + HZ / 10);
+ }
+
++#define AIRTIME_QUANTUM_SHIFT 3
++
+ struct ieee80211_bss {
+ u32 device_ts_beacon, device_ts_presp;
+
+@@ -1343,10 +1345,12 @@ struct ieee80211_local {
+ spinlock_t handle_wake_tx_queue_lock;
+
+ u16 airtime_flags;
++ u32 aql_txq_limit_bc;
+ u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
+ u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
+ u32 aql_threshold;
+ atomic_t aql_total_pending_airtime;
++ atomic_t aql_bc_pending_airtime;
+ atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
+
+ const struct ieee80211_ops *ops;
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index 6518ca5..81a9645 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -944,6 +944,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
+ spin_lock_init(&local->rx_path_lock);
+ spin_lock_init(&local->queue_stop_reason_lock);
+
++ local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ INIT_LIST_HEAD(&local->active_txqs[i]);
+ spin_lock_init(&local->active_txq_lock[i]);
+@@ -1562,24 +1563,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
+ debugfs_hw_add(local);
+ rate_control_add_debugfs(local);
+
+- rtnl_lock();
+- wiphy_lock(hw->wiphy);
+-
+- /* add one default STA interface if supported */
+- if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
+- !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
+- struct vif_params params = {0};
+-
+- result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
+- NL80211_IFTYPE_STATION, ¶ms);
+- if (result)
+- wiphy_warn(local->hw.wiphy,
+- "Failed to add default virtual iface\n");
+- }
+-
+- wiphy_unlock(hw->wiphy);
+- rtnl_unlock();
+-
+ #ifdef CONFIG_INET
+ local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
+ result = register_inetaddr_notifier(&local->ifa_notifier);
+diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
+index 32475da..cbc9b5e 100644
+--- a/net/mac80211/mesh.c
++++ b/net/mac80211/mesh.c
+@@ -747,6 +747,9 @@ bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 ctrl_flags)
+ {
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
++ struct ieee80211_mesh_fast_tx_key key = {
++ .type = MESH_FAST_TX_TYPE_LOCAL
++ };
+ struct ieee80211_mesh_fast_tx *entry;
+ struct ieee80211s_hdr *meshhdr;
+ u8 sa[ETH_ALEN] __aligned(2);
+@@ -782,7 +785,10 @@ bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
+ return false;
+ }
+
+- entry = mesh_fast_tx_get(sdata, skb->data);
++ ether_addr_copy(key.addr, skb->data);
++ if (!ether_addr_equal(skb->data + ETH_ALEN, sdata->vif.addr))
++ key.type = MESH_FAST_TX_TYPE_PROXIED;
++ entry = mesh_fast_tx_get(sdata, &key);
+ if (!entry)
+ return false;
+
+diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
+index c472b49..c0c357f 100644
+--- a/net/mac80211/mesh.h
++++ b/net/mac80211/mesh.h
+@@ -134,10 +134,34 @@ struct mesh_path {
+ #define MESH_FAST_TX_CACHE_THRESHOLD_SIZE 384
+ #define MESH_FAST_TX_CACHE_TIMEOUT 8000 /* msecs */
+
++/**
++ * enum ieee80211_mesh_fast_tx_type - cached mesh fast tx entry type
++ *
++ * @MESH_FAST_TX_TYPE_LOCAL: tx from the local vif address as SA
++ * @MESH_FAST_TX_TYPE_PROXIED: local tx with a different SA (e.g. bridged)
++ * @MESH_FAST_TX_TYPE_FORWARDED: forwarded from a different mesh point
++ */
++enum ieee80211_mesh_fast_tx_type {
++ MESH_FAST_TX_TYPE_LOCAL,
++ MESH_FAST_TX_TYPE_PROXIED,
++ MESH_FAST_TX_TYPE_FORWARDED,
++};
++
++/**
++ * struct ieee80211_mesh_fast_tx_key - cached mesh fast tx entry key
++ *
++ * @addr: The Ethernet DA for this entry
++ * @type: cache entry type
++ */
++struct ieee80211_mesh_fast_tx_key {
++ u8 addr[ETH_ALEN] __aligned(2);
++ enum ieee80211_mesh_fast_tx_type type;
++};
++
+ /**
+ * struct ieee80211_mesh_fast_tx - cached mesh fast tx entry
+ * @rhash: rhashtable pointer
+- * @addr_key: The Ethernet DA which is the key for this entry
++ * @key: the lookup key for this cache entry
+ * @fast_tx: base fast_tx data
+ * @hdr: cached mesh and rfc1042 headers
+ * @hdrlen: length of mesh + rfc1042
+@@ -148,7 +172,7 @@ struct mesh_path {
+ */
+ struct ieee80211_mesh_fast_tx {
+ struct rhash_head rhash;
+- u8 addr_key[ETH_ALEN] __aligned(2);
++ struct ieee80211_mesh_fast_tx_key key;
+
+ struct ieee80211_fast_tx fast_tx;
+ u8 hdr[sizeof(struct ieee80211s_hdr) + sizeof(rfc1042_header)];
+@@ -334,7 +358,8 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
+
+ bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
+ struct ieee80211_mesh_fast_tx *
+-mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr);
++mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_mesh_fast_tx_key *key);
+ bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 ctrl_flags);
+ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
+diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
+index 91b55d6..93f6a03 100644
+--- a/net/mac80211/mesh_pathtbl.c
++++ b/net/mac80211/mesh_pathtbl.c
+@@ -37,8 +37,8 @@ static const struct rhashtable_params mesh_rht_params = {
+ static const struct rhashtable_params fast_tx_rht_params = {
+ .nelem_hint = 10,
+ .automatic_shrinking = true,
+- .key_len = ETH_ALEN,
+- .key_offset = offsetof(struct ieee80211_mesh_fast_tx, addr_key),
++ .key_len = sizeof(struct ieee80211_mesh_fast_tx_key),
++ .key_offset = offsetof(struct ieee80211_mesh_fast_tx, key),
+ .head_offset = offsetof(struct ieee80211_mesh_fast_tx, rhash),
+ .hashfn = mesh_table_hash,
+ };
+@@ -431,20 +431,21 @@ static void mesh_fast_tx_entry_free(struct mesh_tx_cache *cache,
+ }
+
+ struct ieee80211_mesh_fast_tx *
+-mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr)
++mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_mesh_fast_tx_key *key)
+ {
+ struct ieee80211_mesh_fast_tx *entry;
+ struct mesh_tx_cache *cache;
+
+ cache = &sdata->u.mesh.tx_cache;
+- entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
++ entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params);
+ if (!entry)
+ return NULL;
+
+ if (!(entry->mpath->flags & MESH_PATH_ACTIVE) ||
+ mpath_expired(entry->mpath)) {
+ spin_lock_bh(&cache->walk_lock);
+- entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
++ entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params);
+ if (entry)
+ mesh_fast_tx_entry_free(cache, entry);
+ spin_unlock_bh(&cache->walk_lock);
+@@ -489,18 +490,24 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
+ if (!sta)
+ return;
+
++ build.key.type = MESH_FAST_TX_TYPE_LOCAL;
+ if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) {
+ /* This is required to keep the mppath alive */
+ mppath = mpp_path_lookup(sdata, meshhdr->eaddr1);
+ if (!mppath)
+ return;
+ build.mppath = mppath;
++ if (!ether_addr_equal(meshhdr->eaddr2, sdata->vif.addr))
++ build.key.type = MESH_FAST_TX_TYPE_PROXIED;
+ } else if (ieee80211_has_a4(hdr->frame_control)) {
+ mppath = mpath;
+ } else {
+ return;
+ }
+
++ if (!ether_addr_equal(hdr->addr4, sdata->vif.addr))
++ build.key.type = MESH_FAST_TX_TYPE_FORWARDED;
++
+ /* rate limit, in case fast xmit can't be enabled */
+ if (mppath->fast_tx_check == jiffies)
+ return;
+@@ -547,7 +554,7 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
+ }
+ }
+
+- memcpy(build.addr_key, mppath->dst, ETH_ALEN);
++ memcpy(build.key.addr, mppath->dst, ETH_ALEN);
+ build.timestamp = jiffies;
+ build.fast_tx.band = info->band;
+ build.fast_tx.da_offs = offsetof(struct ieee80211_hdr, addr3);
+diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
+index 62f323a..74413d7 100644
+--- a/net/mac80211/rc80211_minstrel_ht.c
++++ b/net/mac80211/rc80211_minstrel_ht.c
+@@ -582,6 +582,14 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
+ int cur_tp_avg, cur_group, cur_idx;
+ int max_gpr_group, max_gpr_idx;
+ int max_gpr_tp_avg, max_gpr_prob;
++ int min_dur;
++
++ min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
++ minstrel_get_duration(mi->max_tp_rate[1]));
++
++ /* make the rate at least 18% slower than max tp rates */
++ if (minstrel_get_duration(index) <= min_dur * 19 / 16)
++ return;
+
+ cur_group = MI_RATE_GROUP(index);
+ cur_idx = MI_RATE_IDX(index);
+@@ -603,11 +611,6 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
+ !minstrel_ht_is_legacy_group(max_tp_group))
+ return;
+
+- /* skip rates faster than max tp rate with lower prob */
+- if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
+- mrs->prob_avg < max_tp_prob)
+- return;
+-
+ max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
+ max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
+ max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
+@@ -665,40 +668,6 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
+
+ }
+
+-/*
+- * Try to increase robustness of max_prob rate by decrease number of
+- * streams if possible.
+- */
+-static inline void
+-minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
+-{
+- struct minstrel_mcs_group_data *mg;
+- int tmp_max_streams, group, tmp_idx, tmp_prob;
+- int tmp_tp = 0;
+-
+- if (!mi->sta->deflink.ht_cap.ht_supported)
+- return;
+-
+- group = MI_RATE_GROUP(mi->max_tp_rate[0]);
+- tmp_max_streams = minstrel_mcs_groups[group].streams;
+- for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
+- mg = &mi->groups[group];
+- if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
+- continue;
+-
+- tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
+- tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
+-
+- if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
+- (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
+- mi->max_prob_rate = mg->max_group_prob_rate;
+- tmp_tp = minstrel_ht_get_tp_avg(mi, group,
+- tmp_idx,
+- tmp_prob);
+- }
+- }
+-}
+-
+ static u16
+ __minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
+ enum minstrel_sample_type type)
+@@ -771,7 +740,8 @@ minstrel_ht_calc_rate_stats(struct minstrel_priv *mp,
+ unsigned int cur_prob;
+
+ if (unlikely(mrs->attempts > 0)) {
+- cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
++ cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
++ mrs->attempts + mrs->last_attempts);
+ minstrel_filter_avg_add(&mrs->prob_avg,
+ &mrs->prob_avg_1, cur_prob);
+ mrs->att_hist += mrs->attempts;
+@@ -1177,8 +1147,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
+
+ mi->max_prob_rate = tmp_max_prob_rate;
+
+- /* Try to increase robustness of max_prob_rate*/
+- minstrel_ht_prob_rate_reduce_streams(mi);
+ minstrel_ht_refill_sample_rates(mi);
+
+ #ifdef CPTCFG_MAC80211_DEBUGFS
+@@ -1257,7 +1225,7 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp,
+ }
+
+ static void
+-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
++minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
+ {
+ int group, orig_group;
+
+@@ -1272,11 +1240,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
+ minstrel_mcs_groups[orig_group].streams)
+ continue;
+
+- if (primary)
+- *idx = mi->groups[group].max_group_tp_rate[0];
+- else
+- *idx = mi->groups[group].max_group_tp_rate[1];
+- break;
++ *idx = mi->groups[group].max_group_prob_rate;
+ }
+ }
+
+@@ -1287,7 +1251,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_tx_info *info = st->info;
+ struct minstrel_ht_sta *mi = priv_sta;
+ struct ieee80211_tx_rate *ar = info->status.rates;
+- struct minstrel_rate_stats *rate, *rate2;
++ struct minstrel_rate_stats *rate;
+ struct minstrel_priv *mp = priv;
+ u32 update_interval = mp->update_interval;
+ bool last, update = false;
+@@ -1355,18 +1319,13 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
+ /*
+ * check for sudden death of spatial multiplexing,
+ * downgrade to a lower number of streams if necessary.
++ * only do this for the max_prob_rate to prevent spurious
++ * rate fluctuations when the link changes suddenly
+ */
+- rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
++ rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
+ if (rate->attempts > 30 &&
+ rate->success < rate->attempts / 4) {
+- minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
+- update = true;
+- }
+-
+- rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
+- if (rate2->attempts > 30 &&
+- rate2->success < rate2->attempts / 4) {
+- minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
++ minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
+ update = true;
+ }
+ }
+diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
+index f385cf6..1f78a94 100644
+--- a/net/mac80211/rc80211_minstrel_ht.h
++++ b/net/mac80211/rc80211_minstrel_ht.h
+@@ -14,7 +14,7 @@
+
+ /* scaled fraction values */
+ #define MINSTREL_SCALE 12
+-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
++#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
+ #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
+
+ #define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 42ffd1e..be724c2 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2767,7 +2767,10 @@ ieee80211_rx_mesh_fast_forward(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int hdrlen)
+ {
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+- struct ieee80211_mesh_fast_tx *entry = NULL;
++ struct ieee80211_mesh_fast_tx_key key = {
++ .type = MESH_FAST_TX_TYPE_FORWARDED
++ };
++ struct ieee80211_mesh_fast_tx *entry;
+ struct ieee80211s_hdr *mesh_hdr;
+ struct tid_ampdu_tx *tid_tx;
+ struct sta_info *sta;
+@@ -2776,9 +2779,13 @@ ieee80211_rx_mesh_fast_forward(struct ieee80211_sub_if_data *sdata,
+
+ mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(eth));
+ if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
+- entry = mesh_fast_tx_get(sdata, mesh_hdr->eaddr1);
++ ether_addr_copy(key.addr, mesh_hdr->eaddr1);
+ else if (!(mesh_hdr->flags & MESH_FLAGS_AE))
+- entry = mesh_fast_tx_get(sdata, skb->data);
++ ether_addr_copy(key.addr, skb->data);
++ else
++ return false;
++
++ entry = mesh_fast_tx_get(sdata, &key);
+ if (!entry)
+ return false;
+
+diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
+index 32d050c..411a610 100644
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -912,6 +912,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
+
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ mesh_accept_plinks_update(sdata);
++ ieee80211_check_fast_xmit(sta);
+
+ ieee80211_check_fast_xmit(sta);
+
+@@ -2354,28 +2355,27 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
+ struct sta_info *sta, u8 ac,
+ u16 tx_airtime, bool tx_completed)
+ {
++ atomic_t *counter;
+ int tx_pending;
+
+ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
+ return;
+
+- if (!tx_completed) {
+- if (sta)
+- atomic_add(tx_airtime,
+- &sta->airtime[ac].aql_tx_pending);
++ if (sta)
++ counter = &sta->airtime[ac].aql_tx_pending;
++ else
++ counter = &local->aql_bc_pending_airtime;
+
++ if (!tx_completed) {
++ atomic_add(tx_airtime, counter);
+ atomic_add(tx_airtime, &local->aql_total_pending_airtime);
+ atomic_add(tx_airtime, &local->aql_ac_pending_airtime[ac]);
+ return;
+ }
+
+- if (sta) {
+- tx_pending = atomic_sub_return(tx_airtime,
+- &sta->airtime[ac].aql_tx_pending);
+- if (tx_pending < 0)
+- atomic_cmpxchg(&sta->airtime[ac].aql_tx_pending,
+- tx_pending, 0);
+- }
++ tx_pending = atomic_sub_return(tx_airtime, counter);
++ if (tx_pending < 0)
++ atomic_cmpxchg(counter, tx_pending, 0);
+
+ atomic_sub(tx_airtime, &local->aql_total_pending_airtime);
+ tx_pending = atomic_sub_return(tx_airtime,
+@@ -2439,6 +2439,13 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate,
+
+ sband = local->hw.wiphy->bands[band];
+
++ if (!sband) {
++ wiphy_warn(local->hw.wiphy,
++ "Invalid band %d\n",
++ band);
++ break;
++ }
++
+ if (WARN_ON_ONCE(!sband->bitrates))
+ break;
+
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index 141b094..f479d87 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3978,9 +3978,8 @@ begin:
+ encap_out:
+ info->control.vif = vif;
+
+- if (tx.sta &&
+- wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
+- bool ampdu = txq->ac != IEEE80211_AC_VO;
++ if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
++ bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
+ u32 airtime;
+
+ airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
+@@ -4043,6 +4042,7 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
+ struct ieee80211_txq *ret = NULL;
+ struct txq_info *txqi = NULL, *head = NULL;
+ bool found_eligible_txq = false;
++ bool aql_check;
+
+ spin_lock_bh(&local->active_txq_lock[ac]);
+
+@@ -4066,26 +4066,26 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
+ if (!head)
+ head = txqi;
+
++ aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
++ if (aql_check)
++ found_eligible_txq = true;
++
+ if (txqi->txq.sta) {
+ struct sta_info *sta = container_of(txqi->txq.sta,
+ struct sta_info, sta);
+- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
+- s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
+-
+- if (aql_check)
+- found_eligible_txq = true;
+-
+- if (deficit < 0)
++ if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
+ sta->airtime[txqi->txq.ac].deficit +=
+- sta->airtime_weight;
+-
+- if (deficit < 0 || !aql_check) {
+- list_move_tail(&txqi->schedule_order,
+- &local->active_txqs[txqi->txq.ac]);
+- goto begin;
++ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
++ aql_check = false;
+ }
+ }
+
++ if (!aql_check) {
++ list_move_tail(&txqi->schedule_order,
++ &local->active_txqs[txqi->txq.ac]);
++ goto begin;
++ }
++
+ if (txqi->schedule_round == local->schedule_round[ac])
+ goto out;
+
+@@ -4150,7 +4150,8 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
+ return true;
+
+ if (!txq->sta)
+- return true;
++ return atomic_read(&local->aql_bc_pending_airtime) <
++ local->aql_txq_limit_bc;
+
+ if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
+ return true;
+@@ -4199,15 +4200,15 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+
+ spin_lock_bh(&local->active_txq_lock[ac]);
+
+- if (!txqi->txq.sta)
+- goto out;
+-
+ if (list_empty(&txqi->schedule_order))
+ goto out;
+
+ if (!ieee80211_txq_schedule_airtime_check(local, ac))
+ goto out;
+
++ if (!txqi->txq.sta)
++ goto out;
++
+ list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
+ schedule_order) {
+ if (iter == txqi)
+@@ -4220,7 +4221,8 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+ }
+ sta = container_of(iter->txq.sta, struct sta_info, sta);
+ if (ieee80211_sta_deficit(sta, ac) < 0)
+- sta->airtime[ac].deficit += sta->airtime_weight;
++ sta->airtime[ac].deficit += sta->airtime_weight <<
++ AIRTIME_QUANTUM_SHIFT;
+ list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
+ }
+
+@@ -4228,7 +4230,7 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+ if (sta->airtime[ac].deficit >= 0)
+ goto out;
+
+- sta->airtime[ac].deficit += sta->airtime_weight;
++ sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
+ list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
+ spin_unlock_bh(&local->active_txq_lock[ac]);
+
+diff --git a/net/wireless/ap.c b/net/wireless/ap.c
+index 9a9a870..9cd0ab4 100644
+--- a/net/wireless/ap.c
++++ b/net/wireless/ap.c
+@@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ if (!wdev->links[link_id].ap.beacon_interval)
+ return -ENOENT;
+
++ cfg80211_update_last_available(wdev->wiphy,
++ &wdev->links[link_id].ap.chandef);
++
+ err = rdev_stop_ap(rdev, dev, link_id);
+ if (!err) {
+ wdev->conn_owner_nlportid = 0;
+@@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ if (notify)
+ nl80211_send_ap_stopped(wdev, link_id);
+
+- /* Should we apply the grace period during beaconing interface
+- * shutdown also?
+- */
+ cfg80211_sched_dfs_chan_update(rdev);
+ }
+
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index 14c27bc..4bac395 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -560,6 +560,8 @@ static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
+
+ c->dfs_state = dfs_state;
+ c->dfs_state_entered = jiffies;
++ if (dfs_state == NL80211_DFS_AVAILABLE)
++ c->dfs_state_last_available = jiffies;
+ }
+ }
+
+@@ -1049,6 +1051,49 @@ static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
+ return true;
+ }
+
++static void
++__cfg80211_update_last_available(struct wiphy *wiphy,
++ u32 center_freq,
++ u32 bandwidth)
++{
++ struct ieee80211_channel *c;
++ u32 freq, start_freq, end_freq;
++
++ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
++ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
++
++ /*
++ * Check entire range of channels for the bandwidth.
++ * If any channel in between is disabled or has not
++ * had gone through CAC return false
++ */
++ for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
++ c = ieee80211_get_channel_khz(wiphy, freq);
++ if (!c)
++ return;
++
++ c->dfs_state_last_available = jiffies;
++ }
++}
++
++void cfg80211_update_last_available(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef)
++{
++ int width;
++
++ width = cfg80211_chandef_get_width(chandef);
++ if (width < 0)
++ return;
++
++ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
++ width);
++ if (chandef->width != NL80211_CHAN_WIDTH_80P80)
++ return;
++
++ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
++ width);
++}
++
+ static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+ {
+diff --git a/net/wireless/core.c b/net/wireless/core.c
+index 0cd5c78..ac9417e 100644
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -662,21 +662,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
+ c->limits[j].max > 1))
+ return -EINVAL;
+
+- /*
+- * This isn't well-defined right now. If you have an
+- * IBSS interface, then its beacon interval may change
+- * by joining other networks, and nothing prevents it
+- * from doing that.
+- * So technically we probably shouldn't even allow AP
+- * and IBSS in the same interface, but it seems that
+- * some drivers support that, possibly only with fixed
+- * beacon intervals for IBSS.
+- */
+- if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
+- c->beacon_int_min_gcd)) {
+- return -EINVAL;
+- }
+-
+ cnt += c->limits[j].max;
+ /*
+ * Don't advertise an unsupported type
+diff --git a/net/wireless/core.h b/net/wireless/core.h
+index 2e19279..7bef6b0 100644
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -467,6 +467,8 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
+ enum nl80211_dfs_state dfs_state);
+
+ void cfg80211_dfs_channels_update_work(struct work_struct *work);
++void cfg80211_update_last_available(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef);
+
+ void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
+
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 3b0fe7c..c7e62eb 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1037,6 +1037,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
+ radar_event = NL80211_RADAR_NOP_FINISHED;
++ timeout = c->dfs_state_entered +
++ msecs_to_jiffies(time_dfs_update);
+ } else {
+ if (regulatory_pre_cac_allowed(wiphy) ||
+ cfg80211_any_wiphy_oper_chan(wiphy, c))
+@@ -1044,11 +1046,10 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
+
+ time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
+ radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
++ timeout = c->dfs_state_last_available +
++ msecs_to_jiffies(time_dfs_update);
+ }
+
+- timeout = c->dfs_state_entered +
+- msecs_to_jiffies(time_dfs_update);
+-
+ if (time_after_eq(jiffies, timeout)) {
+ c->dfs_state = NL80211_DFS_USABLE;
+ c->dfs_state_entered = jiffies;
+diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
+index 1387106..49aac4c 100644
+--- a/net/wireless/sysfs.c
++++ b/net/wireless/sysfs.c
+@@ -24,18 +24,35 @@ static inline struct cfg80211_registered_device *dev_to_rdev(
+ return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
+ }
+
+-#define SHOW_FMT(name, fmt, member) \
++#define SHOW_FMT(name, fmt, member, mode) \
+ static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
+ } \
+-static DEVICE_ATTR_RO(name)
++static DEVICE_ATTR_##mode(name)
+
+-SHOW_FMT(index, "%d", wiphy_idx);
+-SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
+-SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
++static ssize_t macaddress_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ u8 mac[ETH_ALEN];
++
++ if (!mac_pton(buf, mac))
++ return -EINVAL;
++
++ if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
++ return -EINVAL;
++
++ memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN);
++
++ return strnlen(buf, len);
++}
++
++SHOW_FMT(index, "%d", wiphy_idx, RO);
++SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW);
++SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO);
+
+ static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
new file mode 100644
index 0000000..b7d52f5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
@@ -0,0 +1,27 @@
+From ab5065afa6302d7241b2252a6f2ebdba8dc9763b Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 18 Jan 2022 20:29:44 +0800
+Subject: [PATCH 08/61] mtk: mac80211: do not setup twt when twt responder is
+ false
+
+---
+ net/mac80211/rx.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index be724c2..89a1199 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -3458,6 +3458,9 @@ ieee80211_process_rx_twt_action(struct ieee80211_rx_data *rx)
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ return false;
+
++ if (!sdata->vif.bss_conf.twt_responder)
++ return false;
++
+ if (!rx->local->ops->add_twt_setup)
+ return false;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
new file mode 100644
index 0000000..d5a498d
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
@@ -0,0 +1,61 @@
+From e27e641fd771a6d882a61a889b9295cdab5dcc72 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 29 Mar 2022 16:06:30 +0800
+Subject: [PATCH 09/61] mtk: cfg80211: extend CAC time for weather radar
+ channels
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h | 1 +
+ net/wireless/chan.c | 7 +++++++
+ net/wireless/nl80211.c | 3 +++
+ 3 files changed, 11 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 4c5daf9..d987b62 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -163,6 +163,7 @@ enum ieee80211_channel_flags {
+ (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+
+ #define IEEE80211_DFS_MIN_CAC_TIME_MS 60000
++#define IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS 600000
+ #define IEEE80211_DFS_MIN_NOP_TIME_MS (30 * 60 * 1000)
+
+ /**
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index 4bac395..2224329 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -1152,6 +1152,13 @@ static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
+ if (!(c->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
++ /* weather radar in ETSI */
++ if (reg_get_dfs_region(wiphy) == NL80211_DFS_ETSI &&
++ freq >= MHZ_TO_KHZ(5600) && freq <= MHZ_TO_KHZ(5640) &&
++ dfs_cac_ms < IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS &&
++ c->dfs_state == NL80211_DFS_USABLE)
++ dfs_cac_ms = IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS;
++
+ if (c->dfs_cac_ms > dfs_cac_ms)
+ dfs_cac_ms = c->dfs_cac_ms;
+ }
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 3d11013..b8c8848 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10019,6 +10019,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ if (WARN_ON(!cac_time_ms))
+ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+
++ pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
++ __func__, dfs_region, chandef.center_freq1, chandef.center_freq2, cac_time_ms);
++
+ err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
+ if (!err) {
+ wdev->links[0].ap.chandef = chandef;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
new file mode 100644
index 0000000..1fefb28
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
@@ -0,0 +1,28 @@
+From 639c4598fd67a554c8502d113eb64ef3cf7660d4 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 1 Apr 2022 09:15:21 +0800
+Subject: [PATCH 10/61] mtk: mac80211: it's invalid case when frag_threshold is
+ greater than 2346
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ net/wireless/nl80211.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index b8c8848..54e19b1 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -3701,6 +3701,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+ goto out;
+ }
+
++ if (frag_threshold >= 2346)
++ frag_threshold = (u32) -1;
++
+ if (frag_threshold != (u32) -1) {
+ /*
+ * Fragments (apart from the last one) are required to
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
new file mode 100644
index 0000000..5b1cb2a
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
@@ -0,0 +1,529 @@
+From 60adbabe8df3bdbab9bd3c2146f19b4c83b69def Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 22 Sep 2022 14:27:41 +0800
+Subject: [PATCH 11/61] mtk: cfg80211: implement DFS status show, cac and nop
+ skip command via debugfs
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Refactor DFS debugfs command for MLO
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I5e50523684bdc5086a18d36177e0d81530ba0e3f
+---
+ include/net/cfg80211.h | 1 +
+ net/mac80211/cfg.c | 25 +++
+ net/wireless/core.h | 3 +
+ net/wireless/debugfs.c | 326 +++++++++++++++++++++++++++++++++++++++-
+ net/wireless/mlme.c | 6 +
+ net/wireless/rdev-ops.h | 14 ++
+ net/wireless/trace.h | 13 ++
+ 7 files changed, 381 insertions(+), 7 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index d987b62..c55028f 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4965,6 +4965,7 @@ struct cfg80211_ops {
+ struct cfg80211_set_hw_timestamp *hwts);
+ int (*set_ttlm)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ttlm_params *params);
++ void (*skip_cac)(struct wireless_dev *wdev, unsigned int link_id);
+ };
+
+ /*
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 72e64be..3f4c129 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -5055,6 +5055,30 @@ ieee80211_set_ttlm(struct wiphy *wiphy, struct net_device *dev,
+ return ieee80211_req_neg_ttlm(sdata, params);
+ }
+
++static void
++ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
++{
++ struct net_device *dev = wdev->netdev;
++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++ struct ieee80211_link_data *link;
++ unsigned int cac_time_ms;
++
++ link = sdata_dereference(sdata->link[link_id], sdata);
++ if (!link)
++ return;
++
++ wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
++ &link->dfs_cac_timer_work);
++ if (wdev->cac_started) {
++ ieee80211_link_release_channel(link);
++ cac_time_ms = wdev->cac_time_ms;
++ wdev->cac_start_time = jiffies -
++ msecs_to_jiffies(cac_time_ms + 1);
++ cfg80211_cac_event(wdev->netdev, &link->conf->chanreq.oper,
++ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
++ }
++}
++
+ const struct cfg80211_ops mac80211_config_ops = {
+ .add_virtual_intf = ieee80211_add_iface,
+ .del_virtual_intf = ieee80211_del_iface,
+@@ -5168,4 +5192,5 @@ const struct cfg80211_ops mac80211_config_ops = {
+ .del_link_station = ieee80211_del_link_station,
+ .set_hw_timestamp = ieee80211_set_hw_timestamp,
+ .set_ttlm = ieee80211_set_ttlm,
++ .skip_cac = ieee80211_skip_cac,
+ };
+diff --git a/net/wireless/core.h b/net/wireless/core.h
+index 7bef6b0..ae6b7fa 100644
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -86,6 +86,9 @@ struct cfg80211_registered_device {
+
+ struct wireless_dev *background_radar_wdev;
+ struct cfg80211_chan_def background_radar_chandef;
++ bool background_cac_started;
++ unsigned long background_cac_start_time;
++ unsigned int background_cac_time_ms;
+ struct delayed_work background_cac_done_wk;
+ struct work_struct background_cac_abort_wk;
+
+diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
+index 7c59a25..a246b2c 100644
+--- a/net/wireless/debugfs.c
++++ b/net/wireless/debugfs.c
+@@ -10,6 +10,7 @@
+ #include <linux/slab.h>
+ #include "core.h"
+ #include "debugfs.h"
++#include "rdev-ops.h"
+
+ #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
+ static ssize_t name## _read(struct file *file, char __user *userbuf, \
+@@ -97,18 +98,329 @@ static const struct file_operations ht40allow_map_ops = {
+ .llseek = default_llseek,
+ };
+
+-#define DEBUGFS_ADD(name) \
+- debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
++static int dfs_print_chan(struct ieee80211_channel *chan, int remain_time, int wait_time,
++ char *buf, int buf_size, int offset, bool is_background)
++{
++ if (WARN_ON(offset > buf_size))
++ return 0;
++
++ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
++ offset += scnprintf(buf + offset, buf_size - offset,
++ " Channel = %d, DFS_state = Unavailable",
++ chan->hw_value);
++ if (remain_time > 0)
++ offset += scnprintf(buf + offset, buf_size - offset,
++ ", Non-occupancy Remain Time = %d / %d [sec]",
++ remain_time, wait_time);
++ else
++ offset += scnprintf(buf + offset, buf_size - offset,
++ ", Changing state...");
++ } else if (chan->dfs_state == NL80211_DFS_USABLE) {
++ offset += scnprintf(buf + offset, buf_size - offset,
++ " Channel = %d, DFS_state = Usable",
++ chan->hw_value);
++ if (remain_time > 0)
++ offset += scnprintf(buf + offset, buf_size - offset,
++ ", CAC Remain Time = %d / %d [sec]",
++ remain_time, wait_time);
++ } else if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
++ offset += scnprintf(buf + offset, buf_size - offset,
++ " Channel = %d, DFS_state = Available",
++ chan->hw_value);
++ } else {
++ offset += scnprintf(buf + offset, buf_size - offset,
++ " Channel = %d, DFS_state = Unknown",
++ chan->hw_value);
++ }
++
++ if (is_background)
++ offset += scnprintf(buf + offset, buf_size - offset,
++ " (background chain)");
++ offset += scnprintf(buf + offset, buf_size - offset, "\n");
++
++ return offset;
++}
++
++static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev, char *buf,
++ unsigned int buf_size, unsigned int offset)
++{
++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++ struct cfg80211_chan_def *chandef;
++ struct cfg80211_chan_def *background_chandef = &rdev->background_radar_chandef;
++ enum nl80211_band band;
++ struct ieee80211_supported_band *sband;
++ struct ieee80211_channel *chan;
++ unsigned long jiffies_passed;
++ unsigned int link_id;
++ int i, remain_time = 0, wait_time_ms = 0;
++ bool is_background;
++
++ for (band = 0; band < NUM_NL80211_BANDS; band++)
++ if (wiphy->bands[band] &&
++ wiphy->bands[band]->band == NL80211_BAND_5GHZ)
++ sband = wiphy->bands[band];
++
++ if (!sband) {
++ offset += scnprintf(buf + offset, buf_size - offset, "No 5G band\n");
++ return offset;
++ }
++
++ for_each_valid_link(wdev, link_id) {
++ chandef = wdev_chandef(wdev, link_id);
++ if (!chandef || !chandef->chan ||
++ chandef->chan->band != NL80211_BAND_5GHZ)
++ continue;
++
++ offset += scnprintf(buf + offset, buf_size - offset,
++ "Link %d DFS channel:\n", link_id);
++ for (i = 0; i < sband->n_channels; i++) {
++ is_background = false;
++ chan = &sband->channels[i];
++
++ if (!(chan->flags & IEEE80211_CHAN_RADAR))
++ continue;
++
++ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
++ jiffies_passed = jiffies - chan->dfs_state_entered;
++ wait_time_ms = IEEE80211_DFS_MIN_NOP_TIME_MS;
++ remain_time = (wait_time_ms - jiffies_to_msecs(jiffies_passed));
++ if (remain_time > wait_time_ms)
++ remain_time = 0;
++ } else if (chan->dfs_state == NL80211_DFS_USABLE) {
++ if (wdev->cac_started &&
++ cfg80211_is_sub_chan(chandef, chan, false)) {
++ jiffies_passed = jiffies - wdev->cac_start_time;
++ wait_time_ms = wdev->cac_time_ms;
++ remain_time = (wait_time_ms -
++ jiffies_to_msecs(jiffies_passed));
++ }
++
++ if (rdev->background_radar_wdev == wdev &&
++ rdev->background_cac_started &&
++ cfg80211_is_sub_chan(background_chandef, chan, false)) {
++ jiffies_passed = jiffies - rdev->background_cac_start_time;
++ wait_time_ms = rdev->background_cac_time_ms;
++ remain_time = (wait_time_ms -
++ jiffies_to_msecs(jiffies_passed));
++ is_background = true;
++ }
++
++ if (remain_time > wait_time_ms)
++ remain_time = 0;
++
++ } else {
++ if (rdev->background_radar_wdev == wdev &&
++ cfg80211_is_sub_chan(background_chandef, chan, false))
++ is_background = true;
++ }
++
++ offset = dfs_print_chan(chan, remain_time / 1000, wait_time_ms / 1000,
++ buf, buf_size, offset, is_background);
++ remain_time = 0;
++ }
++ }
++
++ return offset;
++}
++
++static ssize_t dfs_status_read(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct wiphy *wiphy = file->private_data;
++ struct wireless_dev *wdev;
++ char *buf;
++ unsigned int offset = 0, buf_size = PAGE_SIZE, r;
++ const char * const iftype_str[] = {
++ [NL80211_IFTYPE_UNSPECIFIED] = "unspecified",
++ [NL80211_IFTYPE_ADHOC] = "adhoc",
++ [NL80211_IFTYPE_STATION] = "station",
++ [NL80211_IFTYPE_AP] = "ap",
++ [NL80211_IFTYPE_AP_VLAN] = "ap vlan",
++ [NL80211_IFTYPE_WDS] = "wds",
++ [NL80211_IFTYPE_MONITOR] = "monitor",
++ [NL80211_IFTYPE_MESH_POINT] = "mesh point",
++ [NL80211_IFTYPE_P2P_CLIENT] = "p2p client",
++ [NL80211_IFTYPE_P2P_GO] = "p2p go",
++ [NL80211_IFTYPE_P2P_DEVICE] = "p2p device",
++ [NL80211_IFTYPE_OCB] = "ocb",
++ [NL80211_IFTYPE_NAN] = "nan",
++ };
++
++ buf = kzalloc(buf_size, GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++ offset += scnprintf(buf + offset, buf_size - offset,
++ "wdev 0x%x\n"
++ "interface type %s\n",
++ wdev->identifier, iftype_str[wdev->iftype]);
++ offset = dfs_status_read_wdev(wiphy, wdev, buf, buf_size, offset);
++ }
++
++ r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
++
++ kfree(buf);
++
++ return r;
++}
++
++static const struct file_operations dfs_status_ops = {
++ .read = dfs_status_read,
++ .open = simple_open,
++ .llseek = default_llseek,
++};
++
++static int
++dfs_nop_skip(void *data, u64 val)
++{
++ struct wiphy *wiphy = data;
++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++ bool en = !!val;
++ enum nl80211_band band;
++ struct ieee80211_supported_band *sband;
++ struct ieee80211_channel *chan;
++ u32 nop_time = IEEE80211_DFS_MIN_NOP_TIME_MS;
++ int i;
++
++ if (!en)
++ return 0;
++
++ for (band = 0; band < NUM_NL80211_BANDS; band++) {
++ sband = wiphy->bands[band];
++ if (!sband)
++ continue;
++ for (i = 0; i < sband->n_channels; i++) {
++ chan = &sband->channels[i];
++
++ if (!(chan->flags & IEEE80211_CHAN_RADAR))
++ continue;
++
++ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
++ // Let current jiffies > dfs_state_entered_jiffies + NOP time
++ chan->dfs_state_entered = jiffies -
++ msecs_to_jiffies(nop_time + 1);
++ }
++ }
++ }
++
++ cfg80211_sched_dfs_chan_update(rdev);
++
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_nop_ops, NULL,
++ dfs_nop_skip, "0x%08llx\n");
++
++static int
++dfs_cac_skip(void *data, u64 val)
++{
++#define CAC_SKIP_MASK BIT(0)
++#define CAC_SKIP_BACKGROUND_MASK BIT(1)
++ struct wiphy *wiphy = data;
++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++ struct wireless_dev *wdev;
++ struct cfg80211_chan_def *c;
++ unsigned int link_id, skip_mode = val;
++ unsigned long cac_time;
++
++ if (!skip_mode || skip_mode > (CAC_SKIP_MASK | CAC_SKIP_BACKGROUND_MASK))
++ return 0;
++
++ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++ if (skip_mode & CAC_SKIP_MASK) {
++ for_each_valid_link(wdev, link_id) {
++ c = wdev_chandef(wdev, link_id);
++ if (!c || !c->chan ||
++ c->chan->band != NL80211_BAND_5GHZ)
++ continue;
++
++ if (cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0 &&
++ cfg80211_chandef_dfs_usable(wiphy, c) && wdev->cac_started) {
++ rdev_skip_cac(rdev, wdev, link_id);
++ }
++ }
++ }
++
++ if ((skip_mode & CAC_SKIP_BACKGROUND_MASK) &&
++ rdev->background_radar_wdev == wdev &&
++ rdev->background_radar_chandef.chan) {
++ c = &rdev->background_radar_chandef;
++
++ if ((cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0) &&
++ cfg80211_chandef_dfs_usable(wiphy, c) &&
++ rdev->background_cac_started) {
++ // Let current jiffies > dfs_state_entered_jiffies + CAC time
++ cac_time = rdev->background_cac_time_ms;
++ rdev->background_cac_start_time = jiffies -
++ msecs_to_jiffies(cac_time + 1);
++ cancel_delayed_work(&rdev->background_cac_done_wk);
++ queue_delayed_work(cfg80211_wq, &rdev->background_cac_done_wk, 0);
++ }
++ }
++ }
++
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_cac_ops, NULL,
++ dfs_cac_skip, "0x%08llx\n");
++
++static int
++dfs_available_reset(void *data, u64 val)
++{
++ struct wiphy *wiphy = data;
++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++ bool en = !!val;
++ enum nl80211_band band;
++ struct ieee80211_supported_band *sband;
++ struct ieee80211_channel *chan;
++ int i;
++
++ if (!en)
++ return 0;
++
++ for (band = 0; band < NUM_NL80211_BANDS; band++) {
++ sband = wiphy->bands[band];
++ if (!sband)
++ continue;
++ for (i = 0; i < sband->n_channels; i++) {
++ chan = &sband->channels[i];
++
++ if (!(chan->flags & IEEE80211_CHAN_RADAR))
++ continue;
++
++ if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
++ chan->dfs_state = NL80211_DFS_USABLE;
++ chan->dfs_state_entered = jiffies;
++ }
++ }
++ }
++
++ cfg80211_sched_dfs_chan_update(rdev);
++
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(dfs_available_reset_ops, NULL,
++ dfs_available_reset, "0x%08llx\n");
++
++#define DEBUGFS_ADD(name, chmod) \
++ debugfs_create_file(#name, chmod, phyd, &rdev->wiphy, &name## _ops)
+
+ void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
+ {
+ struct dentry *phyd = rdev->wiphy.debugfsdir;
+
+- DEBUGFS_ADD(rts_threshold);
+- DEBUGFS_ADD(fragmentation_threshold);
+- DEBUGFS_ADD(short_retry_limit);
+- DEBUGFS_ADD(long_retry_limit);
+- DEBUGFS_ADD(ht40allow_map);
++ DEBUGFS_ADD(rts_threshold, 0444);
++ DEBUGFS_ADD(fragmentation_threshold, 0444);
++ DEBUGFS_ADD(short_retry_limit, 0444);
++ DEBUGFS_ADD(long_retry_limit, 0444);
++ DEBUGFS_ADD(ht40allow_map, 0444);
++ DEBUGFS_ADD(dfs_status, 0444);
++ DEBUGFS_ADD(dfs_skip_nop, 0600);
++ DEBUGFS_ADD(dfs_skip_cac, 0600);
++ DEBUGFS_ADD(dfs_available_reset, 0600);
+ }
+
+ struct debugfs_read_work {
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index c7e62eb..d42b65b 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1177,13 +1177,16 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
+ queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
+ cfg80211_sched_dfs_chan_update(rdev);
+ wdev = rdev->background_radar_wdev;
++ rdev->background_cac_started = false;
+ break;
+ case NL80211_RADAR_CAC_ABORTED:
+ if (!cancel_delayed_work(&rdev->background_cac_done_wk))
+ return;
+ wdev = rdev->background_radar_wdev;
++ rdev->background_cac_started = false;
+ break;
+ case NL80211_RADAR_CAC_STARTED:
++ rdev->background_cac_started = true;
+ break;
+ default:
+ return;
+@@ -1203,6 +1206,7 @@ cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
+ chandef, event);
+ wiphy_unlock(&rdev->wiphy);
+ }
++EXPORT_SYMBOL(cfg80211_background_cac_event);
+
+ void cfg80211_background_cac_done_wk(struct work_struct *work)
+ {
+@@ -1264,8 +1268,10 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
+ if (!cac_time_ms)
+ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+
++ rdev->background_cac_time_ms = cac_time_ms;
+ rdev->background_radar_chandef = *chandef;
+ rdev->background_radar_wdev = wdev; /* Get offchain ownership */
++ rdev->background_cac_start_time = jiffies;
+
+ __cfg80211_background_cac_event(rdev, wdev, chandef,
+ NL80211_RADAR_CAC_STARTED);
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index 466828f..2ae7fc5 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1542,4 +1542,18 @@ rdev_set_ttlm(struct cfg80211_registered_device *rdev,
+
+ return ret;
+ }
++
++static inline int
++rdev_skip_cac(struct cfg80211_registered_device *rdev,
++ struct wireless_dev *wdev, unsigned int link_id)
++{
++ if (!rdev->ops->skip_cac)
++ return -EOPNOTSUPP;
++
++ trace_rdev_skip_cac(wdev, link_id);
++ rdev->ops->skip_cac(wdev, link_id);
++ trace_rdev_return_void(&rdev->wiphy);
++
++ return 0;
++}
+ #endif /* __CFG80211_RDEV_OPS */
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index 7073a70..aa3284f 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -4005,6 +4005,19 @@ TRACE_EVENT(rdev_set_ttlm,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+ );
+
++TRACE_EVENT(rdev_skip_cac,
++ TP_PROTO(struct wireless_dev *wdev, unsigned int link_id),
++ TP_ARGS(wdev, link_id),
++ TP_STRUCT__entry(
++ WDEV_ENTRY
++ __field(unsigned int, link_id)
++ ),
++ TP_fast_assign(
++ WDEV_ASSIGN;
++ __entry->link_id = link_id;
++ ),
++ TP_printk(WDEV_PR_FMT ", link_id: %d", WDEV_PR_ARG, __entry->link_id)
++);
+ #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
+
+ #undef TRACE_INCLUDE_PATH
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
new file mode 100644
index 0000000..ee54b68
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
@@ -0,0 +1,26 @@
+From f3260628a2860a7af6176205fb201ffe7a5334e7 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 4 Oct 2022 10:47:05 +0800
+Subject: [PATCH 12/61] mtk: mac80211: Set TWT Information Frame Disabled bit
+ as 1.
+
+This modification means that current implementation do not support twt information frame.
+---
+ net/mac80211/s1g.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index d4ed0c0..27eccbb 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -102,6 +102,7 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
+
+ twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
++ twt->control |= IEEE80211_TWT_CONTROL_RX_DISABLED;
+
+ /* broadcast TWT not supported yet */
+ if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-mtk-mac80211-check-the-control-channel-before-downgr.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-mtk-mac80211-check-the-control-channel-before-downgr.patch
new file mode 100644
index 0000000..4671638
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-mtk-mac80211-check-the-control-channel-before-downgr.patch
@@ -0,0 +1,55 @@
+From 32cfa2d7e115d5cdeeb130fbec61a248e9fe3676 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 16 Dec 2022 03:31:06 +0800
+Subject: [PATCH 13/61] mtk: mac80211: check the control channel before
+ downgrading the bandwidth
+
+Change-Id: I25f0495818194a18e4acf23d6c45393f12e32080
+---
+ net/mac80211/mlme.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index b653c7d..77e5898 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -4994,6 +4994,26 @@ ieee80211_determine_our_sta_mode_assoc(struct ieee80211_sub_if_data *sdata,
+ conn->bw_limit, tmp.bw_limit);
+ }
+
++static bool ieee80211_check_same_ctrl_channel(struct ieee80211_sub_if_data *sdata,
++ const struct cfg80211_chan_def *chandef)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_chanctx *ctx;
++
++ lockdep_assert_wiphy(local->hw.wiphy);
++
++ list_for_each_entry(ctx, &local->chanctx_list, list) {
++ if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
++ continue;
++ if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
++ continue;
++ if (chandef->chan == ctx->conf.def.chan)
++ return true;
++ }
++
++ return false;
++}
++
+ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_link_data *link,
+ int link_id,
+@@ -5073,6 +5093,9 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
+ chanreq.oper.width == NL80211_CHAN_WIDTH_10)
+ return ret;
+
++ if (!ret || !ieee80211_check_same_ctrl_channel(sdata, &chanreq.oper))
++ return ret;
++
+ while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) {
+ ieee80211_chanreq_downgrade(&chanreq, conn);
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch
new file mode 100644
index 0000000..8c29c26
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch
@@ -0,0 +1,55 @@
+From ead7bd30ec2e85057d69c23429fe7cc2a184e45c Mon Sep 17 00:00:00 2001
+From: TomLiu <tomml.liu@mediatek.com>
+Date: Wed, 14 Dec 2022 00:26:50 -0800
+Subject: [PATCH 14/61] mtk: mac80211: fix tx amsdu aggregation
+
+---
+ include/net/mac80211.h | 7 +++++++
+ net/mac80211/agg-tx.c | 6 ++++--
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index c8375b1..8cda233 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3043,6 +3043,13 @@ static inline void _ieee80211_hw_set(struct ieee80211_hw *hw,
+ }
+ #define ieee80211_hw_set(hw, flg) _ieee80211_hw_set(hw, IEEE80211_HW_##flg)
+
++static inline void _ieee80211_hw_clear(struct ieee80211_hw *hw,
++ enum ieee80211_hw_flags flg)
++{
++ return __clear_bit(flg, hw->flags);
++}
++#define ieee80211_hw_clear(hw, flg) _ieee80211_hw_clear(hw, IEEE80211_HW_##flg)
++
+ /**
+ * struct ieee80211_scan_request - hw scan request
+ *
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 21d55dc..068b5b9 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -66,7 +66,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+- u16 capab;
++ u16 capab = 0;
++ bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
+
+ skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+
+@@ -95,7 +96,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
+
+ mgmt->u.action.u.addba_req.dialog_token = dialog_token;
+- capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
++ if (amsdu)
++ capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
+ capab |= IEEE80211_ADDBA_PARAM_POLICY_MASK;
+ capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK);
+ capab |= u16_encode_bits(agg_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
new file mode 100644
index 0000000..d02a2c0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
@@ -0,0 +1,125 @@
+From 5c16c13e56474ee77a9bd8ea59aeaeaae8923ea8 Mon Sep 17 00:00:00 2001
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Wed, 18 May 2022 15:10:22 +0800
+Subject: [PATCH 15/61] mtk: mac80211: add fill receive path ops to get wed idx
+
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Change-Id: Ib3dbf4a1d960e4b0aad3ebdb1327912ae087f877
+---
+ include/net/mac80211.h | 5 +++++
+ net/mac80211/driver-ops.h | 13 +++++++++++++
+ net/mac80211/iface.c | 23 +++++++++++++++++++++++
+ net/mac80211/util.c | 9 +++++++++
+ 4 files changed, 50 insertions(+)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 8cda233..01cfcc0 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4390,6 +4390,8 @@ struct ieee80211_prep_tx_info {
+ * resolve a path for hardware flow offloading
+ * @can_activate_links: Checks if a specific active_links bitmap is
+ * supported by the driver.
++ * @net_fill_receive_path: Called from .ndo_fill_receive_path in order to
++ * get a path for hardware flow offloading
+ * @change_vif_links: Change the valid links on an interface, note that while
+ * removing the old link information is still valid (link_conf pointer),
+ * but may immediately disappear after the function returns. The old or
+@@ -4778,6 +4780,9 @@ struct ieee80211_ops {
+ bool (*can_activate_links)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 active_links);
++ int (*net_fill_receive_path)(struct ieee80211_hw *hw,
++ struct net_device_path_ctx *ctx,
++ struct net_device_path *path);
+ int (*change_vif_links)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 old_links, u16 new_links,
+diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
+index 1eda9ec..9be87b8 100644
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1649,6 +1649,19 @@ static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
+ return ret;
+ }
+
++static inline int drv_net_fill_receive_path(struct ieee80211_local *local,
++ struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++ int ret = -EOPNOTSUPP;
++
++ if (local->ops->net_fill_receive_path)
++ ret = local->ops->net_fill_receive_path(&local->hw,
++ ctx, path);
++
++ return ret;
++}
++
+ static inline int drv_net_setup_tc(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct net_device *dev,
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 6363e8c..0ae31a9 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -988,6 +988,28 @@ out:
+ return ret;
+ }
+
++static int ieee80211_netdev_fill_receive_path(struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++ struct ieee80211_sub_if_data *sdata;
++ struct ieee80211_local *local;
++ int ret = -ENOENT;
++
++ sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev);
++ local = sdata->local;
++
++ if (!local->ops->net_fill_receive_path)
++ return -EOPNOTSUPP;
++
++ rcu_read_lock();
++
++ ret = drv_net_fill_receive_path(local, ctx, path);
++
++ rcu_read_unlock();
++
++ return ret;
++}
++
+ static const struct net_device_ops ieee80211_dataif_8023_ops = {
+ #if LINUX_VERSION_IS_LESS(4,10,0)
+ .ndo_change_mtu = __change_mtu,
+@@ -1006,6 +1028,7 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = {
+ #endif
+
+ .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
++ .ndo_fill_receive_path = ieee80211_netdev_fill_receive_path,
+ .ndo_setup_tc = ieee80211_netdev_setup_tc,
+ };
+
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index cda398d..dd06bd2 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -874,6 +874,15 @@ struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
+ }
+ EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
+
++struct net_device *ieee80211_vif_to_netdev(struct ieee80211_vif *vif)
++{
++ if (!vif)
++ return NULL;
++
++ return vif_to_sdata(vif)->dev;
++}
++EXPORT_SYMBOL_GPL(ieee80211_vif_to_netdev);
++
+ /*
+ * Nothing should have been stuffed into the workqueue during
+ * the suspend->resume cycle. Since we can't check each caller
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-mtk-mac80211-track-obss-color-bitmap.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-mtk-mac80211-track-obss-color-bitmap.patch
new file mode 100644
index 0000000..dbbfb55
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-mtk-mac80211-track-obss-color-bitmap.patch
@@ -0,0 +1,85 @@
+From 71be2e718dc884fb35433338dc21d6547b237819 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Mon, 13 Mar 2023 05:23:37 +0800
+Subject: [PATCH 16/61] mtk: mac80211: track obss color bitmap
+
+Track OBSS BSS color when receive their beacon.
+
+Adding 2 tracepoint for debug, usage:
+echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_bitmap/enable
+echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_collision/enable
+
+Change-Id: I24ebcc3181c05476b41107ce8fe7f3d4c8907e81
+---
+ include/net/mac80211.h | 1 +
+ net/mac80211/rx.c | 6 +++++-
+ net/mac80211/trace.h | 22 ++++++++++++++++++++++
+ 3 files changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 01cfcc0..965a026 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -762,6 +762,7 @@ struct ieee80211_bss_conf {
+ } he_oper;
+ struct ieee80211_he_obss_pd he_obss_pd;
+ struct cfg80211_he_bss_color he_bss_color;
++ u64 used_color_bitmap;
+ struct ieee80211_fils_discovery fils_discovery;
+ u32 unsol_bcast_probe_resp_interval;
+ struct cfg80211_bitrate_mask beacon_tx_rate;
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 89a1199..65982a6 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -3395,9 +3395,13 @@ ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
+
+ color = le32_get_bits(he_oper->he_oper_params,
+ IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
++
++ bss_conf->used_color_bitmap |= BIT_ULL(color);
++
++ // trace_bss_color_bitmap(color, bss_conf->used_color_bitmap);
+ if (color == bss_conf->he_bss_color.color)
+ ieee80211_obss_color_collision_notify(&rx->sdata->vif,
+- BIT_ULL(color));
++ bss_conf->used_color_bitmap);
+ }
+ }
+
+diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
+index 8e758b5..9ec45ce 100644
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -3145,6 +3145,28 @@ TRACE_EVENT(drv_neg_ttlm_res,
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->res
+ )
+ );
++
++TRACE_EVENT(bss_color_bitmap,
++ TP_PROTO(u8 color,
++ u64 color_bitmap),
++
++ TP_ARGS(color, color_bitmap),
++
++ TP_STRUCT__entry(
++ __field(u8, color)
++ __field(u64, color_bitmap)
++ ),
++
++ TP_fast_assign(
++ __entry->color = color;
++ __entry->color_bitmap = color_bitmap;
++ ),
++
++ TP_printk(
++ "color=%u color_bitmap=0x%llx", __entry->color, __entry->color_bitmap
++ )
++);
++
+ #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+
+ #undef TRACE_INCLUDE_PATH
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
new file mode 100644
index 0000000..31e8a28
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
@@ -0,0 +1,43 @@
+From cd9eddae9afaece218189e1d3ccaffe256b3ccc8 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Fri, 14 Apr 2023 05:05:17 +0800
+Subject: [PATCH 17/61] mtk: mac80211: update max_bssid_indicator based on real
+ BSS numbers
+
+Fix max_bssid_indicator get empty value due to wrong pointer.
+
+CR-Id: WCNCR00259302
+Change-Id: I2a9bcd96e9432569a9968ba9e519c55ebe13cdfe
+---
+ net/mac80211/cfg.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 3f4c129..cb91223 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1167,9 +1167,11 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ /* copy in optional mbssid_ies */
+ if (mbssid) {
+ u8 *pos = new->tail + new->tail_len;
++ u8 *bssid_indicator;
+
+ new->mbssid_ies = (void *)pos;
+ pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
++ bssid_indicator = pos + 2;
+ pos += ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies,
+ mbssid);
+ if (rnr) {
+@@ -1178,8 +1180,7 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ ieee80211_copy_rnr_beacon(pos, new->rnr_ies, rnr);
+ }
+ /* update bssid_indicator */
+- link_conf->bssid_indicator =
+- ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
++ sdata->vif.bss_conf.bssid_indicator = *(bssid_indicator);
+ }
+
+ if (csa) {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-mac80211-support-configurable-addba-resp-time.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-mac80211-support-configurable-addba-resp-time.patch
new file mode 100644
index 0000000..a4b0d15
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-mac80211-support-configurable-addba-resp-time.patch
@@ -0,0 +1,42 @@
+From cc6fe6fd359d941ccd376aa6f185eaeb0020fb04 Mon Sep 17 00:00:00 2001
+From: Lian Chen <lian.chen@mediatek.com>
+Date: Wed, 7 Jun 2023 15:30:34 +0800
+Subject: [PATCH 18/61] mtk: mac80211: support configurable addba resp time.
+
+---
+ net/mac80211/agg-tx.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 068b5b9..af3d8e6 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -16,10 +16,16 @@
+ #include <linux/slab.h>
+ #include <linux/export.h>
+ #include <net/mac80211.h>
++#include <linux/moduleparam.h>
+ #include "ieee80211_i.h"
+ #include "driver-ops.h"
+ #include "wme.h"
+
++static int addba_resp_wait_count = 2;
++module_param(addba_resp_wait_count, int, 0644);
++MODULE_PARM_DESC(addba_resp_wait_count,
++ "Number of ADDBA_RESP_INTERVAL to wait for addba response");
++
+ /**
+ * DOC: TX A-MPDU aggregation
+ *
+@@ -466,7 +472,7 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
+ lockdep_assert_wiphy(sta->local->hw.wiphy);
+
+ /* activate the timer for the recipient's addBA response */
+- mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
++ mod_timer(&tid_tx->addba_resp_timer, jiffies + addba_resp_wait_count * ADDBA_RESP_INTERVAL);
+ ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
+ sta->sta.addr, tid);
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
new file mode 100644
index 0000000..244689c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
@@ -0,0 +1,185 @@
+From 103b24de6741cb11d088e6e2be478c7839d32ec4 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:25:24 +0800
+Subject: [PATCH 19/61] mtk: mac80211: add sta-assisted DFS state update
+ mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Ida821ebb42825cd1895eaccfbbfda1106e598009
+---
+ include/net/cfg80211.h | 14 +++++++++
+ include/uapi/linux/nl80211.h | 6 ++++
+ net/mac80211/mlme.c | 14 +++++++++
+ net/wireless/chan.c | 61 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 95 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index c55028f..19cf7f7 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -8679,6 +8679,20 @@ void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp);
+
++/**
++ * cfg80211_sta_update_dfs_state - Update channel's DFS state during STA channel switch,
++ * association, and disassociation
++ * @wdev: the wireless device
++ * @bss_chandef: the current BSS channel definition
++ * @csa_chandef: the CSA channel definition
++ * @associated: whether STA is during association or disassociation process
++ *
++ */
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++ const struct cfg80211_chan_def *bss_chandef,
++ const struct cfg80211_chan_def *csa_chandef,
++ bool associated);
++
+ /**
+ * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
+ * @wiphy: the wiphy
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index f917bc6..7999a65 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6822,6 +6822,10 @@ enum nl80211_smps_mode {
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ * when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ * when STA is disconnected or leaving the channel
+ */
+ enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+@@ -6830,6 +6834,8 @@ enum nl80211_radar_event {
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
++ NL80211_RADAR_STA_CAC_SKIPPED,
++ NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+
+ /**
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 77e5898..52d1cd8 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2170,6 +2170,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
+ sdata->csa_blocked_tx = true;
+ }
+
++ cfg80211_sta_update_dfs_state(&sdata->wdev,
++ &link->conf->chanreq.oper,
++ &link->csa_chanreq.oper,
++ sdata->vif.cfg.assoc);
++
+ cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper,
+ link->link_id, csa_ie.count,
+ csa_ie.mode);
+@@ -3245,6 +3250,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+ link = sdata_dereference(sdata->link[link_id], sdata);
+ if (!link)
+ continue;
++
++ cfg80211_sta_update_dfs_state(&sdata->wdev,
++ &link->conf->chanreq.oper,
++ NULL, sdata->vif.cfg.assoc);
+ ieee80211_link_release_channel(link);
+ }
+
+@@ -5512,6 +5521,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ if (link->tx_conf[ac].uapsd)
+ resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
++
++ if (status_code == WLAN_STATUS_SUCCESS)
++ cfg80211_sta_update_dfs_state(&sdata->wdev,
++ &link->conf->chanreq.oper,
++ NULL, sdata->vif.cfg.assoc);
+ }
+
+ if (ieee80211_vif_is_mld(&sdata->vif)) {
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index 2224329..c851db8 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -14,6 +14,7 @@
+ #include <net/cfg80211.h>
+ #include "core.h"
+ #include "rdev-ops.h"
++#include "nl80211.h"
+
+ static bool cfg80211_valid_60g_freq(u32 freq)
+ {
+@@ -1671,6 +1672,66 @@ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
+ }
+ EXPORT_SYMBOL(cfg80211_any_usable_channels);
+
++static void cfg80211_sta_radar_notify(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef,
++ enum nl80211_radar_event event)
++{
++ struct wireless_dev *wdev;
++
++ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++ if (cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) {
++ nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef,
++ event, wdev->netdev, GFP_KERNEL);
++ return;
++ }
++ }
++}
++
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++ const struct cfg80211_chan_def *bss_chandef,
++ const struct cfg80211_chan_def *csa_chandef,
++ bool associated)
++{
++ bool csa_active = !!csa_chandef;
++ enum nl80211_dfs_state dfs_state = NL80211_DFS_USABLE;
++ enum nl80211_radar_event event = NL80211_RADAR_STA_CAC_EXPIRED;
++
++ if (!bss_chandef || !bss_chandef->chan ||
++ bss_chandef->chan->band != NL80211_BAND_5GHZ)
++ return;
++
++ /* assume csa channel is cac completed */
++ if (csa_active &&
++ (cfg80211_chandef_dfs_usable(wdev->wiphy, csa_chandef) ||
++ cfg80211_chandef_dfs_available(wdev->wiphy, csa_chandef))) {
++ cfg80211_set_dfs_state(wdev->wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
++ cfg80211_sta_radar_notify(wdev->wiphy, csa_chandef,
++ NL80211_RADAR_STA_CAC_SKIPPED);
++ netdev_info(wdev->netdev, "Set CSA channel's DFS state to available\n");
++ }
++
++ /* avoid updating the dfs state during nop */
++ if (!cfg80211_chandef_dfs_usable(wdev->wiphy, bss_chandef) &&
++ !cfg80211_chandef_dfs_available(wdev->wiphy, bss_chandef))
++ return;
++
++ if (associated && !csa_active) {
++ dfs_state = NL80211_DFS_AVAILABLE;
++ event = NL80211_RADAR_STA_CAC_SKIPPED;
++ }
++
++ cfg80211_set_dfs_state(wdev->wiphy, bss_chandef, dfs_state);
++ cfg80211_sta_radar_notify(wdev->wiphy, bss_chandef, event);
++
++ if (csa_active)
++ netdev_info(wdev->netdev, "Set origin channel's DFS state to usable\n");
++ else
++ netdev_info(wdev->netdev, "Set BSS channel's DFS state to %s due to %s\n",
++ (dfs_state == NL80211_DFS_USABLE) ? "usable" : "available",
++ associated ? "association" : "disassociation");
++}
++EXPORT_SYMBOL(cfg80211_sta_update_dfs_state);
++
+ struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
+ unsigned int link_id)
+ {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
new file mode 100644
index 0000000..1810958
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
@@ -0,0 +1,28 @@
+From 0be90bf2df2df04a24a376f1aab1078874a7d5cc Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Fri, 17 Mar 2023 17:36:01 +0800
+Subject: [PATCH 20/61] mtk: nl80211: Mark DFS channel as available for CSA.
+
+---
+ net/wireless/nl80211.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 54e19b1..7085133 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10247,6 +10247,11 @@ skip_beacons:
+ if (err)
+ goto free;
+
++ /* Use RADAR_BACKGROUND attribute here for skipping CAC */
++ if (info->attrs[NL80211_ATTR_RADAR_BACKGROUND]) {
++ cfg80211_set_dfs_state(&rdev->wiphy, ¶ms.chandef, NL80211_DFS_AVAILABLE);
++ }
++
+ if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef,
+ wdev->iftype)) {
+ err = -EINVAL;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
new file mode 100644
index 0000000..acb1a8a
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
@@ -0,0 +1,29 @@
+From 5f16034ca52f980a612f1fddb4d730480d12decd Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 27 Jul 2023 10:25:59 +0800
+Subject: [PATCH 21/61] mtk: cfg80211: fix early return in
+ cfg80211_stop_background_radar_detection
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/wireless/mlme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index d42b65b..56095b3 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1292,9 +1292,9 @@ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
+ return;
+
+ rdev_set_radar_background(rdev, NULL);
+- rdev->background_radar_wdev = NULL; /* Release offchain ownership */
+
+ __cfg80211_background_cac_event(rdev, wdev,
+ &rdev->background_radar_chandef,
+ NL80211_RADAR_CAC_ABORTED);
++ rdev->background_radar_wdev = NULL; /* Release offchain ownership */
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
new file mode 100644
index 0000000..c8c5f69
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
@@ -0,0 +1,29 @@
+From b4960511afc66cff070b19204da6e8cc54cf9630 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 27 Jul 2023 10:27:04 +0800
+Subject: [PATCH 22/61] mtk: cfg80211: add background radar stop when
+ background channel is overlapped with operating channel
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/wireless/nl80211.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 7085133..1f1856b 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10031,6 +10031,10 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ wdev->cac_started = true;
+ wdev->cac_start_time = jiffies;
+ wdev->cac_time_ms = cac_time_ms;
++ if (rdev->background_cac_started &&
++ cfg80211_is_sub_chan(&chandef, rdev->background_radar_chandef.chan, false)) {
++ cfg80211_stop_background_radar_detection(rdev->background_radar_wdev);
++ }
+ }
+ unlock:
+ wiphy_unlock(wiphy);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
new file mode 100644
index 0000000..6fdac3c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
@@ -0,0 +1,26 @@
+From 9ccc4eaa0168f60bd9f78f4122433ec0528a70cd Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Thu, 3 Aug 2023 07:17:44 +0800
+Subject: [PATCH 23/61] mtk: mac80211: avoid kernel warning of
+ check_flush_dependency
+
+---
+ net/mac80211/main.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index 81a9645..e9d8581 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -1458,7 +1458,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
+ hw->queues = IEEE80211_MAX_QUEUES;
+
+ local->workqueue =
+- alloc_ordered_workqueue("%s", 0, wiphy_name(local->hw.wiphy));
++ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, wiphy_name(local->hw.wiphy));
+ if (!local->workqueue) {
+ result = -ENOMEM;
+ goto fail_workqueue;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch
new file mode 100644
index 0000000..edfa07a
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch
@@ -0,0 +1,41 @@
+From d9d2bfeed54c0506a2f387354464d3ef474e1777 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 7 Aug 2023 19:00:53 +0800
+Subject: [PATCH 24/61] mtk: mac80211: avoid calling switch_vif_chanctx when
+ use_chanctx is false
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I2876d763ed1ecb5b7c7e5c53b2623dcd34ece774
+---
+ net/mac80211/chan.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index 32094ef..8043d1d 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -1219,13 +1219,15 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
+ list_del(&link->reserved_chanctx_list);
+ link->reserved_chanctx = NULL;
+
+- err = drv_switch_vif_chanctx(local, vif_chsw, 1,
+- CHANCTX_SWMODE_REASSIGN_VIF);
+- if (err) {
+- if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+- ieee80211_free_chanctx(local, new_ctx, false);
++ if (!local->emulate_chanctx) {
++ err = drv_switch_vif_chanctx(local, vif_chsw, 1,
++ CHANCTX_SWMODE_REASSIGN_VIF);
++ if (err) {
++ if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
++ ieee80211_free_chanctx(local, new_ctx, false);
+
+- goto out;
++ goto out;
++ }
+ }
+
+ list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-add-EHT-BA1024-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-add-EHT-BA1024-support.patch
new file mode 100644
index 0000000..c16cfe8
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-add-EHT-BA1024-support.patch
@@ -0,0 +1,116 @@
+From 61947ae7c04a86c4d6526d080e37e53fe4d17599 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Sun, 25 Dec 2022 22:43:46 +0800
+Subject: [PATCH 25/61] mtk: mac80211: add EHT BA1024 support
+
+---
+ include/linux/ieee80211.h | 2 ++
+ net/mac80211/agg-tx.c | 45 +++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 45 insertions(+), 2 deletions(-)
+
+diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
+index 95c39b7..70f0135 100644
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -1391,6 +1391,8 @@ struct ieee80211_mgmt {
+ __le16 status;
+ __le16 capab;
+ __le16 timeout;
++ /* followed by BA Extension */
++ u8 variable[0];
+ } __packed addba_resp;
+ struct{
+ u8 action_code;
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index af3d8e6..5cf478e 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -72,10 +72,17 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
++ struct ieee80211_addba_ext_ie *addba_ext;
++ u8 *pos;
+ u16 capab = 0;
+ bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
+
+- skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
++ if (agg_size >= 1024)
++ skb = dev_alloc_skb(sizeof(*mgmt) +
++ 2 + sizeof(struct ieee80211_addba_ext_ie) +
++ local->hw.extra_tx_headroom);
++ else
++ skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+
+ if (!skb)
+ return;
+@@ -114,6 +121,15 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ mgmt->u.action.u.addba_req.start_seq_num =
+ cpu_to_le16(start_seq_num << 4);
+
++ if (agg_size >= 1024 ) {
++ pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
++ *pos++ = WLAN_EID_ADDBA_EXT;
++ *pos++ = sizeof(struct ieee80211_addba_ext_ie);
++ addba_ext = (struct ieee80211_addba_ext_ie *)pos;
++ addba_ext->data = u8_encode_bits(agg_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT,
++ IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
++ }
++
+ ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+ }
+
+@@ -481,8 +497,11 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
+ sta->ampdu_mlme.addba_req_num[tid]++;
+ spin_unlock_bh(&sta->lock);
+
+- if (sta->sta.deflink.he_cap.has_he) {
++ if (sta->sta.deflink.eht_cap.has_eht) {
+ buf_size = local->hw.max_tx_aggregation_subframes;
++ } else if (sta->sta.deflink.he_cap.has_he) {
++ buf_size = min_t(u16, local->hw.max_tx_aggregation_subframes,
++ IEEE80211_MAX_AMPDU_BUF_HE);
+ } else {
+ /*
+ * We really should use what the driver told us it will
+@@ -980,8 +999,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
+ {
+ struct tid_ampdu_tx *tid_tx;
+ struct ieee80211_txq *txq;
++ struct ieee802_11_elems *elems;
+ u16 capab, tid, buf_size;
+ bool amsdu;
++ int ext_ie_len;
+
+ lockdep_assert_wiphy(sta->local->hw.wiphy);
+
+@@ -989,6 +1010,26 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
+ amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK;
+ tid = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_TID_MASK);
+ buf_size = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
++ ext_ie_len = len - offsetof(struct ieee80211_mgmt,
++ u.action.u.addba_resp.variable);
++
++ if (ext_ie_len < 0)
++ goto next;
++
++ elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_resp.variable,
++ ext_ie_len, true, NULL);
++
++ if (elems && !elems->parse_error) {
++ if (sta->sta.deflink.eht_cap.has_eht && elems->addba_ext_ie) {
++ u8 buf_size_1k = u8_get_bits(elems->addba_ext_ie->data,
++ IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
++ buf_size |= buf_size_1k << IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT;
++ }
++ }
++
++ if (elems)
++ kfree(elems);
++next:
+ buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
+
+ txq = sta->sta.txq[tid];
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
new file mode 100644
index 0000000..91b1b07
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
@@ -0,0 +1,440 @@
+From f2f18cce7c4d2467312ccebd927308d65d5cc27b Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Sun, 25 Dec 2022 22:43:46 +0800
+Subject: [PATCH 26/61] mtk: mac80211: add rate duration for EHT rate.
+
+---
+ net/mac80211/airtime.c | 349 ++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 346 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c
+index fdf8b65..370477c 100644
+--- a/net/mac80211/airtime.c
++++ b/net/mac80211/airtime.c
+@@ -55,10 +55,21 @@
+ #define HE_DURATION_S(shift, streams, gi, bps) \
+ (HE_DURATION(streams, gi, bps) >> shift)
+
++/* Transmit duration for the raw data part of an average sized packet */
++#define EHT_GI_08 HE_GI_08
++#define EHT_GI_16 HE_GI_16
++#define EHT_GI_32 HE_GI_32
++
++#define EHT_DURATION(streams, gi, bps) \
++ HE_DURATION(streams, gi, bps)
++#define EHT_DURATION_S(shift, streams, gi, bps) \
++ HE_DURATION_S(shift, streams, gi, bps)
++
+ #define BW_20 0
+ #define BW_40 1
+ #define BW_80 2
+ #define BW_160 3
++#define BW_320 4
+
+ /*
+ * Define group sort order: HT40 -> SGI -> #streams
+@@ -68,17 +79,26 @@
+ #define IEEE80211_VHT_STREAM_GROUPS 8 /* BW(=4) * SGI(=2) */
+
+ #define IEEE80211_HE_MAX_STREAMS 8
++#define IEEE80211_HE_STREAM_GROUPS 12 /* BW(=4) * GI(=3) */
++
++#define IEEE80211_EHT_MAX_STREAMS 16
++#define IEEE80211_EHT_STREAM_GROUPS 15 /* BW(=5) * GI(=3) */
+
+ #define IEEE80211_HT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
+ IEEE80211_HT_STREAM_GROUPS)
+ #define IEEE80211_VHT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
+ IEEE80211_VHT_STREAM_GROUPS)
++#define IEEE80211_HE_GROUPS_NB (IEEE80211_HE_MAX_STREAMS * \
++ IEEE80211_HE_STREAM_GROUPS)
++#define IEEE80211_EHT_GROUPS_NB (IEEE80211_EHT_MAX_STREAMS * \
++ IEEE80211_EHT_STREAM_GROUPS)
+
+ #define IEEE80211_HT_GROUP_0 0
+ #define IEEE80211_VHT_GROUP_0 (IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB)
+ #define IEEE80211_HE_GROUP_0 (IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB)
++#define IEEE80211_EHT_GROUP_0 (IEEE80211_HE_GROUP_0 + IEEE80211_HE_GROUPS_NB)
+
+-#define MCS_GROUP_RATES 12
++#define MCS_GROUP_RATES 14
+
+ #define HT_GROUP_IDX(_streams, _sgi, _ht40) \
+ IEEE80211_HT_GROUP_0 + \
+@@ -203,6 +223,59 @@
+ #define HE_GROUP(_streams, _gi, _bw) \
+ __HE_GROUP(_streams, _gi, _bw, \
+ HE_GROUP_SHIFT(_streams, _gi, _bw))
++
++#define EHT_BW2VBPS(_bw, r5, r4, r3, r2, r1) \
++ (_bw == BW_320 ? r5 : _bw == BW_160 ? r4 : _bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
++
++#define EHT_GROUP_IDX(_streams, _gi, _bw) \
++ (IEEE80211_EHT_GROUP_0 + \
++ IEEE80211_EHT_MAX_STREAMS * 3 * (_bw) + \
++ IEEE80211_EHT_MAX_STREAMS * (_gi) + \
++ (_streams) - 1)
++
++#define __EHT_GROUP(_streams, _gi, _bw, _s) \
++ [EHT_GROUP_IDX(_streams, _gi, _bw)] = { \
++ .shift = _s, \
++ .duration = { \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 1960, 979, 489, 230, 115)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 3920, 1958, 979, 475, 230)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 5880, 2937, 1468, 705, 345)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 7840, 3916, 1958, 936, 475)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 11760, 5875, 2937, 1411, 705)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 15680, 7833, 3916, 1872, 936)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 17640, 8827, 4406, 2102, 1051)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 19600, 9806, 4896, 2347, 1166)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 23520, 11764, 5875, 2808, 1411)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 26133, 13060, 6523, 3124, 1555)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 29400, 14702, 7344, 3513, 1756)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 32666, 16329, 8164, 3902, 1944)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 35280, 17640, 8820, 4212, 2106)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 39200, 19600, 9800, 4680, 2340)) \
++ } \
++}
++
++#define EHT_GROUP_SHIFT(_streams, _gi, _bw) \
++ GROUP_SHIFT(EHT_DURATION(_streams, _gi, \
++ EHT_BW2VBPS(_bw, 1960, 979, 489, 230, 115)))
++
++#define EHT_GROUP(_streams, _gi, _bw) \
++ __EHT_GROUP(_streams, _gi, _bw, \
++ EHT_GROUP_SHIFT(_streams, _gi, _bw))
++
+ struct mcs_group {
+ u8 shift;
+ u16 duration[MCS_GROUP_RATES];
+@@ -376,6 +449,262 @@ static const struct mcs_group airtime_mcs_groups[] = {
+ HE_GROUP(6, HE_GI_32, BW_160),
+ HE_GROUP(7, HE_GI_32, BW_160),
+ HE_GROUP(8, HE_GI_32, BW_160),
++
++ /* EHT */
++ EHT_GROUP( 1, EHT_GI_08, BW_20),
++ EHT_GROUP( 2, EHT_GI_08, BW_20),
++ EHT_GROUP( 3, EHT_GI_08, BW_20),
++ EHT_GROUP( 4, EHT_GI_08, BW_20),
++ EHT_GROUP( 5, EHT_GI_08, BW_20),
++ EHT_GROUP( 6, EHT_GI_08, BW_20),
++ EHT_GROUP( 7, EHT_GI_08, BW_20),
++ EHT_GROUP( 8, EHT_GI_08, BW_20),
++ EHT_GROUP( 9, EHT_GI_08, BW_20),
++ EHT_GROUP(10, EHT_GI_08, BW_20),
++ EHT_GROUP(11, EHT_GI_08, BW_20),
++ EHT_GROUP(12, EHT_GI_08, BW_20),
++ EHT_GROUP(13, EHT_GI_08, BW_20),
++ EHT_GROUP(14, EHT_GI_08, BW_20),
++ EHT_GROUP(15, EHT_GI_08, BW_20),
++ EHT_GROUP(16, EHT_GI_08, BW_20),
++
++ EHT_GROUP( 1, EHT_GI_16, BW_20),
++ EHT_GROUP( 2, EHT_GI_16, BW_20),
++ EHT_GROUP( 3, EHT_GI_16, BW_20),
++ EHT_GROUP( 4, EHT_GI_16, BW_20),
++ EHT_GROUP( 5, EHT_GI_16, BW_20),
++ EHT_GROUP( 6, EHT_GI_16, BW_20),
++ EHT_GROUP( 7, EHT_GI_16, BW_20),
++ EHT_GROUP( 8, EHT_GI_16, BW_20),
++ EHT_GROUP( 9, EHT_GI_16, BW_20),
++ EHT_GROUP(10, EHT_GI_16, BW_20),
++ EHT_GROUP(11, EHT_GI_16, BW_20),
++ EHT_GROUP(12, EHT_GI_16, BW_20),
++ EHT_GROUP(13, EHT_GI_16, BW_20),
++ EHT_GROUP(14, EHT_GI_16, BW_20),
++ EHT_GROUP(15, EHT_GI_16, BW_20),
++ EHT_GROUP(16, EHT_GI_16, BW_20),
++
++ EHT_GROUP( 1, EHT_GI_32, BW_20),
++ EHT_GROUP( 2, EHT_GI_32, BW_20),
++ EHT_GROUP( 3, EHT_GI_32, BW_20),
++ EHT_GROUP( 4, EHT_GI_32, BW_20),
++ EHT_GROUP( 5, EHT_GI_32, BW_20),
++ EHT_GROUP( 6, EHT_GI_32, BW_20),
++ EHT_GROUP( 7, EHT_GI_32, BW_20),
++ EHT_GROUP( 8, EHT_GI_32, BW_20),
++ EHT_GROUP( 9, EHT_GI_32, BW_20),
++ EHT_GROUP(10, EHT_GI_32, BW_20),
++ EHT_GROUP(11, EHT_GI_32, BW_20),
++ EHT_GROUP(12, EHT_GI_32, BW_20),
++ EHT_GROUP(13, EHT_GI_32, BW_20),
++ EHT_GROUP(14, EHT_GI_32, BW_20),
++ EHT_GROUP(15, EHT_GI_32, BW_20),
++ EHT_GROUP(16, EHT_GI_32, BW_20),
++
++ EHT_GROUP( 1, EHT_GI_08, BW_40),
++ EHT_GROUP( 2, EHT_GI_08, BW_40),
++ EHT_GROUP( 3, EHT_GI_08, BW_40),
++ EHT_GROUP( 4, EHT_GI_08, BW_40),
++ EHT_GROUP( 5, EHT_GI_08, BW_40),
++ EHT_GROUP( 6, EHT_GI_08, BW_40),
++ EHT_GROUP( 7, EHT_GI_08, BW_40),
++ EHT_GROUP( 8, EHT_GI_08, BW_40),
++ EHT_GROUP( 9, EHT_GI_08, BW_40),
++ EHT_GROUP(10, EHT_GI_08, BW_40),
++ EHT_GROUP(11, EHT_GI_08, BW_40),
++ EHT_GROUP(12, EHT_GI_08, BW_40),
++ EHT_GROUP(13, EHT_GI_08, BW_40),
++ EHT_GROUP(14, EHT_GI_08, BW_40),
++ EHT_GROUP(15, EHT_GI_08, BW_40),
++ EHT_GROUP(16, EHT_GI_08, BW_40),
++
++ EHT_GROUP( 1, EHT_GI_16, BW_40),
++ EHT_GROUP( 2, EHT_GI_16, BW_40),
++ EHT_GROUP( 3, EHT_GI_16, BW_40),
++ EHT_GROUP( 4, EHT_GI_16, BW_40),
++ EHT_GROUP( 5, EHT_GI_16, BW_40),
++ EHT_GROUP( 6, EHT_GI_16, BW_40),
++ EHT_GROUP( 7, EHT_GI_16, BW_40),
++ EHT_GROUP( 8, EHT_GI_16, BW_40),
++ EHT_GROUP( 9, EHT_GI_16, BW_40),
++ EHT_GROUP(10, EHT_GI_16, BW_40),
++ EHT_GROUP(11, EHT_GI_16, BW_40),
++ EHT_GROUP(12, EHT_GI_16, BW_40),
++ EHT_GROUP(13, EHT_GI_16, BW_40),
++ EHT_GROUP(14, EHT_GI_16, BW_40),
++ EHT_GROUP(15, EHT_GI_16, BW_40),
++ EHT_GROUP(16, EHT_GI_16, BW_40),
++
++ EHT_GROUP( 1, EHT_GI_32, BW_40),
++ EHT_GROUP( 2, EHT_GI_32, BW_40),
++ EHT_GROUP( 3, EHT_GI_32, BW_40),
++ EHT_GROUP( 4, EHT_GI_32, BW_40),
++ EHT_GROUP( 5, EHT_GI_32, BW_40),
++ EHT_GROUP( 6, EHT_GI_32, BW_40),
++ EHT_GROUP( 7, EHT_GI_32, BW_40),
++ EHT_GROUP( 8, EHT_GI_32, BW_40),
++ EHT_GROUP( 9, EHT_GI_32, BW_40),
++ EHT_GROUP(10, EHT_GI_32, BW_40),
++ EHT_GROUP(11, EHT_GI_32, BW_40),
++ EHT_GROUP(12, EHT_GI_32, BW_40),
++ EHT_GROUP(13, EHT_GI_32, BW_40),
++ EHT_GROUP(14, EHT_GI_32, BW_40),
++ EHT_GROUP(15, EHT_GI_32, BW_40),
++ EHT_GROUP(16, EHT_GI_32, BW_40),
++
++ EHT_GROUP( 1, EHT_GI_08, BW_80),
++ EHT_GROUP( 2, EHT_GI_08, BW_80),
++ EHT_GROUP( 3, EHT_GI_08, BW_80),
++ EHT_GROUP( 4, EHT_GI_08, BW_80),
++ EHT_GROUP( 5, EHT_GI_08, BW_80),
++ EHT_GROUP( 6, EHT_GI_08, BW_80),
++ EHT_GROUP( 7, EHT_GI_08, BW_80),
++ EHT_GROUP( 8, EHT_GI_08, BW_80),
++ EHT_GROUP( 9, EHT_GI_08, BW_80),
++ EHT_GROUP(10, EHT_GI_08, BW_80),
++ EHT_GROUP(11, EHT_GI_08, BW_80),
++ EHT_GROUP(12, EHT_GI_08, BW_80),
++ EHT_GROUP(13, EHT_GI_08, BW_80),
++ EHT_GROUP(14, EHT_GI_08, BW_80),
++ EHT_GROUP(15, EHT_GI_08, BW_80),
++ EHT_GROUP(16, EHT_GI_08, BW_80),
++
++ EHT_GROUP( 1, EHT_GI_16, BW_80),
++ EHT_GROUP( 2, EHT_GI_16, BW_80),
++ EHT_GROUP( 3, EHT_GI_16, BW_80),
++ EHT_GROUP( 4, EHT_GI_16, BW_80),
++ EHT_GROUP( 5, EHT_GI_16, BW_80),
++ EHT_GROUP( 6, EHT_GI_16, BW_80),
++ EHT_GROUP( 7, EHT_GI_16, BW_80),
++ EHT_GROUP( 8, EHT_GI_16, BW_80),
++ EHT_GROUP( 9, EHT_GI_16, BW_80),
++ EHT_GROUP(10, EHT_GI_16, BW_80),
++ EHT_GROUP(11, EHT_GI_16, BW_80),
++ EHT_GROUP(12, EHT_GI_16, BW_80),
++ EHT_GROUP(13, EHT_GI_16, BW_80),
++ EHT_GROUP(14, EHT_GI_16, BW_80),
++ EHT_GROUP(15, EHT_GI_16, BW_80),
++ EHT_GROUP(16, EHT_GI_16, BW_80),
++
++ EHT_GROUP( 1, EHT_GI_32, BW_80),
++ EHT_GROUP( 2, EHT_GI_32, BW_80),
++ EHT_GROUP( 3, EHT_GI_32, BW_80),
++ EHT_GROUP( 4, EHT_GI_32, BW_80),
++ EHT_GROUP( 5, EHT_GI_32, BW_80),
++ EHT_GROUP( 6, EHT_GI_32, BW_80),
++ EHT_GROUP( 7, EHT_GI_32, BW_80),
++ EHT_GROUP( 8, EHT_GI_32, BW_80),
++ EHT_GROUP( 9, EHT_GI_32, BW_80),
++ EHT_GROUP(10, EHT_GI_32, BW_80),
++ EHT_GROUP(11, EHT_GI_32, BW_80),
++ EHT_GROUP(12, EHT_GI_32, BW_80),
++ EHT_GROUP(13, EHT_GI_32, BW_80),
++ EHT_GROUP(14, EHT_GI_32, BW_80),
++ EHT_GROUP(15, EHT_GI_32, BW_80),
++ EHT_GROUP(16, EHT_GI_32, BW_80),
++
++ EHT_GROUP( 1, EHT_GI_08, BW_160),
++ EHT_GROUP( 2, EHT_GI_08, BW_160),
++ EHT_GROUP( 3, EHT_GI_08, BW_160),
++ EHT_GROUP( 4, EHT_GI_08, BW_160),
++ EHT_GROUP( 5, EHT_GI_08, BW_160),
++ EHT_GROUP( 6, EHT_GI_08, BW_160),
++ EHT_GROUP( 7, EHT_GI_08, BW_160),
++ EHT_GROUP( 8, EHT_GI_08, BW_160),
++ EHT_GROUP( 9, EHT_GI_08, BW_160),
++ EHT_GROUP(10, EHT_GI_08, BW_160),
++ EHT_GROUP(11, EHT_GI_08, BW_160),
++ EHT_GROUP(12, EHT_GI_08, BW_160),
++ EHT_GROUP(13, EHT_GI_08, BW_160),
++ EHT_GROUP(14, EHT_GI_08, BW_160),
++ EHT_GROUP(15, EHT_GI_08, BW_160),
++ EHT_GROUP(16, EHT_GI_08, BW_160),
++
++ EHT_GROUP( 1, EHT_GI_16, BW_160),
++ EHT_GROUP( 2, EHT_GI_16, BW_160),
++ EHT_GROUP( 3, EHT_GI_16, BW_160),
++ EHT_GROUP( 4, EHT_GI_16, BW_160),
++ EHT_GROUP( 5, EHT_GI_16, BW_160),
++ EHT_GROUP( 6, EHT_GI_16, BW_160),
++ EHT_GROUP( 7, EHT_GI_16, BW_160),
++ EHT_GROUP( 8, EHT_GI_16, BW_160),
++ EHT_GROUP( 9, EHT_GI_16, BW_160),
++ EHT_GROUP(10, EHT_GI_16, BW_160),
++ EHT_GROUP(11, EHT_GI_16, BW_160),
++ EHT_GROUP(12, EHT_GI_16, BW_160),
++ EHT_GROUP(13, EHT_GI_16, BW_160),
++ EHT_GROUP(14, EHT_GI_16, BW_160),
++ EHT_GROUP(15, EHT_GI_16, BW_160),
++ EHT_GROUP(16, EHT_GI_16, BW_160),
++
++ EHT_GROUP( 1, EHT_GI_32, BW_160),
++ EHT_GROUP( 2, EHT_GI_32, BW_160),
++ EHT_GROUP( 3, EHT_GI_32, BW_160),
++ EHT_GROUP( 4, EHT_GI_32, BW_160),
++ EHT_GROUP( 5, EHT_GI_32, BW_160),
++ EHT_GROUP( 6, EHT_GI_32, BW_160),
++ EHT_GROUP( 7, EHT_GI_32, BW_160),
++ EHT_GROUP( 8, EHT_GI_32, BW_160),
++ EHT_GROUP( 9, EHT_GI_32, BW_160),
++ EHT_GROUP(10, EHT_GI_32, BW_160),
++ EHT_GROUP(11, EHT_GI_32, BW_160),
++ EHT_GROUP(12, EHT_GI_32, BW_160),
++ EHT_GROUP(13, EHT_GI_32, BW_160),
++ EHT_GROUP(14, EHT_GI_32, BW_160),
++ EHT_GROUP(15, EHT_GI_32, BW_160),
++ EHT_GROUP(16, EHT_GI_32, BW_160),
++
++ EHT_GROUP( 1, EHT_GI_08, BW_320),
++ EHT_GROUP( 2, EHT_GI_08, BW_320),
++ EHT_GROUP( 3, EHT_GI_08, BW_320),
++ EHT_GROUP( 4, EHT_GI_08, BW_320),
++ EHT_GROUP( 5, EHT_GI_08, BW_320),
++ EHT_GROUP( 6, EHT_GI_08, BW_320),
++ EHT_GROUP( 7, EHT_GI_08, BW_320),
++ EHT_GROUP( 8, EHT_GI_08, BW_320),
++ EHT_GROUP( 9, EHT_GI_08, BW_320),
++ EHT_GROUP(10, EHT_GI_08, BW_320),
++ EHT_GROUP(11, EHT_GI_08, BW_320),
++ EHT_GROUP(12, EHT_GI_08, BW_320),
++ EHT_GROUP(13, EHT_GI_08, BW_320),
++ EHT_GROUP(14, EHT_GI_08, BW_320),
++ EHT_GROUP(15, EHT_GI_08, BW_320),
++ EHT_GROUP(16, EHT_GI_08, BW_320),
++
++ EHT_GROUP( 1, EHT_GI_16, BW_320),
++ EHT_GROUP( 2, EHT_GI_16, BW_320),
++ EHT_GROUP( 3, EHT_GI_16, BW_320),
++ EHT_GROUP( 4, EHT_GI_16, BW_320),
++ EHT_GROUP( 5, EHT_GI_16, BW_320),
++ EHT_GROUP( 6, EHT_GI_16, BW_320),
++ EHT_GROUP( 7, EHT_GI_16, BW_320),
++ EHT_GROUP( 8, EHT_GI_16, BW_320),
++ EHT_GROUP( 9, EHT_GI_16, BW_320),
++ EHT_GROUP(10, EHT_GI_16, BW_320),
++ EHT_GROUP(11, EHT_GI_16, BW_320),
++ EHT_GROUP(12, EHT_GI_16, BW_320),
++ EHT_GROUP(13, EHT_GI_16, BW_320),
++ EHT_GROUP(14, EHT_GI_16, BW_320),
++ EHT_GROUP(15, EHT_GI_16, BW_320),
++ EHT_GROUP(16, EHT_GI_16, BW_320),
++
++ EHT_GROUP( 1, EHT_GI_32, BW_320),
++ EHT_GROUP( 2, EHT_GI_32, BW_320),
++ EHT_GROUP( 3, EHT_GI_32, BW_320),
++ EHT_GROUP( 4, EHT_GI_32, BW_320),
++ EHT_GROUP( 5, EHT_GI_32, BW_320),
++ EHT_GROUP( 6, EHT_GI_32, BW_320),
++ EHT_GROUP( 7, EHT_GI_32, BW_320),
++ EHT_GROUP( 8, EHT_GI_32, BW_320),
++ EHT_GROUP( 9, EHT_GI_32, BW_320),
++ EHT_GROUP(10, EHT_GI_32, BW_320),
++ EHT_GROUP(11, EHT_GI_32, BW_320),
++ EHT_GROUP(12, EHT_GI_32, BW_320),
++ EHT_GROUP(13, EHT_GI_32, BW_320),
++ EHT_GROUP(14, EHT_GI_32, BW_320),
++ EHT_GROUP(15, EHT_GI_32, BW_320),
++ EHT_GROUP(16, EHT_GI_32, BW_320),
+ };
+
+ static u32
+@@ -422,6 +751,9 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
+ case RATE_INFO_BW_160:
+ bw = BW_160;
+ break;
++ case RATE_INFO_BW_320:
++ bw = BW_320;
++ break;
+ default:
+ WARN_ON_ONCE(1);
+ return 0;
+@@ -443,11 +775,20 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
+ idx = status->rate_idx;
+ group = HE_GROUP_IDX(streams, status->he_gi, bw);
+ break;
++ case RX_ENC_EHT:
++ streams = status->nss;
++ idx = status->rate_idx;
++ group = EHT_GROUP_IDX(streams, status->he_gi, bw);
++ break;
+ default:
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
++ if (WARN_ON_ONCE((status->encoding != RX_ENC_EHT && streams > 8) ||
++ (status->encoding == RX_ENC_EHT && streams > 16)))
++ return 0;
++
+ if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) ||
+ (status->encoding == RX_ENC_HE && streams > 8)))
+ return 0;
+@@ -517,7 +858,9 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
+ stat->nss = ri->nss;
+ stat->rate_idx = ri->mcs;
+
+- if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
++ if (ri->flags & RATE_INFO_FLAGS_EHT_MCS)
++ stat->encoding = RX_ENC_EHT;
++ else if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
+ stat->encoding = RX_ENC_HE;
+ else if (ri->flags & RATE_INFO_FLAGS_VHT_MCS)
+ stat->encoding = RX_ENC_VHT;
+@@ -529,7 +872,7 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
+ if (ri->flags & RATE_INFO_FLAGS_SHORT_GI)
+ stat->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+
+- stat->he_gi = ri->he_gi;
++ stat->he_gi = (ri->flags & RATE_INFO_FLAGS_EHT_MCS) ? ri->eht_gi : ri->he_gi;
+
+ if (stat->encoding != RX_ENC_LEGACY)
+ return true;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
new file mode 100644
index 0000000..8de0ea7
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
@@ -0,0 +1,28 @@
+From baa27bc8d70bab050630a127c7d58d140ac9ed21 Mon Sep 17 00:00:00 2001
+From: ye he <ye.he@mediatek.com>
+Date: Wed, 22 Feb 2023 16:09:32 +0800
+Subject: [PATCH 27/61] mtk: mac80211: add send bar action when recieve addba
+ rsp
+
+Signed-off-by: ye he <ye.he@mediatek.com>
+---
+ net/mac80211/agg-tx.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 5cf478e..8480b64 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -1080,7 +1080,8 @@ next:
+
+ tid_tx->buf_size = buf_size;
+ tid_tx->amsdu = amsdu;
+-
++ ieee80211_send_bar(&sta->sdata->vif, sta->sta.addr,
++ tid, 0);
+ if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))
+ ieee80211_agg_tx_operational(local, sta, tid);
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-inrease-beacon-loss-count.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-inrease-beacon-loss-count.patch
new file mode 100644
index 0000000..c6a9e9d
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-inrease-beacon-loss-count.patch
@@ -0,0 +1,33 @@
+From 235df226520ae9c161bed6fe45920331662b26e6 Mon Sep 17 00:00:00 2001
+From: Amit Khatri <amit.khatri@mediatek.com>
+Date: Thu, 6 Apr 2023 21:37:33 +0800
+Subject: [PATCH 28/61] mtk: mac80211: inrease beacon loss count
+
+as per eagle code beacone loss time out is
+4 seconds.
+in 2G connection getting beacon loss logs in routed client
+scenario.
+
+so increasing beacon loss count from 7 to 20
+
+Signed-off-by: Amit Khatri <amit.khatri@mediatek.com>
+---
+ net/mac80211/mlme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 52d1cd8..a27682e 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -66,7 +66,7 @@ MODULE_PARM_DESC(max_probe_tries,
+ * probe on beacon miss before declaring the connection lost
+ * default to what we want.
+ */
+-static int beacon_loss_count = 7;
++static int beacon_loss_count = 20;
+ module_param(beacon_loss_count, int, 0644);
+ MODULE_PARM_DESC(beacon_loss_count,
+ "Number of beacon intervals before we decide beacon was lost.");
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-cfg80211-add-support-for-updating-background-cha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-cfg80211-add-support-for-updating-background-cha.patch
new file mode 100644
index 0000000..ffc8f1c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-cfg80211-add-support-for-updating-background-cha.patch
@@ -0,0 +1,88 @@
+From e4e9d4cdfc3b5e27a3ad3f05810a96447ccf1f56 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 09:49:02 +0800
+Subject: [PATCH 29/61] mtk: cfg80211: add support for updating background
+ channel
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h | 14 ++++++++++++++
+ include/uapi/linux/nl80211.h | 6 ++++++
+ net/wireless/mlme.c | 12 ++++++++++++
+ 3 files changed, 32 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 19cf7f7..bd516d1 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -8693,6 +8693,20 @@ void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
+ const struct cfg80211_chan_def *csa_chandef,
+ bool associated);
+
++/**
++ * cfg80211_background_radar_update_channel - notify background chandef has been updated
++ * @wiphy: the wiphy
++ * @chandef: the updated chandef
++ * @expand: whether or not the operating channel should expand its width
++ * after offchan CAC
++ *
++ * Update the background chandef based on driver's decision, and notify the userspace
++ * that the current channel of background chain should be updated.
++ */
++void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef,
++ bool expand);
++
+ /**
+ * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
+ * @wiphy: the wiphy
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index 7999a65..e859c96 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6822,6 +6822,10 @@ enum nl80211_smps_mode {
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
++ * driver.
++ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
++ * driver and required to expand main operating channel.
+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
+ * when receiving CSA/assoc resp
+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
+@@ -6834,6 +6838,8 @@ enum nl80211_radar_event {
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
++ NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
++ NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
+ NL80211_RADAR_STA_CAC_SKIPPED,
+ NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 56095b3..3da7886 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1281,6 +1281,18 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
+ return 0;
+ }
+
++void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef,
++ bool expand)
++{
++ enum nl80211_radar_event event;
++
++ event = expand ? NL80211_RADAR_BACKGROUND_CHAN_EXPAND :
++ NL80211_RADAR_BACKGROUND_CHAN_UPDATE;
++ nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef, event, NULL, GFP_ATOMIC);
++}
++EXPORT_SYMBOL(cfg80211_background_radar_update_channel);
++
+ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
+ {
+ struct wiphy *wiphy = wdev->wiphy;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
new file mode 100644
index 0000000..ca8cdc0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
@@ -0,0 +1,26 @@
+From bfe137c0bceae36a34e766d2623893c4b1cbdd53 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 7 Jul 2023 17:17:30 +0800
+Subject: [PATCH 30/61] mtk: mac80211: Allow STA interface to set TX queue
+ parameters
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ net/wireless/nl80211.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 1f1856b..079fedf 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -3557,6 +3557,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+ }
+
+ if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
++ netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+ result = -EINVAL;
+ goto out;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
new file mode 100644
index 0000000..f070841
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
@@ -0,0 +1,150 @@
+From 2a5c863cc42d21d20a25496426696c95cea45312 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Fri, 23 Jun 2023 05:53:50 +0800
+Subject: [PATCH 31/61] mtk: mac80211: export ieee80211_tpt_led_trig_tx/rx for
+ driver
+
+Whenever the H/W path is enabled and traffic is in the binding state,
+mac80211 is not aware of the traffic. Consequently, the LED does not
+blink for that reason.
+
+The ieee80211_tpt_led_trig_tx/rx functions are exported for the driver
+so that we can report the tx and rx bytes from the driver when
+the H/W path is being used.
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ include/net/mac80211.h | 17 +++++++++++++++++
+ net/mac80211/led.c | 16 ++++++++++++++++
+ net/mac80211/led.h | 17 -----------------
+ net/mac80211/rx.c | 2 +-
+ net/mac80211/tx.c | 4 ++--
+ 5 files changed, 36 insertions(+), 20 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 965a026..860ad6e 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4893,6 +4893,8 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+ unsigned int flags,
+ const struct ieee80211_tpt_blink *blink_table,
+ unsigned int blink_table_len);
++void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes);
++void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes);
+ #endif
+ /**
+ * ieee80211_get_tx_led_name - get name of TX LED
+@@ -5003,6 +5005,21 @@ ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags,
+ #endif
+ }
+
++static inline void
++ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
++{
++#ifdef CPTCFG_MAC80211_LEDS
++ __ieee80211_tpt_led_trig_tx(hw, bytes);
++#endif
++}
++
++static inline void
++ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
++{
++#ifdef CPTCFG_MAC80211_LEDS
++ __ieee80211_tpt_led_trig_rx(hw, bytes);
++#endif
++}
+ /**
+ * ieee80211_unregister_hw - Unregister a hardware device
+ *
+diff --git a/net/mac80211/led.c b/net/mac80211/led.c
+index b992430..3109501 100644
+--- a/net/mac80211/led.c
++++ b/net/mac80211/led.c
+@@ -364,6 +364,22 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+ }
+ EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
+
++void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
++{
++ struct ieee80211_local *local = hw_to_local(hw);
++ if (atomic_read(&local->tpt_led_active))
++ local->tpt_led_trigger->tx_bytes += bytes;
++}
++EXPORT_SYMBOL(__ieee80211_tpt_led_trig_tx);
++
++void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
++{
++ struct ieee80211_local *local = hw_to_local(hw);
++ if (atomic_read(&local->tpt_led_active))
++ local->tpt_led_trigger->rx_bytes += bytes;
++}
++EXPORT_SYMBOL(__ieee80211_tpt_led_trig_rx);
++
+ static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+ {
+ struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+diff --git a/net/mac80211/led.h b/net/mac80211/led.h
+index 59f5a83..f381790 100644
+--- a/net/mac80211/led.h
++++ b/net/mac80211/led.h
+@@ -65,22 +65,5 @@ static inline void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
+ unsigned int types_off)
+ {
+ }
+-#endif
+
+-static inline void
+-ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, int bytes)
+-{
+-#ifdef CPTCFG_MAC80211_LEDS
+- if (atomic_read(&local->tpt_led_active))
+- local->tpt_led_trigger->tx_bytes += bytes;
+ #endif
+-}
+-
+-static inline void
+-ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, int bytes)
+-{
+-#ifdef CPTCFG_MAC80211_LEDS
+- if (atomic_read(&local->tpt_led_active))
+- local->tpt_led_trigger->rx_bytes += bytes;
+-#endif
+-}
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 65982a6..fe3d9fb 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -5469,7 +5469,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+ if (skb) {
+ if ((status->flag & RX_FLAG_8023) ||
+ ieee80211_is_data_present(hdr->frame_control))
+- ieee80211_tpt_led_trig_rx(local, skb->len);
++ ieee80211_tpt_led_trig_rx(&local->hw, skb->len);
+
+ if (status->flag & RX_FLAG_8023)
+ __ieee80211_rx_handle_8023(hw, pubsta, skb, list);
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index f479d87..344f4bf 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4340,7 +4340,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ len = 0;
+ out:
+ if (len)
+- ieee80211_tpt_led_trig_tx(local, len);
++ ieee80211_tpt_led_trig_tx(&local->hw, len);
+ rcu_read_unlock();
+ }
+
+@@ -4671,7 +4671,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ sta->deflink.tx_stats.packets[queue] += skbs;
+ sta->deflink.tx_stats.bytes[queue] += len;
+
+- ieee80211_tpt_led_trig_tx(local, len);
++ ieee80211_tpt_led_trig_tx(&local->hw, len);
+
+ ieee80211_tx_8023(sdata, skb, sta, false);
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
new file mode 100644
index 0000000..d879e5e
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
@@ -0,0 +1,136 @@
+From fc5c4fd0f0edc27c7a21e43a51e1a90a2f7b17b2 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Tue, 22 Aug 2023 05:02:53 +0800
+Subject: [PATCH 32/61] mtk: mac80211: add packet count input for
+ dev_sw_netstat_rx_add
+
+---
+ backport-include/linux/netdevice.h | 12 ++++++++----
+ drivers/net/usb/qmi_wwan.c | 2 +-
+ .../net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c | 2 +-
+ .../net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c | 2 +-
+ net/mac80211/rx.c | 8 ++++----
+ 5 files changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/backport-include/linux/netdevice.h b/backport-include/linux/netdevice.h
+index 1d2ac66..04d76d7 100644
+--- a/backport-include/linux/netdevice.h
++++ b/backport-include/linux/netdevice.h
+@@ -112,13 +112,15 @@ void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
+ #define netif_rx_any_context LINUX_BACKPORT(netif_rx_any_context)
+ int netif_rx_any_context(struct sk_buff *skb);
+
+-static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
++static inline void dev_sw_netstats_rx_add(struct net_device *dev,
++ unsigned int packets,
++ unsigned int len)
+ {
+ struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
+
+ u64_stats_update_begin(&tstats->syncp);
+ tstats->rx_bytes += len;
+- tstats->rx_packets++;
++ tstats->rx_packets += packets;
+ u64_stats_update_end(&tstats->syncp);
+ }
+
+@@ -140,13 +142,15 @@ static inline void dev_sw_netstats_tx_add(struct net_device *dev,
+
+ #if LINUX_VERSION_IS_LESS(5,10,0)
+ #define dev_sw_netstats_rx_add LINUX_BACKPORT(dev_sw_netstats_rx_add)
+-static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
++static inline void dev_sw_netstats_rx_add(struct net_device *dev,
++ unsigned int packets,
++ unsigned int len)
+ {
+ struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
+
+ u64_stats_update_begin(&tstats->syncp);
+ tstats->rx_bytes += len;
+- tstats->rx_packets++;
++ tstats->rx_packets += packets;
+ u64_stats_update_end(&tstats->syncp);
+ }
+ #endif /* < 5.10 */
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index f7f640f..436e37d 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -228,7 +228,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+ net->stats.rx_errors++;
+ return 0;
+ } else {
+- dev_sw_netstats_rx_add(net, pkt_len);
++ dev_sw_netstats_rx_add(net, 1, pkt_len);
+ }
+
+ skip:
+diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
+index c1a53e1..01ff00f 100644
+--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
++++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
+@@ -756,7 +756,7 @@ static int qtnf_pcie_pearl_rx_poll(struct napi_struct *napi, int budget)
+ skb_put(skb, psize);
+ ndev = qtnf_classify_skb(bus, skb);
+ if (likely(ndev)) {
+- dev_sw_netstats_rx_add(ndev, skb->len);
++ dev_sw_netstats_rx_add(ndev, 1, skb->len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(napi, skb);
+ } else {
+diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
+index ef5c069..8136745 100644
+--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
++++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
+@@ -662,7 +662,7 @@ static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
+ skb_put(skb, psize);
+ ndev = qtnf_classify_skb(bus, skb);
+ if (likely(ndev)) {
+- dev_sw_netstats_rx_add(ndev, skb->len);
++ dev_sw_netstats_rx_add(ndev, 1, skb->len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_receive_skb(skb);
+ } else {
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index fe3d9fb..da3fc51 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -863,7 +863,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
+
+ if (skb) {
+ skb->dev = sdata->dev;
+- dev_sw_netstats_rx_add(skb->dev, skb->len);
++ dev_sw_netstats_rx_add(skb->dev, 1, skb->len);
+ netif_receive_skb(skb);
+ }
+ }
+@@ -2672,7 +2672,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
+ skb = rx->skb;
+ xmit_skb = NULL;
+
+- dev_sw_netstats_rx_add(dev, skb->len);
++ dev_sw_netstats_rx_add(dev, 1, skb->len);
+
+ if (rx->sta) {
+ /* The seqno index has the same property as needed
+@@ -4111,7 +4111,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
+ }
+
+ prev_dev = sdata->dev;
+- dev_sw_netstats_rx_add(sdata->dev, skb->len);
++ dev_sw_netstats_rx_add(sdata->dev, 1, skb->len);
+ }
+
+ if (prev_dev) {
+@@ -4819,7 +4819,7 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
+
+ skb->dev = fast_rx->dev;
+
+- dev_sw_netstats_rx_add(fast_rx->dev, skb->len);
++ dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
+
+ /* The seqno index has the same property as needed
+ * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
new file mode 100644
index 0000000..081bd72
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
@@ -0,0 +1,92 @@
+From 4d85822963f5d150be638d78ea84c5199f80d676 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Wed, 16 Aug 2023 07:23:34 +0800
+Subject: [PATCH 33/61] mtk: mac80211: add per-bss flag to support vendors
+ counter
+
+Change-Id: I73fcae64d933c66b9eef9b6f55f7bb1e4ce19501
+---
+ include/uapi/linux/nl80211.h | 1 +
+ net/mac80211/rx.c | 8 ++++++--
+ net/mac80211/tx.c | 13 ++++++++++---
+ 3 files changed, 17 insertions(+), 5 deletions(-)
+
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index e859c96..220d20d 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6620,6 +6620,7 @@ enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
+ NL80211_EXT_FEATURE_DFS_CONCURRENT,
+ NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
++ NL80211_EXT_FEATURE_STAS_COUNT,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index da3fc51..06725f3 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2672,7 +2672,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
+ skb = rx->skb;
+ xmit_skb = NULL;
+
+- dev_sw_netstats_rx_add(dev, 1, skb->len);
++ if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
++ NL80211_EXT_FEATURE_STAS_COUNT) || !rx->sta)
++ dev_sw_netstats_rx_add(dev, 1, skb->len);
+
+ if (rx->sta) {
+ /* The seqno index has the same property as needed
+@@ -4819,7 +4821,9 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
+
+ skb->dev = fast_rx->dev;
+
+- dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
++ if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
++ NL80211_EXT_FEATURE_STAS_COUNT))
++ dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
+
+ /* The seqno index has the same property as needed
+ * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index 344f4bf..a2ed041 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3559,7 +3559,9 @@ ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
+ if (key)
+ info->control.hw_key = &key->conf;
+
+- dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
++ if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
++ NL80211_EXT_FEATURE_STAS_COUNT))
++ dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
+
+ if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+@@ -4330,7 +4332,9 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ goto out;
+ }
+
+- dev_sw_netstats_tx_add(dev, 1, skb->len);
++ if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
++ NL80211_EXT_FEATURE_STAS_COUNT) || !sta)
++ dev_sw_netstats_tx_add(dev, 1, skb->len);
+
+ ieee80211_xmit(sdata, sta, skb);
+ }
+@@ -4667,7 +4671,10 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ info->status_data_idr = 1;
+ }
+
+- dev_sw_netstats_tx_add(dev, skbs, len);
++ if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
++ NL80211_EXT_FEATURE_STAS_COUNT))
++ dev_sw_netstats_tx_add(dev, skbs, len);
++
+ sta->deflink.tx_stats.packets[queue] += skbs;
+ sta->deflink.tx_stats.bytes[queue] += len;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
new file mode 100644
index 0000000..9faae94
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
@@ -0,0 +1,28 @@
+From 2531350e6d261ef54adb85ce59c5012dc4d5db90 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 25 Oct 2023 13:37:00 +0800
+Subject: [PATCH 34/61] mtk: mac80211: set eht_support to false when AP is not
+ in EHT mode
+
+CR-ID: WCNCR00240597
+Change-Id: I826428e26ba70a0e77fcc8c9bc0dca440060e880
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ net/mac80211/cfg.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index cb91223..215b5d2 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1384,6 +1384,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ link_conf->eht_su_beamformer = false;
+ link_conf->eht_su_beamformee = false;
+ link_conf->eht_mu_beamformer = false;
++ link_conf->eht_support = false;
+ }
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
new file mode 100644
index 0000000..50e53f9
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
@@ -0,0 +1,127 @@
+From ba967d748120a6681f3e0f106dfaf3ad882034c0 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Thu, 9 Nov 2023 11:37:37 +0800
+Subject: [PATCH 35/61] mtk: mac80211: Add cert mode to disable ba timeout
+
+Add a switch of certification mode in debugfs as cert_mode. In the case
+we use it to disable BA timeout from STA to prevent crashing STA.
+
+CR-Id: WCNCR00259302
+Change-Id: I537715030d2eeae3c795725093c79c4878086f1a
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+
+Move the variable 'cert_mode' from ieee80211_local to ieee80211_hw
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ include/net/mac80211.h | 6 ++++++
+ net/mac80211/agg-tx.c | 5 ++++-
+ net/mac80211/debugfs.c | 49 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 59 insertions(+), 1 deletion(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 860ad6e..89bba19 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3028,8 +3028,14 @@ struct ieee80211_hw {
+ u32 max_mtu;
+ const s8 *tx_power_levels;
+ u8 max_txpwr_levels_idx;
++ bool cert_mode;
+ };
+
++static inline bool ieee80211_is_cert_mode(struct ieee80211_hw *hw)
++{
++ return hw->cert_mode;
++}
++
+ static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
+ enum ieee80211_hw_flags flg)
+ {
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 8480b64..7117576 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -1090,7 +1090,10 @@ next:
+ tid_tx->timeout =
+ le16_to_cpu(mgmt->u.action.u.addba_resp.timeout);
+
+- if (tid_tx->timeout) {
++ /* In the case of certification env, testbed STA cannot accept frequent DelBA.
++ * Therefore, we remove the session timer check here to avoid crashing testbed STA.
++ */
++ if (tid_tx->timeout && !ieee80211_is_cert_mode(&local->hw)) {
+ mod_timer(&tid_tx->session_timer,
+ TU_TO_EXP_TIME(tid_tx->timeout));
+ tid_tx->last_tx = jiffies;
+diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
+index f9c5ed8..d655a19 100644
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -450,6 +450,54 @@ static const struct file_operations reset_ops = {
+ };
+ #endif
+
++static ssize_t cert_mode_read(struct file *file,
++ char __user *user_buf,
++ size_t count,
++ loff_t *ppos)
++{
++ struct ieee80211_local *local = file->private_data;
++ char buf[32];
++ int len = 0;
++
++ len = scnprintf(buf, sizeof(buf), "cert_mode: %d\n",
++ local->hw.cert_mode);
++
++ return simple_read_from_buffer(user_buf, count, ppos,
++ buf, len);
++}
++
++static ssize_t cert_mode_write(struct file *file,
++ const char __user *user_buf,
++ size_t count,
++ loff_t *ppos)
++{
++ struct ieee80211_local *local = file->private_data;
++ char buf[16];
++
++ if (count >= sizeof(buf))
++ return -EINVAL;
++
++ if (copy_from_user(buf, user_buf, count))
++ return -EFAULT;
++
++ if (count && buf[count - 1] == '\n')
++ buf[count - 1] = '\0';
++ else
++ buf[count] = '\0';
++
++ if (kstrtobool(buf, &local->hw.cert_mode))
++ return -EINVAL;
++
++ return count;
++}
++
++static const struct file_operations cert_mode_ops = {
++ .write = cert_mode_write,
++ .read = cert_mode_read,
++ .open = simple_open,
++ .llseek = noop_llseek,
++};
++
+ static const char *hw_flag_names[] = {
+ #define FLAG(F) [IEEE80211_HW_##F] = #F
+ FLAG(HAS_RATE_CONTROL),
+@@ -684,6 +732,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
+ debugfs_create_u32("aql_threshold", 0600,
+ phyd, &local->aql_threshold);
+
++ DEBUGFS_ADD_MODE(cert_mode, 0644);
+ statsd = debugfs_create_dir("statistics", phyd);
+
+ #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-backports-update-kernel-version-check-for-eth_hw_add.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-backports-update-kernel-version-check-for-eth_hw_add.patch
new file mode 100644
index 0000000..95fac53
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-backports-update-kernel-version-check-for-eth_hw_add.patch
@@ -0,0 +1,41 @@
+From 0bf57d6aacff7859372d546ef491f2722098bc0c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 27 Nov 2023 16:39:36 +0800
+Subject: [PATCH 36/61] backports: update kernel version check for
+ eth_hw_addr_set()
+
+Kernel v5.4.260 has added this API, so update kernel version check in
+backports include.
+
+CR-Id: WCNCR00238098
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I708aabbe51de9d480932ce03b30d004bf1d67ebd
+---
+ backport-include/linux/etherdevice.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/backport-include/linux/etherdevice.h b/backport-include/linux/etherdevice.h
+index 51a7d6d..ecc3bc2 100644
+--- a/backport-include/linux/etherdevice.h
++++ b/backport-include/linux/etherdevice.h
+@@ -39,7 +39,7 @@ static inline void u64_to_ether_addr(u64 u, u8 *addr)
+ }
+ #endif /* LINUX_VERSION_IS_LESS(4,11,0) */
+
+-#if LINUX_VERSION_IS_LESS(5,15,0)
++#if LINUX_VERSION_IS_LESS(5,4,260)
+ /**
+ * eth_hw_addr_set - Assign Ethernet address to a net_device
+ * @dev: pointer to net_device structure
+@@ -51,7 +51,7 @@ static inline void eth_hw_addr_set(struct net_device *dev, const u8 *addr)
+ {
+ ether_addr_copy(dev->dev_addr, addr);
+ }
+-#endif /* LINUX_VERSION_IS_LESS(5,15,0) */
++#endif /* LINUX_VERSION_IS_LESS(5,4,260) */
+
+ #if LINUX_VERSION_IS_LESS(5,16,0)
+ static inline int backport_device_get_mac_address(struct device *dev, char *addr)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch
new file mode 100644
index 0000000..88560ce
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch
@@ -0,0 +1,68 @@
+From 9c1106eb80f31723780a6eb098b5c146a33d45a2 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Wed, 29 Nov 2023 13:51:13 +0800
+Subject: [PATCH 37/61] mac80211: mtk: ACS channel time is reset by ch_restore
+
+Issue:
+There's a chance that the channel time for duty channel is zero in ACS
+scan.
+
+Root cause:
+The chan_stat may be reset when restore to duty channel.
+Mac80211 will notify to hostapd when scan done and then restore to duty
+channel.
+And mt76 will clear scan flag after restore done.
+If hostapd get the chan_stat before channel_restore, will get the
+correct channel time;
+If hostapd get the chan_stat after channel_restore, will get zero
+channel time;
+
+Solution:
+When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
+Mac80211 scan state will be set in scanning, and will be reset after
+scan done and before restore to duty channel.
+
+CR-Id: WCNCR00357653
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+Change-Id: I480a956c8a362929040d941247d74ff0e5d06ffb
+---
+ include/net/mac80211.h | 7 +++++++
+ net/mac80211/util.c | 9 +++++++++
+ 2 files changed, 16 insertions(+)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 89bba19..d7b1f9d 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -7665,4 +7665,11 @@ int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode);
+
++/**
++ * ieee80211_get_scanning - get scanning bitmask
++ *
++ * @hw: pointer as obtained from ieee80211_alloc_hw()
++ */
++unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw);
++
+ #endif /* MAC80211_H */
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index dd06bd2..55f1566 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -4340,3 +4340,12 @@ ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef)
+ return IEEE80211_CONN_BW_LIMIT_20;
+ }
+ }
++
++unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw)
++{
++ struct ieee80211_local *local = hw_to_local(hw);
++
++ return local->scanning;
++}
++EXPORT_SYMBOL(ieee80211_get_scanning);
++
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
new file mode 100644
index 0000000..f958799
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
@@ -0,0 +1,45 @@
+From 8a8535abf02e5cb6f0dcaf924e33183a1a1fa410 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Thu, 30 Nov 2023 14:01:29 +0800
+Subject: [PATCH 38/61] mtk: mac80211: Fix SMPS action frame cap check
+
+Fix SMPS action frame cap check.
+Due to 6G band doesn't have HT cap, we change cap check into each action
+frame section.
+
+CR-Id: WCNCR00259302
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+Change-Id: I65d2cc29efdb4a78ac670890842496a2056fc2ba
+---
+ net/mac80211/rx.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 06725f3..1d273ef 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -3528,9 +3528,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
+
+ switch (mgmt->u.action.category) {
+ case WLAN_CATEGORY_HT:
+- /* reject HT action frames from stations not supporting HT */
+- if (!rx->link_sta->pub->ht_cap.ht_supported)
+- goto invalid;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+@@ -3549,6 +3546,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
+ enum ieee80211_smps_mode smps_mode;
+ struct sta_opmode_info sta_opmode = {};
+
++ if (rx->link_sta->pub->he_cap.has_he &&
++ !(rx->link_sta->pub->he_cap.he_cap_elem.mac_cap_info[5] &
++ IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS))
++ goto invalid;
++
+ if (sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+ goto handled;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch
new file mode 100644
index 0000000..4c56b5c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch
@@ -0,0 +1,29 @@
+From 9de692d990d4d83e957ea89e3fc23367f696bbc3 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 00:27:15 +0800
+Subject: [PATCH 39/61] mtk: mac80211: allow multiple links for STA vif
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I92d07be6ec87523ef3fdc3855151daea382eb290
+---
+ net/mac80211/link.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/net/mac80211/link.c b/net/mac80211/link.c
+index 43f9672..a33a845 100644
+--- a/net/mac80211/link.c
++++ b/net/mac80211/link.c
+@@ -168,10 +168,7 @@ static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
+ WARN_ON(dormant_links);
+ break;
+ case NL80211_IFTYPE_STATION:
+- if (sdata->vif.active_links)
+- break;
+ sdata->vif.active_links = valid_links & ~dormant_links;
+- WARN_ON(hweight16(sdata->vif.active_links) > 1);
+ break;
+ default:
+ WARN_ON(1);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-increase-association-timeout-time.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-increase-association-timeout-time.patch
new file mode 100644
index 0000000..ff69c5a
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-increase-association-timeout-time.patch
@@ -0,0 +1,30 @@
+From 48d97cc83f3098481fbc2a6d38da75e94703708f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 00:35:11 +0800
+Subject: [PATCH 40/61] mtk: mac80211: increase association timeout time
+
+Prevent from sending multiple association requests while AP is already
+hanlding the request.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I5ea21682b2498c73acc88095a665db1a3f7e7302
+---
+ net/mac80211/mlme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index a27682e..84ea805 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -7202,7 +7202,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
+ */
+ if (status_acked) {
+ ifmgd->assoc_data->timeout =
+- jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
++ jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT * 4;
+ run_again(sdata, ifmgd->assoc_data->timeout);
+ } else {
+ ifmgd->assoc_data->timeout = jiffies - 1;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch
new file mode 100644
index 0000000..7427cc3
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch
@@ -0,0 +1,28 @@
+From c115759fb0b11ba3ce09d078e8609203dc5e772f Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 10 Nov 2023 15:34:57 +0800
+Subject: [PATCH 41/61] mtk: mac80211: fix crash when starting tx ba session
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Change-Id: I62a463f9a37b25d4a2bfaff00e721c369e532e98
+---
+ net/mac80211/agg-tx.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 7117576..da9b7f0 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -642,7 +642,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
+ "Requested to start BA session on reserved tid=%d", tid))
+ return -EINVAL;
+
+- if (!pubsta->deflink.ht_cap.ht_supported &&
++ if (!sta->sdata->vif.active_links &&
++ !pubsta->deflink.ht_cap.ht_supported &&
+ sta->sdata->vif.bss_conf.chanreq.oper.chan->band != NL80211_BAND_6GHZ)
+ return -EINVAL;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch
new file mode 100644
index 0000000..09b76a0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch
@@ -0,0 +1,31 @@
+From 7ec8b9cd491c09f83dfc03976fa0160f89c60eb1 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 19 Dec 2023 17:42:56 +0800
+Subject: [PATCH 42/61] mtk: mac80211: use link address for eapol source in
+ ieee80211_tx_control_port()
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I67453fa4299a25a921a971cd5a2b7098844c5b8d
+---
+ net/mac80211/tx.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index a2ed041..e72bb7e 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -6237,9 +6237,10 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
+ * for MLO STA, the SA should be the AP MLD address, but
+ * the link ID has been selected already
+ */
+- if (sta && sta->sta.mlo)
++ if (sta && sta->sta.mlo && link_id == IEEE80211_LINK_UNSPECIFIED)
+ memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN);
+ }
++
+ rcu_read_unlock();
+
+ start_xmit:
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch
new file mode 100644
index 0000000..6ad3b95
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch
@@ -0,0 +1,473 @@
+From 1c581514b49ffe917cb40d4e073c56b6d1f47416 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:35:17 +0800
+Subject: [PATCH 43/61] cfg80211: mtk: implement DFS radar detect for MLO
+
+Implement DFS radar detect for MLO
+1. Add link id info for radar detection in MLD
+2. Note that the radar detection flow requires channel switch, which is not yet
+complete in MLO, so postpone it.
+ (a) cac_started, cac_start_time should be moved into wdev->link, but
+channel switch will use it, so wait until channel switch is completed.
+ (b) ieee80211_dfs_cac_cancel, ieee80211_dfs_radar_detected_work, ...
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Ic3004607081ae407dd05095769f9e2855a068dd2
+
+rework radar detected flow for mlo
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I1c76d0b12db52fdb14e73c410d7da69de65bf115
+---
+ include/net/cfg80211.h | 4 +++-
+ net/mac80211/cfg.c | 26 +++++++++++++++++++-------
+ net/mac80211/ieee80211_i.h | 2 +-
+ net/mac80211/main.c | 6 +++---
+ net/mac80211/pm.c | 9 ++++++++-
+ net/mac80211/util.c | 26 +++++++++++++++++++-------
+ net/wireless/core.c | 4 ++--
+ net/wireless/mlme.c | 4 ++--
+ net/wireless/nl80211.c | 10 +++++-----
+ net/wireless/rdev-ops.h | 13 +++++++------
+ net/wireless/reg.c | 15 +++++++++------
+ net/wireless/trace.h | 28 ++++++++++++++++++++++------
+ 12 files changed, 100 insertions(+), 47 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index bd516d1..e55309b 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4856,10 +4856,12 @@ struct cfg80211_ops {
+
+ int (*start_radar_detection)(struct wiphy *wiphy,
+ struct net_device *dev,
++ unsigned int link_id,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms);
+ void (*end_cac)(struct wiphy *wiphy,
+- struct net_device *dev);
++ struct net_device *dev,
++ unsigned int link_id);
+ int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie);
+ int (*crit_proto_start)(struct wiphy *wiphy,
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 215b5d2..3233e2a 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3443,12 +3443,14 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
+
+ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *dev,
++ unsigned int link_id,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+ {
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_chan_req chanreq = { .oper = *chandef };
+ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_link_data *link;
+ int err;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+@@ -3458,16 +3460,20 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ goto out_unlock;
+ }
+
++ link = sdata_dereference(sdata->link[link_id], sdata);
++ if (!link)
++ return -ENOLINK;
++
+ /* whatever, but channel contexts should not complain about that one */
+- sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
+- sdata->deflink.needed_rx_chains = local->rx_chains;
++ link->smps_mode = IEEE80211_SMPS_OFF;
++ link->needed_rx_chains = local->rx_chains;
+
+- err = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
++ err = ieee80211_link_use_channel(link, &chanreq,
+ IEEE80211_CHANCTX_SHARED);
+ if (err)
+ goto out_unlock;
+
+- wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
++ wiphy_delayed_work_queue(wiphy, &link->dfs_cac_timer_work,
+ msecs_to_jiffies(cac_time_ms));
+
+ out_unlock:
+@@ -3475,23 +3481,29 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ }
+
+ static void ieee80211_end_cac(struct wiphy *wiphy,
+- struct net_device *dev)
++ struct net_device *dev,
++ unsigned int link_id)
+ {
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_link_data *link;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
++ link = sdata_dereference(sdata->link[link_id], sdata);
++ if (!link)
++ return;
++
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ /* it might be waiting for the local->mtx, but then
+ * by the time it gets it, sdata->wdev.cac_started
+ * will no longer be true
+ */
+ wiphy_delayed_work_cancel(wiphy,
+- &sdata->deflink.dfs_cac_timer_work);
++ &link->dfs_cac_timer_work);
+
+ if (sdata->wdev.cac_started) {
+- ieee80211_link_release_channel(&sdata->deflink);
++ ieee80211_link_release_channel(link);
+ sdata->wdev.cac_started = false;
+ }
+ }
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index c5781c3..608e442 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2592,7 +2592,7 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+ bool ieee80211_is_radar_required(struct ieee80211_local *local);
+
+ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work);
+-void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
++void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_id);
+ void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
+ struct wiphy_work *work);
+ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index e9d8581..f6f0509 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -1173,8 +1173,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
+ if (comb->num_different_channels > 1)
+ return -EINVAL;
+ }
+- } else {
+- /* DFS is not supported with multi-channel combinations yet */
++ }/* else {
++ // DFS is not supported with multi-channel combinations yet
+ for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
+ const struct ieee80211_iface_combination *comb;
+
+@@ -1184,7 +1184,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
+ comb->num_different_channels > 1)
+ return -EINVAL;
+ }
+- }
++ } */
+
+ /* Only HW csum features are currently compatible with mac80211 */
+ if (WARN_ON(hw->netdev_features & ~MAC80211_SUPPORTED_FEATURES))
+diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
+index c1fa26e..5a25245 100644
+--- a/net/mac80211/pm.c
++++ b/net/mac80211/pm.c
+@@ -22,6 +22,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+ {
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
++ struct ieee80211_chanctx *ctx;
+ struct sta_info *sta;
+
+ if (!local->open_count)
+@@ -32,7 +33,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+
+ ieee80211_scan_cancel(local);
+
+- ieee80211_dfs_cac_cancel(local);
++ list_for_each_entry(ctx, &local->chanctx_list, list) {
++ struct ieee80211_link_data *link, *tmp;
++
++ list_for_each_entry_safe(link, tmp, &ctx->assigned_links,
++ assigned_chanctx_list)
++ ieee80211_dfs_cac_cancel(local, link->link_id);
++ }
+
+ ieee80211_roc_purge(local, NULL);
+
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 55f1566..e5ac902 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3450,9 +3450,10 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+ return ts;
+ }
+
+-void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
++void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_id)
+ {
+ struct ieee80211_sub_if_data *sdata;
++ struct ieee80211_link_data *link;
+ struct cfg80211_chan_def chandef;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+@@ -3462,12 +3463,20 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+ * by the time it gets it, sdata->wdev.cac_started
+ * will no longer be true
+ */
++ link = sdata_dereference(sdata->link[link_id], sdata);
++ if (!link)
++ continue;
++
++ if (link->conf->chanreq.oper.chan &&
++ link->conf->chanreq.oper.chan->band != NL80211_BAND_5GHZ)
++ return;
++
+ wiphy_delayed_work_cancel(local->hw.wiphy,
+- &sdata->deflink.dfs_cac_timer_work);
++ &link->dfs_cac_timer_work);
+
+ if (sdata->wdev.cac_started) {
+- chandef = sdata->vif.bss_conf.chanreq.oper;
+- ieee80211_link_release_channel(&sdata->deflink);
++ chandef = link->conf->chanreq.oper;
++ ieee80211_link_release_channel(link);
+ cfg80211_cac_event(sdata->dev,
+ &chandef,
+ NL80211_RADAR_CAC_ABORTED,
+@@ -3483,20 +3492,23 @@ void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
+ container_of(work, struct ieee80211_local, radar_detected_work);
+ struct cfg80211_chan_def chandef = local->hw.conf.chandef;
+ struct ieee80211_chanctx *ctx;
++ struct ieee80211_link_data *link, *tmp;
+ int num_chanctx = 0;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
+- if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
++ if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER ||
++ !ctx->conf.def.chan || ctx->conf.def.chan->band != NL80211_BAND_5GHZ)
+ continue;
+
+ num_chanctx++;
+ chandef = ctx->conf.def;
++ list_for_each_entry_safe(link, tmp, &ctx->assigned_links,
++ assigned_chanctx_list)
++ ieee80211_dfs_cac_cancel(local, link->link_id);
+ }
+
+- ieee80211_dfs_cac_cancel(local);
+-
+ if (num_chanctx > 1)
+ /* XXX: multi-channel is not supported yet */
+ WARN_ON(1);
+diff --git a/net/wireless/core.c b/net/wireless/core.c
+index ac9417e..16f40f3 100644
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -629,10 +629,10 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
+ if (WARN_ON(!c->num_different_channels))
+ return -EINVAL;
+
+- /* DFS only works on one channel. */
++ /* DFS only works on one channel.
+ if (WARN_ON(c->radar_detect_widths &&
+ (c->num_different_channels > 1)))
+- return -EINVAL;
++ return -EINVAL; */
+
+ if (WARN_ON(!c->n_limits))
+ return -EINVAL;
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 3da7886..a957989 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1118,9 +1118,9 @@ void cfg80211_cac_event(struct net_device *netdev,
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ unsigned long timeout;
+
+- /* not yet supported */
++ /* not yet supported
+ if (wdev->valid_links)
+- return;
++ return; */
+
+ trace_cfg80211_cac_event(netdev, event);
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 079fedf..2045cdc 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -9964,6 +9964,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ struct cfg80211_chan_def chandef;
+ enum nl80211_dfs_regions dfs_region;
+ unsigned int cac_time_ms;
++ unsigned int link_id = nl80211_link_id(info->attrs);
+ int err = -EINVAL;
+
+ flush_delayed_work(&rdev->dfs_update_channels_wk);
+@@ -9998,7 +9999,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ goto unlock;
+ }
+
+- if (netif_carrier_ok(dev)) {
++ if (!wdev->valid_links && netif_carrier_ok(dev)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+@@ -10026,9 +10027,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
+ __func__, dfs_region, chandef.center_freq1, chandef.center_freq2, cac_time_ms);
+
+- err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
++ err = rdev_start_radar_detection(rdev, dev, link_id, &chandef, cac_time_ms);
+ if (!err) {
+- wdev->links[0].ap.chandef = chandef;
++ wdev->links[link_id].ap.chandef = chandef;
+ wdev->cac_started = true;
+ wdev->cac_start_time = jiffies;
+ wdev->cac_time_ms = cac_time_ms;
+@@ -17304,8 +17305,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
+ .doit = nl80211_start_radar_detection,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
+- NL80211_FLAG_NO_WIPHY_MTX |
+- NL80211_FLAG_MLO_UNSUPPORTED),
++ NL80211_FLAG_NO_WIPHY_MTX),
+ },
+ {
+ .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index 2ae7fc5..561637c 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1197,15 +1197,16 @@ rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
+ static inline int
+ rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
++ unsigned int link_id,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+ {
+ int ret = -EOPNOTSUPP;
+
+- trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
+- cac_time_ms);
++ trace_rdev_start_radar_detection(&rdev->wiphy, dev, link_id,
++ chandef, cac_time_ms);
+ if (rdev->ops->start_radar_detection)
+- ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
++ ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev, link_id,
+ chandef, cac_time_ms);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+@@ -1213,11 +1214,11 @@ rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
+
+ static inline void
+ rdev_end_cac(struct cfg80211_registered_device *rdev,
+- struct net_device *dev)
++ struct net_device *dev, unsigned int link_id)
+ {
+- trace_rdev_end_cac(&rdev->wiphy, dev);
++ trace_rdev_end_cac(&rdev->wiphy, dev, link_id);
+ if (rdev->ops->end_cac)
+- rdev->ops->end_cac(&rdev->wiphy, dev);
++ rdev->ops->end_cac(&rdev->wiphy, dev, link_id);
+ trace_rdev_return_void(&rdev->wiphy);
+ }
+
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 2c0c1f1..6883aa0 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -4246,17 +4246,20 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
+ */
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ struct cfg80211_chan_def *chandef;
++ unsigned int link_id;
+
+ if (!wdev->cac_started)
+ continue;
+
+- /* FIXME: radar detection is tied to link 0 for now */
+- chandef = wdev_chandef(wdev, 0);
+- if (!chandef)
+- continue;
++ for_each_valid_link(wdev, link_id) {
++ chandef = wdev_chandef(wdev, link_id);
++ if (!chandef || !chandef->chan ||
++ chandef->chan->band != NL80211_BAND_5GHZ)
++ continue;
+
+- if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
+- rdev_end_cac(rdev, wdev->netdev);
++ if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
++ rdev_end_cac(rdev, wdev->netdev, link_id);
++ }
+ }
+ }
+
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index aa3284f..22b1fcc 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -731,9 +731,22 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
+ TP_ARGS(wiphy, netdev)
+ );
+
+-DEFINE_EVENT(wiphy_netdev_evt, rdev_end_cac,
+- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+- TP_ARGS(wiphy, netdev)
++TRACE_EVENT(rdev_end_cac,
++ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
++ unsigned int link_id),
++ TP_ARGS(wiphy, netdev, link_id),
++ TP_STRUCT__entry(
++ WIPHY_ENTRY
++ NETDEV_ENTRY
++ __field(u32, link_id)
++ ),
++ TP_fast_assign(
++ WIPHY_ASSIGN;
++ NETDEV_ASSIGN;
++ __entry->link_id = link_id;
++ ),
++ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id=%u",
++ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id)
+ );
+
+ DECLARE_EVENT_CLASS(station_add_change,
+@@ -2577,24 +2590,27 @@ TRACE_EVENT(rdev_external_auth,
+
+ TRACE_EVENT(rdev_start_radar_detection,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
++ unsigned int link_id,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms),
+- TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
++ TP_ARGS(wiphy, netdev, link_id, chandef, cac_time_ms),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
++ __field(u32, link_id)
+ CHAN_DEF_ENTRY
+ __field(u32, cac_time_ms)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
++ __entry->link_id = link_id;
+ CHAN_DEF_ASSIGN(chandef);
+ __entry->cac_time_ms = cac_time_ms;
+ ),
+- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
++ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id=%u, " CHAN_DEF_PR_FMT
+ ", cac_time_ms=%u",
+- WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
++ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id, CHAN_DEF_PR_ARG,
+ __entry->cac_time_ms)
+ );
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-wifi-mac80211-add-wds-mlo-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-wifi-mac80211-add-wds-mlo-support.patch
new file mode 100644
index 0000000..a82a203
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-wifi-mac80211-add-wds-mlo-support.patch
@@ -0,0 +1,125 @@
+From 14fd30e05515edba0fcca8a7238d402cb2d9dd27 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 19 Jan 2024 15:22:00 +0800
+Subject: [PATCH 44/61] mtk: wifi: mac80211: add wds mlo support
+
+Support WDS mode when using MLO.
+1. Remove use_4addr check.
+2. Copy link information to AP_VLAN interface.
+3. Fill 4addr nullfunc by mld address.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I620bb51e47965c2558751ae266609dad61d421b6
+---
+ net/mac80211/cfg.c | 7 -------
+ net/mac80211/iface.c | 19 +++++++++++--------
+ net/mac80211/mlme.c | 15 +++++++++------
+ 3 files changed, 20 insertions(+), 21 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 3233e2a..0cb68fb 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -229,10 +229,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
+ if (params->use_4addr == ifmgd->use_4addr)
+ return 0;
+
+- /* FIXME: no support for 4-addr MLO yet */
+- if (ieee80211_vif_is_mld(&sdata->vif))
+- return -EOPNOTSUPP;
+-
+ sdata->u.mgd.use_4addr = params->use_4addr;
+ if (!ifmgd->associated)
+ return 0;
+@@ -4922,9 +4918,6 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy,
+
+ lockdep_assert_wiphy(sdata->local->hw.wiphy);
+
+- if (wdev->use_4addr)
+- return -EOPNOTSUPP;
+-
+ return ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
+ }
+
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 0ae31a9..083da12 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -381,19 +381,22 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
+ nsdata->vif.type))
+ return -ENOTUNIQ;
+
+- /* No support for VLAN with MLO yet */
+- if (iftype == NL80211_IFTYPE_AP_VLAN &&
+- sdata->wdev.use_4addr &&
+- nsdata->vif.type == NL80211_IFTYPE_AP &&
+- nsdata->vif.valid_links)
+- return -EOPNOTSUPP;
+-
+ /*
+ * can only add VLANs to enabled APs
+ */
+ if (iftype == NL80211_IFTYPE_AP_VLAN &&
+- nsdata->vif.type == NL80211_IFTYPE_AP)
++ nsdata->vif.type == NL80211_IFTYPE_AP) {
++ int i;
++
+ sdata->bss = &nsdata->u.ap;
++ sdata->vif.valid_links = nsdata->vif.valid_links;
++ sdata->vif.active_links = nsdata->vif.active_links;
++ sdata->vif.dormant_links = nsdata->vif.dormant_links;
++ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
++ sdata->link[i] = nsdata->link[i];
++ sdata->vif.link_conf[i] = nsdata->vif.link_conf[i];
++ }
++ }
+ }
+ }
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 84ea805..674f3f5 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -1829,6 +1829,7 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+ {
+ struct sk_buff *skb;
+ struct ieee80211_hdr *nullfunc;
++ u8 assoc_link_id = ifmgd->assoc_data->assoc_link_id;
+ __le16 fc;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+@@ -1844,11 +1845,17 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
+ nullfunc->frame_control = fc;
+- memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
+- memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
+
++ if (ieee80211_vif_is_mld(&sdata->vif)) {
++ memcpy(nullfunc->addr1, sdata->vif.cfg.ap_addr, ETH_ALEN);
++ memcpy(nullfunc->addr3, sdata->vif.cfg.ap_addr, ETH_ALEN);
++ } else {
++ memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
++ memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
++ }
++
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
+ ieee80211_tx_skb(sdata, skb);
+@@ -8229,10 +8236,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
+ size += req->links[i].elems_len;
+
+- /* FIXME: no support for 4-addr MLO yet */
+- if (sdata->u.mgd.use_4addr && req->link_id >= 0)
+- return -EOPNOTSUPP;
+-
+ assoc_data = kzalloc(size, GFP_KERNEL);
+ if (!assoc_data)
+ return -ENOMEM;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch
new file mode 100644
index 0000000..7f3eef0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch
@@ -0,0 +1,39 @@
+From 9a234460e4fcc0013ade098c9e8efb6f201068fc Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Jan 2024 18:37:15 +0800
+Subject: [PATCH 45/61] mtk: mac80211: fix ieee80211_probe_client warning
+
+Only get chanctx for non-mld VIF.
+
+CR-Id: WCNCR00238098
+Change-Id: I0a6861fc16d11e97fb29b775d5e641925b7dd92f
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ net/mac80211/cfg.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 0cb68fb..6e3b28e 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4211,11 +4211,13 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+ qos = sta->sta.wme;
+
+ chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
+- if (WARN_ON(!chanctx_conf)) {
+- ret = -EINVAL;
+- goto unlock;
++ if (!ieee80211_vif_is_mld(&sdata->vif)) {
++ if (WARN_ON(!chanctx_conf)) {
++ ret = -EINVAL;
++ goto unlock;
++ }
+ }
+- band = chanctx_conf->def.chan->band;
++ band = chanctx_conf ? chanctx_conf->def.chan->band : 0;
+
+ if (qos) {
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch
new file mode 100644
index 0000000..745751b
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch
@@ -0,0 +1,43 @@
+From b22d9da27623db1d7c3bf7f677afcdb99ec5ec27 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 31 Jan 2024 11:40:28 +0800
+Subject: [PATCH 46/61] mtk: mac80211: remove link != 0 warn on in
+ rate_control_rate_update for mlo channel switch
+
+Remove link warning for mlo channel switch
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/rate.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
+index bbdd3b8..8f54562 100644
+--- a/net/mac80211/rate.c
++++ b/net/mac80211/rate.c
+@@ -100,13 +100,19 @@ void rate_control_rate_update(struct ieee80211_local *local,
+ struct ieee80211_sta *ista = &sta->sta;
+ void *priv_sta = sta->rate_ctrl_priv;
+ struct ieee80211_chanctx_conf *chanctx_conf;
++ struct ieee80211_link_data *link;
+
+- WARN_ON(link_id != 0);
++ // WARN_ON(link_id != 0);
+
+ if (ref && ref->ops->rate_update) {
+ rcu_read_lock();
+
+- chanctx_conf = rcu_dereference(sta->sdata->vif.bss_conf.chanctx_conf);
++ link = rcu_dereference(sta->sdata->link[link_id]);
++ if (!link) {
++ rcu_read_unlock();
++ return;
++ }
++ chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch
new file mode 100644
index 0000000..d3324ff
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch
@@ -0,0 +1,41 @@
+From 588692667ae3d8a198760c19d335d33b0dca6ba0 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 1 Feb 2024 17:46:49 +0800
+Subject: [PATCH 47/61] mtk: mac80211: fix radar required of link issue in
+ reserve_reassign and reserve_assign
+
+link->radar_required is not updated in
+ieee80211_link_use_reserved_assign & ieee80211_link_use_reserved_reassign
+This will lead to DFS RDD init incomplete (RDD_CAC_START, RDD_CAC_END &
+RDD_DET_MODE is not set to fw)
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I44eeb32be5ff42202639f94418d079930369fde9
+---
+ net/mac80211/chan.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index 8043d1d..ac22524 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -1207,6 +1207,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
+ if (link_conf->chanreq.oper.width != link->reserved.oper.width)
+ changed = BSS_CHANGED_BANDWIDTH;
+
++ link->radar_required = link->reserved_radar_required;
+ ieee80211_link_update_chanreq(link, &link->reserved);
+
+ _ieee80211_change_chanctx(local, new_ctx, old_ctx, chanreq, link);
+@@ -1290,6 +1291,7 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
+ list_del(&link->reserved_chanctx_list);
+ link->reserved_chanctx = NULL;
+
++ link->radar_required = link->reserved_radar_required;
+ err = ieee80211_assign_link_chanctx(link, new_ctx);
+ if (err) {
+ if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch
new file mode 100644
index 0000000..eb3bca0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch
@@ -0,0 +1,390 @@
+From d65bfb08d34fadb98ad271a91365004e8b674b84 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 1 Feb 2024 10:57:39 +0800
+Subject: [PATCH 48/61] mtk: cfg80211: rework cac started, cac start time for
+ multi-link support
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Ib47630f1a01fd0d4bf7edb1964b2907f2b4ec8f3
+---
+ include/net/cfg80211.h | 20 ++++++++++++++------
+ net/mac80211/cfg.c | 26 +++++++++++++-------------
+ net/mac80211/iface.c | 22 +++++++++++++++-------
+ net/mac80211/mlme.c | 4 ++--
+ net/mac80211/pm.c | 10 ++++------
+ net/mac80211/scan.c | 2 +-
+ net/mac80211/util.c | 6 +++---
+ net/wireless/debugfs.c | 10 ++++++----
+ net/wireless/mlme.c | 13 +++++++------
+ net/wireless/nl80211.c | 8 ++++----
+ net/wireless/reg.c | 4 ++--
+ 11 files changed, 71 insertions(+), 54 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index e55309b..e88cc1e 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -6223,11 +6223,6 @@ struct wireless_dev {
+ u32 owner_nlportid;
+ bool nl_owner_dead;
+
+- /* FIXME: need to rework radar detection for MLO */
+- bool cac_started;
+- unsigned long cac_start_time;
+- unsigned int cac_time_ms;
+-
+ #ifdef CPTCFG_CFG80211_WEXT
+ /* wext data */
+ struct {
+@@ -6294,8 +6289,11 @@ struct wireless_dev {
+ struct cfg80211_internal_bss *current_bss;
+ } client;
+ };
++ unsigned long cac_start_time;
++ unsigned int cac_time_ms;
+ } links[IEEE80211_MLD_MAX_NUM_LINKS];
+ u16 valid_links;
++ u16 cac_links;
+ };
+
+ static inline const u8 *wdev_address(struct wireless_dev *wdev)
+@@ -6350,6 +6348,15 @@ static inline void WARN_INVALID_LINK_ID(struct wireless_dev *wdev,
+ if (!(link_info)->valid_links || \
+ ((link_info)->valid_links & BIT(link_id)))
+
++#define for_each_cac_link(link_info, link_id) \
++ for (link_id = 0; \
++ link_id < ((link_info)->cac_links ? \
++ ARRAY_SIZE((link_info)->links) : 1); \
++ link_id++) \
++ if (!(link_info)->cac_links || \
++ ((link_info)->cac_links & BIT(link_id)))
++
++
+ /**
+ * DOC: Utility functions
+ *
+@@ -8669,6 +8676,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
+ /**
+ * cfg80211_cac_event - Channel availability check (CAC) event
+ * @netdev: network device
++ * @link_id: the link ID for MLO, must be 0 for non-MLO
+ * @chandef: chandef for the current channel
+ * @event: type of event
+ * @gfp: context flags
+@@ -8677,7 +8685,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
+ * or aborted. This must be called to notify the completion of a CAC process,
+ * also by full-MAC drivers.
+ */
+-void cfg80211_cac_event(struct net_device *netdev,
++void cfg80211_cac_event(struct net_device *netdev, unsigned int link_id,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp);
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 6e3b28e..871623d 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1658,10 +1658,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ ieee80211_link_info_change_notify(sdata, link,
+ BSS_CHANGED_BEACON_ENABLED);
+
+- if (sdata->wdev.cac_started) {
++ if (sdata->wdev.cac_links & BIT(link_id)) {
+ chandef = link_conf->chanreq.oper;
+ wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
+- cfg80211_cac_event(sdata->dev, &chandef,
++ cfg80211_cac_event(sdata->dev, link_id, &chandef,
+ NL80211_RADAR_CAC_ABORTED,
+ GFP_KERNEL);
+ }
+@@ -3492,15 +3492,15 @@ static void ieee80211_end_cac(struct wiphy *wiphy,
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ /* it might be waiting for the local->mtx, but then
+- * by the time it gets it, sdata->wdev.cac_started
++ * by the time it gets it, sdata->wdev.cac_links & BIT(link_id)
+ * will no longer be true
+ */
+ wiphy_delayed_work_cancel(wiphy,
+ &link->dfs_cac_timer_work);
+
+- if (sdata->wdev.cac_started) {
++ if (sdata->wdev.cac_links & BIT(link_id)) {
+ ieee80211_link_release_channel(link);
+- sdata->wdev.cac_started = false;
++ sdata->wdev.cac_links &= ~BIT(link_id);
+ }
+ }
+ }
+@@ -3959,12 +3959,12 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ if (!list_empty(&local->roc_list) || local->scanning)
+ return -EBUSY;
+
+- if (sdata->wdev.cac_started)
+- return -EBUSY;
+-
+ if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS))
+ return -EINVAL;
+
++ if (sdata->wdev.cac_links & BIT(link_id))
++ return -EBUSY;
++
+ link_data = wiphy_dereference(wiphy, sdata->link[link_id]);
+ if (!link_data)
+ return -ENOLINK;
+@@ -5078,12 +5078,12 @@ ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
+
+ wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+ &link->dfs_cac_timer_work);
+- if (wdev->cac_started) {
++ if (wdev->cac_links & BIT(link_id)) {
+ ieee80211_link_release_channel(link);
+- cac_time_ms = wdev->cac_time_ms;
+- wdev->cac_start_time = jiffies -
+- msecs_to_jiffies(cac_time_ms + 1);
+- cfg80211_cac_event(wdev->netdev, &link->conf->chanreq.oper,
++ cac_time_ms = wdev->links[link_id].cac_time_ms;
++ wdev->links[link_id].cac_start_time = jiffies -
++ msecs_to_jiffies(cac_time_ms + 1);
++ cfg80211_cac_event(wdev->netdev, link_id, &link->conf->chanreq.oper,
+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+ }
+ }
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 083da12..cdeb787 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -559,13 +559,21 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
+ wiphy_delayed_work_cancel(local->hw.wiphy,
+ &sdata->deflink.dfs_cac_timer_work);
+
+- if (sdata->wdev.cac_started) {
+- chandef = sdata->vif.bss_conf.chanreq.oper;
+- WARN_ON(local->suspended);
+- ieee80211_link_release_channel(&sdata->deflink);
+- cfg80211_cac_event(sdata->dev, &chandef,
+- NL80211_RADAR_CAC_ABORTED,
+- GFP_KERNEL);
++ if (sdata->wdev.cac_links) {
++ struct ieee80211_link_data *link;
++ unsigned int link_id;
++
++ for_each_cac_link(&sdata->wdev, link_id) {
++ link = sdata_dereference(sdata->link[link_id], sdata);
++ if (!link)
++ continue;
++ chandef = link->conf->chanreq.oper;
++ WARN_ON(local->suspended);
++ ieee80211_link_release_channel(link);
++ cfg80211_cac_event(sdata->dev, link_id, &chandef,
++ NL80211_RADAR_CAC_ABORTED,
++ GFP_KERNEL);
++ }
+ }
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 674f3f5..e4564de 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2611,9 +2611,9 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
+
+ lockdep_assert_wiphy(sdata->local->hw.wiphy);
+
+- if (sdata->wdev.cac_started) {
++ if (sdata->wdev.cac_links & BIT(link->link_id)) {
+ ieee80211_link_release_channel(link);
+- cfg80211_cac_event(sdata->dev, &chandef,
++ cfg80211_cac_event(sdata->dev, link->link_id, &chandef,
+ NL80211_RADAR_CAC_FINISHED,
+ GFP_KERNEL);
+ }
+diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
+index 5a25245..6fae264 100644
+--- a/net/mac80211/pm.c
++++ b/net/mac80211/pm.c
+@@ -22,7 +22,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+ {
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+- struct ieee80211_chanctx *ctx;
+ struct sta_info *sta;
+
+ if (!local->open_count)
+@@ -33,12 +32,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+
+ ieee80211_scan_cancel(local);
+
+- list_for_each_entry(ctx, &local->chanctx_list, list) {
+- struct ieee80211_link_data *link, *tmp;
++ list_for_each_entry(sdata, &local->interfaces, list) {
++ unsigned int link_id;
+
+- list_for_each_entry_safe(link, tmp, &ctx->assigned_links,
+- assigned_chanctx_list)
+- ieee80211_dfs_cac_cancel(local, link->link_id);
++ for_each_cac_link(&sdata->wdev, link_id)
++ ieee80211_dfs_cac_cancel(local, link_id);
+ }
+
+ ieee80211_roc_purge(local, NULL);
+diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
+index 977f8eb..5a430cb 100644
+--- a/net/mac80211/scan.c
++++ b/net/mac80211/scan.c
+@@ -584,7 +584,7 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
+ return false;
+
+ list_for_each_entry(sdata_iter, &local->interfaces, list) {
+- if (sdata_iter->wdev.cac_started)
++ if (sdata_iter->wdev.cac_links)
+ return false;
+ }
+
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index e5ac902..732232a 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3460,7 +3460,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_i
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ /* it might be waiting for the local->mtx, but then
+- * by the time it gets it, sdata->wdev.cac_started
++ * by the time it gets it, sdata->wdev.cac_links & BIT(link_id)
+ * will no longer be true
+ */
+ link = sdata_dereference(sdata->link[link_id], sdata);
+@@ -3474,10 +3474,10 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_i
+ wiphy_delayed_work_cancel(local->hw.wiphy,
+ &link->dfs_cac_timer_work);
+
+- if (sdata->wdev.cac_started) {
++ if (sdata->wdev.cac_links & BIT(link_id)) {
+ chandef = link->conf->chanreq.oper;
+ ieee80211_link_release_channel(link);
+- cfg80211_cac_event(sdata->dev,
++ cfg80211_cac_event(sdata->dev, link_id,
+ &chandef,
+ NL80211_RADAR_CAC_ABORTED,
+ GFP_KERNEL);
+diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
+index a246b2c..cbeed9f 100644
+--- a/net/wireless/debugfs.c
++++ b/net/wireless/debugfs.c
+@@ -187,10 +187,11 @@ static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev,
+ if (remain_time > wait_time_ms)
+ remain_time = 0;
+ } else if (chan->dfs_state == NL80211_DFS_USABLE) {
+- if (wdev->cac_started &&
++ if ((wdev->cac_links & BIT(link_id)) &&
+ cfg80211_is_sub_chan(chandef, chan, false)) {
+- jiffies_passed = jiffies - wdev->cac_start_time;
+- wait_time_ms = wdev->cac_time_ms;
++ jiffies_passed = jiffies -
++ wdev->links[link_id].cac_start_time;
++ wait_time_ms = wdev->links[link_id].cac_time_ms;
+ remain_time = (wait_time_ms -
+ jiffies_to_msecs(jiffies_passed));
+ }
+@@ -336,7 +337,8 @@ dfs_cac_skip(void *data, u64 val)
+ continue;
+
+ if (cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0 &&
+- cfg80211_chandef_dfs_usable(wiphy, c) && wdev->cac_started) {
++ cfg80211_chandef_dfs_usable(wiphy, c) &&
++ (wdev->cac_links & BIT(link_id))) {
+ rdev_skip_cac(rdev, wdev, link_id);
+ }
+ }
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index a957989..265c2f2 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1109,7 +1109,7 @@ void __cfg80211_radar_event(struct wiphy *wiphy,
+ }
+ EXPORT_SYMBOL(__cfg80211_radar_event);
+
+-void cfg80211_cac_event(struct net_device *netdev,
++void cfg80211_cac_event(struct net_device *netdev, unsigned int link_id,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp)
+ {
+@@ -1124,13 +1124,14 @@ void cfg80211_cac_event(struct net_device *netdev,
+
+ trace_cfg80211_cac_event(netdev, event);
+
+- if (WARN_ON(!wdev->cac_started && event != NL80211_RADAR_CAC_STARTED))
++ if (WARN_ON(!(wdev->cac_links & BIT(link_id)) &&
++ event != NL80211_RADAR_CAC_STARTED))
+ return;
+
+ switch (event) {
+ case NL80211_RADAR_CAC_FINISHED:
+- timeout = wdev->cac_start_time +
+- msecs_to_jiffies(wdev->cac_time_ms);
++ timeout = wdev->links[link_id].cac_start_time +
++ msecs_to_jiffies(wdev->links[link_id].cac_time_ms);
+ WARN_ON(!time_after_eq(jiffies, timeout));
+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+ memcpy(&rdev->cac_done_chandef, chandef,
+@@ -1139,10 +1140,10 @@ void cfg80211_cac_event(struct net_device *netdev,
+ cfg80211_sched_dfs_chan_update(rdev);
+ fallthrough;
+ case NL80211_RADAR_CAC_ABORTED:
+- wdev->cac_started = false;
++ wdev->cac_links &= ~BIT(link_id);
+ break;
+ case NL80211_RADAR_CAC_STARTED:
+- wdev->cac_started = true;
++ wdev->cac_links |= BIT(link_id);
+ break;
+ default:
+ WARN_ON(1);
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 2045cdc..2a34884 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10004,7 +10004,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ goto unlock;
+ }
+
+- if (wdev->cac_started) {
++ if (wdev->cac_links & BIT(link_id)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+@@ -10030,9 +10030,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ err = rdev_start_radar_detection(rdev, dev, link_id, &chandef, cac_time_ms);
+ if (!err) {
+ wdev->links[link_id].ap.chandef = chandef;
+- wdev->cac_started = true;
+- wdev->cac_start_time = jiffies;
+- wdev->cac_time_ms = cac_time_ms;
++ wdev->cac_links |= BIT(link_id);
++ wdev->links[link_id].cac_start_time = jiffies;
++ wdev->links[link_id].cac_time_ms = cac_time_ms;
+ if (rdev->background_cac_started &&
+ cfg80211_is_sub_chan(&chandef, rdev->background_radar_chandef.chan, false)) {
+ cfg80211_stop_background_radar_detection(rdev->background_radar_wdev);
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 6883aa0..ebe0ed4 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -4248,10 +4248,10 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
+ struct cfg80211_chan_def *chandef;
+ unsigned int link_id;
+
+- if (!wdev->cac_started)
++ if (!wdev->cac_links)
+ continue;
+
+- for_each_valid_link(wdev, link_id) {
++ for_each_cac_link(wdev, link_id) {
+ chandef = wdev_chandef(wdev, link_id);
+ if (!chandef || !chandef->chan ||
+ chandef->chan->band != NL80211_BAND_5GHZ)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch
new file mode 100644
index 0000000..d6fd5f4
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch
@@ -0,0 +1,94 @@
+From cf275081660642ebfc9aaef0722ac499922fb17b Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 6 Feb 2024 15:03:49 +0800
+Subject: [PATCH 49/61] mtk: mac80211: remove links when removing AP_VLAN
+ interface
+
+Remove links information when removing AP_VLAN interface.
+Without this patch, there would be a kernel crash when station disconnect
+from AP.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: Idffdc83cd6a13bc01b7f877fb748a8a08e99c212
+---
+ net/mac80211/cfg.c | 14 ++++++++++++++
+ net/mac80211/iface.c | 4 ++--
+ net/mac80211/mlme.c | 1 -
+ net/wireless/util.c | 8 ++++++++
+ 4 files changed, 24 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 871623d..c3b9d10 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4929,6 +4929,20 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy,
+ {
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
++ if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) {
++ int i;
++
++ sdata->vif.valid_links = 0;
++ sdata->vif.active_links = 0;
++ sdata->vif.dormant_links = 0;
++ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
++ rcu_assign_pointer(sdata->link[i], NULL);
++ rcu_assign_pointer(sdata->vif.link_conf[i], NULL);
++ }
++
++ return;
++ }
++
+ lockdep_assert_wiphy(sdata->local->hw.wiphy);
+
+ ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index cdeb787..83b579a 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -393,8 +393,8 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
+ sdata->vif.active_links = nsdata->vif.active_links;
+ sdata->vif.dormant_links = nsdata->vif.dormant_links;
+ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+- sdata->link[i] = nsdata->link[i];
+- sdata->vif.link_conf[i] = nsdata->vif.link_conf[i];
++ rcu_assign_pointer(sdata->link[i], nsdata->link[i]);
++ rcu_assign_pointer(sdata->vif.link_conf[i], nsdata->vif.link_conf[i]);
+ }
+ }
+ }
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index e4564de..ebdcf57 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -1829,7 +1829,6 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+ {
+ struct sk_buff *skb;
+ struct ieee80211_hdr *nullfunc;
+- u8 assoc_link_id = ifmgd->assoc_data->assoc_link_id;
+ __le16 fc;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+diff --git a/net/wireless/util.c b/net/wireless/util.c
+index 2bde8a3..d03f612 100644
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -2826,6 +2826,14 @@ void cfg80211_remove_links(struct wireless_dev *wdev)
+ {
+ unsigned int link_id;
+
++ if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) {
++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
++
++ if (rdev->ops->del_intf_link)
++ rdev->ops->del_intf_link(&rdev->wiphy, wdev, 0);
++
++ return;
++ }
+ /*
+ * links are controlled by upper layers (userspace/cfg)
+ * only for AP mode, so only remove them here for AP
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
new file mode 100644
index 0000000..25fbe6c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
@@ -0,0 +1,39 @@
+From de8d3f6fa5fdebd105e1c7e99f635c94e7f37344 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 25 Jan 2024 14:07:23 +0800
+Subject: [PATCH 50/61] mtk: mac80211: fix AP mgmt not encrypted in WDS mode
+ with PMF on
+
+In ieee80211_tx_prepare(), if tx->sta is still NULL after calling
+sta_info_get(), the skb might be mgmt for WDS peer, so sta_info_get_bss()
+if called to find sta from AP_VLAN, and then interface type & 4-addr
+using is checked.
+
+Change-Id: Ie94fbb22ca2cf7a96d94897e74314b5c61153380
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/tx.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index e72bb7e..2808bc2 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1234,6 +1234,13 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
+ if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) {
+ tx->sta = sta_info_get(sdata, hdr->addr1);
+ aggr_check = true;
++
++ if (!tx->sta) {
++ tx->sta = sta_info_get_bss(sdata, hdr->addr1);
++ if (!tx->sta || !tx->sta->sdata->wdev.use_4addr ||
++ tx->sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
++ tx->sta = NULL;
++ }
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch
new file mode 100644
index 0000000..e31009b
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch
@@ -0,0 +1,35 @@
+From edef4ae672b3d124b573c1c9856d6435f30b6bb5 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 15 Feb 2024 14:30:02 +0800
+Subject: [PATCH 51/61] mtk: mac80211: fix ieee80211_ht_cap_ie_to_sta_ht_cap
+ warn on
+
+Fix ieee80211_ht_cap_ie_to_sta_ht_cap warning.
+For MLD with a 2/5G primary link, auth/assoc is done in the 2G or 5G link.
+Therefore, 6G link will enter ieee80211_ht_cap_ie_to_sta_ht_cap, as elems->ht_cap_elem of 2/5G is NOT NULL.
+This should be avoided; otherwise, if 6G is bw 320, then the warning will be triggered.
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I3be945036afe677a54e328685fd2fe7b725c6ed5
+---
+ net/mac80211/mlme.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index ebdcf57..b9d10e9 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -4398,7 +4398,8 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
+ sband = local->hw.wiphy->bands[link->conf->chanreq.oper.chan->band];
+
+ /* Set up internal HT/VHT capabilities */
+- if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT)
++ if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT &&
++ !is_6ghz)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ elems->ht_cap_elem,
+ link_sta);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch
new file mode 100644
index 0000000..ffba153
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch
@@ -0,0 +1,34 @@
+From 9bd4c9f67e46919eff0f755bfe675a6ac5c1ac4e Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 16 Feb 2024 17:38:22 +0800
+Subject: [PATCH 52/61] mtk: mac80211: fix mac address to support hw path in
+ station mode
+
+Use AP's MLD address instead of using deflink.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I6df2419d165406ef9b31ccb19600685d121e6d56
+---
+ net/mac80211/iface.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 83b579a..026436e 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -983,7 +983,10 @@ static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
+ }
+ }
+
+- sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
++ if (ieee80211_vif_is_mld(&sdata->vif))
++ sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
++ else
++ sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
+ break;
+ default:
+ goto out;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch
new file mode 100644
index 0000000..3318c20
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch
@@ -0,0 +1,165 @@
+From e564e83652ae93e37f624eac45f98d3ee17cb811 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 22 Feb 2024 15:21:49 +0800
+Subject: [PATCH 53/61] mtk: mac80211: workaround for configuring txpower in
+ mld ap
+
+As for mt76 design, we expect to set txpower per link. So, we add
+another parameter to ieee80211_recalc_txpower function to set
+txpower to per link. For the functions that mac80211 don't pass link
+id to which, we specify to use the FIRST link as the parameter.
+
+Apply the patch will make uci and iw set txpower commamd only effect
+the link which id is 0 when we enable mld AP.
+
+CR-Id: WCNCR00259302
+---
+ net/mac80211/cfg.c | 15 ++++++++++++---
+ net/mac80211/chan.c | 4 ++--
+ net/mac80211/ieee80211_i.h | 5 +++--
+ net/mac80211/iface.c | 15 ++++++++-------
+ net/mac80211/mlme.c | 2 +-
+ 5 files changed, 26 insertions(+), 15 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index c3b9d10..cca3e08 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3059,7 +3059,11 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ sdata->vif.bss_conf.txpower_type = txp_type;
+ }
+
+- ieee80211_recalc_txpower(sdata, update_txp_type);
++ /* Due to mac80211 not pass link id to here, use first link for now */
++ if (ieee80211_vif_is_mld(&sdata->vif))
++ ieee80211_recalc_txpower(sdata, update_txp_type, sdata->link[0]);
++ else
++ ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
+
+ return 0;
+ }
+@@ -3090,7 +3094,12 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ continue;
+- ieee80211_recalc_txpower(sdata, update_txp_type);
++ /* Due to mac80211 not pass link id to here, use first link for now */
++ if (ieee80211_vif_is_mld(&sdata->vif))
++ ieee80211_recalc_txpower(sdata, update_txp_type, sdata->link[0]);
++ else
++ ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
++
+ }
+
+ if (has_monitor) {
+@@ -3102,7 +3111,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ update_txp_type = true;
+ sdata->vif.bss_conf.txpower_type = txp_type;
+
+- ieee80211_recalc_txpower(sdata, update_txp_type);
++ ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
+ }
+ }
+
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index ac22524..f09cac4 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -842,7 +842,7 @@ out:
+ }
+
+ if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
+- ieee80211_recalc_txpower(sdata, false);
++ ieee80211_recalc_txpower(sdata, false, link);
+ ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL);
+ }
+
+@@ -1570,7 +1570,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
+ link,
+ changed);
+
+- ieee80211_recalc_txpower(sdata, false);
++ ieee80211_recalc_txpower(sdata, false, link);
+ }
+
+ ieee80211_recalc_chanctx_chantype(local, ctx);
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index 608e442..6f1b783 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2023,9 +2023,10 @@ void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
+ int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
+ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
+
+-bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
++bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_link_data *link);
+ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+- bool update_bss);
++ bool update_bss, struct ieee80211_link_data *link);
+ void ieee80211_recalc_offload(struct ieee80211_local *local);
+
+ static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 026436e..3400811 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -44,13 +44,14 @@
+
+ static void ieee80211_iface_work(struct wiphy *wiphy, struct wiphy_work *work);
+
+-bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
++bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_link_data *link)
+ {
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int power;
+
+ rcu_read_lock();
+- chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
++ chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return false;
+@@ -65,8 +66,8 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+ if (sdata->deflink.ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->deflink.ap_power_level);
+
+- if (power != sdata->vif.bss_conf.txpower) {
+- sdata->vif.bss_conf.txpower = power;
++ if (power != link->conf->txpower) {
++ link->conf->txpower = power;
+ ieee80211_hw_config(sdata->local, 0);
+ return true;
+ }
+@@ -75,11 +76,11 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+ }
+
+ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+- bool update_bss)
++ bool update_bss, struct ieee80211_link_data *link)
+ {
+- if (__ieee80211_recalc_txpower(sdata) ||
++ if (__ieee80211_recalc_txpower(sdata, link) ||
+ (update_bss && ieee80211_sdata_running(sdata)))
+- ieee80211_link_info_change_notify(sdata, &sdata->deflink,
++ ieee80211_link_info_change_notify(sdata, link,
+ BSS_CHANGED_TXPOWER);
+ }
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index b9d10e9..2efd98e 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2362,7 +2362,7 @@ static u64 ieee80211_handle_pwr_constr(struct ieee80211_link_data *link,
+ }
+
+ link->ap_power_level = new_ap_level;
+- if (__ieee80211_recalc_txpower(sdata))
++ if (__ieee80211_recalc_txpower(sdata, link))
+ return BSS_CHANGED_TXPOWER;
+ return 0;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch
new file mode 100644
index 0000000..35c82f2
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch
@@ -0,0 +1,49 @@
+From 487ac10c66d16a0dd2ef526b7223cce87cbded40 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 21 Feb 2024 16:32:13 +0800
+Subject: [PATCH 54/61] mtk: mac80211: send broadcast/multicast mgmt. via all
+ links.
+
+This patch makes broadcast/multicast mgmt. be sent via all links.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-id: Id1bd06694a73cc29b3f571a57c4c864ee3742441
+---
+ net/mac80211/offchannel.c | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
+index 3ed5fc5..ec1d7a1 100644
+--- a/net/mac80211/offchannel.c
++++ b/net/mac80211/offchannel.c
+@@ -976,7 +976,25 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ }
+
+ if (!need_offchan) {
+- ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
++ unsigned long links = sdata->vif.active_links;
++ if (is_multicast_ether_addr(mgmt->da) && hweight16(links) > 1) {
++ unsigned int link;
++ struct sk_buff *dskb;
++
++ for_each_set_bit(link, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ dskb = skb_clone(skb, GFP_ATOMIC);
++ if (dskb) {
++ ieee80211_tx_skb_tid(sdata, dskb, 7, link);
++ } else {
++ ret = -ENOMEM;
++ kfree_skb(skb);
++ goto out_unlock;
++ }
++ }
++ kfree_skb(skb);
++ } else {
++ ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
++ }
+ ret = 0;
+ goto out_unlock;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch
new file mode 100644
index 0000000..cd79879
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch
@@ -0,0 +1,32 @@
+From 6d5903a35436fb71f4a245f7b9ddd6279d640adc Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 15 Mar 2024 14:34:11 +0800
+Subject: [PATCH 55/61] mtk: mac80211: fix mlo BW 160 channel switch issue
+
+The link_id argument for cfg80211_ch_switch_started_notify is missing
+after maintainer rebasing for chanreq
+The original commit has link_id instead of 0
+https://patchwork.kernel.org/project/linux-wireless/list/?series=821321&state=*
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/cfg.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index cca3e08..856c956 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4045,7 +4045,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ }
+
+ cfg80211_ch_switch_started_notify(sdata->dev,
+- &link_data->csa_chanreq.oper, 0,
++ &link_data->csa_chanreq.oper, link_id,
+ params->count, params->block_tx);
+
+ if (changed) {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch
new file mode 100644
index 0000000..3a2ff85
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch
@@ -0,0 +1,64 @@
+From 67aa5809167470add6b82fb0049ce6d33327592e Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 21 Mar 2024 10:58:59 +0800
+Subject: [PATCH 56/61] mtk: mac80211: extend IEEE80211_KEY_FLAG_GENERATE_MMIE
+ to other ciphers
+
+This commit extends the flag IEEE80211_KEY_FLAG_GENERATE_MMIE to
+CMAC-256 and GMAC for the same reason that this flag was added.
+(a0b449: mac80211: add IEEE80211_KEY_FLAG_GENERATE_MMIE to ieee80211_key_flags)
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I0ce5f8894de42a42e257ffce75c51399a440f003
+---
+ net/mac80211/wpa.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
+index daf1bcc..e66f917 100644
+--- a/net/mac80211/wpa.c
++++ b/net/mac80211/wpa.c
+@@ -903,7 +903,8 @@ ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
+
+ info = IEEE80211_SKB_CB(skb);
+
+- if (info->control.hw_key)
++ if (info->control.hw_key &&
++ !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
+ return TX_CONTINUE;
+
+ if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+@@ -919,6 +920,9 @@ ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
+
+ bip_ipn_set64(mmie->sequence_number, pn64);
+
++ if (info->control.hw_key)
++ return TX_CONTINUE;
++
+ bip_aad(skb, aad);
+
+ /* MIC = AES-256-CMAC(IGTK, AAD || Management Frame Body || MMIE, 128)
+@@ -1048,7 +1052,8 @@ ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
+
+ info = IEEE80211_SKB_CB(skb);
+
+- if (info->control.hw_key)
++ if (info->control.hw_key &&
++ !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
+ return TX_CONTINUE;
+
+ if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+@@ -1064,6 +1069,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
+
+ bip_ipn_set64(mmie->sequence_number, pn64);
+
++ if (info->control.hw_key)
++ return TX_CONTINUE;
++
+ bip_aad(skb, aad);
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch
new file mode 100644
index 0000000..1995700
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch
@@ -0,0 +1,33 @@
+From 4bd23c637585903378fcd35828cd9d0e01d7d565 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 25 Mar 2024 18:59:35 +0800
+Subject: [PATCH 57/61] mtk: wifi: mt76: mt7996: not to check 'need_offchan'
+ for MLD multicast mgmt.
+
+Multicast mgmt. sent by the MLD AP should be transmitted via all links,
+so it is not necessary to check 'need_offchan'.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I850d337ea98456b8d81fd767ceea1727333aa6ca
+---
+ net/mac80211/offchannel.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
+index ec1d7a1..a571be0 100644
+--- a/net/mac80211/offchannel.c
++++ b/net/mac80211/offchannel.c
+@@ -874,7 +874,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ /* Check if the operating channel is the requested channel */
+ if (!params->chan && mlo_sta) {
+ need_offchan = false;
+- } else if (!need_offchan) {
++ } else if (!need_offchan && !(ieee80211_vif_is_mld(&sdata->vif) &&
++ is_multicast_ether_addr(mgmt->da))) {
+ struct ieee80211_chanctx_conf *chanctx_conf = NULL;
+ int i;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch
new file mode 100644
index 0000000..eb8db1a
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch
@@ -0,0 +1,54 @@
+From 70e027ce4ac9e4de16829c88ee846b7e12f14953 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 25 Mar 2024 16:26:34 +0800
+Subject: [PATCH 58/61] mtk: wifi: mt76: mt7996: assign link address to the
+ header of broadcast mgmt.
+
+AAD calculation should use link addr as input for broadcast mgmt. skb.
+This commit assigns link address to the header of cloned broadcast mgmt.
+for the correct AAD calculation.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: Ieeee17cc2121fdbc9eb34f171f2d4da583d91154
+---
+ net/mac80211/offchannel.c | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
+index a571be0..15948f9 100644
+--- a/net/mac80211/offchannel.c
++++ b/net/mac80211/offchannel.c
+@@ -981,16 +981,26 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ if (is_multicast_ether_addr(mgmt->da) && hweight16(links) > 1) {
+ unsigned int link;
+ struct sk_buff *dskb;
++ struct ieee80211_hdr *hdr;
++ struct ieee80211_bss_conf *conf;
+
+ for_each_set_bit(link, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ conf = rcu_dereference(sdata->vif.link_conf[link]);
++ if (!conf)
++ continue;
++
+ dskb = skb_clone(skb, GFP_ATOMIC);
+- if (dskb) {
+- ieee80211_tx_skb_tid(sdata, dskb, 7, link);
+- } else {
++ if (!dskb) {
+ ret = -ENOMEM;
+ kfree_skb(skb);
+ goto out_unlock;
+ }
++
++ /* Assign link address */
++ hdr = (void *)dskb->data;
++ memcpy(hdr->addr2, conf->addr, ETH_ALEN);
++ memcpy(hdr->addr3, conf->addr, ETH_ALEN);
++ ieee80211_tx_skb_tid(sdata, dskb, 7, link);
+ }
+ kfree_skb(skb);
+ } else {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch
new file mode 100644
index 0000000..2a098b4
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch
@@ -0,0 +1,45 @@
+From 0f6fde17638d83518432f95ef7b75f2ef883ff1e Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 26 Mar 2024 11:36:35 +0800
+Subject: [PATCH 59/61] mtk: wifi: mt76: mt7996: Do MLD address translation
+ before STA process BMC mgmt. frame
+
+In the function ieee80211_prepare_and_rx_handle(), BMC mgmt. frames are
+not MLD translated since the AAD calculation needs the header being link
+addressed. However, after the AAD calculation, STA processes the mgmt.
+frames on an MLD level, and it fails to match the link address in the
+header with the self MLD address.
+
+This commit does MLD address translation again after the AAD calculation
+and before STA's mgmt. frames processing.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I2a96ced315780b09f0cfc142f0f09b5f4a9a7312
+---
+ net/mac80211/mlme.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 2efd98e..e4d5eac 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -6931,6 +6931,15 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
+ return;
+ }
+
++ /* Do MLD address translation for Multicast/Broadcast frame. */
++ if (is_multicast_ether_addr(mgmt->da) && !ieee80211_is_probe_resp(fc) &&
++ !ieee80211_is_beacon(fc)) {
++ if (ether_addr_equal(mgmt->sa, link->conf->bssid))
++ ether_addr_copy(mgmt->sa, sdata->vif.cfg.ap_addr);
++ if (ether_addr_equal(mgmt->bssid, link->conf->bssid))
++ ether_addr_copy(mgmt->bssid, sdata->vif.cfg.ap_addr);
++ }
++
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_BEACON:
+ ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch
new file mode 100644
index 0000000..a3ead0e
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch
@@ -0,0 +1,46 @@
+From 5b90a58e50482689a246cfce127bc44ad05f79af Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 12 Apr 2024 11:45:41 +0800
+Subject: [PATCH 60/61] mtk: wifi: mac80211: defer enabling beacon for MLD AP
+
+Do not enable beacon on the first beacon update (NL80211_CMD_NEW_BEACON)
+for MLD AP, let it start from the next beacon update
+(NL80211_CMD_SET_BEACON).
+This is used to make sure that MLD AP start beacon after all links
+finish settings.
+
+CR-Id: WCNCR00238098
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: Id2a4137b9101bae3777037f8d26b0380bfe1da48
+---
+ net/mac80211/cfg.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 856c956..e091ccd 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1424,7 +1424,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ }
+
+ link_conf->dtim_period = params->dtim_period;
+- link_conf->enable_beacon = true;
++ link_conf->enable_beacon = !ieee80211_vif_is_mld(&sdata->vif);
+ link_conf->allow_p2p_go_ps = sdata->vif.p2p;
+ link_conf->twt_responder = params->twt_responder;
+ link_conf->he_obss_pd = params->he_obss_pd;
+@@ -1491,6 +1491,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ ieee80211_recalc_dtim(local, sdata);
+ ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_SSID);
+ ieee80211_link_info_change_notify(sdata, link, changed);
++ /* for MLD AP, enable_beacon is false during the first beacon set,
++ * enable it after that. This allows userspace to control the
++ * beacon enable timing.
++ */
++ link_conf->enable_beacon = true;
+
+ if (ieee80211_num_beaconing_links(sdata) <= 1)
+ netif_carrier_on(dev);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch
new file mode 100644
index 0000000..6673cb6
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch
@@ -0,0 +1,31 @@
+From 250ed9ee9f5f7e422dd4d96c1ba328b517c55040 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 18 Apr 2024 18:08:48 +0800
+Subject: [PATCH 61/61] mtk: wifi: mac80211: fix radar trigger issue due to
+ refactoring to single wiphy
+
+Since we change to single wiphy, we cannot directly return during cac cancel if the link conf in local->interfaces list is not for 5G band.
+local->interfaces might contain 2/5/6G sdata
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 732232a..d030ebc 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3469,7 +3469,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_i
+
+ if (link->conf->chanreq.oper.chan &&
+ link->conf->chanreq.oper.chan->band != NL80211_BAND_5GHZ)
+- return;
++ continue;
+
+ wiphy_delayed_work_cancel(local->hw.wiphy,
+ &link->dfs_cac_timer_work);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/110-mac80211_keep_keys_on_stop_ap.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/110-mac80211_keep_keys_on_stop_ap.patch
deleted file mode 100644
index 7bf99be..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/110-mac80211_keep_keys_on_stop_ap.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 27 Oct 2014 00:00:00 +0100
-Subject: [PATCH] mac80211: preseve AP mode keys across STA reconnect
-
-Used for AP+STA support in OpenWrt - preserve AP mode keys across STA reconnect
----
- net/mac80211/cfg.c | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1635,7 +1635,6 @@ static int ieee80211_stop_ap(struct wiph
- link_conf->bssid_indicator = 0;
-
- __sta_info_flush(sdata, true);
-- ieee80211_free_keys(sdata, true);
-
- link_conf->enable_beacon = false;
- sdata->beacon_rate_set = false;
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/120-cfg80211_allow_perm_addr_change.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/120-cfg80211_allow_perm_addr_change.patch
deleted file mode 100644
index f315ae5..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/120-cfg80211_allow_perm_addr_change.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 11 Dec 2014 00:00:00 +0100
-Subject: [PATCH] cfg80211: add support for changing the device mac address via
- sysfs
-
----
- net/wireless/sysfs.c | 27 ++++++++++++++++++++++-----
- 1 file changed, 22 insertions(+), 5 deletions(-)
-
---- a/net/wireless/sysfs.c
-+++ b/net/wireless/sysfs.c
-@@ -24,18 +24,35 @@ static inline struct cfg80211_registered
- return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
- }
-
--#define SHOW_FMT(name, fmt, member) \
-+#define SHOW_FMT(name, fmt, member, mode) \
- static ssize_t name ## _show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
- { \
- return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
- } \
--static DEVICE_ATTR_RO(name)
-+static DEVICE_ATTR_##mode(name)
-
--SHOW_FMT(index, "%d", wiphy_idx);
--SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
--SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
-+static ssize_t macaddress_store(struct device *dev,
-+ struct device_attribute *attr,
-+ const char *buf, size_t len)
-+{
-+ u8 mac[ETH_ALEN];
-+
-+ if (!mac_pton(buf, mac))
-+ return -EINVAL;
-+
-+ if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
-+ return -EINVAL;
-+
-+ memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN);
-+
-+ return strnlen(buf, len);
-+}
-+
-+SHOW_FMT(index, "%d", wiphy_idx, RO);
-+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW);
-+SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO);
-
- static ssize_t name_show(struct device *dev,
- struct device_attribute *attr,
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/130-disable_auto_vif.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/130-disable_auto_vif.patch
deleted file mode 100644
index 43507f2..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/130-disable_auto_vif.patch
+++ /dev/null
@@ -1,27 +0,0 @@
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -1393,24 +1393,6 @@ int ieee80211_register_hw(struct ieee802
- debugfs_hw_add(local);
- rate_control_add_debugfs(local);
-
-- rtnl_lock();
-- wiphy_lock(hw->wiphy);
--
-- /* add one default STA interface if supported */
-- if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
-- !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
-- struct vif_params params = {0};
--
-- result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
-- NL80211_IFTYPE_STATION, ¶ms);
-- if (result)
-- wiphy_warn(local->hw.wiphy,
-- "Failed to add default virtual iface\n");
-- }
--
-- wiphy_unlock(hw->wiphy);
-- rtnl_unlock();
--
- #ifdef CONFIG_INET
- local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
- result = register_inetaddr_notifier(&local->ifa_notifier);
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/210-ap_scan.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/210-ap_scan.patch
deleted file mode 100644
index 1e9676f..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/210-ap_scan.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 3 Oct 2012 00:00:00 +0200
-Subject: [PATCH] mac80211: allow scans in access point mode (for site survey)
-
----
- net/mac80211/cfg.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -2847,6 +2847,8 @@ static int ieee80211_scan(struct wiphy *
- */
- fallthrough;
- case NL80211_IFTYPE_AP:
-+ /* skip check */
-+ break;
- /*
- * If the scan has been forced (and the driver supports
- * forcing), don't care about being beaconing already.
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/301-mac80211-sta-randomize-BA-session-dialog-token-alloc.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/301-mac80211-sta-randomize-BA-session-dialog-token-alloc.patch
deleted file mode 100644
index 1034e2c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/301-mac80211-sta-randomize-BA-session-dialog-token-alloc.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From b478e06a16a8baa00c5ecc87c1d636981f2206d5 Mon Sep 17 00:00:00 2001
-From: Johannes Berg <johannes.berg@intel.com>
-Date: Tue, 29 Oct 2019 10:25:25 +0100
-Subject: [PATCH] mac80211: sta: randomize BA session dialog token allocator
-
-We currently always start the dialog token generator at zero,
-so the first dialog token we use is always 1. This would be
-OK if we had a perfect guarantee that we always do a proper
-deauth/re-auth handshake, but in IBSS mode this doesn't always
-happen properly.
-
-To make problems with block ack (aggregation) sessions getting
-stuck less likely, randomize the dialog token so if we start a
-new session but the peer still has old state for us, it can
-better detect this.
-
-This is really just a workaround to make things a bit more
-robust than they are now - a better fix would be to do a full
-authentication handshake in IBSS mode upon having discovered a
-new station, and on the receiver resetting the state (removing
-and re-adding the station) on receiving the authentication
-packet.
-
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
- net/mac80211/sta_info.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -561,6 +561,11 @@ __sta_info_alloc(struct ieee80211_sub_if
- INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
- INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
- mutex_init(&sta->ampdu_mlme.mtx);
-+#if LINUX_VERSION_IS_LESS(6,2,0)
-+ sta->ampdu_mlme.dialog_token_allocator = prandom_u32_max(U8_MAX);
-+#else
-+ sta->ampdu_mlme.dialog_token_allocator = get_random_u32_below(U8_MAX);
-+#endif
- #ifdef CPTCFG_MAC80211_MESH
- if (ieee80211_vif_is_mesh(&sdata->vif)) {
- sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/302-mac80211-minstrel_ht-fix-MINSTREL_FRAC-macro.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/302-mac80211-minstrel_ht-fix-MINSTREL_FRAC-macro.patch
deleted file mode 100644
index 0d475b7..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/302-mac80211-minstrel_ht-fix-MINSTREL_FRAC-macro.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 28 Apr 2021 21:03:13 +0200
-Subject: [PATCH] mac80211: minstrel_ht: fix MINSTREL_FRAC macro
-
-Add missing braces to avoid issues with e.g. using additions in the
-div expression
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rc80211_minstrel_ht.h
-+++ b/net/mac80211/rc80211_minstrel_ht.h
-@@ -14,7 +14,7 @@
-
- /* scaled fraction values */
- #define MINSTREL_SCALE 12
--#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
-+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
- #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
-
- #define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/303-mac80211-minstrel_ht-reduce-fluctuations-in-rate-pro.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/303-mac80211-minstrel_ht-reduce-fluctuations-in-rate-pro.patch
deleted file mode 100644
index f26477e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/303-mac80211-minstrel_ht-reduce-fluctuations-in-rate-pro.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 6 Feb 2021 16:08:01 +0100
-Subject: [PATCH] mac80211: minstrel_ht: reduce fluctuations in rate
- probability stats
-
-In some scenarios when there is a lot of fluctuation in packet error rates,
-rate switching can be amplified when the statistics get skewed by time slots
-with very few tries.
-Make the input data to the moving average more smooth by adding the
-success/attempts count from the last stats window as well. This has the
-advantage of smoothing the data without introducing any extra lag to sampling
-rates.
-This significantly improves rate stability on a strong test link subjected to
-periodic noise bursts generated with a SDR
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rc80211_minstrel_ht.c
-+++ b/net/mac80211/rc80211_minstrel_ht.c
-@@ -769,7 +769,8 @@ minstrel_ht_calc_rate_stats(struct minst
- unsigned int cur_prob;
-
- if (unlikely(mrs->attempts > 0)) {
-- cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
-+ cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
-+ mrs->attempts + mrs->last_attempts);
- minstrel_filter_avg_add(&mrs->prob_avg,
- &mrs->prob_avg_1, cur_prob);
- mrs->att_hist += mrs->attempts;
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/304-mac80211-minstrel_ht-rework-rate-downgrade-code-and-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/304-mac80211-minstrel_ht-rework-rate-downgrade-code-and-.patch
deleted file mode 100644
index 9b3cc3a..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/304-mac80211-minstrel_ht-rework-rate-downgrade-code-and-.patch
+++ /dev/null
@@ -1,151 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 6 Feb 2021 16:33:14 +0100
-Subject: [PATCH] mac80211: minstrel_ht: rework rate downgrade code and
- max_prob rate selection
-
-The current fallback code for fast rate switching on potentially failing rates
-is triggering too often if there is some strong noise on the channel. This can
-lead to wild fluctuations in the rate selection.
-Additionally, switching down to max_prob_rate can create a significant gap down
-in throughput, especially when using only 2 spatial streams, because max_prob_rate
-is limited to using fewer streams than the max_tp rates.
-In order to improve throughput without reducing reliability too much, use the
-rate downgrade code for the max_prob_rate only, and allow the non-downgraded
-max_prob_rate to use as many spatial streams as the max_tp rates
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rc80211_minstrel_ht.c
-+++ b/net/mac80211/rc80211_minstrel_ht.c
-@@ -580,6 +580,14 @@ minstrel_ht_set_best_prob_rate(struct mi
- int cur_tp_avg, cur_group, cur_idx;
- int max_gpr_group, max_gpr_idx;
- int max_gpr_tp_avg, max_gpr_prob;
-+ int min_dur;
-+
-+ min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
-+ minstrel_get_duration(mi->max_tp_rate[1]));
-+
-+ /* make the rate at least 18% slower than max tp rates */
-+ if (minstrel_get_duration(index) <= min_dur * 19 / 16)
-+ return;
-
- cur_group = MI_RATE_GROUP(index);
- cur_idx = MI_RATE_IDX(index);
-@@ -601,11 +609,6 @@ minstrel_ht_set_best_prob_rate(struct mi
- !minstrel_ht_is_legacy_group(max_tp_group))
- return;
-
-- /* skip rates faster than max tp rate with lower prob */
-- if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
-- mrs->prob_avg < max_tp_prob)
-- return;
--
- max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
- max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
- max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
-@@ -663,40 +666,6 @@ minstrel_ht_assign_best_tp_rates(struct
-
- }
-
--/*
-- * Try to increase robustness of max_prob rate by decrease number of
-- * streams if possible.
-- */
--static inline void
--minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
--{
-- struct minstrel_mcs_group_data *mg;
-- int tmp_max_streams, group, tmp_idx, tmp_prob;
-- int tmp_tp = 0;
--
-- if (!mi->sta->deflink.ht_cap.ht_supported)
-- return;
--
-- group = MI_RATE_GROUP(mi->max_tp_rate[0]);
-- tmp_max_streams = minstrel_mcs_groups[group].streams;
-- for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
-- mg = &mi->groups[group];
-- if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
-- continue;
--
-- tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
-- tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
--
-- if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
-- (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
-- mi->max_prob_rate = mg->max_group_prob_rate;
-- tmp_tp = minstrel_ht_get_tp_avg(mi, group,
-- tmp_idx,
-- tmp_prob);
-- }
-- }
--}
--
- static u16
- __minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
- enum minstrel_sample_type type)
-@@ -1176,8 +1145,6 @@ minstrel_ht_update_stats(struct minstrel
-
- mi->max_prob_rate = tmp_max_prob_rate;
-
-- /* Try to increase robustness of max_prob_rate*/
-- minstrel_ht_prob_rate_reduce_streams(mi);
- minstrel_ht_refill_sample_rates(mi);
-
- #ifdef CPTCFG_MAC80211_DEBUGFS
-@@ -1256,7 +1223,7 @@ minstrel_ht_ri_txstat_valid(struct minst
- }
-
- static void
--minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
-+minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
- {
- int group, orig_group;
-
-@@ -1271,11 +1238,7 @@ minstrel_downgrade_rate(struct minstrel_
- minstrel_mcs_groups[orig_group].streams)
- continue;
-
-- if (primary)
-- *idx = mi->groups[group].max_group_tp_rate[0];
-- else
-- *idx = mi->groups[group].max_group_tp_rate[1];
-- break;
-+ *idx = mi->groups[group].max_group_prob_rate;
- }
- }
-
-@@ -1286,7 +1249,7 @@ minstrel_ht_tx_status(void *priv, struct
- struct ieee80211_tx_info *info = st->info;
- struct minstrel_ht_sta *mi = priv_sta;
- struct ieee80211_tx_rate *ar = info->status.rates;
-- struct minstrel_rate_stats *rate, *rate2;
-+ struct minstrel_rate_stats *rate;
- struct minstrel_priv *mp = priv;
- u32 update_interval = mp->update_interval;
- bool last, update = false;
-@@ -1354,18 +1317,13 @@ minstrel_ht_tx_status(void *priv, struct
- /*
- * check for sudden death of spatial multiplexing,
- * downgrade to a lower number of streams if necessary.
-+ * only do this for the max_prob_rate to prevent spurious
-+ * rate fluctuations when the link changes suddenly
- */
-- rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
-+ rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
- if (rate->attempts > 30 &&
- rate->success < rate->attempts / 4) {
-- minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
-- update = true;
-- }
--
-- rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
-- if (rate2->attempts > 30 &&
-- rate2->success < rate2->attempts / 4) {
-- minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
-+ minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
- update = true;
- }
- }
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/305-mac80211-increase-quantum-for-airtime-scheduler.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/305-mac80211-increase-quantum-for-airtime-scheduler.patch
deleted file mode 100644
index e4575ca..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/305-mac80211-increase-quantum-for-airtime-scheduler.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 26 Jun 2022 11:43:25 +0200
-Subject: [PATCH] mac80211: increase quantum for airtime scheduler
-
-Given the typical AQL budget and queue length, a quantum of 256 with the
-default station weight often requires iterating over all queues frequently,
-until one of them becomes eligible.
-Improve performance by using 8 times station weight as scheduler quantum
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -92,6 +92,8 @@ extern const u8 ieee80211_ac_to_qos_mask
- */
- #define AIRTIME_ACTIVE_DURATION (HZ / 10)
-
-+#define AIRTIME_QUANTUM_SHIFT 3
-+
- struct ieee80211_bss {
- u32 device_ts_beacon, device_ts_presp;
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4060,7 +4060,7 @@ struct ieee80211_txq *ieee80211_next_txq
-
- if (deficit < 0)
- sta->airtime[txqi->txq.ac].deficit +=
-- sta->airtime_weight;
-+ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
-
- if (deficit < 0 || !aql_check) {
- list_move_tail(&txqi->schedule_order,
-@@ -4203,7 +4203,8 @@ bool ieee80211_txq_may_transmit(struct i
- }
- sta = container_of(iter->txq.sta, struct sta_info, sta);
- if (ieee80211_sta_deficit(sta, ac) < 0)
-- sta->airtime[ac].deficit += sta->airtime_weight;
-+ sta->airtime[ac].deficit += sta->airtime_weight <<
-+ AIRTIME_QUANTUM_SHIFT;
- list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
- }
-
-@@ -4211,7 +4212,7 @@ bool ieee80211_txq_may_transmit(struct i
- if (sta->airtime[ac].deficit >= 0)
- goto out;
-
-- sta->airtime[ac].deficit += sta->airtime_weight;
-+ sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
- list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
- spin_unlock_bh(&local->active_txq_lock[ac]);
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/310-mac80211-split-mesh-fast-tx-cache-into-local-proxied.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/310-mac80211-split-mesh-fast-tx-cache-into-local-proxied.patch
deleted file mode 100644
index 4853cef..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/310-mac80211-split-mesh-fast-tx-cache-into-local-proxied.patch
+++ /dev/null
@@ -1,219 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 30 Jun 2023 13:11:51 +0200
-Subject: [PATCH] mac80211: split mesh fast tx cache into
- local/proxied/forwarded
-
-Depending on the origin of the packets (and their SA), 802.11 + mesh headers
-could be filled in differently. In order to properly deal with that, add a
-new field to the lookup key, indicating the type (local, proxied or
-forwarded). This can fix spurious packet drop issues that depend on the order
-in which nodes/hosts communicate with each other.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/mesh.c
-+++ b/net/mac80211/mesh.c
-@@ -765,6 +765,9 @@ bool ieee80211_mesh_xmit_fast(struct iee
- struct sk_buff *skb, u32 ctrl_flags)
- {
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-+ struct ieee80211_mesh_fast_tx_key key = {
-+ .type = MESH_FAST_TX_TYPE_LOCAL
-+ };
- struct ieee80211_mesh_fast_tx *entry;
- struct ieee80211s_hdr *meshhdr;
- u8 sa[ETH_ALEN] __aligned(2);
-@@ -800,7 +803,10 @@ bool ieee80211_mesh_xmit_fast(struct iee
- return false;
- }
-
-- entry = mesh_fast_tx_get(sdata, skb->data);
-+ ether_addr_copy(key.addr, skb->data);
-+ if (!ether_addr_equal(skb->data + ETH_ALEN, sdata->vif.addr))
-+ key.type = MESH_FAST_TX_TYPE_PROXIED;
-+ entry = mesh_fast_tx_get(sdata, &key);
- if (!entry)
- return false;
-
---- a/net/mac80211/mesh.h
-+++ b/net/mac80211/mesh.h
-@@ -134,9 +134,33 @@ struct mesh_path {
- #define MESH_FAST_TX_CACHE_TIMEOUT 8000 /* msecs */
-
- /**
-+ * enum ieee80211_mesh_fast_tx_type - cached mesh fast tx entry type
-+ *
-+ * @MESH_FAST_TX_TYPE_LOCAL: tx from the local vif address as SA
-+ * @MESH_FAST_TX_TYPE_PROXIED: local tx with a different SA (e.g. bridged)
-+ * @MESH_FAST_TX_TYPE_FORWARDED: forwarded from a different mesh point
-+ */
-+enum ieee80211_mesh_fast_tx_type {
-+ MESH_FAST_TX_TYPE_LOCAL,
-+ MESH_FAST_TX_TYPE_PROXIED,
-+ MESH_FAST_TX_TYPE_FORWARDED,
-+};
-+
-+/**
-+ * struct ieee80211_mesh_fast_tx_key - cached mesh fast tx entry key
-+ *
-+ * @addr: The Ethernet DA for this entry
-+ * @type: cache entry type
-+ */
-+struct ieee80211_mesh_fast_tx_key {
-+ u8 addr[ETH_ALEN] __aligned(2);
-+ enum ieee80211_mesh_fast_tx_type type;
-+};
-+
-+/**
- * struct ieee80211_mesh_fast_tx - cached mesh fast tx entry
- * @rhash: rhashtable pointer
-- * @addr_key: The Ethernet DA which is the key for this entry
-+ * @key: the lookup key for this cache entry
- * @fast_tx: base fast_tx data
- * @hdr: cached mesh and rfc1042 headers
- * @hdrlen: length of mesh + rfc1042
-@@ -147,7 +171,7 @@ struct mesh_path {
- */
- struct ieee80211_mesh_fast_tx {
- struct rhash_head rhash;
-- u8 addr_key[ETH_ALEN] __aligned(2);
-+ struct ieee80211_mesh_fast_tx_key key;
-
- struct ieee80211_fast_tx fast_tx;
- u8 hdr[sizeof(struct ieee80211s_hdr) + sizeof(rfc1042_header)];
-@@ -333,7 +357,8 @@ void mesh_path_tx_root_frame(struct ieee
-
- bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
- struct ieee80211_mesh_fast_tx *
--mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr);
-+mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
-+ struct ieee80211_mesh_fast_tx_key *key);
- bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u32 ctrl_flags);
- void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
---- a/net/mac80211/mesh_pathtbl.c
-+++ b/net/mac80211/mesh_pathtbl.c
-@@ -36,8 +36,8 @@ static const struct rhashtable_params me
- static const struct rhashtable_params fast_tx_rht_params = {
- .nelem_hint = 10,
- .automatic_shrinking = true,
-- .key_len = ETH_ALEN,
-- .key_offset = offsetof(struct ieee80211_mesh_fast_tx, addr_key),
-+ .key_len = sizeof(struct ieee80211_mesh_fast_tx_key),
-+ .key_offset = offsetof(struct ieee80211_mesh_fast_tx, key),
- .head_offset = offsetof(struct ieee80211_mesh_fast_tx, rhash),
- .hashfn = mesh_table_hash,
- };
-@@ -426,20 +426,21 @@ static void mesh_fast_tx_entry_free(stru
- }
-
- struct ieee80211_mesh_fast_tx *
--mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr)
-+mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
-+ struct ieee80211_mesh_fast_tx_key *key)
- {
- struct ieee80211_mesh_fast_tx *entry;
- struct mesh_tx_cache *cache;
-
- cache = &sdata->u.mesh.tx_cache;
-- entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
-+ entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params);
- if (!entry)
- return NULL;
-
- if (!(entry->mpath->flags & MESH_PATH_ACTIVE) ||
- mpath_expired(entry->mpath)) {
- spin_lock_bh(&cache->walk_lock);
-- entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
-+ entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params);
- if (entry)
- mesh_fast_tx_entry_free(cache, entry);
- spin_unlock_bh(&cache->walk_lock);
-@@ -484,18 +485,24 @@ void mesh_fast_tx_cache(struct ieee80211
- if (!sta)
- return;
-
-+ build.key.type = MESH_FAST_TX_TYPE_LOCAL;
- if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) {
- /* This is required to keep the mppath alive */
- mppath = mpp_path_lookup(sdata, meshhdr->eaddr1);
- if (!mppath)
- return;
- build.mppath = mppath;
-+ if (!ether_addr_equal(meshhdr->eaddr2, sdata->vif.addr))
-+ build.key.type = MESH_FAST_TX_TYPE_PROXIED;
- } else if (ieee80211_has_a4(hdr->frame_control)) {
- mppath = mpath;
- } else {
- return;
- }
-
-+ if (!ether_addr_equal(hdr->addr4, sdata->vif.addr))
-+ build.key.type = MESH_FAST_TX_TYPE_FORWARDED;
-+
- /* rate limit, in case fast xmit can't be enabled */
- if (mppath->fast_tx_check == jiffies)
- return;
-@@ -542,7 +549,7 @@ void mesh_fast_tx_cache(struct ieee80211
- }
- }
-
-- memcpy(build.addr_key, mppath->dst, ETH_ALEN);
-+ memcpy(build.key.addr, mppath->dst, ETH_ALEN);
- build.timestamp = jiffies;
- build.fast_tx.band = info->band;
- build.fast_tx.da_offs = offsetof(struct ieee80211_hdr, addr3);
-@@ -644,13 +651,19 @@ void mesh_fast_tx_flush_addr(struct ieee
- const u8 *addr)
- {
- struct mesh_tx_cache *cache = &sdata->u.mesh.tx_cache;
-+ struct ieee80211_mesh_fast_tx_key key = {};
- struct ieee80211_mesh_fast_tx *entry;
-+ int i;
-
-+ ether_addr_copy(key.addr, addr);
- cache = &sdata->u.mesh.tx_cache;
- spin_lock_bh(&cache->walk_lock);
-- entry = rhashtable_lookup_fast(&cache->rht, addr, fast_tx_rht_params);
-- if (entry)
-- mesh_fast_tx_entry_free(cache, entry);
-+ for (i = MESH_FAST_TX_TYPE_LOCAL; i < MESH_FAST_TX_TYPE_FORWARDED; i++) {
-+ key.type = i;
-+ entry = rhashtable_lookup_fast(&cache->rht, &key, fast_tx_rht_params);
-+ if (entry)
-+ mesh_fast_tx_entry_free(cache, entry);
-+ }
- spin_unlock_bh(&cache->walk_lock);
- }
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2726,7 +2726,10 @@ ieee80211_rx_mesh_fast_forward(struct ie
- struct sk_buff *skb, int hdrlen)
- {
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-- struct ieee80211_mesh_fast_tx *entry = NULL;
-+ struct ieee80211_mesh_fast_tx_key key = {
-+ .type = MESH_FAST_TX_TYPE_FORWARDED
-+ };
-+ struct ieee80211_mesh_fast_tx *entry;
- struct ieee80211s_hdr *mesh_hdr;
- struct tid_ampdu_tx *tid_tx;
- struct sta_info *sta;
-@@ -2735,9 +2738,13 @@ ieee80211_rx_mesh_fast_forward(struct ie
-
- mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(eth));
- if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
-- entry = mesh_fast_tx_get(sdata, mesh_hdr->eaddr1);
-+ ether_addr_copy(key.addr, mesh_hdr->eaddr1);
- else if (!(mesh_hdr->flags & MESH_FLAGS_AE))
-- entry = mesh_fast_tx_get(sdata, skb->data);
-+ ether_addr_copy(key.addr, skb->data);
-+ else
-+ return false;
-+
-+ entry = mesh_fast_tx_get(sdata, &key);
- if (!entry)
- return false;
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/312-wifi-cfg80211-annotate-iftype_data-pointer-with-spar.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/312-wifi-cfg80211-annotate-iftype_data-pointer-with-spar.patch
deleted file mode 100644
index 91ebfc6..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/312-wifi-cfg80211-annotate-iftype_data-pointer-with-spar.patch
+++ /dev/null
@@ -1,467 +0,0 @@
-From: Johannes Berg <johannes.berg@intel.com>
-Date: Mon, 28 Aug 2023 09:54:39 +0200
-Subject: [PATCH] wifi: cfg80211: annotate iftype_data pointer with sparse
-
-There were are a number of cases in mac80211 and iwlwifi (at
-least) that used the sband->iftype_data pointer directly,
-instead of using the accessors to find the right array entry
-to use.
-
-Make sparse warn when such a thing is done.
-
-To not have a lot of casts, add two helper functions/macros
-
- - ieee80211_set_sband_iftype_data()
- - for_each_sband_iftype_data()
-
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/drivers/net/wireless/ath/ath11k/mac.c
-+++ b/drivers/net/wireless/ath/ath11k/mac.c
-@@ -5893,8 +5893,9 @@ static void ath11k_mac_setup_he_cap(stru
- ar->mac.iftype[NL80211_BAND_2GHZ],
- NL80211_BAND_2GHZ);
- band = &ar->mac.sbands[NL80211_BAND_2GHZ];
-- band->iftype_data = ar->mac.iftype[NL80211_BAND_2GHZ];
-- band->n_iftype_data = count;
-+ _ieee80211_set_sband_iftype_data(band,
-+ ar->mac.iftype[NL80211_BAND_2GHZ],
-+ count);
- }
-
- if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) {
-@@ -5902,8 +5903,9 @@ static void ath11k_mac_setup_he_cap(stru
- ar->mac.iftype[NL80211_BAND_5GHZ],
- NL80211_BAND_5GHZ);
- band = &ar->mac.sbands[NL80211_BAND_5GHZ];
-- band->iftype_data = ar->mac.iftype[NL80211_BAND_5GHZ];
-- band->n_iftype_data = count;
-+ _ieee80211_set_sband_iftype_data(band,
-+ ar->mac.iftype[NL80211_BAND_5GHZ],
-+ count);
- }
-
- if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP &&
-@@ -5912,8 +5914,9 @@ static void ath11k_mac_setup_he_cap(stru
- ar->mac.iftype[NL80211_BAND_6GHZ],
- NL80211_BAND_6GHZ);
- band = &ar->mac.sbands[NL80211_BAND_6GHZ];
-- band->iftype_data = ar->mac.iftype[NL80211_BAND_6GHZ];
-- band->n_iftype_data = count;
-+ _ieee80211_set_sband_iftype_data(band,
-+ ar->mac.iftype[NL80211_BAND_6GHZ],
-+ count);
- }
- }
-
---- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
-+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
-@@ -1078,8 +1078,8 @@ static void iwl_init_he_hw_capab(struct
-
- memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa));
-
-- sband->iftype_data = iftype_data;
-- sband->n_iftype_data = ARRAY_SIZE(iwl_he_eht_capa);
-+ _ieee80211_set_sband_iftype_data(sband, iftype_data,
-+ ARRAY_SIZE(iwl_he_eht_capa));
-
- for (i = 0; i < sband->n_iftype_data; i++)
- iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i],
---- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
-+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
-@@ -1127,8 +1127,7 @@ void mt7915_set_stream_he_caps(struct mt
- n = mt7915_init_he_caps(phy, NL80211_BAND_2GHZ, data);
-
- band = &phy->mt76->sband_2g.sband;
-- band->iftype_data = data;
-- band->n_iftype_data = n;
-+ _ieee80211_set_sband_iftype_data(band, data, n);
- }
-
- if (phy->mt76->cap.has_5ghz) {
-@@ -1136,8 +1135,7 @@ void mt7915_set_stream_he_caps(struct mt
- n = mt7915_init_he_caps(phy, NL80211_BAND_5GHZ, data);
-
- band = &phy->mt76->sband_5g.sband;
-- band->iftype_data = data;
-- band->n_iftype_data = n;
-+ _ieee80211_set_sband_iftype_data(band, data, n);
- }
-
- if (phy->mt76->cap.has_6ghz) {
-@@ -1145,8 +1143,7 @@ void mt7915_set_stream_he_caps(struct mt
- n = mt7915_init_he_caps(phy, NL80211_BAND_6GHZ, data);
-
- band = &phy->mt76->sband_6g.sband;
-- band->iftype_data = data;
-- band->n_iftype_data = n;
-+ _ieee80211_set_sband_iftype_data(band, data, n);
- }
- }
-
---- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
-+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
-@@ -196,8 +196,7 @@ void mt7921_set_stream_he_caps(struct mt
- n = mt7921_init_he_caps(phy, NL80211_BAND_2GHZ, data);
-
- band = &phy->mt76->sband_2g.sband;
-- band->iftype_data = data;
-- band->n_iftype_data = n;
-+ _ieee80211_set_sband_iftype_data(band, data, n);
- }
-
- if (phy->mt76->cap.has_5ghz) {
-@@ -205,16 +204,14 @@ void mt7921_set_stream_he_caps(struct mt
- n = mt7921_init_he_caps(phy, NL80211_BAND_5GHZ, data);
-
- band = &phy->mt76->sband_5g.sband;
-- band->iftype_data = data;
-- band->n_iftype_data = n;
-+ _ieee80211_set_sband_iftype_data(band, data, n);
-
- if (phy->mt76->cap.has_6ghz) {
- data = phy->iftype[NL80211_BAND_6GHZ];
- n = mt7921_init_he_caps(phy, NL80211_BAND_6GHZ, data);
-
- band = &phy->mt76->sband_6g.sband;
-- band->iftype_data = data;
-- band->n_iftype_data = n;
-+ _ieee80211_set_sband_iftype_data(band, data, n);
- }
- }
- }
---- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c
-+++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
-@@ -828,8 +828,7 @@ __mt7996_set_stream_he_eht_caps(struct m
- n++;
- }
-
-- sband->iftype_data = data;
-- sband->n_iftype_data = n;
-+ _ieee80211_set_sband_iftype_data(sband, data, n);
- }
-
- void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy)
---- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
-@@ -1335,7 +1335,7 @@ static int qtnf_cmd_band_fill_iftype(con
- return -EINVAL;
- }
-
-- kfree(band->iftype_data);
-+ kfree((__force void *)band->iftype_data);
- band->iftype_data = NULL;
- band->n_iftype_data = tlv->n_iftype_data;
- if (band->n_iftype_data == 0)
-@@ -1347,7 +1347,8 @@ static int qtnf_cmd_band_fill_iftype(con
- band->n_iftype_data = 0;
- return -ENOMEM;
- }
-- band->iftype_data = iftype_data;
-+
-+ _ieee80211_set_sband_iftype_data(band, iftype_data, tlv->n_iftype_data);
-
- for (i = 0; i < band->n_iftype_data; i++)
- qtnf_cmd_conv_iftype(iftype_data++, &tlv->iftype_data[i]);
---- a/drivers/net/wireless/quantenna/qtnfmac/core.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
-@@ -549,7 +549,7 @@ static void qtnf_core_mac_detach(struct
- if (!wiphy->bands[band])
- continue;
-
-- kfree(wiphy->bands[band]->iftype_data);
-+ kfree((__force void *)wiphy->bands[band]->iftype_data);
- wiphy->bands[band]->n_iftype_data = 0;
-
- kfree(wiphy->bands[band]->channels);
---- a/drivers/net/wireless/realtek/rtw89/core.c
-+++ b/drivers/net/wireless/realtek/rtw89/core.c
-@@ -3359,8 +3359,7 @@ static void rtw89_init_he_cap(struct rtw
- idx++;
- }
-
-- sband->iftype_data = iftype_data;
-- sband->n_iftype_data = idx;
-+ _ieee80211_set_sband_iftype_data(sband, iftype_data, idx);
- }
-
- static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev)
-@@ -3405,11 +3404,11 @@ err:
- hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
- hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL;
- if (sband_2ghz)
-- kfree(sband_2ghz->iftype_data);
-+ kfree((__force void *)sband_2ghz->iftype_data);
- if (sband_5ghz)
-- kfree(sband_5ghz->iftype_data);
-+ kfree((__force void *)sband_5ghz->iftype_data);
- if (sband_6ghz)
-- kfree(sband_6ghz->iftype_data);
-+ kfree((__force void *)sband_6ghz->iftype_data);
- kfree(sband_2ghz);
- kfree(sband_5ghz);
- kfree(sband_6ghz);
-@@ -3421,11 +3420,11 @@ static void rtw89_core_clr_supported_ban
- struct ieee80211_hw *hw = rtwdev->hw;
-
- if (hw->wiphy->bands[NL80211_BAND_2GHZ])
-- kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data);
-+ kfree((__force void *)hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data);
- if (hw->wiphy->bands[NL80211_BAND_5GHZ])
-- kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data);
-+ kfree((__force void *)hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data);
- if (hw->wiphy->bands[NL80211_BAND_6GHZ])
-- kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]->iftype_data);
-+ kfree((__force void *)hw->wiphy->bands[NL80211_BAND_6GHZ]->iftype_data);
- kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]);
- kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]);
- kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]);
---- a/drivers/net/wireless/realtek/rtw89/regd.c
-+++ b/drivers/net/wireless/realtek/rtw89/regd.c
-@@ -377,7 +377,7 @@ bottom:
- return;
-
- wiphy->bands[NL80211_BAND_6GHZ] = NULL;
-- kfree(sband->iftype_data);
-+ kfree((__force void *)sband->iftype_data);
- kfree(sband);
- }
-
---- a/drivers/net/wireless/virtual/mac80211_hwsim.c
-+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
-@@ -4899,25 +4899,19 @@ static const struct ieee80211_sband_ifty
-
- static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband)
- {
-- u16 n_iftype_data;
--
-- if (sband->band == NL80211_BAND_2GHZ) {
-- n_iftype_data = ARRAY_SIZE(sband_capa_2ghz);
-- sband->iftype_data =
-- (struct ieee80211_sband_iftype_data *)sband_capa_2ghz;
-- } else if (sband->band == NL80211_BAND_5GHZ) {
-- n_iftype_data = ARRAY_SIZE(sband_capa_5ghz);
-- sband->iftype_data =
-- (struct ieee80211_sband_iftype_data *)sband_capa_5ghz;
-- } else if (sband->band == NL80211_BAND_6GHZ) {
-- n_iftype_data = ARRAY_SIZE(sband_capa_6ghz);
-- sband->iftype_data =
-- (struct ieee80211_sband_iftype_data *)sband_capa_6ghz;
-- } else {
-- return;
-+ switch (sband->band) {
-+ case NL80211_BAND_2GHZ:
-+ ieee80211_set_sband_iftype_data(sband, sband_capa_2ghz);
-+ break;
-+ case NL80211_BAND_5GHZ:
-+ ieee80211_set_sband_iftype_data(sband, sband_capa_5ghz);
-+ break;
-+ case NL80211_BAND_6GHZ:
-+ ieee80211_set_sband_iftype_data(sband, sband_capa_6ghz);
-+ break;
-+ default:
-+ break;
- }
--
-- sband->n_iftype_data = n_iftype_data;
- }
-
- #ifdef CPTCFG_MAC80211_MESH
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -415,6 +415,19 @@ struct ieee80211_sta_eht_cap {
- u8 eht_ppe_thres[IEEE80211_EHT_PPE_THRES_MAX_LEN];
- };
-
-+/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */
-+#ifdef __CHECKER__
-+/*
-+ * This is used to mark the sband->iftype_data pointer which is supposed
-+ * to be an array with special access semantics (per iftype), but a lot
-+ * of code got it wrong in the past, so with this marking sparse will be
-+ * noisy when the pointer is used directly.
-+ */
-+# define __iftd __attribute__((noderef, address_space(__iftype_data)))
-+#else
-+# define __iftd
-+#endif /* __CHECKER__ */
-+
- /**
- * struct ieee80211_sband_iftype_data - sband data per interface type
- *
-@@ -548,10 +561,48 @@ struct ieee80211_supported_band {
- struct ieee80211_sta_s1g_cap s1g_cap;
- struct ieee80211_edmg edmg_cap;
- u16 n_iftype_data;
-- const struct ieee80211_sband_iftype_data *iftype_data;
-+ const struct ieee80211_sband_iftype_data __iftd *iftype_data;
- };
-
- /**
-+ * _ieee80211_set_sband_iftype_data - set sband iftype data array
-+ * @sband: the sband to initialize
-+ * @iftd: the iftype data array pointer
-+ * @n_iftd: the length of the iftype data array
-+ *
-+ * Set the sband iftype data array; use this where the length cannot
-+ * be derived from the ARRAY_SIZE() of the argument, but prefer
-+ * ieee80211_set_sband_iftype_data() where it can be used.
-+ */
-+static inline void
-+_ieee80211_set_sband_iftype_data(struct ieee80211_supported_band *sband,
-+ const struct ieee80211_sband_iftype_data *iftd,
-+ u16 n_iftd)
-+{
-+ sband->iftype_data = (const void __iftd __force *)iftd;
-+ sband->n_iftype_data = n_iftd;
-+}
-+
-+/**
-+ * ieee80211_set_sband_iftype_data - set sband iftype data array
-+ * @sband: the sband to initialize
-+ * @iftd: the iftype data array
-+ */
-+#define ieee80211_set_sband_iftype_data(sband, iftd) \
-+ _ieee80211_set_sband_iftype_data(sband, iftd, ARRAY_SIZE(iftd))
-+
-+/**
-+ * for_each_sband_iftype_data - iterate sband iftype data entries
-+ * @sband: the sband whose iftype_data array to iterate
-+ * @i: iterator counter
-+ * @iftd: iftype data pointer to set
-+ */
-+#define for_each_sband_iftype_data(sband, i, iftd) \
-+ for (i = 0, iftd = (const void __force *)&(sband)->iftype_data[i]; \
-+ i < (sband)->n_iftype_data; \
-+ i++, iftd = (const void __force *)&(sband)->iftype_data[i])
-+
-+/**
- * ieee80211_get_sband_iftype_data - return sband data for a given iftype
- * @sband: the sband to search for the STA on
- * @iftype: enum nl80211_iftype
-@@ -562,6 +613,7 @@ static inline const struct ieee80211_sba
- ieee80211_get_sband_iftype_data(const struct ieee80211_supported_band *sband,
- u8 iftype)
- {
-+ const struct ieee80211_sband_iftype_data *data;
- int i;
-
- if (WARN_ON(iftype >= NL80211_IFTYPE_MAX))
-@@ -570,10 +622,7 @@ ieee80211_get_sband_iftype_data(const st
- if (iftype == NL80211_IFTYPE_AP_VLAN)
- iftype = NL80211_IFTYPE_AP;
-
-- for (i = 0; i < sband->n_iftype_data; i++) {
-- const struct ieee80211_sband_iftype_data *data =
-- &sband->iftype_data[i];
--
-+ for_each_sband_iftype_data(sband, i, data) {
- if (data->types_mask & BIT(iftype))
- return data;
- }
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -1052,6 +1052,7 @@ int ieee80211_register_hw(struct ieee802
- supp_he = false;
- supp_eht = false;
- for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+ const struct ieee80211_sband_iftype_data *iftd;
- struct ieee80211_supported_band *sband;
-
- sband = local->hw.wiphy->bands[band];
-@@ -1098,11 +1099,7 @@ int ieee80211_register_hw(struct ieee802
- supp_ht = supp_ht || sband->ht_cap.ht_supported;
- supp_vht = supp_vht || sband->vht_cap.vht_supported;
-
-- for (i = 0; i < sband->n_iftype_data; i++) {
-- const struct ieee80211_sband_iftype_data *iftd;
--
-- iftd = &sband->iftype_data[i];
--
-+ for_each_sband_iftype_data(sband, i, iftd) {
- supp_he = supp_he || iftd->he_cap.has_he;
- supp_eht = supp_eht || iftd->eht_cap.has_eht;
- }
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -6,7 +6,7 @@
- *
- * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2013-2014 Intel Mobile Communications GmbH
-- * Copyright 2018-2022 Intel Corporation
-+ * Copyright 2018-2023 Intel Corporation
- */
-
- #include <linux/export.h>
-@@ -1162,8 +1162,7 @@ bool cfg80211_chandef_usable(struct wiph
- if (!sband)
- return false;
-
-- for (i = 0; i < sband->n_iftype_data; i++) {
-- iftd = &sband->iftype_data[i];
-+ for_each_sband_iftype_data(sband, i, iftd) {
- if (!iftd->eht_cap.has_eht)
- continue;
-
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -5,7 +5,7 @@
- * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2013-2014 Intel Mobile Communications GmbH
- * Copyright 2015-2017 Intel Deutschland GmbH
-- * Copyright (C) 2018-2022 Intel Corporation
-+ * Copyright (C) 2018-2023 Intel Corporation
- */
-
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-@@ -819,6 +819,7 @@ int wiphy_register(struct wiphy *wiphy)
-
- /* sanity check supported bands/channels */
- for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+ const struct ieee80211_sband_iftype_data *iftd;
- u16 types = 0;
- bool have_he = false;
-
-@@ -875,14 +876,11 @@ int wiphy_register(struct wiphy *wiphy)
- return -EINVAL;
- }
-
-- for (i = 0; i < sband->n_iftype_data; i++) {
-- const struct ieee80211_sband_iftype_data *iftd;
-+ for_each_sband_iftype_data(sband, i, iftd) {
- bool has_ap, has_non_ap;
- u32 ap_bits = BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_GO);
-
-- iftd = &sband->iftype_data[i];
--
- if (WARN_ON(!iftd->types_mask))
- return -EINVAL;
- if (WARN_ON(types & iftd->types_mask))
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -1907,20 +1907,20 @@ static int nl80211_send_band_rateinfo(st
- struct nlattr *nl_iftype_data =
- nla_nest_start_noflag(msg,
- NL80211_BAND_ATTR_IFTYPE_DATA);
-+ const struct ieee80211_sband_iftype_data *iftd;
- int err;
-
- if (!nl_iftype_data)
- return -ENOBUFS;
-
-- for (i = 0; i < sband->n_iftype_data; i++) {
-+ for_each_sband_iftype_data(sband, i, iftd) {
- struct nlattr *iftdata;
-
- iftdata = nla_nest_start_noflag(msg, i + 1);
- if (!iftdata)
- return -ENOBUFS;
-
-- err = nl80211_send_iftype_data(msg, sband,
-- &sband->iftype_data[i]);
-+ err = nl80211_send_iftype_data(msg, sband, iftd);
- if (err)
- return err;
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/313-wifi-cfg80211-export-DFS-CAC-time-and-usable-state-h.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/313-wifi-cfg80211-export-DFS-CAC-time-and-usable-state-h.patch
deleted file mode 100644
index a41a906..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/313-wifi-cfg80211-export-DFS-CAC-time-and-usable-state-h.patch
+++ /dev/null
@@ -1,111 +0,0 @@
-From 30ca8b0c4d6c9fb1d76e5894b1e8bf7c6a12224d Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Tue, 12 Sep 2023 10:48:55 +0530
-Subject: [PATCH] wifi: cfg80211: export DFS CAC time and usable state helper
- functions
-
-cfg80211 has cfg80211_chandef_dfs_usable() function to know whether
-at least one channel in the chandef is in usable state or not. Also,
-cfg80211_chandef_dfs_cac_time() function is there which tells the CAC
-time required for the given chandef.
-
-Make these two functions visible to drivers by exporting their symbol
-to global list of kernel symbols.
-
-Lower level drivers can make use of these two functions to be aware
-if CAC is required on the given chandef and for how long. For example
-drivers which maintains the CAC state internally can make use of these.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Reviewed-by: Jeff Johnson <quic_jjohnson@quicinc.com>
-Link: https://lore.kernel.org/r/20230912051857.2284-2-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
- include/net/cfg80211.h | 24 ++++++++++++++++++++++++
- net/wireless/chan.c | 2 ++
- net/wireless/core.h | 17 -----------------
- 3 files changed, 26 insertions(+), 17 deletions(-)
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -1008,6 +1008,30 @@ int cfg80211_chandef_dfs_required(struct
- enum nl80211_iftype iftype);
-
- /**
-+ * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable and we
-+ * can/need start CAC on such channel
-+ * @wiphy: the wiphy to validate against
-+ * @chandef: the channel definition to check
-+ *
-+ * Return: true if all channels available and at least
-+ * one channel requires CAC (NL80211_DFS_USABLE)
-+ */
-+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef);
-+
-+/**
-+ * cfg80211_chandef_dfs_cac_time - get the DFS CAC time (in ms) for given
-+ * channel definition
-+ * @wiphy: the wiphy to validate against
-+ * @chandef: the channel definition to check
-+ *
-+ * Returns: DFS CAC time (in ms) which applies for this channel definition
-+ */
-+unsigned int
-+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef);
-+
-+/**
- * nl80211_send_chandef - sends the channel definition.
- * @msg: the msg to send channel definition
- * @chandef: the channel definition to check
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -666,6 +666,7 @@ bool cfg80211_chandef_dfs_usable(struct
-
- return (r1 + r2 > 0);
- }
-+EXPORT_SYMBOL(cfg80211_chandef_dfs_usable);
-
- /*
- * Checks if center frequency of chan falls with in the bandwidth
-@@ -965,6 +966,7 @@ cfg80211_chandef_dfs_cac_time(struct wip
-
- return max(t1, t2);
- }
-+EXPORT_SYMBOL(cfg80211_chandef_dfs_cac_time);
-
- static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
- u32 center_freq, u32 bandwidth,
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -476,29 +476,12 @@ int cfg80211_scan(struct cfg80211_regist
-
- extern struct work_struct cfg80211_disconnect_work;
-
--/**
-- * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
-- * @wiphy: the wiphy to validate against
-- * @chandef: the channel definition to check
-- *
-- * Checks if chandef is usable and we can/need start CAC on such channel.
-- *
-- * Return: true if all channels available and at least
-- * one channel requires CAC (NL80211_DFS_USABLE)
-- */
--bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
-- const struct cfg80211_chan_def *chandef);
--
- void cfg80211_set_dfs_state(struct wiphy *wiphy,
- const struct cfg80211_chan_def *chandef,
- enum nl80211_dfs_state dfs_state);
-
- void cfg80211_dfs_channels_update_work(struct work_struct *work);
-
--unsigned int
--cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
-- const struct cfg80211_chan_def *chandef);
--
- void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
-
- int
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/314-wifi-mac80211-fix-race-condition-on-enabling-fast-xm.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/314-wifi-mac80211-fix-race-condition-on-enabling-fast-xm.patch
deleted file mode 100644
index 12ed214..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/314-wifi-mac80211-fix-race-condition-on-enabling-fast-xm.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 3 Jan 2024 15:10:18 +0100
-Subject: [PATCH] wifi: mac80211: fix race condition on enabling fast-xmit
-
-fast-xmit must only be enabled after the sta has been uploaded to the driver,
-otherwise it could end up passing the not-yet-uploaded sta via drv_tx calls
-to the driver, leading to potential crashes because of uninitialized drv_priv
-data.
-Add a missing sta->uploaded check and re-check fast xmit after inserting a sta.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -914,6 +914,7 @@ static int sta_info_insert_finish(struct
-
- if (ieee80211_vif_is_mesh(&sdata->vif))
- mesh_accept_plinks_update(sdata);
-+ ieee80211_check_fast_xmit(sta);
-
- return 0;
- out_remove:
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3034,7 +3034,7 @@ void ieee80211_check_fast_xmit(struct st
- sdata->vif.type == NL80211_IFTYPE_STATION)
- goto out;
-
-- if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
-+ if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED) || !sta->uploaded)
- goto out;
-
- if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/320-cfg80211-allow-grace-period-for-DFS-available-after-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/320-cfg80211-allow-grace-period-for-DFS-available-after-.patch
deleted file mode 100644
index ec38130..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/320-cfg80211-allow-grace-period-for-DFS-available-after-.patch
+++ /dev/null
@@ -1,149 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 14 Sep 2023 13:17:16 +0200
-Subject: [PATCH] cfg80211: allow grace period for DFS available after beacon
- shutdown
-
-Fixes reconfiguring an AP on a DFS channel in non-ETSI regdomain
-
-Fixes: b35a51c7dd25 ("cfg80211: Make pre-CAC results valid only for ETSI domain")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -175,6 +175,8 @@ enum ieee80211_channel_flags {
- * @dfs_state: current state of this channel. Only relevant if radar is required
- * on this channel.
- * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
-+ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
-+ * channel was available.
- * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
- */
- struct ieee80211_channel {
-@@ -191,6 +193,7 @@ struct ieee80211_channel {
- int orig_mag, orig_mpwr;
- enum nl80211_dfs_state dfs_state;
- unsigned long dfs_state_entered;
-+ unsigned long dfs_state_last_available;
- unsigned int dfs_cac_ms;
- };
-
---- a/net/wireless/ap.c
-+++ b/net/wireless/ap.c
-@@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cf
- if (!wdev->links[link_id].ap.beacon_interval)
- return -ENOENT;
-
-+ cfg80211_update_last_available(wdev->wiphy,
-+ &wdev->links[link_id].ap.chandef);
-+
- err = rdev_stop_ap(rdev, dev, link_id);
- if (!err) {
- wdev->conn_owner_nlportid = 0;
-@@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cf
- if (notify)
- nl80211_send_ap_stopped(wdev, link_id);
-
-- /* Should we apply the grace period during beaconing interface
-- * shutdown also?
-- */
- cfg80211_sched_dfs_chan_update(rdev);
- }
-
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -461,6 +461,8 @@ static void cfg80211_set_chans_dfs_state
-
- c->dfs_state = dfs_state;
- c->dfs_state_entered = jiffies;
-+ if (dfs_state == NL80211_DFS_AVAILABLE)
-+ c->dfs_state_last_available = jiffies;
- }
- }
-
-@@ -874,6 +876,49 @@ static bool cfg80211_get_chans_dfs_avail
- return true;
- }
-
-+static void
-+__cfg80211_update_last_available(struct wiphy *wiphy,
-+ u32 center_freq,
-+ u32 bandwidth)
-+{
-+ struct ieee80211_channel *c;
-+ u32 freq, start_freq, end_freq;
-+
-+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
-+
-+ /*
-+ * Check entire range of channels for the bandwidth.
-+ * If any channel in between is disabled or has not
-+ * had gone through CAC return false
-+ */
-+ for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-+ c = ieee80211_get_channel_khz(wiphy, freq);
-+ if (!c)
-+ return;
-+
-+ c->dfs_state_last_available = jiffies;
-+ }
-+}
-+
-+void cfg80211_update_last_available(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef)
-+{
-+ int width;
-+
-+ width = cfg80211_chandef_get_width(chandef);
-+ if (width < 0)
-+ return;
-+
-+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
-+ width);
-+ if (chandef->width != NL80211_CHAN_WIDTH_80P80)
-+ return;
-+
-+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
-+ width);
-+}
-+
- static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
- const struct cfg80211_chan_def *chandef)
- {
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -481,6 +481,8 @@ void cfg80211_set_dfs_state(struct wiphy
- enum nl80211_dfs_state dfs_state);
-
- void cfg80211_dfs_channels_update_work(struct work_struct *work);
-+void cfg80211_update_last_available(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef);
-
- void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
-
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -930,6 +930,8 @@ void cfg80211_dfs_channels_update_work(s
- if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
- time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
- radar_event = NL80211_RADAR_NOP_FINISHED;
-+ timeout = c->dfs_state_entered +
-+ msecs_to_jiffies(time_dfs_update);
- } else {
- if (regulatory_pre_cac_allowed(wiphy) ||
- cfg80211_any_wiphy_oper_chan(wiphy, c))
-@@ -937,11 +939,10 @@ void cfg80211_dfs_channels_update_work(s
-
- time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
- radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
-+ timeout = c->dfs_state_last_available +
-+ msecs_to_jiffies(time_dfs_update);
- }
-
-- timeout = c->dfs_state_entered +
-- msecs_to_jiffies(time_dfs_update);
--
- if (time_after_eq(jiffies, timeout)) {
- c->dfs_state = NL80211_DFS_USABLE;
- c->dfs_state_entered = jiffies;
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/330-mac80211-add-AQL-support-for-broadcast-packets.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/330-mac80211-add-AQL-support-for-broadcast-packets.patch
deleted file mode 100644
index d53afcf..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/330-mac80211-add-AQL-support-for-broadcast-packets.patch
+++ /dev/null
@@ -1,302 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 9 Feb 2024 19:43:40 +0100
-Subject: [PATCH] mac80211: add AQL support for broadcast packets
-
-Excessive broadcast traffic with little competing unicast traffic can easily
-flood hardware queues, leading to throughput issues. Additionally, filling
-the hardware queues with too many packets breaks FQ for broadcast data.
-Fix this by enabling AQL for broadcast packets.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -3324,6 +3324,7 @@ enum wiphy_params_flags {
- /* The per TXQ device queue limit in airtime */
- #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000
- #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000
-+#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC 50000
-
- /* The per interface airtime threshold to switch to lower queue limit */
- #define IEEE80211_AQL_THRESHOLD 24000
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct f
- "VI %u us\n"
- "BE %u us\n"
- "BK %u us\n"
-+ "BC/MC %u us\n"
- "total %u us\n",
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
-+ atomic_read(&local->aql_bc_pending_airtime),
- atomic_read(&local->aql_total_pending_airtime));
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, len);
-@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct
- "VO %u %u\n"
- "VI %u %u\n"
- "BE %u %u\n"
-- "BK %u %u\n",
-+ "BK %u %u\n"
-+ "BC/MC %u\n",
- local->aql_txq_limit_low[IEEE80211_AC_VO],
- local->aql_txq_limit_high[IEEE80211_AC_VO],
- local->aql_txq_limit_low[IEEE80211_AC_VI],
-@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct
- local->aql_txq_limit_low[IEEE80211_AC_BE],
- local->aql_txq_limit_high[IEEE80211_AC_BE],
- local->aql_txq_limit_low[IEEE80211_AC_BK],
-- local->aql_txq_limit_high[IEEE80211_AC_BK]);
-+ local->aql_txq_limit_high[IEEE80211_AC_BK],
-+ local->aql_txq_limit_bc);
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, len);
- }
-@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struc
- else
- buf[count] = '\0';
-
-+ if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
-+ local->aql_txq_limit_bc = q_limit_low;
-+ return count;
-+ }
-+
- if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
- return -EINVAL;
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -1328,10 +1328,12 @@ struct ieee80211_local {
- spinlock_t handle_wake_tx_queue_lock;
-
- u16 airtime_flags;
-+ u32 aql_txq_limit_bc;
- u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
- u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
- u32 aql_threshold;
- atomic_t aql_total_pending_airtime;
-+ atomic_t aql_bc_pending_airtime;
- atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
-
- const struct ieee80211_ops *ops;
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -788,6 +788,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- spin_lock_init(&local->rx_path_lock);
- spin_lock_init(&local->queue_stop_reason_lock);
-
-+ local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- INIT_LIST_HEAD(&local->active_txqs[i]);
- spin_lock_init(&local->active_txq_lock[i]);
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -2343,13 +2343,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre
-
- void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
- struct sta_info *sta, u8 ac,
-- u16 tx_airtime, bool tx_completed)
-+ u16 tx_airtime, bool tx_completed,
-+ bool mcast)
- {
- int tx_pending;
-
- if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
- return;
-
-+ if (mcast) {
-+ if (!tx_completed) {
-+ atomic_add(tx_airtime, &local->aql_bc_pending_airtime);
-+ return;
-+ }
-+
-+ tx_pending = atomic_sub_return(tx_airtime,
-+ &local->aql_bc_pending_airtime);
-+ if (tx_pending < 0)
-+ atomic_cmpxchg(&local->aql_bc_pending_airtime,
-+ tx_pending, 0);
-+ return;
-+ }
-+
- if (!tx_completed) {
- if (sta)
- atomic_add(tx_airtime,
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -2536,7 +2536,7 @@ static u16 ieee80211_store_ack_skb(struc
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- id = idr_alloc(&local->ack_status_frames, ack_skb,
-- 1, 0x2000, GFP_ATOMIC);
-+ 1, 0x1000, GFP_ATOMIC);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- if (id >= 0) {
-@@ -3958,20 +3958,20 @@ begin:
- encap_out:
- IEEE80211_SKB_CB(skb)->control.vif = vif;
-
-- if (tx.sta &&
-- wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
-- bool ampdu = txq->ac != IEEE80211_AC_VO;
-+ if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
-+ bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
- u32 airtime;
-
- airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
- skb->len, ampdu);
-- if (airtime) {
-- airtime = ieee80211_info_set_tx_time_est(info, airtime);
-- ieee80211_sta_update_pending_airtime(local, tx.sta,
-- txq->ac,
-- airtime,
-- false);
-- }
-+ if (!airtime)
-+ return skb;
-+
-+ airtime = ieee80211_info_set_tx_time_est(info, airtime);
-+ info->tx_time_mc = !tx.sta;
-+ ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac,
-+ airtime, false,
-+ info->tx_time_mc);
- }
-
- return skb;
-@@ -4026,6 +4026,7 @@ struct ieee80211_txq *ieee80211_next_txq
- struct ieee80211_txq *ret = NULL;
- struct txq_info *txqi = NULL, *head = NULL;
- bool found_eligible_txq = false;
-+ bool aql_check;
-
- spin_lock_bh(&local->active_txq_lock[ac]);
-
-@@ -4049,26 +4050,26 @@ struct ieee80211_txq *ieee80211_next_txq
- if (!head)
- head = txqi;
-
-+ aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-+ if (aql_check)
-+ found_eligible_txq = true;
-+
- if (txqi->txq.sta) {
- struct sta_info *sta = container_of(txqi->txq.sta,
- struct sta_info, sta);
-- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-- s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
--
-- if (aql_check)
-- found_eligible_txq = true;
--
-- if (deficit < 0)
-+ if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
- sta->airtime[txqi->txq.ac].deficit +=
- sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
--
-- if (deficit < 0 || !aql_check) {
-- list_move_tail(&txqi->schedule_order,
-- &local->active_txqs[txqi->txq.ac]);
-- goto begin;
-+ aql_check = false;
- }
- }
-
-+ if (!aql_check) {
-+ list_move_tail(&txqi->schedule_order,
-+ &local->active_txqs[txqi->txq.ac]);
-+ goto begin;
-+ }
-+
- if (txqi->schedule_round == local->schedule_round[ac])
- goto out;
-
-@@ -4133,7 +4134,8 @@ bool ieee80211_txq_airtime_check(struct
- return true;
-
- if (!txq->sta)
-- return true;
-+ return atomic_read(&local->aql_bc_pending_airtime) <
-+ local->aql_txq_limit_bc;
-
- if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
- return true;
-@@ -4182,15 +4184,15 @@ bool ieee80211_txq_may_transmit(struct i
-
- spin_lock_bh(&local->active_txq_lock[ac]);
-
-- if (!txqi->txq.sta)
-- goto out;
--
- if (list_empty(&txqi->schedule_order))
- goto out;
-
- if (!ieee80211_txq_schedule_airtime_check(local, ac))
- goto out;
-
-+ if (!txqi->txq.sta)
-+ goto out;
-+
- list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
- schedule_order) {
- if (iter == txqi)
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1116,6 +1116,7 @@ ieee80211_rate_get_vht_nss(const struct
- * link the frame will be transmitted on
- * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC
- * @ack_frame_id: internal frame ID for TX status, used internally
-+ * @tx_time_mc: TX time is for a multicast packet
- * @tx_time_est: TX time estimate in units of 4us, used internally
- * @control: union part for control data
- * @control.rates: TX rates array to try
-@@ -1155,8 +1156,9 @@ struct ieee80211_tx_info {
- /* common information */
- u32 flags;
- u32 band:3,
-- ack_frame_id:13,
-+ ack_frame_id:12,
- hw_queue:4,
-+ tx_time_mc:1,
- tx_time_est:10;
- /* 2 free bits */
-
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -147,7 +147,8 @@ struct airtime_info {
-
- void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
- struct sta_info *sta, u8 ac,
-- u16 tx_airtime, bool tx_completed);
-+ u16 tx_airtime, bool tx_completed,
-+ bool mcast);
-
- struct sta_info;
-
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -716,7 +716,7 @@ static void ieee80211_report_used_skb(st
- ieee80211_sta_update_pending_airtime(local, sta,
- skb_get_queue_mapping(skb),
- tx_time_est,
-- true);
-+ true, info->tx_time_mc);
- rcu_read_unlock();
- }
-
-@@ -1127,10 +1127,11 @@ void ieee80211_tx_status_ext(struct ieee
- /* Do this here to avoid the expensive lookup of the sta
- * in ieee80211_report_used_skb().
- */
-+ bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc;
- ieee80211_sta_update_pending_airtime(local, sta,
- skb_get_queue_mapping(skb),
- tx_time_est,
-- true);
-+ true, mcast);
- ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
- }
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/331-wifi-mac80211-only-call-drv_sta_rc_update-for-upload.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/331-wifi-mac80211-only-call-drv_sta_rc_update-for-upload.patch
deleted file mode 100644
index 167b9e3..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/331-wifi-mac80211-only-call-drv_sta_rc_update-for-upload.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 21 Feb 2024 14:41:40 +0100
-Subject: [PATCH] wifi: mac80211: only call drv_sta_rc_update for uploaded
- stations
-
-When a station has not been uploaded yet, receiving SMPS or channel width
-notification action frames can lead to rate_control_rate_update calling
-drv_sta_rc_update with uninitialized driver private data.
-Fix this by adding a missing check for sta->uploaded.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rate.c
-+++ b/net/mac80211/rate.c
-@@ -119,7 +119,8 @@ void rate_control_rate_update(struct iee
- rcu_read_unlock();
- }
-
-- drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
-+ if (sta->uploaded)
-+ drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
- }
-
- int ieee80211_rate_control_register(const struct rate_control_ops *ops)
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/332-wifi-mac80211-check-clear-fast-rx-for-non-4addr-sta-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/332-wifi-mac80211-check-clear-fast-rx-for-non-4addr-sta-.patch
deleted file mode 100644
index 02b4345..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/332-wifi-mac80211-check-clear-fast-rx-for-non-4addr-sta-.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 16 Mar 2024 08:37:21 +0100
-Subject: [PATCH] wifi: mac80211: check/clear fast rx for non-4addr sta VLAN
- changes
-
-When moving a station out of a VLAN and deleting the VLAN afterwards, the
-fast_rx entry still holds a pointer to the VLAN's netdev, which can cause
-use-after-free bugs. Fix this by immediately calling ieee80211_check_fast_rx
-after the VLAN change.
-
-Cc: stable@vger.kernel.org
-Reported-by: ranygh@riseup.net
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -2184,15 +2184,14 @@ static int ieee80211_change_station(stru
- }
-
- if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
-- sta->sdata->u.vlan.sta) {
-- ieee80211_clear_fast_rx(sta);
-+ sta->sdata->u.vlan.sta)
- RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
-- }
-
- if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
- ieee80211_vif_dec_num_mcast(sta->sdata);
-
- sta->sdata = vlansdata;
-+ ieee80211_check_fast_rx(sta);
- ieee80211_check_fast_xmit(sta);
-
- if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/400-allow-ibss-mixed.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/400-allow-ibss-mixed.patch
deleted file mode 100644
index 7a96f9f..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/400-allow-ibss-mixed.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From: Hauke Mehrtens <hauke@hauke-m.de>
-Date: Mon, 24 Feb 2020 00:00:00 +0100
-Subject: [PATCH] mac80211: Allow IBSS mode and different beacon intervals
-
-ath10k-ct supports the combination to select IBSS (ADHOC) mode and
-different beacon intervals together. mac80211 does not like this
-combination, but Ben says this is ok, so remove this check.
-
-ath10k-ct starting with version 5.2 allows the combination of
-NL80211_IFTYPE_ADHOC and beacon_int_min_gcd in ath10k_10x_ct_if_comb
-which triggers this warning. Ben told me that this is not a big problem
-and we should ignore this.
----
- net/wireless/core.c | 15 ---------------
- 1 file changed, 15 deletions(-)
-
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -651,21 +651,6 @@ static int wiphy_verify_combinations(str
- c->limits[j].max > 1))
- return -EINVAL;
-
-- /*
-- * This isn't well-defined right now. If you have an
-- * IBSS interface, then its beacon interval may change
-- * by joining other networks, and nothing prevents it
-- * from doing that.
-- * So technically we probably shouldn't even allow AP
-- * and IBSS in the same interface, but it seems that
-- * some drivers support that, possibly only with fixed
-- * beacon intervals for IBSS.
-- */
-- if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
-- c->beacon_int_min_gcd)) {
-- return -EINVAL;
-- }
--
- cnt += c->limits[j].max;
- /*
- * Don't advertise an unsupported type
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/780-avoid-crashing-missing-band.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/780-avoid-crashing-missing-band.patch
deleted file mode 100644
index 35fa961..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/780-avoid-crashing-missing-band.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From: David Bauer <mail@david-bauer.net>
-Date: Thu, 30 Nov 2023 07:32:52 +0100
-Subject: [PATCH] mac80211: avoid crashing on invalid band info
-
-Frequent crashes have been observed on MT7916 based platforms. While the
-root of these crashes are currently unknown, they happen when decoding
-rate information of connected STAs in AP mode. The rate-information is
-associated with a band which is not available on the PHY.
-
-Check for this condition in order to avoid crashing the whole system.
-This patch should be removed once the roout cause has been found and
-fixed.
-
-Link: https://github.com/freifunk-gluon/gluon/issues/2980
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
-
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -2445,6 +2445,13 @@ static void sta_stats_decode_rate(struct
-
- sband = local->hw.wiphy->bands[band];
-
-+ if (!sband) {
-+ wiphy_warn(local->hw.wiphy,
-+ "Invalid band %d\n",
-+ band);
-+ break;
-+ }
-+
- if (WARN_ON_ONCE(!sband->bitrates))
- break;
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch
deleted file mode 100644
index 26af6a2..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch
+++ /dev/null
@@ -1,29 +0,0 @@
---- a/backport-include/linux/of_net.h
-+++ /dev/null
-@@ -1,26 +0,0 @@
--#ifndef _BP_OF_NET_H
--#define _BP_OF_NET_H
--#include_next <linux/of_net.h>
--#include <linux/version.h>
--#include <linux/etherdevice.h>
--
--/* The behavior of of_get_mac_address() changed in kernel 5.2, it now
-- * returns an error code and not NULL in case of an error.
-- */
--#if LINUX_VERSION_IS_LESS(5,13,0)
--static inline int backport_of_get_mac_address(struct device_node *np, u8 *mac_out)
--{
-- const void *mac = of_get_mac_address(np);
--
-- if (!mac)
-- return -ENODEV;
-- if (IS_ERR(mac))
-- return PTR_ERR(mac);
-- ether_addr_copy(mac_out, mac);
--
-- return 0;
--}
--#define of_get_mac_address LINUX_BACKPORT(of_get_mac_address)
--#endif /* < 5.2 */
--
--#endif /* _BP_OF_NET_H */
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0001-backports-add-SKB_DROP_REASON-and-kfree_skb_reason-b.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0001-backports-add-SKB_DROP_REASON-and-kfree_skb_reason-b.patch
deleted file mode 100644
index 46f1c4d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0001-backports-add-SKB_DROP_REASON-and-kfree_skb_reason-b.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 0d65414c2ec0772539b728d075aab23b026b553b Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 23 Feb 2024 09:10:09 +0800
-Subject: [PATCH 1/3] backports: add SKB_DROP_REASON and kfree_skb_reason
- backport on Kernel 5.4
-
----
- backport-include/linux/skbuff.h | 8 ++++++++
- backport-include/net/dropreason-core.h | 6 ++++++
- 2 files changed, 14 insertions(+)
-
-diff --git a/backport-include/linux/skbuff.h b/backport-include/linux/skbuff.h
-index c1592c2..9e23251 100644
---- a/backport-include/linux/skbuff.h
-+++ b/backport-include/linux/skbuff.h
-@@ -144,4 +144,12 @@ static inline u64 skb_get_kcov_handle(struct sk_buff *skb)
- #define napi_build_skb build_skb
- #endif
-
-+#if LINUX_VERSION_IS_LESS(6,0,0)
-+#define kfree_skb_reason LINUX_BACKPORT(kfree_skb_reason)
-+static inline void kfree_skb_reason(struct sk_buff *skb, int reason)
-+{
-+ return kfree_skb(skb);
-+}
-+#endif
-+
- #endif /* __BACKPORT_SKBUFF_H */
-diff --git a/backport-include/net/dropreason-core.h b/backport-include/net/dropreason-core.h
-index ab8532e..31c60b9 100644
---- a/backport-include/net/dropreason-core.h
-+++ b/backport-include/net/dropreason-core.h
-@@ -13,6 +13,12 @@
-
- #include <linux/version.h>
-
-+/* backport for 5.4 */
-+#if LINUX_VERSION_IS_LESS(5,5,0)
-+#define SKB_DROP_REASON_NOT_SPECIFIED 2
-+#define SKB_DROP_REASON_MAX 69
-+#endif
-+
- #if LINUX_VERSION_IS_LESS(5,18,0)
- #define SKB_NOT_DROPPED_YET SKB_DROP_REASON_MAX
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0002-backports-update-kernel-version-check-for-eth_hw_add.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0002-backports-update-kernel-version-check-for-eth_hw_add.patch
deleted file mode 100644
index f6699a6..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0002-backports-update-kernel-version-check-for-eth_hw_add.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From ad78259cef325771ddf30132bd0c80e4a11b4841 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 27 Nov 2023 16:39:36 +0800
-Subject: [PATCH 2/3] backports: update kernel version check for
- eth_hw_addr_set()
-
-Kernel v5.4.260 has added this API, so update kernel version check in
-backports include.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- backport-include/linux/etherdevice.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/backport-include/linux/etherdevice.h b/backport-include/linux/etherdevice.h
-index 51a7d6d..ecc3bc2 100644
---- a/backport-include/linux/etherdevice.h
-+++ b/backport-include/linux/etherdevice.h
-@@ -39,7 +39,7 @@ static inline void u64_to_ether_addr(u64 u, u8 *addr)
- }
- #endif /* LINUX_VERSION_IS_LESS(4,11,0) */
-
--#if LINUX_VERSION_IS_LESS(5,15,0)
-+#if LINUX_VERSION_IS_LESS(5,4,260)
- /**
- * eth_hw_addr_set - Assign Ethernet address to a net_device
- * @dev: pointer to net_device structure
-@@ -51,7 +51,7 @@ static inline void eth_hw_addr_set(struct net_device *dev, const u8 *addr)
- {
- ether_addr_copy(dev->dev_addr, addr);
- }
--#endif /* LINUX_VERSION_IS_LESS(5,15,0) */
-+#endif /* LINUX_VERSION_IS_LESS(5,4,260) */
-
- #if LINUX_VERSION_IS_LESS(5,16,0)
- static inline int backport_device_get_mac_address(struct device *dev, char *addr)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0003-backports-Revert-cfg80211-allow-grace-period-for-DFS.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0003-backports-Revert-cfg80211-allow-grace-period-for-DFS.patch
deleted file mode 100644
index ad5ef2e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/bp-0003-backports-Revert-cfg80211-allow-grace-period-for-DFS.patch
+++ /dev/null
@@ -1,177 +0,0 @@
-From 23681abf649eaf1eda27dcfd3d17b80e1edeb951 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 19 Oct 2023 09:59:24 +0800
-Subject: [PATCH 3/3] backports: Revert cfg80211: allow grace period for DFS
- available after beacon shutdown
-
-revert 320-cfg80211-allow-grace-period-for-DFS-available-after-.patch
-This patch will lead to channel switch fail when background radar is
-enabled.
-When AP channel switch to USABLE DFS channel,
-1. AP will restart, and the DFS state of the previously operated DFS channel
- will not be cleared immediately if this patch is applied.
-2. Background radar will perform CAC on the specified DFS channel for AP.
-3. AP will choose an AVAILABLE channel to operate on.
-Therefore, AP might select those DFS channels whose DFS state would be
-cleared after the grace period, resulting in channel switch failure.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 3 ---
- net/wireless/ap.c | 6 +++---
- net/wireless/chan.c | 45 ------------------------------------------
- net/wireless/core.h | 2 --
- net/wireless/mlme.c | 7 +++----
- 5 files changed, 6 insertions(+), 57 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index a77e8a5..0a825af 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -175,8 +175,6 @@ enum ieee80211_channel_flags {
- * @dfs_state: current state of this channel. Only relevant if radar is required
- * on this channel.
- * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
-- * @dfs_state_last_available: timestamp (jiffies) of the last time when the
-- * channel was available.
- * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
- */
- struct ieee80211_channel {
-@@ -193,7 +191,6 @@ struct ieee80211_channel {
- int orig_mag, orig_mpwr;
- enum nl80211_dfs_state dfs_state;
- unsigned long dfs_state_entered;
-- unsigned long dfs_state_last_available;
- unsigned int dfs_cac_ms;
- };
-
-diff --git a/net/wireless/ap.c b/net/wireless/ap.c
-index 63641aa..0962770 100644
---- a/net/wireless/ap.c
-+++ b/net/wireless/ap.c
-@@ -30,9 +30,6 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
- if (!wdev->links[link_id].ap.beacon_interval)
- return -ENOENT;
-
-- cfg80211_update_last_available(wdev->wiphy,
-- &wdev->links[link_id].ap.chandef);
--
- err = rdev_stop_ap(rdev, dev, link_id);
- if (!err) {
- wdev->conn_owner_nlportid = 0;
-@@ -44,6 +41,9 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
- if (notify)
- nl80211_send_ap_stopped(wdev, link_id);
-
-+ /* Should we apply the grace period during beaconing interface
-+ * shutdown also?
-+ */
- cfg80211_sched_dfs_chan_update(rdev);
- }
-
-diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index f8348bc..510079f 100644
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -461,8 +461,6 @@ static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
-
- c->dfs_state = dfs_state;
- c->dfs_state_entered = jiffies;
-- if (dfs_state == NL80211_DFS_AVAILABLE)
-- c->dfs_state_last_available = jiffies;
- }
- }
-
-@@ -876,49 +874,6 @@ static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
- return true;
- }
-
--static void
--__cfg80211_update_last_available(struct wiphy *wiphy,
-- u32 center_freq,
-- u32 bandwidth)
--{
-- struct ieee80211_channel *c;
-- u32 freq, start_freq, end_freq;
--
-- start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-- end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
--
-- /*
-- * Check entire range of channels for the bandwidth.
-- * If any channel in between is disabled or has not
-- * had gone through CAC return false
-- */
-- for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-- c = ieee80211_get_channel_khz(wiphy, freq);
-- if (!c)
-- return;
--
-- c->dfs_state_last_available = jiffies;
-- }
--}
--
--void cfg80211_update_last_available(struct wiphy *wiphy,
-- const struct cfg80211_chan_def *chandef)
--{
-- int width;
--
-- width = cfg80211_chandef_get_width(chandef);
-- if (width < 0)
-- return;
--
-- __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
-- width);
-- if (chandef->width != NL80211_CHAN_WIDTH_80P80)
-- return;
--
-- __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
-- width);
--}
--
- static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
- const struct cfg80211_chan_def *chandef)
- {
-diff --git a/net/wireless/core.h b/net/wireless/core.h
-index 9aef18e..46aa2a0 100644
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -481,8 +481,6 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
- enum nl80211_dfs_state dfs_state);
-
- void cfg80211_dfs_channels_update_work(struct work_struct *work);
--void cfg80211_update_last_available(struct wiphy *wiphy,
-- const struct cfg80211_chan_def *chandef);
-
- void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
-
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index 0002464..3cdfbd2 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -930,8 +930,6 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
- if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
- time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
- radar_event = NL80211_RADAR_NOP_FINISHED;
-- timeout = c->dfs_state_entered +
-- msecs_to_jiffies(time_dfs_update);
- } else {
- if (regulatory_pre_cac_allowed(wiphy) ||
- cfg80211_any_wiphy_oper_chan(wiphy, c))
-@@ -939,10 +937,11 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
-
- time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
- radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
-- timeout = c->dfs_state_last_available +
-- msecs_to_jiffies(time_dfs_update);
- }
-
-+ timeout = c->dfs_state_entered +
-+ msecs_to_jiffies(time_dfs_update);
-+
- if (time_after_eq(jiffies, timeout)) {
- c->dfs_state = NL80211_DFS_USABLE;
- c->dfs_state_entered = jiffies;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0001-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0001-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
deleted file mode 100644
index a054a6b..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0001-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 45ed016428208e89e136b5af7e50bec57d63fdf6 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Tue, 18 Jan 2022 20:29:44 +0800
-Subject: [PATCH 01/37] mtk: mac80211: do not setup twt when twt responder is
- false
-
----
- net/mac80211/rx.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 961ef53..c23b74a 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3422,6 +3422,9 @@ ieee80211_process_rx_twt_action(struct ieee80211_rx_data *rx)
- if (sdata->vif.type != NL80211_IFTYPE_AP)
- return false;
-
-+ if (!sdata->vif.bss_conf.twt_responder)
-+ return false;
-+
- if (!rx->local->ops->add_twt_setup)
- return false;
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0002-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0002-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
deleted file mode 100644
index c4f3a37..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0002-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 3481facddee5af51f45c35d9d9dcd2586acbab38 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 29 Mar 2022 16:06:30 +0800
-Subject: [PATCH 02/37] mtk: cfg80211: extend CAC time for weather radar
- channels
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 1 +
- net/wireless/chan.c | 7 +++++++
- net/wireless/nl80211.c | 3 +++
- 3 files changed, 11 insertions(+)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 0a825af..25f15d0 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -149,6 +149,7 @@ enum ieee80211_channel_flags {
- (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
-
- #define IEEE80211_DFS_MIN_CAC_TIME_MS 60000
-+#define IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS 600000
- #define IEEE80211_DFS_MIN_NOP_TIME_MS (30 * 60 * 1000)
-
- /**
-diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index 510079f..30a2f00 100644
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -932,6 +932,13 @@ static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
- if (!(c->flags & IEEE80211_CHAN_RADAR))
- continue;
-
-+ /* weather radar in ETSI */
-+ if (reg_get_dfs_region(wiphy) == NL80211_DFS_ETSI &&
-+ freq >= MHZ_TO_KHZ(5600) && freq <= MHZ_TO_KHZ(5640) &&
-+ dfs_cac_ms < IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS &&
-+ c->dfs_state == NL80211_DFS_USABLE)
-+ dfs_cac_ms = IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS;
-+
- if (c->dfs_cac_ms > dfs_cac_ms)
- dfs_cac_ms = c->dfs_cac_ms;
- }
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 193e7d6..dd76503 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10011,6 +10011,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- if (WARN_ON(!cac_time_ms))
- cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
-
-+ pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
-+ __func__, dfs_region, chandef.center_freq1, chandef.center_freq2, cac_time_ms);
-+
- err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
- if (!err) {
- wdev->links[0].ap.chandef = chandef;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0003-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0003-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
deleted file mode 100644
index 381e843..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0003-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 5a633b424cf49f50e211aa89712150028532407f Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Fri, 1 Apr 2022 09:15:21 +0800
-Subject: [PATCH 03/37] mtk: mac80211: it's invalid case when frag_threshold is
- greater than 2346
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- net/wireless/nl80211.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index dd76503..508edc1 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -3651,6 +3651,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
- goto out;
- }
-
-+ if (frag_threshold >= 2346)
-+ frag_threshold = (u32) -1;
-+
- if (frag_threshold != (u32) -1) {
- /*
- * Fragments (apart from the last one) are required to
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0004-mtk-mac80211-add-support-for-runtime-set-inband-disc.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0004-mtk-mac80211-add-support-for-runtime-set-inband-disc.patch
deleted file mode 100644
index 5644758..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0004-mtk-mac80211-add-support-for-runtime-set-inband-disc.patch
+++ /dev/null
@@ -1,183 +0,0 @@
-From 1cc0213bb5d4152c41cd6dd00eef4a613a5a0a99 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 19 Oct 2022 13:45:42 +0800
-Subject: [PATCH 04/37] mtk: mac80211: add support for runtime set inband
- discovery
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- include/net/cfg80211.h | 1 +
- include/net/mac80211.h | 1 +
- include/uapi/linux/nl80211.h | 1 +
- net/mac80211/cfg.c | 30 +++++++++++++++++++++++++++++-
- net/wireless/nl80211.c | 33 ++++++++++++++++++++++++++++-----
- 5 files changed, 60 insertions(+), 6 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 25f15d0..5b2e242 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -1379,6 +1379,7 @@ struct cfg80211_fils_discovery {
- u32 max_interval;
- size_t tmpl_len;
- const u8 *tmpl;
-+ u8 disable;
- };
-
- /**
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 231abd1..4eac89a 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -526,6 +526,7 @@ struct ieee80211_ftm_responder_params {
- struct ieee80211_fils_discovery {
- u32 min_interval;
- u32 max_interval;
-+ u8 disable;
- };
-
- /**
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index 88eb85c..72ef4d9 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -7619,6 +7619,7 @@ enum nl80211_fils_discovery_attributes {
- NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
- NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
- NL80211_FILS_DISCOVERY_ATTR_TMPL,
-+ NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INTE,
-
- /* keep last */
- __NL80211_FILS_DISCOVERY_ATTR_LAST,
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 6b62644..3c07357 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -993,6 +993,7 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
- fd = &link_conf->fils_discovery;
- fd->min_interval = params->min_interval;
- fd->max_interval = params->max_interval;
-+ fd->disable = params->disable;
-
- old = sdata_dereference(link->u.ap.fils_discovery, sdata);
- new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
-@@ -1516,9 +1517,11 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_link_data *link;
- struct beacon_data *old;
-+ struct cfg80211_ap_settings *ap_params;
-+ struct ieee80211_supported_band *sband;
-+ u64 changed = 0;
- int err;
- struct ieee80211_bss_conf *link_conf;
-- u64 changed = 0;
-
- sdata_assert_lock(sdata);
-
-@@ -1549,6 +1552,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
- changed |= BSS_CHANGED_HE_BSS_COLOR;
- }
-
-+ sband = ieee80211_get_sband(sdata);
-+ if (!sband)
-+ return -EINVAL;
-+
-+ if (sband->band == NL80211_BAND_6GHZ) {
-+ ap_params = container_of(params, struct cfg80211_ap_settings, beacon);
-+
-+ if(ap_params->unsol_bcast_probe_resp.interval) {
-+ err = ieee80211_set_unsol_bcast_probe_resp(sdata,
-+ &ap_params->unsol_bcast_probe_resp,
-+ link, link_conf);
-+ if (err < 0)
-+ return err;
-+ changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
-+ } else {
-+ err = ieee80211_set_fils_discovery(sdata,
-+ &ap_params->fils_discovery,
-+ link, link_conf);
-+
-+ if (err < 0)
-+ return err;
-+ changed |= BSS_CHANGED_FILS_DISCOVERY;
-+ }
-+ }
-+
- ieee80211_link_info_change_notify(sdata, link, changed);
- return 0;
- }
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 508edc1..abb9585 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -424,6 +424,7 @@ nl80211_fils_discovery_policy[NL80211_FILS_DISCOVERY_ATTR_MAX + 1] = {
- [NL80211_FILS_DISCOVERY_ATTR_INT_MAX] = NLA_POLICY_MAX(NLA_U32, 10000),
- [NL80211_FILS_DISCOVERY_ATTR_TMPL] =
- NLA_POLICY_BINARY_RANGE(NL80211_FILS_DISCOVERY_TMPL_MIN_LEN, IEEE80211_MAX_DATA_LEN),
-+ [NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INTE] = NLA_POLICY_MAX(NLA_U32, 20),
- };
-
- static const struct nla_policy
-@@ -5692,6 +5693,8 @@ static int nl80211_parse_fils_discovery(struct cfg80211_registered_device *rdev,
- fd->tmpl = nla_data(tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]);
- fd->min_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN]);
- fd->max_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX]);
-+ fd->disable = !(fd->max_interval ||
-+ nla_get_u32(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INTE]));
-
- return 0;
- }
-@@ -6243,7 +6246,8 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
- unsigned int link_id = nl80211_link_id(info->attrs);
- struct net_device *dev = info->user_ptr[1];
- struct wireless_dev *wdev = dev->ieee80211_ptr;
-- struct cfg80211_beacon_data params;
-+ struct cfg80211_ap_settings ap_params;
-+ struct cfg80211_beacon_data *params;
- int err;
-
- if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-@@ -6256,17 +6260,36 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
- if (!wdev->links[link_id].ap.beacon_interval)
- return -EINVAL;
-
-- err = nl80211_parse_beacon(rdev, info->attrs, ¶ms, info->extack);
-+ memset(&ap_params, 0, sizeof(ap_params));
-+ params = &ap_params.beacon;
-+
-+ err = nl80211_parse_beacon(rdev, info->attrs, params, info->extack);
- if (err)
- goto out;
-
-+ if (info->attrs[NL80211_ATTR_FILS_DISCOVERY]) {
-+ err = nl80211_parse_fils_discovery(rdev,
-+ info->attrs[NL80211_ATTR_FILS_DISCOVERY],
-+ &ap_params);
-+ if (err)
-+ goto out;
-+ }
-+
-+ if (info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]) {
-+ err = nl80211_parse_unsol_bcast_probe_resp(rdev,
-+ info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP],
-+ &ap_params);
-+ if (err)
-+ goto out;
-+ }
-+
- wdev_lock(wdev);
-- err = rdev_change_beacon(rdev, dev, ¶ms);
-+ err = rdev_change_beacon(rdev, dev, params);
- wdev_unlock(wdev);
-
- out:
-- kfree(params.mbssid_ies);
-- kfree(params.rnr_ies);
-+ kfree(params->mbssid_ies);
-+ kfree(params->rnr_ies);
- return err;
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0005-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0005-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
deleted file mode 100644
index 79b5cc4..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0005-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
+++ /dev/null
@@ -1,500 +0,0 @@
-From 3ef5311c371391368404bbe466ff1904155dac8f Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 22 Sep 2022 14:27:41 +0800
-Subject: [PATCH 05/37] mtk: cfg80211: implement DFS status show, cac and nop
- skip command via debugfs
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 1 +
- net/mac80211/cfg.c | 20 +++
- net/wireless/core.h | 3 +
- net/wireless/debugfs.c | 311 +++++++++++++++++++++++++++++++++++++++-
- net/wireless/mlme.c | 6 +
- net/wireless/rdev-ops.h | 14 ++
- net/wireless/trace.h | 12 ++
- 7 files changed, 360 insertions(+), 7 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 5b2e242..9567c1b 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -4841,6 +4841,7 @@ struct cfg80211_ops {
- struct link_station_del_parameters *params);
- int (*set_hw_timestamp)(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_set_hw_timestamp *hwts);
-+ void (*skip_cac)(struct wireless_dev *wdev);
- };
-
- /*
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 3c07357..af284d2 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -5057,6 +5057,25 @@ static int ieee80211_set_hw_timestamp(struct wiphy *wiphy,
- return local->ops->set_hw_timestamp(&local->hw, &sdata->vif, hwts);
- }
-
-+static void
-+ieee80211_skip_cac(struct wireless_dev *wdev)
-+{
-+ struct net_device *dev = wdev->netdev;
-+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-+ struct cfg80211_chan_def chandef = sdata->deflink.conf->chandef;
-+ unsigned int cac_time_ms;
-+
-+ cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work);
-+ if (wdev->cac_started) {
-+ ieee80211_link_release_channel(&sdata->deflink);
-+ cac_time_ms = wdev->cac_time_ms;
-+ wdev->cac_start_time = jiffies -
-+ msecs_to_jiffies(cac_time_ms + 1);
-+ cfg80211_cac_event(wdev->netdev, &chandef,
-+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
-+ }
-+}
-+
- const struct cfg80211_ops mac80211_config_ops = {
- .add_virtual_intf = ieee80211_add_iface,
- .del_virtual_intf = ieee80211_del_iface,
-@@ -5169,4 +5188,5 @@ const struct cfg80211_ops mac80211_config_ops = {
- .mod_link_station = ieee80211_mod_link_station,
- .del_link_station = ieee80211_del_link_station,
- .set_hw_timestamp = ieee80211_set_hw_timestamp,
-+ .skip_cac = ieee80211_skip_cac,
- };
-diff --git a/net/wireless/core.h b/net/wireless/core.h
-index 46aa2a0..8e9a2c9 100644
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -86,6 +86,9 @@ struct cfg80211_registered_device {
-
- struct wireless_dev *background_radar_wdev;
- struct cfg80211_chan_def background_radar_chandef;
-+ bool background_cac_started;
-+ unsigned long background_cac_start_time;
-+ unsigned int background_cac_time_ms;
- struct delayed_work background_cac_done_wk;
- struct work_struct background_cac_abort_wk;
-
-diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
-index 0878b16..a228a14 100644
---- a/net/wireless/debugfs.c
-+++ b/net/wireless/debugfs.c
-@@ -9,6 +9,7 @@
- #include <linux/slab.h>
- #include "core.h"
- #include "debugfs.h"
-+#include "rdev-ops.h"
-
- #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
- static ssize_t name## _read(struct file *file, char __user *userbuf, \
-@@ -96,16 +97,312 @@ static const struct file_operations ht40allow_map_ops = {
- .llseek = default_llseek,
- };
-
--#define DEBUGFS_ADD(name) \
-- debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
-+static int dfs_print_chan(struct ieee80211_channel *chan, int remain_time, int wait_time,
-+ char *buf, int buf_size, int offset, bool is_background)
-+{
-+ if (WARN_ON(offset > buf_size))
-+ return 0;
-+
-+ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ " Channel = %d, DFS_state = Unavailable",
-+ chan->hw_value);
-+ if (remain_time > 0)
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ ", Non-occupancy Remain Time = %d / %d [sec]",
-+ remain_time, wait_time);
-+ else
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ ", Changing state...");
-+ } else if (chan->dfs_state == NL80211_DFS_USABLE) {
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ " Channel = %d, DFS_state = Usable",
-+ chan->hw_value);
-+ if (remain_time > 0)
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ ", CAC Remain Time = %d / %d [sec]",
-+ remain_time, wait_time);
-+ } else if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ " Channel = %d, DFS_state = Available",
-+ chan->hw_value);
-+ } else {
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ " Channel = %d, DFS_state = Unknown",
-+ chan->hw_value);
-+ }
-+
-+ if (is_background)
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ " (background chain)");
-+ offset += scnprintf(buf + offset, buf_size - offset, "\n");
-+
-+ return offset;
-+}
-+
-+static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev, char *buf,
-+ unsigned int buf_size, unsigned int offset)
-+{
-+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+ struct cfg80211_chan_def *chandef = &wdev->links[0].ap.chandef;
-+ struct cfg80211_chan_def *background_chandef = &rdev->background_radar_chandef;
-+ enum nl80211_band band;
-+ struct ieee80211_supported_band *sband;
-+ struct ieee80211_channel *chan;
-+ unsigned long jiffies_passed;
-+ int i, remain_time = 0, wait_time_ms = 0;
-+ bool is_background;
-+
-+ offset += scnprintf(buf + offset, buf_size - offset, "DFS Channel:\n");
-+
-+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+ sband = wiphy->bands[band];
-+ if (!sband)
-+ continue;
-+ for (i = 0; i < sband->n_channels; i++) {
-+ is_background = false;
-+ chan = &sband->channels[i];
-+
-+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
-+ continue;
-+
-+ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
-+ jiffies_passed = jiffies - chan->dfs_state_entered;
-+ wait_time_ms = IEEE80211_DFS_MIN_NOP_TIME_MS;
-+ remain_time = (wait_time_ms - jiffies_to_msecs(jiffies_passed));
-+ if (remain_time > wait_time_ms)
-+ remain_time = 0;
-+ } else if (chan->dfs_state == NL80211_DFS_USABLE) {
-+ if (wdev->cac_started &&
-+ cfg80211_is_sub_chan(chandef, chan, false)) {
-+ jiffies_passed = jiffies - wdev->cac_start_time;
-+ wait_time_ms = wdev->cac_time_ms;
-+ remain_time = (wait_time_ms -
-+ jiffies_to_msecs(jiffies_passed));
-+ }
-+
-+ if (rdev->background_radar_wdev == wdev &&
-+ rdev->background_cac_started &&
-+ cfg80211_is_sub_chan(background_chandef, chan, false)) {
-+ jiffies_passed = jiffies - rdev->background_cac_start_time;
-+ wait_time_ms = rdev->background_cac_time_ms;
-+ remain_time = (wait_time_ms -
-+ jiffies_to_msecs(jiffies_passed));
-+ is_background = true;
-+ }
-+
-+ if (remain_time > wait_time_ms)
-+ remain_time = 0;
-+
-+ } else {
-+ if (rdev->background_radar_wdev == wdev &&
-+ cfg80211_is_sub_chan(background_chandef, chan, false))
-+ is_background = true;
-+ }
-+
-+ offset = dfs_print_chan(chan, remain_time / 1000, wait_time_ms / 1000,
-+ buf, buf_size, offset, is_background);
-+ remain_time = 0;
-+ }
-+ }
-+
-+ return offset;
-+}
-+
-+static ssize_t dfs_status_read(struct file *file, char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct wiphy *wiphy = file->private_data;
-+ struct wireless_dev *wdev;
-+ char *buf;
-+ unsigned int offset = 0, buf_size = PAGE_SIZE, r;
-+ const char * const iftype_str[] = {
-+ [NL80211_IFTYPE_UNSPECIFIED] = "unspecified",
-+ [NL80211_IFTYPE_ADHOC] = "adhoc",
-+ [NL80211_IFTYPE_STATION] = "station",
-+ [NL80211_IFTYPE_AP] = "ap",
-+ [NL80211_IFTYPE_AP_VLAN] = "ap vlan",
-+ [NL80211_IFTYPE_WDS] = "wds",
-+ [NL80211_IFTYPE_MONITOR] = "monitor",
-+ [NL80211_IFTYPE_MESH_POINT] = "mesh point",
-+ [NL80211_IFTYPE_P2P_CLIENT] = "p2p client",
-+ [NL80211_IFTYPE_P2P_GO] = "p2p go",
-+ [NL80211_IFTYPE_P2P_DEVICE] = "p2p device",
-+ [NL80211_IFTYPE_OCB] = "ocb",
-+ [NL80211_IFTYPE_NAN] = "nan",
-+ };
-+
-+ buf = kzalloc(buf_size, GFP_KERNEL);
-+ if (!buf)
-+ return -ENOMEM;
-+
-+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
-+ offset += scnprintf(buf + offset, buf_size - offset,
-+ "wdev 0x%x\n"
-+ "interface type %s\n",
-+ wdev->identifier, iftype_str[wdev->iftype]);
-+ offset = dfs_status_read_wdev(wiphy, wdev, buf, buf_size, offset);
-+ }
-+
-+ r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
-+
-+ kfree(buf);
-+
-+ return r;
-+}
-+
-+static const struct file_operations dfs_status_ops = {
-+ .read = dfs_status_read,
-+ .open = simple_open,
-+ .llseek = default_llseek,
-+};
-+
-+static int
-+dfs_nop_skip(void *data, u64 val)
-+{
-+ struct wiphy *wiphy = data;
-+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+ bool en = !!val;
-+ enum nl80211_band band;
-+ struct ieee80211_supported_band *sband;
-+ struct ieee80211_channel *chan;
-+ u32 nop_time = IEEE80211_DFS_MIN_NOP_TIME_MS;
-+ int i;
-+
-+ if (!en)
-+ return 0;
-+
-+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+ sband = wiphy->bands[band];
-+ if (!sband)
-+ continue;
-+ for (i = 0; i < sband->n_channels; i++) {
-+ chan = &sband->channels[i];
-+
-+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
-+ continue;
-+
-+ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
-+ // Let current jiffies > dfs_state_entered_jiffies + NOP time
-+ chan->dfs_state_entered = jiffies -
-+ msecs_to_jiffies(nop_time + 1);
-+ }
-+ }
-+ }
-+
-+ cfg80211_sched_dfs_chan_update(rdev);
-+
-+ return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_nop_ops, NULL,
-+ dfs_nop_skip, "0x%08llx\n");
-+
-+static int
-+dfs_cac_skip(void *data, u64 val)
-+{
-+#define CAC_SKIP_MASK BIT(0)
-+#define CAC_SKIP_BACKGROUND_MASK BIT(1)
-+ struct wiphy *wiphy = data;
-+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+ struct wireless_dev *wdev;
-+ struct cfg80211_chan_def *chandef;
-+ unsigned int skip_mode = val;
-+ unsigned long cac_time;
-+ struct ieee80211_channel *chan;
-+
-+ if (!skip_mode || skip_mode > (CAC_SKIP_MASK | CAC_SKIP_BACKGROUND_MASK))
-+ return 0;
-+
-+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
-+ if ((skip_mode & CAC_SKIP_MASK) && wdev->links[0].ap.chandef.chan) {
-+ chandef = &wdev->links[0].ap.chandef;
-+ chan = chandef->chan;
-+
-+ if ((cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) &&
-+ cfg80211_chandef_dfs_usable(wiphy, chandef) && wdev->cac_started) {
-+ rdev_skip_cac(rdev, wdev);
-+ }
-+ }
-+
-+ if ((skip_mode & CAC_SKIP_BACKGROUND_MASK) &&
-+ rdev->background_radar_wdev == wdev &&
-+ rdev->background_radar_chandef.chan) {
-+ chandef = &rdev->background_radar_chandef;
-+ chan = chandef->chan;
-+
-+ if ((cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) &&
-+ cfg80211_chandef_dfs_usable(wiphy, chandef) &&
-+ rdev->background_cac_started) {
-+ // Let current jiffies > dfs_state_entered_jiffies + CAC time
-+ cac_time = rdev->background_cac_time_ms;
-+ rdev->background_cac_start_time = jiffies -
-+ msecs_to_jiffies(cac_time + 1);
-+ cancel_delayed_work(&rdev->background_cac_done_wk);
-+ queue_delayed_work(cfg80211_wq, &rdev->background_cac_done_wk, 0);
-+ }
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_cac_ops, NULL,
-+ dfs_cac_skip, "0x%08llx\n");
-+
-+static int
-+dfs_available_reset(void *data, u64 val)
-+{
-+ struct wiphy *wiphy = data;
-+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+ bool en = !!val;
-+ enum nl80211_band band;
-+ struct ieee80211_supported_band *sband;
-+ struct ieee80211_channel *chan;
-+ int i;
-+
-+ if (!en)
-+ return 0;
-+
-+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+ sband = wiphy->bands[band];
-+ if (!sband)
-+ continue;
-+ for (i = 0; i < sband->n_channels; i++) {
-+ chan = &sband->channels[i];
-+
-+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
-+ continue;
-+
-+ if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
-+ chan->dfs_state = NL80211_DFS_USABLE;
-+ chan->dfs_state_entered = jiffies;
-+ }
-+ }
-+ }
-+
-+ cfg80211_sched_dfs_chan_update(rdev);
-+
-+ return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(dfs_available_reset_ops, NULL,
-+ dfs_available_reset, "0x%08llx\n");
-+
-+#define DEBUGFS_ADD(name, chmod) \
-+ debugfs_create_file(#name, chmod, phyd, &rdev->wiphy, &name## _ops)
-
- void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
- {
- struct dentry *phyd = rdev->wiphy.debugfsdir;
-
-- DEBUGFS_ADD(rts_threshold);
-- DEBUGFS_ADD(fragmentation_threshold);
-- DEBUGFS_ADD(short_retry_limit);
-- DEBUGFS_ADD(long_retry_limit);
-- DEBUGFS_ADD(ht40allow_map);
-+ DEBUGFS_ADD(rts_threshold, 0444);
-+ DEBUGFS_ADD(fragmentation_threshold, 0444);
-+ DEBUGFS_ADD(short_retry_limit, 0444);
-+ DEBUGFS_ADD(long_retry_limit, 0444);
-+ DEBUGFS_ADD(ht40allow_map, 0444);
-+ DEBUGFS_ADD(dfs_status, 0444);
-+ DEBUGFS_ADD(dfs_skip_nop, 0600);
-+ DEBUGFS_ADD(dfs_skip_cac, 0600);
-+ DEBUGFS_ADD(dfs_available_reset, 0600);
- }
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index 3cdfbd2..70b4013 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1069,13 +1069,16 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
- queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
- cfg80211_sched_dfs_chan_update(rdev);
- wdev = rdev->background_radar_wdev;
-+ rdev->background_cac_started = false;
- break;
- case NL80211_RADAR_CAC_ABORTED:
- if (!cancel_delayed_work(&rdev->background_cac_done_wk))
- return;
- wdev = rdev->background_radar_wdev;
-+ rdev->background_cac_started = false;
- break;
- case NL80211_RADAR_CAC_STARTED:
-+ rdev->background_cac_started = true;
- break;
- default:
- return;
-@@ -1095,6 +1098,7 @@ cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
- chandef, event);
- wiphy_unlock(&rdev->wiphy);
- }
-+EXPORT_SYMBOL(cfg80211_background_cac_event);
-
- void cfg80211_background_cac_done_wk(struct work_struct *work)
- {
-@@ -1156,8 +1160,10 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
- if (!cac_time_ms)
- cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
-
-+ rdev->background_cac_time_ms = cac_time_ms;
- rdev->background_radar_chandef = *chandef;
- rdev->background_radar_wdev = wdev; /* Get offchain ownership */
-+ rdev->background_cac_start_time = jiffies;
-
- __cfg80211_background_cac_event(rdev, wdev, chandef,
- NL80211_RADAR_CAC_STARTED);
-diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
-index 72c1282..5a4854c 100644
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -1524,4 +1524,18 @@ rdev_set_hw_timestamp(struct cfg80211_registered_device *rdev,
-
- return ret;
- }
-+
-+static inline int
-+rdev_skip_cac(struct cfg80211_registered_device *rdev,
-+ struct wireless_dev *wdev)
-+{
-+ if (!rdev->ops->skip_cac)
-+ return -EOPNOTSUPP;
-+
-+ trace_rdev_skip_cac(wdev);
-+ rdev->ops->skip_cac(wdev);
-+ trace_rdev_return_void(&rdev->wiphy);
-+
-+ return 0;
-+}
- #endif /* __CFG80211_RDEV_OPS */
-diff --git a/net/wireless/trace.h b/net/wireless/trace.h
-index fc955a9..01dcfd3 100644
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -3981,6 +3981,18 @@ TRACE_EVENT(cfg80211_links_removed,
- __entry->link_mask)
- );
-
-+TRACE_EVENT(rdev_skip_cac,
-+ TP_PROTO(struct wireless_dev *wdev),
-+
-+ TP_ARGS(wdev),
-+
-+ TP_STRUCT__entry(WDEV_ENTRY),
-+
-+ TP_fast_assign(WDEV_ASSIGN;),
-+
-+ TP_printk(WDEV_PR_FMT, WDEV_PR_ARG)
-+);
-+
- #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
-
- #undef TRACE_INCLUDE_PATH
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0006-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0006-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
deleted file mode 100644
index 05fc4c3..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0006-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From a00c69d75f0ed444651391781f46351ea1417779 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 4 Oct 2022 10:47:05 +0800
-Subject: [PATCH 06/37] mtk: mac80211: Set TWT Information Frame Disabled bit
- as 1.
-
-This modification means that current implementation do not support twt information frame.
----
- net/mac80211/s1g.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
-index c1f964e..d9d84db 100644
---- a/net/mac80211/s1g.c
-+++ b/net/mac80211/s1g.c
-@@ -101,6 +101,7 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
-
- twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
-+ twt->control |= IEEE80211_TWT_CONTROL_RX_DISABLED;
-
- /* broadcast TWT not supported yet */
- if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0007-mtk-mac80211-check-the-control-channel-before-downgr.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0007-mtk-mac80211-check-the-control-channel-before-downgr.patch
deleted file mode 100644
index ce63f41..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0007-mtk-mac80211-check-the-control-channel-before-downgr.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From a2cb7d582c92644990fb3833b70ea3f18a539a02 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 16 Dec 2022 03:31:06 +0800
-Subject: [PATCH 07/37] mtk: mac80211: check the control channel before
- downgrading the bandwidth
-
----
- net/mac80211/mlme.c | 23 +++++++++++++++++++++++
- 1 file changed, 23 insertions(+)
-
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 73f8df0..24d34d4 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -4814,6 +4814,26 @@ ieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata,
- return true;
- }
-
-+static bool ieee80211_check_same_ctrl_channel(struct ieee80211_sub_if_data *sdata,
-+ const struct cfg80211_chan_def *chandef)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_chanctx *ctx;
-+
-+ mutex_lock(&local->chanctx_mtx);
-+ list_for_each_entry(ctx, &local->chanctx_list, list) {
-+ if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
-+ continue;
-+ if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
-+ continue;
-+ if (chandef->chan == ctx->conf.def.chan)
-+ return true;
-+ }
-+
-+ mutex_unlock(&local->chanctx_mtx);
-+ return false;
-+}
-+
- static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_link_data *link,
- struct cfg80211_bss *cbss,
-@@ -5056,6 +5076,9 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
- chandef.width == NL80211_CHAN_WIDTH_10)
- goto out;
-
-+ if (!ret || !ieee80211_check_same_ctrl_channel(sdata, &chandef))
-+ goto out;
-+
- while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
- *conn_flags |=
- ieee80211_chandef_downgrade(&chandef);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0008-mtk-mac80211-fix-tx-amsdu-aggregation.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0008-mtk-mac80211-fix-tx-amsdu-aggregation.patch
deleted file mode 100644
index 87b4945..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0008-mtk-mac80211-fix-tx-amsdu-aggregation.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 500f2f8a8f6690f496f35da6b75ab559298f4403 Mon Sep 17 00:00:00 2001
-From: TomLiu <tomml.liu@mediatek.com>
-Date: Wed, 14 Dec 2022 00:26:50 -0800
-Subject: [PATCH 08/37] mtk: mac80211: fix tx amsdu aggregation
-
----
- include/net/mac80211.h | 7 +++++++
- net/mac80211/agg-tx.c | 6 ++++--
- 2 files changed, 11 insertions(+), 2 deletions(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 4eac89a..dcff7af 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -2924,6 +2924,13 @@ static inline void _ieee80211_hw_set(struct ieee80211_hw *hw,
- }
- #define ieee80211_hw_set(hw, flg) _ieee80211_hw_set(hw, IEEE80211_HW_##flg)
-
-+static inline void _ieee80211_hw_clear(struct ieee80211_hw *hw,
-+ enum ieee80211_hw_flags flg)
-+{
-+ return __clear_bit(flg, hw->flags);
-+}
-+#define ieee80211_hw_clear(hw, flg) _ieee80211_hw_clear(hw, IEEE80211_HW_##flg)
-+
- /**
- * struct ieee80211_scan_request - hw scan request
- *
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index b6b7726..80cd642 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -66,7 +66,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
-- u16 capab;
-+ u16 capab = 0;
-+ bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
-
- skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
-
-@@ -95,7 +96,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
-
- mgmt->u.action.u.addba_req.dialog_token = dialog_token;
-- capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
-+ if (amsdu)
-+ capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
- capab |= IEEE80211_ADDBA_PARAM_POLICY_MASK;
- capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK);
- capab |= u16_encode_bits(agg_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0009-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0009-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
deleted file mode 100644
index 7e8993c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0009-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From 02ac6da3f76e0da03d30d5b7b59f24190495accd Mon Sep 17 00:00:00 2001
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Wed, 18 May 2022 15:10:22 +0800
-Subject: [PATCH 09/37] mtk: mac80211: add fill receive path ops to get wed idx
-
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
----
- include/net/mac80211.h | 5 +++++
- net/mac80211/driver-ops.h | 13 +++++++++++++
- net/mac80211/iface.c | 23 +++++++++++++++++++++++
- net/mac80211/util.c | 9 +++++++++
- 4 files changed, 50 insertions(+)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index dcff7af..65ba482 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -4258,6 +4258,8 @@ struct ieee80211_prep_tx_info {
- * disable background CAC/radar detection.
- * @net_fill_forward_path: Called from .ndo_fill_forward_path in order to
- * resolve a path for hardware flow offloading
-+ * @net_fill_receive_path: Called from .ndo_fill_receive_path in order to
-+ * get a path for hardware flow offloading
- * @change_vif_links: Change the valid links on an interface, note that while
- * removing the old link information is still valid (link_conf pointer),
- * but may immediately disappear after the function returns. The old or
-@@ -4634,6 +4636,9 @@ struct ieee80211_ops {
- struct ieee80211_sta *sta,
- struct net_device_path_ctx *ctx,
- struct net_device_path *path);
-+ int (*net_fill_receive_path)(struct ieee80211_hw *hw,
-+ struct net_device_path_ctx *ctx,
-+ struct net_device_path *path);
- int (*change_vif_links)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- u16 old_links, u16 new_links,
-diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
-index 7d8cec9..576f893 100644
---- a/net/mac80211/driver-ops.h
-+++ b/net/mac80211/driver-ops.h
-@@ -1524,6 +1524,19 @@ static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
- return ret;
- }
-
-+static inline int drv_net_fill_receive_path(struct ieee80211_local *local,
-+ struct net_device_path_ctx *ctx,
-+ struct net_device_path *path)
-+{
-+ int ret = -EOPNOTSUPP;
-+
-+ if (local->ops->net_fill_receive_path)
-+ ret = local->ops->net_fill_receive_path(&local->hw,
-+ ctx, path);
-+
-+ return ret;
-+}
-+
- static inline int drv_net_setup_tc(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct net_device *dev,
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index 6e3bfb4..4de8d3d 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -930,6 +930,28 @@ out:
- return ret;
- }
-
-+static int ieee80211_netdev_fill_receive_path(struct net_device_path_ctx *ctx,
-+ struct net_device_path *path)
-+{
-+ struct ieee80211_sub_if_data *sdata;
-+ struct ieee80211_local *local;
-+ int ret = -ENOENT;
-+
-+ sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev);
-+ local = sdata->local;
-+
-+ if (!local->ops->net_fill_receive_path)
-+ return -EOPNOTSUPP;
-+
-+ rcu_read_lock();
-+
-+ ret = drv_net_fill_receive_path(local, ctx, path);
-+
-+ rcu_read_unlock();
-+
-+ return ret;
-+}
-+
- static const struct net_device_ops ieee80211_dataif_8023_ops = {
- .ndo_open = ieee80211_open,
- .ndo_stop = ieee80211_stop,
-@@ -939,6 +961,7 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = {
- .ndo_set_mac_address = ieee80211_change_mac,
- .ndo_get_stats64 = ieee80211_get_stats64,
- .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
-+ .ndo_fill_receive_path = ieee80211_netdev_fill_receive_path,
- .ndo_setup_tc = ieee80211_netdev_setup_tc,
- };
-
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index 172173b..fd82488 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -868,6 +868,15 @@ struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
- }
- EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
-
-+struct net_device *ieee80211_vif_to_netdev(struct ieee80211_vif *vif)
-+{
-+ if (!vif)
-+ return NULL;
-+
-+ return vif_to_sdata(vif)->dev;
-+}
-+EXPORT_SYMBOL_GPL(ieee80211_vif_to_netdev);
-+
- /*
- * Nothing should have been stuffed into the workqueue during
- * the suspend->resume cycle. Since we can't check each caller
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0010-mtk-mac80211-fix-build-error-on-Linux-Kernel-5.4.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0010-mtk-mac80211-fix-build-error-on-Linux-Kernel-5.4.patch
deleted file mode 100644
index f37dcf9..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0010-mtk-mac80211-fix-build-error-on-Linux-Kernel-5.4.patch
+++ /dev/null
@@ -1,119 +0,0 @@
-From d878da2558d32ce7ec97dad083a38392e653e54e Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Tue, 13 Dec 2022 09:04:49 +0800
-Subject: [PATCH 10/37] mtk: mac80211: fix build error on Linux Kernel 5.4
-
----
- include/linux/ieee80211.h | 8 +++-----
- net/mac80211/rc80211_minstrel_ht.c | 2 ++
- net/mac80211/wpa.c | 4 ++--
- net/wireless/nl80211.c | 12 +++++-------
- 4 files changed, 12 insertions(+), 14 deletions(-)
-
-diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
-index 2fa1862..e53b73e 100644
---- a/include/linux/ieee80211.h
-+++ b/include/linux/ieee80211.h
-@@ -310,11 +310,9 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
- struct ieee80211_hdr {
- __le16 frame_control;
- __le16 duration_id;
-- struct_group(addrs,
-- u8 addr1[ETH_ALEN];
-- u8 addr2[ETH_ALEN];
-- u8 addr3[ETH_ALEN];
-- );
-+ u8 addr1[ETH_ALEN];
-+ u8 addr2[ETH_ALEN];
-+ u8 addr3[ETH_ALEN];
- __le16 seq_ctrl;
- u8 addr4[ETH_ALEN];
- } __packed __aligned(2);
-diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
-index 3562903..3768947 100644
---- a/net/mac80211/rc80211_minstrel_ht.c
-+++ b/net/mac80211/rc80211_minstrel_ht.c
-@@ -10,7 +10,9 @@
- #include <linux/random.h>
- #include <linux/moduleparam.h>
- #include <linux/ieee80211.h>
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
- #include <linux/minmax.h>
-+#endif
- #include <net/mac80211.h>
- #include "rate.h"
- #include "sta_info.h"
-diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
-index 2d8e38b..0bb3907 100644
---- a/net/mac80211/wpa.c
-+++ b/net/mac80211/wpa.c
-@@ -351,7 +351,7 @@ static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad)
- * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
- put_unaligned_be16(len_a, &aad[0]);
- put_unaligned(mask_fc, (__le16 *)&aad[2]);
-- memcpy(&aad[4], &hdr->addrs, 3 * ETH_ALEN);
-+ memcpy(&aad[4], &hdr->addr1, 3 * ETH_ALEN);
-
- /* Mask Seq#, leave Frag# */
- aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f;
-@@ -792,7 +792,7 @@ static void bip_aad(struct sk_buff *skb, u8 *aad)
- IEEE80211_FCTL_MOREDATA);
- put_unaligned(mask_fc, (__le16 *) &aad[0]);
- /* A1 || A2 || A3 */
-- memcpy(aad + 2, &hdr->addrs, 3 * ETH_ALEN);
-+ memcpy(aad + 2, &hdr->addr1, 3 * ETH_ALEN);
- }
-
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index abb9585..c652a01 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -462,11 +462,6 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
- [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
- };
-
--static struct netlink_range_validation nl80211_punct_bitmap_range = {
-- .min = 0,
-- .max = 0xffff,
--};
--
- static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
- [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
- [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
-@@ -806,8 +801,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
- [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN),
- [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
- [NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT },
-- [NL80211_ATTR_PUNCT_BITMAP] =
-- NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
-+ [NL80211_ATTR_PUNCT_BITMAP] = NLA_POLICY_RANGE(NLA_U8, 0, 0xffff),
-
- [NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS] = { .type = NLA_U16 },
- [NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG },
-@@ -16729,9 +16723,11 @@ static const struct genl_ops nl80211_ops[] = {
- /* can be retrieved by unprivileged users */
- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY),
- },
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
- };
-
- static const struct genl_small_ops nl80211_small_ops[] = {
-+#endif
- {
- .cmd = NL80211_CMD_SET_WIPHY,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
-@@ -17576,8 +17572,10 @@ static struct genl_family nl80211_fam __genl_ro_after_init = {
- .module = THIS_MODULE,
- .ops = nl80211_ops,
- .n_ops = ARRAY_SIZE(nl80211_ops),
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
- .small_ops = nl80211_small_ops,
- .n_small_ops = ARRAY_SIZE(nl80211_small_ops),
-+#endif
- #if LINUX_VERSION_IS_GEQ(6,1,0)
- .resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1,
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0011-mtk-mac80211-track-obss-color-bitmap.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0011-mtk-mac80211-track-obss-color-bitmap.patch
deleted file mode 100644
index 89bb49e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0011-mtk-mac80211-track-obss-color-bitmap.patch
+++ /dev/null
@@ -1,82 +0,0 @@
-From c4919b0d1dd5c92c081852be3ef37511f0406475 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Mon, 13 Mar 2023 05:23:37 +0800
-Subject: [PATCH 11/37] mtk: mac80211: track obss color bitmap
-
-Track OBSS BSS color when receive their beacon.
-
-Adding 2 tracepoint for debug, usage:
-echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_bitmap/enable
-echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_collision/enable
----
- include/net/mac80211.h | 1 +
- net/mac80211/rx.c | 6 +++++-
- net/mac80211/trace.h | 21 +++++++++++++++++++++
- 3 files changed, 27 insertions(+), 1 deletion(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 65ba482..1a13d47 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -744,6 +744,7 @@ struct ieee80211_bss_conf {
- } he_oper;
- struct ieee80211_he_obss_pd he_obss_pd;
- struct cfg80211_he_bss_color he_bss_color;
-+ u64 used_color_bitmap;
- struct ieee80211_fils_discovery fils_discovery;
- u32 unsol_bcast_probe_resp_interval;
- struct cfg80211_bitrate_mask beacon_tx_rate;
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index c23b74a..2df16de 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3355,9 +3355,13 @@ ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
-
- color = le32_get_bits(he_oper->he_oper_params,
- IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
-+
-+ bss_conf->used_color_bitmap |= BIT_ULL(color);
-+
-+ // trace_bss_color_bitmap(color, bss_conf->used_color_bitmap);
- if (color == bss_conf->he_bss_color.color)
- ieee80211_obss_color_collision_notify(&rx->sdata->vif,
-- BIT_ULL(color),
-+ bss_conf->used_color_bitmap,
- GFP_ATOMIC);
- }
- }
-diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
-index b8c53b4..814aed6 100644
---- a/net/mac80211/trace.h
-+++ b/net/mac80211/trace.h
-@@ -3060,6 +3060,27 @@ TRACE_EVENT(stop_queue,
- )
- );
-
-+TRACE_EVENT(bss_color_bitmap,
-+ TP_PROTO(u8 color,
-+ u64 color_bitmap),
-+
-+ TP_ARGS(color, color_bitmap),
-+
-+ TP_STRUCT__entry(
-+ __field(u8, color)
-+ __field(u64, color_bitmap)
-+ ),
-+
-+ TP_fast_assign(
-+ __entry->color = color;
-+ __entry->color_bitmap = color_bitmap;
-+ ),
-+
-+ TP_printk(
-+ "color=%u color_bitmap=0x%llx", __entry->color, __entry->color_bitmap
-+ )
-+);
-+
- #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
-
- #undef TRACE_INCLUDE_PATH
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0012-mtk-mac80211-ageout-color-bitmap.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0012-mtk-mac80211-ageout-color-bitmap.patch
deleted file mode 100644
index 4d838c8..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0012-mtk-mac80211-ageout-color-bitmap.patch
+++ /dev/null
@@ -1,146 +0,0 @@
-From 999bc7e5454349e1d43e7eaedccbbf291446a0fd Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Mon, 13 Mar 2023 05:36:59 +0800
-Subject: [PATCH 12/37] mtk: mac80211: ageout color bitmap
-
-Adding a periodic work which runs once per second to check BSS color.
-OBSS BSS Color will be ageout if not seen for 10 seconds.
----
- include/net/mac80211.h | 1 +
- net/mac80211/cfg.c | 30 ++++++++++++++++++++++++++++++
- net/mac80211/ieee80211_i.h | 5 +++++
- net/mac80211/iface.c | 5 +++++
- net/mac80211/link.c | 2 ++
- net/mac80211/rx.c | 1 +
- 6 files changed, 44 insertions(+)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 1a13d47..361fe92 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -745,6 +745,7 @@ struct ieee80211_bss_conf {
- struct ieee80211_he_obss_pd he_obss_pd;
- struct cfg80211_he_bss_color he_bss_color;
- u64 used_color_bitmap;
-+ u64 color_last_seen[64];
- struct ieee80211_fils_discovery fils_discovery;
- u32 unsol_bcast_probe_resp_interval;
- struct cfg80211_bitrate_mask beacon_tx_rate;
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index af284d2..de3d181 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4889,6 +4889,36 @@ out:
- return err;
- }
-
-+void
-+ieee80211_color_aging_work(struct work_struct *work)
-+{
-+ struct ieee80211_sub_if_data *sdata =
-+ container_of(work, struct ieee80211_sub_if_data,
-+ deflink.color_aging_work.work);
-+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
-+ int i = 0;
-+
-+ sdata_lock(sdata);
-+
-+ if (!ieee80211_sdata_running(sdata))
-+ goto unlock;
-+
-+ for (i = 0; i < IEEE80211_BSS_COLOR_MAX; i++) {
-+ /* ageout if not seen for a period */
-+ if ((bss_conf->used_color_bitmap & BIT_ULL(i)) &&
-+ time_before(bss_conf->color_last_seen[i],
-+ jiffies - IEEE80211_BSS_COLOR_AGEOUT_TIME * HZ)) {
-+ bss_conf->used_color_bitmap &= ~BIT_ULL(i);
-+ }
-+ }
-+
-+ ieee80211_queue_delayed_work(&sdata->local->hw,
-+ &sdata->deflink.color_aging_work, HZ);
-+
-+unlock:
-+ sdata_unlock(sdata);
-+}
-+
- static int
- ieee80211_set_radar_background(struct wiphy *wiphy,
- struct cfg80211_chan_def *chandef)
-diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
-index aecc401..6b0b149 100644
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -992,6 +992,7 @@ struct ieee80211_link_data {
-
- struct work_struct color_change_finalize_work;
- struct delayed_work color_collision_detect_work;
-+ struct delayed_work color_aging_work;
- u64 color_bitmap;
-
- /* context reservation -- protected with chanctx_mtx */
-@@ -1991,9 +1992,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work);
- int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_csa_settings *params);
-
-+#define IEEE80211_BSS_COLOR_AGEOUT_TIME 10
-+#define IEEE80211_BSS_COLOR_MAX 64
-+
- /* color change handling */
- void ieee80211_color_change_finalize_work(struct work_struct *work);
- void ieee80211_color_collision_detection_work(struct work_struct *work);
-+void ieee80211_color_aging_work(struct work_struct *work);
-
- /* interface handling */
- #define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index 4de8d3d..b3de593 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -541,6 +541,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
- cancel_work_sync(&sdata->deflink.color_change_finalize_work);
-
- cancel_delayed_work_sync(&sdata->deflink.dfs_cac_timer_work);
-+ cancel_delayed_work_sync(&sdata->deflink.color_aging_work);
-
- if (sdata->wdev.cac_started) {
- chandef = sdata->vif.bss_conf.chandef;
-@@ -1474,6 +1475,10 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
-
- set_bit(SDATA_STATE_RUNNING, &sdata->state);
-
-+ if (sdata->vif.type == NL80211_IFTYPE_AP)
-+ ieee80211_queue_delayed_work(&sdata->local->hw,
-+ &sdata->deflink.color_aging_work, HZ);
-+
- return 0;
- err_del_interface:
- drv_remove_interface(local, sdata);
-diff --git a/net/mac80211/link.c b/net/mac80211/link.c
-index 16cbaea..116100a 100644
---- a/net/mac80211/link.c
-+++ b/net/mac80211/link.c
-@@ -47,6 +47,8 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
- INIT_LIST_HEAD(&link->reserved_chanctx_list);
- INIT_DELAYED_WORK(&link->dfs_cac_timer_work,
- ieee80211_dfs_cac_timer_work);
-+ INIT_DELAYED_WORK(&link->color_aging_work,
-+ ieee80211_color_aging_work);
-
- if (!deflink) {
- switch (sdata->vif.type) {
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 2df16de..fead07e 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3357,6 +3357,7 @@ ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
- IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
-
- bss_conf->used_color_bitmap |= BIT_ULL(color);
-+ bss_conf->color_last_seen[color] = jiffies;
-
- // trace_bss_color_bitmap(color, bss_conf->used_color_bitmap);
- if (color == bss_conf->he_bss_color.color)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0013-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0013-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
deleted file mode 100644
index 3f0264a..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0013-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 27ea38045f6ef4a8296d8e019114aa1508e3ad9f Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 14 Apr 2023 05:05:17 +0800
-Subject: [PATCH 13/37] mtk: mac80211: update max_bssid_indicator based on real
- BSS numbers
-
----
- net/mac80211/cfg.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index de3d181..9b9be1a 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1186,9 +1186,11 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- /* copy in optional mbssid_ies */
- if (mbssid) {
- u8 *pos = new->tail + new->tail_len;
-+ u8 *bssid_indicator;
-
- new->mbssid_ies = (void *)pos;
- pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
-+ bssid_indicator = pos + 2;
- pos += ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies,
- mbssid);
- if (rnr) {
-@@ -1197,8 +1199,7 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- ieee80211_copy_rnr_beacon(pos, new->rnr_ies, rnr);
- }
- /* update bssid_indicator */
-- link_conf->bssid_indicator =
-- ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
-+ sdata->vif.bss_conf.bssid_indicator = *(bssid_indicator);
- }
-
- if (csa) {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0014-mtk-mac80211-support-configurable-addba-resp-time.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0014-mtk-mac80211-support-configurable-addba-resp-time.patch
deleted file mode 100644
index 3b04947..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0014-mtk-mac80211-support-configurable-addba-resp-time.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 9b75b1851bdef31b88ebdce320c65e41cc12b7aa Mon Sep 17 00:00:00 2001
-From: Lian Chen <lian.chen@mediatek.com>
-Date: Wed, 7 Jun 2023 15:30:34 +0800
-Subject: [PATCH 14/37] mtk: mac80211: support configurable addba resp time.
-
----
- net/mac80211/agg-tx.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 80cd642..7f66e69 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -16,10 +16,16 @@
- #include <linux/slab.h>
- #include <linux/export.h>
- #include <net/mac80211.h>
-+#include <linux/moduleparam.h>
- #include "ieee80211_i.h"
- #include "driver-ops.h"
- #include "wme.h"
-
-+static int addba_resp_wait_count = 2;
-+module_param(addba_resp_wait_count, int, 0644);
-+MODULE_PARM_DESC(addba_resp_wait_count,
-+ "Number of ADDBA_RESP_INTERVAL to wait for addba response");
-+
- /**
- * DOC: TX A-MPDU aggregation
- *
-@@ -466,7 +472,7 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
- lockdep_assert_held(&sta->ampdu_mlme.mtx);
-
- /* activate the timer for the recipient's addBA response */
-- mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
-+ mod_timer(&tid_tx->addba_resp_timer, jiffies + addba_resp_wait_count * ADDBA_RESP_INTERVAL);
- ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
- sta->sta.addr, tid);
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0015-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0015-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
deleted file mode 100644
index 6fc06d2..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0015-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
+++ /dev/null
@@ -1,181 +0,0 @@
-From c30a607cc3e06ced08c3b93f13706fb158a4d137 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:25:24 +0800
-Subject: [PATCH 15/37] mtk: mac80211: add sta-assisted DFS state update
- mechanism
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 14 +++++++++
- include/uapi/linux/nl80211.h | 6 ++++
- net/mac80211/mlme.c | 12 ++++++++
- net/wireless/chan.c | 60 ++++++++++++++++++++++++++++++++++++
- 4 files changed, 92 insertions(+)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 9567c1b..0b5a4e6 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -8526,6 +8526,20 @@ void cfg80211_cac_event(struct net_device *netdev,
- const struct cfg80211_chan_def *chandef,
- enum nl80211_radar_event event, gfp_t gfp);
-
-+/**
-+ * cfg80211_sta_update_dfs_state - Update channel's DFS state during STA channel switch,
-+ * association, and disassociation
-+ * @wdev: the wireless device
-+ * @bss_chandef: the current BSS channel definition
-+ * @csa_chandef: the CSA channel definition
-+ * @associated: whether STA is during association or disassociation process
-+ *
-+ */
-+void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
-+ const struct cfg80211_chan_def *bss_chandef,
-+ const struct cfg80211_chan_def *csa_chandef,
-+ bool associated);
-+
- /**
- * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
- * @wiphy: the wiphy
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index 72ef4d9..60c6f79 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -6671,6 +6671,10 @@ enum nl80211_smps_mode {
- * applicable for ETSI dfs domain where pre-CAC is valid for ever.
- * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
- * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-+ * when receiving CSA/assoc resp
-+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-+ * when STA is disconnected or leaving the channel
- */
- enum nl80211_radar_event {
- NL80211_RADAR_DETECTED,
-@@ -6679,6 +6683,8 @@ enum nl80211_radar_event {
- NL80211_RADAR_NOP_FINISHED,
- NL80211_RADAR_PRE_CAC_EXPIRED,
- NL80211_RADAR_CAC_STARTED,
-+ NL80211_RADAR_STA_CAC_SKIPPED,
-+ NL80211_RADAR_STA_CAC_EXPIRED,
- };
-
- /**
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 24d34d4..6077e89 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -1981,6 +1981,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- mutex_unlock(&local->mtx);
-
-+ cfg80211_sta_update_dfs_state(&sdata->wdev,
-+ &sdata->vif.bss_conf.chandef,
-+ &link->csa_chandef,
-+ sdata->vif.cfg.assoc);
-+
- cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
- link->link_id, csa_ie.count,
- csa_ie.mode, 0);
-@@ -3066,6 +3071,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
- sizeof(sdata->vif.bss_conf.tx_pwr_env));
-
- ieee80211_vif_set_links(sdata, 0, 0);
-+
-+ cfg80211_sta_update_dfs_state(&sdata->wdev,
-+ &sdata->vif.bss_conf.chandef,
-+ NULL, sdata->vif.cfg.assoc);
- }
-
- static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
-@@ -5469,6 +5478,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
- event.u.mlme.status = MLME_SUCCESS;
- drv_event_callback(sdata->local, sdata, &event);
- sdata_info(sdata, "associated\n");
-+ cfg80211_sta_update_dfs_state(&sdata->wdev,
-+ &sdata->vif.bss_conf.chandef,
-+ NULL, sdata->vif.cfg.assoc);
-
- info.success = 1;
- }
-diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index 30a2f00..41644e2 100644
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -14,6 +14,7 @@
- #include <net/cfg80211.h>
- #include "core.h"
- #include "rdev-ops.h"
-+#include "nl80211.h"
-
- static bool cfg80211_valid_60g_freq(u32 freq)
- {
-@@ -1438,6 +1439,65 @@ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
- }
- EXPORT_SYMBOL(cfg80211_any_usable_channels);
-
-+static void cfg80211_sta_radar_notify(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef,
-+ enum nl80211_radar_event event)
-+{
-+ struct wireless_dev *wdev;
-+
-+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
-+ if (cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) {
-+ nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef,
-+ event, wdev->netdev, GFP_KERNEL);
-+ return;
-+ }
-+ }
-+}
-+
-+void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
-+ const struct cfg80211_chan_def *bss_chandef,
-+ const struct cfg80211_chan_def *csa_chandef,
-+ bool associated)
-+{
-+ bool csa_active = !!csa_chandef;
-+ enum nl80211_dfs_state dfs_state = NL80211_DFS_USABLE;
-+ enum nl80211_radar_event event = NL80211_RADAR_STA_CAC_EXPIRED;
-+
-+ if (!bss_chandef)
-+ return;
-+
-+ /* assume csa channel is cac completed */
-+ if (csa_active &&
-+ (cfg80211_chandef_dfs_usable(wdev->wiphy, csa_chandef) ||
-+ cfg80211_chandef_dfs_available(wdev->wiphy, csa_chandef))) {
-+ cfg80211_set_dfs_state(wdev->wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
-+ cfg80211_sta_radar_notify(wdev->wiphy, csa_chandef,
-+ NL80211_RADAR_STA_CAC_SKIPPED);
-+ netdev_info(wdev->netdev, "Set CSA channel's DFS state to available\n");
-+ }
-+
-+ /* avoid updating the dfs state during nop */
-+ if (!cfg80211_chandef_dfs_usable(wdev->wiphy, bss_chandef) &&
-+ !cfg80211_chandef_dfs_available(wdev->wiphy, bss_chandef))
-+ return;
-+
-+ if (associated && !csa_active) {
-+ dfs_state = NL80211_DFS_AVAILABLE;
-+ event = NL80211_RADAR_STA_CAC_SKIPPED;
-+ }
-+
-+ cfg80211_set_dfs_state(wdev->wiphy, bss_chandef, dfs_state);
-+ cfg80211_sta_radar_notify(wdev->wiphy, bss_chandef, event);
-+
-+ if (csa_active)
-+ netdev_info(wdev->netdev, "Set origin channel's DFS state to usable\n");
-+ else
-+ netdev_info(wdev->netdev, "Set BSS channel's DFS state to %s due to %s\n",
-+ (dfs_state == NL80211_DFS_USABLE) ? "usable" : "available",
-+ associated ? "association" : "disassociation");
-+}
-+EXPORT_SYMBOL(cfg80211_sta_update_dfs_state);
-+
- struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
- unsigned int link_id)
- {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0016-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0016-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
deleted file mode 100644
index e216b2d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0016-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From e915ce4271b5d63fb7018295f0bd853e38b198c0 Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Fri, 17 Mar 2023 17:36:01 +0800
-Subject: [PATCH 16/37] mtk: nl80211: Mark DFS channel as available for CSA.
-
----
- net/wireless/nl80211.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index c652a01..3046677 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10269,6 +10269,11 @@ skip_beacons:
- if (err)
- goto free;
-
-+ /* Use RADAR_BACKGROUND attribute here for skipping CAC */
-+ if (info->attrs[NL80211_ATTR_RADAR_BACKGROUND]) {
-+ cfg80211_set_dfs_state(&rdev->wiphy, ¶ms.chandef, NL80211_DFS_AVAILABLE);
-+ }
-+
- if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef,
- wdev->iftype)) {
- err = -EINVAL;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0017-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0017-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
deleted file mode 100644
index f607975..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0017-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From e95e57878ced57d49e5aefe777f8922761d2f8f1 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 27 Jul 2023 10:25:59 +0800
-Subject: [PATCH 17/37] mtk: cfg80211: fix early return in
- cfg80211_stop_background_radar_detection
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/wireless/mlme.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index 70b4013..e264609 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1184,9 +1184,9 @@ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
- return;
-
- rdev_set_radar_background(rdev, NULL);
-- rdev->background_radar_wdev = NULL; /* Release offchain ownership */
-
- __cfg80211_background_cac_event(rdev, wdev,
- &rdev->background_radar_chandef,
- NL80211_RADAR_CAC_ABORTED);
-+ rdev->background_radar_wdev = NULL; /* Release offchain ownership */
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0018-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0018-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
deleted file mode 100644
index b4d9669..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0018-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 7aa02107ca82d71f0a6f2d892bbf72b76fe8d2d0 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 27 Jul 2023 10:27:04 +0800
-Subject: [PATCH 18/37] mtk: cfg80211: add background radar stop when
- background channel is overlapped with operating channel
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/wireless/nl80211.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 3046677..2be678c 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10040,6 +10040,10 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- wdev->cac_started = true;
- wdev->cac_start_time = jiffies;
- wdev->cac_time_ms = cac_time_ms;
-+ if (rdev->background_cac_started &&
-+ cfg80211_is_sub_chan(&chandef, rdev->background_radar_chandef.chan, false)) {
-+ cfg80211_stop_background_radar_detection(rdev->background_radar_wdev);
-+ }
- }
- unlock:
- wiphy_unlock(wiphy);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0019-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0019-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
deleted file mode 100644
index 748cf7c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0019-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From b72e19fd852a014531dcfc667eb82f2a26b9196b Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Thu, 3 Aug 2023 07:17:44 +0800
-Subject: [PATCH 19/37] mtk: mac80211: avoid kernel warning of
- check_flush_dependency
-
----
- net/mac80211/main.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/mac80211/main.c b/net/mac80211/main.c
-index a7acd22..168f09d 100644
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -1286,7 +1286,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
- hw->queues = IEEE80211_MAX_QUEUES;
-
- local->workqueue =
-- alloc_ordered_workqueue("%s", 0, wiphy_name(local->hw.wiphy));
-+ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, wiphy_name(local->hw.wiphy));
- if (!local->workqueue) {
- result = -ENOMEM;
- goto fail_workqueue;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0020-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0020-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch
deleted file mode 100644
index bb3981b..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0020-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 4accf70da3e00dfc1021d751310b4564fb1fad3f Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 7 Aug 2023 19:00:53 +0800
-Subject: [PATCH 20/37] mtk: mac80211: avoid calling switch_vif_chanctx when
- use_chanctx is false
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/mac80211/chan.c | 14 ++++++++------
- 1 file changed, 8 insertions(+), 6 deletions(-)
-
-diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
-index 6895275..96f1ad0 100644
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -1304,13 +1304,15 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
- list_del(&link->reserved_chanctx_list);
- link->reserved_chanctx = NULL;
-
-- err = drv_switch_vif_chanctx(local, vif_chsw, 1,
-- CHANCTX_SWMODE_REASSIGN_VIF);
-- if (err) {
-- if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
-- ieee80211_free_chanctx(local, new_ctx);
-+ if (local->use_chanctx) {
-+ err = drv_switch_vif_chanctx(local, vif_chsw, 1,
-+ CHANCTX_SWMODE_REASSIGN_VIF);
-+ if (err) {
-+ if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
-+ ieee80211_free_chanctx(local, new_ctx);
-
-- goto out;
-+ goto out;
-+ }
- }
-
- list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0021-mtk-mac80211-Add-utilities-for-converting-op_class.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0021-mtk-mac80211-Add-utilities-for-converting-op_class.patch
deleted file mode 100644
index 124ac8e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0021-mtk-mac80211-Add-utilities-for-converting-op_class.patch
+++ /dev/null
@@ -1,201 +0,0 @@
-From 3012337540e2bfdf708cdd113c25c2501bf63da7 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 14 Aug 2023 18:03:29 +0800
-Subject: [PATCH 21/37] mtk: mac80211: Add utilities for converting op_class
-
-These utilities include converting op_class to nl80211 channel width and
-center frequency.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- include/net/cfg80211.h | 25 ++++++++
- net/wireless/util.c | 130 ++++++++++++++++++++++++++++++++++++++++-
- 2 files changed, 154 insertions(+), 1 deletion(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 0b5a4e6..19c8abe 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -8723,6 +8723,31 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev,
- bool ieee80211_operating_class_to_band(u8 operating_class,
- enum nl80211_band *band);
-
-+/**
-+ * ieee80211_operating_class_to_center_freq - convert operating class to
-+ * center frequency
-+ *
-+ * @operating_class: the operating class to convert
-+ * @chan: the ieee80211_channel to convert
-+ * @center_freq1: cneter frequency 1 pointer to fill
-+ * @center_freq2: cneter frequency 2 pointer to fill
-+ *
-+ * Returns %true if the conversion was successful, %false otherwise.
-+ */
-+bool ieee80211_operating_class_to_center_freq(u8 operating_class,
-+ struct ieee80211_channel *chan,
-+ u32 *center_freq1,
-+ u32 *center_freq2);
-+
-+/**
-+ * ieee80211_operating_class_to_chan_width - convert operating class to
-+ * nl80211 channel width
-+ *
-+ * @operating_class: the operating class to convert
-+ */
-+enum nl80211_chan_width
-+ieee80211_operating_class_to_chan_width(u8 operating_class);
-+
- /**
- * ieee80211_chandef_to_operating_class - convert chandef to operation class
- *
-diff --git a/net/wireless/util.c b/net/wireless/util.c
-index 1783ab9..13cb986 100644
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -1981,7 +1981,7 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
- case 128 ... 130:
- *band = NL80211_BAND_5GHZ;
- return true;
-- case 131 ... 135:
-+ case 131 ... 137:
- *band = NL80211_BAND_6GHZ;
- return true;
- case 81:
-@@ -1999,6 +1999,134 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
- }
- EXPORT_SYMBOL(ieee80211_operating_class_to_band);
-
-+bool ieee80211_operating_class_to_center_freq(u8 operating_class,
-+ struct ieee80211_channel *chan,
-+ u32 *center_freq1,
-+ u32 *center_freq2)
-+{
-+ u32 control_freq, offset;
-+ enum nl80211_band band;
-+
-+ control_freq = chan->center_freq;
-+ if (!ieee80211_operating_class_to_band(operating_class, &band))
-+ return false;
-+
-+ if (band != chan->band)
-+ return false;
-+
-+ if (control_freq >= 5955)
-+ offset = control_freq - 5955;
-+ else if (control_freq >= 5745)
-+ offset = control_freq - 5745;
-+ else if (control_freq >= 5180)
-+ offset = control_freq - 5180;
-+ offset /= 20;
-+
-+ *center_freq2 = 0;
-+ switch (operating_class) {
-+ case 81: /* 2 GHz band; 20 MHz; channels 1..13 */
-+ case 82: /* 2 GHz band; 20 MHz; channel 14 */
-+ case 115: /* 5 GHz band; 20 MHz; channels 36,40,44,48 */
-+ case 118: /* 5 GHz band; 20 MHz; channels 52,56,60,64 */
-+ case 121: /* 5 GHz band; 20 MHz; channels 100..144 */
-+ case 124: /* 5 GHz band; 20 MHz; channels 149,153,157,161 */
-+ case 125: /* 5 GHz band; 20 MHz; channels 149..177 */
-+ case 131: /* 6 GHz band; 20 MHz; channels 1..233*/
-+ case 136: /* 6 GHz band; 20 MHz; channel 2 */
-+ *center_freq1 = control_freq;
-+ return true;
-+ case 83: /* 2 GHz band; 40 MHz; channels 1..9 */
-+ case 116: /* 5 GHz band; 40 MHz; channels 36,44 */
-+ case 119: /* 5 GHz band; 40 MHz; channels 52,60 */
-+ case 122: /* 5 GHz band; 40 MHz; channels 100,108,116,124,132,140 */
-+ case 126: /* 5 GHz band; 40 MHz; channels 149,157,165,173 */
-+ *center_freq1 = control_freq + 10;
-+ return true;
-+ case 84: /* 2 GHz band; 40 MHz; channels 5..13 */
-+ case 117: /* 5 GHz band; 40 MHz; channels 40,48 */
-+ case 120: /* 5 GHz band; 40 MHz; channels 56,64 */
-+ case 123: /* 5 GHz band; 40 MHz; channels 104,112,120,128,136,144 */
-+ case 127: /* 5 GHz band; 40 MHz; channels 153,161,169,177 */
-+ *center_freq1 = control_freq - 10;
-+ return true;
-+ case 132: /* 6 GHz band; 40 MHz; channels 1,5,..,229*/
-+ *center_freq1 = control_freq + 10 - (offset & 1) * 20;
-+ return true;
-+ case 128: /* 5 GHz band; 80 MHz; channels 36..64,100..144,149..177 */
-+ *center_freq1 = control_freq + 30 - (offset & 3) * 20;
-+ return true;
-+ case 130: /* 5 GHz band; 80+80 MHz; channels 36..64,100..144,149..177 */
-+ /* TODO How to know the center_freq2 of 80+80 MHz?*/
-+ *center_freq1 = 0;
-+ return false;
-+ case 133: /* 6 GHz band; 80 MHz; channels 1,5,..,229 */
-+ *center_freq1 = control_freq + 30 - (offset & 3) * 20;
-+ return true;
-+ case 129: /* 5 GHz band; 160 MHz; channels 36..64,100..144,149..177 */
-+ *center_freq1 = control_freq + 70 - (offset & 7) * 20;
-+ return true;
-+ case 134: /* 6 GHz band; 160 MHz; channels 1,5,..,229 */
-+ *center_freq1 = control_freq + 70 - (offset & 7) * 20;
-+ return true;
-+ case 135: /* 6 GHz band; 80+80 MHz; channels 1,5,..,229 */
-+ /* TODO How to know the center_freq2 of 80+80 MHz?*/
-+ *center_freq1 = 0;
-+ return false;
-+ case 137: /* 6 GHz band; 320 MHz; channels 1,5,..,229 */
-+ /* TODO it's 320-1 or 320-2 channelization? */
-+ /* Currently convert to 320-1 */
-+ *center_freq1 = control_freq + 150 - (offset & 15) * 20;
-+ return true;
-+ default:
-+ return false;
-+ }
-+}
-+EXPORT_SYMBOL(ieee80211_operating_class_to_center_freq);
-+
-+enum nl80211_chan_width
-+ieee80211_operating_class_to_chan_width(u8 operating_class)
-+{
-+ switch (operating_class) {
-+ case 81: /* 2 GHz band; 20 MHz; channels 1..13 */
-+ case 82: /* 2 GHz band; 20 MHz; channel 14 */
-+ case 115: /* 5 GHz band; 20 MHz; channels 36,40,44,48 */
-+ case 118: /* 5 GHz band; 20 MHz; channels 52,56,60,64 */
-+ case 121: /* 5 GHz band; 20 MHz; channels 100..144 */
-+ case 124: /* 5 GHz band; 20 MHz; channels 149,153,157,161 */
-+ case 125: /* 5 GHz band; 20 MHz; channels 149..177 */
-+ case 131: /* 6 GHz band; 20 MHz; channels 1..233*/
-+ case 136: /* 6 GHz band; 20 MHz; channel 2 */
-+ return NL80211_CHAN_WIDTH_20;
-+ case 83: /* 2 GHz band; 40 MHz; channels 1..9 */
-+ case 84: /* 2 GHz band; 40 MHz; channels 5..13 */
-+ case 116: /* 5 GHz band; 40 MHz; channels 36,44 */
-+ case 117: /* 5 GHz band; 40 MHz; channels 40,48 */
-+ case 119: /* 5 GHz band; 40 MHz; channels 52,60 */
-+ case 120: /* 5 GHz band; 40 MHz; channels 56,64 */
-+ case 122: /* 5 GHz band; 40 MHz; channels 100,108,116,124,132,140 */
-+ case 123: /* 5 GHz band; 40 MHz; channels 104,112,120,128,136,144 */
-+ case 126: /* 5 GHz band; 40 MHz; channels 149,157,165,173 */
-+ case 127: /* 5 GHz band; 40 MHz; channels 153,161,169,177 */
-+ case 132: /* 6 GHz band; 40 MHz; channels 1,5,..,229*/
-+ return NL80211_CHAN_WIDTH_40;
-+ case 128: /* 5 GHz band; 80 MHz; channels 36..64,100..144,149..177 */
-+ case 133: /* 6 GHz band; 80 MHz; channels 1,5,..,229 */
-+ return NL80211_CHAN_WIDTH_80;
-+ case 130: /* 5 GHz band; 80+80 MHz; channels 36..64,100..144,149..177 */
-+ case 135: /* 6 GHz band; 80+80 MHz; channels 1,5,..,229 */
-+ return NL80211_CHAN_WIDTH_80P80;
-+ case 129: /* 5 GHz band; 160 MHz; channels 36..64,100..144,149..177 */
-+ case 134: /* 6 GHz band; 160 MHz; channels 1,5,..,229 */
-+ return NL80211_CHAN_WIDTH_160;
-+ case 137: /* 6 GHz band; 320 MHz; channels 1,5,..,229 */
-+ return NL80211_CHAN_WIDTH_320;
-+ default:
-+ WARN_ON(1);
-+ return NL80211_CHAN_WIDTH_20_NOHT;
-+ }
-+}
-+EXPORT_SYMBOL(ieee80211_operating_class_to_chan_width);
-+
- bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
- u8 *op_class)
- {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0022-mtk-mac80211-refactor-STA-CSA-parsing-flows.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0022-mtk-mac80211-refactor-STA-CSA-parsing-flows.patch
deleted file mode 100644
index af8fb94..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0022-mtk-mac80211-refactor-STA-CSA-parsing-flows.patch
+++ /dev/null
@@ -1,598 +0,0 @@
-From 8ecaec9854b8449c824f77a5b3b6c6bba6720b9d Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 28 Sep 2023 09:28:50 +0800
-Subject: [PATCH 22/37] mtk: mac80211: refactor STA CSA parsing flows
-
-This patch changes the flows when STA parses csa IE:
-
-1. If Extended CSA IE is presented, this patch uses the subfield op_class
- to build the VHT and HE operation in 5 GHz and 6 GHz, respectively.
-2. If Extended CSA IE is NOT presented but the Wide Bandwidth Channel
- Switch IE is presented, the new channel width is at least 40 MHz.
- Therefore this patch first prepares a 40 MHz chandef, then build the
- VHT and HE operation in 5 GHz and 6 GHz, respectively.
- Note that in a 6 GHz BSS, the subfield of Wide Bandwidth Channel Switch
- IE might be the VHT, deprecated VHT or HE operation, so this patch
- checks the combination of new channel width, ccfs0 and ccfs1 to build
- the correct HE operation.
-3. From the HE/VHT operation created in step 1 or 2, this patch then
- creates the chandef and assigns it to csa_ie.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- net/mac80211/spectmgmt.c | 508 ++++++++++++++++++++++++++++++++++-----
- 1 file changed, 454 insertions(+), 54 deletions(-)
-
-diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
-index 871cdac..dee7a60 100644
---- a/net/mac80211/spectmgmt.c
-+++ b/net/mac80211/spectmgmt.c
-@@ -19,6 +19,332 @@
- #include "sta_info.h"
- #include "wme.h"
-
-+static inline void
-+op_class_to_6ghz_he_eht_oper(u8 op_class, struct ieee80211_channel *chan,
-+ struct ieee80211_he_operation *he_oper,
-+ struct ieee80211_eht_operation *eht_oper)
-+{
-+ u8 new_chan_width;
-+ u32 he_oper_params, center_freq1 = 0, center_freq2 = 0;
-+ struct ieee80211_he_6ghz_oper *he_6ghz_oper;
-+ struct ieee80211_eht_operation_info *eht_oper_info;
-+
-+ new_chan_width = ieee80211_operating_class_to_chan_width(op_class);
-+ if (!ieee80211_operating_class_to_center_freq(op_class, chan,
-+ ¢er_freq1,
-+ ¢er_freq2)) {
-+ new_chan_width = NL80211_CHAN_WIDTH_20;
-+ center_freq1 = chan->center_freq;
-+ }
-+
-+ he_oper_params =
-+ u32_encode_bits(1, IEEE80211_HE_OPERATION_6GHZ_OP_INFO);
-+ he_oper->he_oper_params = cpu_to_le32(he_oper_params);
-+
-+ he_6ghz_oper = (struct ieee80211_he_6ghz_oper *)he_oper->optional;
-+ he_6ghz_oper->primary =
-+ ieee80211_frequency_to_channel(chan->center_freq);
-+ he_6ghz_oper->ccfs0 = ieee80211_frequency_to_channel(center_freq1);
-+ he_6ghz_oper->ccfs1 = center_freq2 ?
-+ ieee80211_frequency_to_channel(center_freq2) : 0;
-+
-+ switch (new_chan_width) {
-+ case NL80211_CHAN_WIDTH_320:
-+ /* Cannot derive center frequency for 320 MHZ from op_class
-+ * since it might be 320-1 or 320-2
-+ */
-+ WARN_ON(1);
-+ break;
-+ case NL80211_CHAN_WIDTH_160:
-+ he_6ghz_oper->ccfs1 = he_6ghz_oper->ccfs0;
-+ he_6ghz_oper->ccfs0 += chan->center_freq < center_freq1 ? -8 : 8;
-+ fallthrough;
-+ case NL80211_CHAN_WIDTH_80P80:
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
-+ break;
-+ case NL80211_CHAN_WIDTH_80:
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
-+ break;
-+ case NL80211_CHAN_WIDTH_40:
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
-+ break;
-+ default:
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ;
-+ break;
-+ }
-+
-+ eht_oper->params = IEEE80211_EHT_OPER_INFO_PRESENT;
-+
-+ eht_oper_info =
-+ (struct ieee80211_eht_operation_info *)eht_oper->optional;
-+ eht_oper_info->control = he_6ghz_oper->control;
-+ eht_oper_info->ccfs0 = he_6ghz_oper->ccfs0;
-+ eht_oper_info->ccfs1 = he_6ghz_oper->ccfs1;
-+}
-+
-+static inline void
-+wbcs_ie_to_6ghz_he_eht_oper(const struct ieee80211_wide_bw_chansw_ie *wbcs_ie,
-+ u8 new_chan_no,
-+ struct ieee80211_he_operation *he_oper,
-+ struct ieee80211_eht_operation *eht_oper)
-+{
-+ u32 he_oper_params;
-+ struct ieee80211_he_6ghz_oper *he_6ghz_oper;
-+ struct ieee80211_eht_operation_info *eht_oper_info;
-+ bool fallback_20mhz;
-+ u8 ccfs_diff;
-+
-+ he_oper_params =
-+ u32_encode_bits(1, IEEE80211_HE_OPERATION_6GHZ_OP_INFO);
-+ he_oper->he_oper_params = cpu_to_le32(he_oper_params);
-+
-+ he_6ghz_oper = (struct ieee80211_he_6ghz_oper *)he_oper->optional;
-+ he_6ghz_oper->primary = new_chan_no;
-+
-+ /* The Wide Bandwidth Channel Switch IE in a 6 GHz BSS migth be
-+ * deprecated VHT operation, VHT operation (IEEE 802.11-2020 9.4.2.160)
-+ * or HE operation (IEEE P80211be D3.2 9.4.2.159).
-+ * Check the combination of width, ccfs0 and ccfs1 to build the correct
-+ * HE/EHT operation.
-+ */
-+ he_6ghz_oper->ccfs0 = wbcs_ie->new_center_freq_seg0;
-+ he_6ghz_oper->ccfs1 = wbcs_ie->new_center_freq_seg1;
-+ switch (wbcs_ie->new_channel_width) {
-+ case 0:
-+ /* Must be [deprecated] VHT operation with 40 MHz bandwidth */
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
-+ break;
-+ case 1:
-+ if (he_6ghz_oper->ccfs1) {
-+ /* VHT operation with 160/80P80 MHz bandwidth */
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
-+ } else if ((he_6ghz_oper->ccfs0 - 7) % 16 == 0) {
-+ /* [deprecated] VHT operation with 80 MHz bandwidth */
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
-+ } else {
-+ /* HE operation with 40 MHz bandwidth */
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
-+ }
-+ break;
-+ case 2:
-+ if ((he_6ghz_oper->ccfs0 - 15) % 32 == 0) {
-+ /* deprecated VHT operation with 160 MHz bandwidth */
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
-+ he_6ghz_oper->ccfs1 = he_6ghz_oper->ccfs0;
-+ he_6ghz_oper->ccfs0 +=
-+ new_chan_no < he_6ghz_oper->ccfs0 ? -8 : 8;
-+ } else {
-+ /* HE operation with 80 MHz bandwidth */
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
-+ }
-+ break;
-+ case 3:
-+ /* Can be
-+ * 1. deprecated VHT operation with 80P80 MHz bandwidth
-+ * 2. HE operation with 160/80P80 MHz bandwidth
-+ */
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
-+ break;
-+ case 4:
-+ /* 320 MHz bandwidth
-+ * TODO channel switch to 320 MHz bandwidth should be indiated
-+ * by Bandwidth Indication IE (IEEE P80211be D3.2 9.4.2.159)
-+ */
-+ he_6ghz_oper->control = IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ;
-+ break;
-+ default:
-+ /* Ignore invalid width */
-+ break;
-+ }
-+
-+ /* Validate the relationship between new channel width and center frequency
-+ * segments, and fallback to 20 MHz if the relationship is wrong.
-+ */
-+ fallback_20mhz = false;
-+ switch (he_6ghz_oper->control) {
-+ case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ:
-+ if ((he_6ghz_oper->ccfs0 - 3) % 8 != 0)
-+ fallback_20mhz = true;
-+ break;
-+ case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ:
-+ if ((he_6ghz_oper->ccfs0 - 7) % 16 != 0)
-+ fallback_20mhz = true;
-+ break;
-+ case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ:
-+ ccfs_diff = abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0);
-+ if ((ccfs_diff == 8 && (he_6ghz_oper->ccfs1 - 15) % 32 != 0) ||
-+ (ccfs_diff > 16 && ((he_6ghz_oper->ccfs0 - 7) % 16 != 0 ||
-+ (he_6ghz_oper->ccfs1 - 7) % 16 != 0)))
-+ fallback_20mhz = true;
-+ break;
-+ case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ:
-+ if ((he_6ghz_oper->ccfs1 - 31) % 32 != 0)
-+ fallback_20mhz = true;
-+ break;
-+ }
-+
-+ if (fallback_20mhz) {
-+ he_6ghz_oper->control =
-+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ;
-+ he_6ghz_oper->ccfs0 = he_6ghz_oper->primary;
-+ he_6ghz_oper->ccfs1 = 0;
-+ }
-+
-+ eht_oper->params = IEEE80211_EHT_OPER_INFO_PRESENT;
-+ eht_oper_info =
-+ (struct ieee80211_eht_operation_info *)eht_oper->optional;
-+ eht_oper_info->control = he_6ghz_oper->control;
-+ eht_oper_info->ccfs0 = he_6ghz_oper->ccfs0;
-+ eht_oper_info->ccfs1 = he_6ghz_oper->ccfs1;
-+}
-+
-+static inline void
-+op_class_to_ht_vht_oper(u8 op_class, struct ieee80211_channel *chan,
-+ struct ieee80211_ht_operation *ht_oper,
-+ struct ieee80211_vht_operation *vht_oper)
-+{
-+ u8 new_chan_width;
-+ u32 center_freq1 = 0, center_freq2 = 0;
-+
-+ new_chan_width = ieee80211_operating_class_to_chan_width(op_class);
-+ if (!ieee80211_operating_class_to_center_freq(op_class, chan,
-+ ¢er_freq1,
-+ ¢er_freq2)) {
-+ new_chan_width = NL80211_CHAN_WIDTH_20;
-+ center_freq1 = chan->center_freq;
-+ }
-+
-+ vht_oper->center_freq_seg0_idx =
-+ ieee80211_frequency_to_channel(center_freq1);
-+ vht_oper->center_freq_seg1_idx = center_freq2 ?
-+ ieee80211_frequency_to_channel(center_freq2) : 0;
-+
-+ ht_oper->ht_param = (chan->center_freq / 20) & 1 ?
-+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE :
-+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
-+
-+ switch (new_chan_width) {
-+ case NL80211_CHAN_WIDTH_320:
-+ WARN_ON(1);
-+ break;
-+ case NL80211_CHAN_WIDTH_160:
-+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
-+ vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx;
-+ vht_oper->center_freq_seg0_idx +=
-+ chan->center_freq < center_freq1 ? -8 : 8;
-+ break;
-+ case NL80211_CHAN_WIDTH_80P80:
-+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
-+ break;
-+ case NL80211_CHAN_WIDTH_80:
-+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
-+ break;
-+ default:
-+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
-+ if (chan->center_freq != center_freq1)
-+ ht_oper->ht_param = chan->center_freq > center_freq1 ?
-+ IEEE80211_HT_PARAM_CHA_SEC_BELOW :
-+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
-+ else
-+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
-+ }
-+
-+ ht_oper->operation_mode =
-+ cpu_to_le16(vht_oper->center_freq_seg1_idx <<
-+ IEEE80211_HT_OP_MODE_CCFS2_SHIFT);
-+}
-+
-+static inline void
-+wbcs_ie_to_ht_vht_oper(struct ieee80211_channel *chan,
-+ const struct ieee80211_wide_bw_chansw_ie *wbcs_ie,
-+ struct ieee80211_ht_operation *ht_oper,
-+ struct ieee80211_vht_operation *vht_oper)
-+{
-+ u8 new_chan_width, new_ccfs0, new_ccfs1;
-+ bool fallback_20mhz;
-+
-+ new_chan_width = wbcs_ie->new_channel_width;
-+ new_ccfs0 = wbcs_ie->new_center_freq_seg0;
-+ new_ccfs1 = wbcs_ie->new_center_freq_seg1;
-+
-+ /* Validate the relationship between new channel width and center frequency
-+ * segments, and fallback to 20 MHz if the relationship is wrong.
-+ */
-+ fallback_20mhz = false;
-+ switch (new_chan_width) {
-+ case IEEE80211_VHT_CHANWIDTH_USE_HT:
-+ /* If the wide bandwidth channel switch IE is presented,
-+ * the new channel width is at least 40 MHz.
-+ */
-+ if (!new_ccfs1) {
-+ new_ccfs0 -= (new_ccfs0 >= 149) ? 151 : 38;
-+ if (new_ccfs0 % 8 != 0)
-+ fallback_20mhz = true;
-+ } else {
-+ fallback_20mhz = true;
-+ }
-+ break;
-+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
-+ if (!new_ccfs1) {
-+ new_ccfs0 -= (new_ccfs0 >= 149) ? 155 : 42;
-+ if (new_ccfs0 % 16 != 0)
-+ fallback_20mhz = true;
-+ break;
-+ } else if (abs(new_ccfs1 - new_ccfs0) == 8) {
-+ new_ccfs0 = new_ccfs1;
-+ new_ccfs1 = 0;
-+ }
-+ fallthrough;
-+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
-+ if (!new_ccfs1) {
-+ if (new_ccfs0 != 50 && new_ccfs0 != 114 && new_ccfs0 != 163)
-+ fallback_20mhz = true;
-+ break;
-+ }
-+ fallthrough;
-+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
-+ new_ccfs0 -= (new_ccfs0 >= 149) ? 155 : 42;
-+ new_ccfs1 -= (new_ccfs1 >= 149) ? 155 : 42;
-+ if (new_ccfs0 % 16 != 0 || new_ccfs1 % 16 != 0)
-+ fallback_20mhz = true;
-+ break;
-+ default:
-+ fallback_20mhz = true;
-+ }
-+
-+ if (fallback_20mhz) {
-+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
-+
-+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
-+ vht_oper->center_freq_seg0_idx =
-+ ieee80211_frequency_to_channel(chan->center_freq);
-+ vht_oper->center_freq_seg1_idx = 0;
-+
-+ } else {
-+ ht_oper->ht_param = (chan->center_freq / 20) & 1 ?
-+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE :
-+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
-+
-+ vht_oper->chan_width = new_chan_width;
-+ vht_oper->center_freq_seg0_idx = wbcs_ie->new_center_freq_seg0;
-+ vht_oper->center_freq_seg1_idx = wbcs_ie->new_center_freq_seg1;
-+ }
-+
-+ ht_oper->operation_mode = cpu_to_le16(vht_oper->center_freq_seg1_idx <<
-+ IEEE80211_HT_OP_MODE_CCFS2_SHIFT);
-+}
-+
- int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems,
- enum nl80211_band current_band,
-@@ -26,19 +352,27 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- ieee80211_conn_flags_t conn_flags, u8 *bssid,
- struct ieee80211_csa_ie *csa_ie)
- {
-+ struct ieee80211_supported_band *sband;
- enum nl80211_band new_band = current_band;
-- int new_freq;
-- u8 new_chan_no;
-+ int new_freq, size;
-+ u8 new_chan_no = 0, new_op_class = 0;
- struct ieee80211_channel *new_chan;
-- struct cfg80211_chan_def new_vht_chandef = {};
-+ struct cfg80211_chan_def new_chandef = {};
- const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
- const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
-+ const struct ieee80211_ext_chansw_ie *ext_chansw_ie;
-+ struct ieee80211_ht_operation *ht_oper;
-+ struct ieee80211_vht_operation *vht_oper;
-+ struct ieee80211_he_operation *he_oper;
-+ struct ieee80211_eht_operation *eht_oper;
-+ struct ieee80211_sta_ht_cap sta_ht_cap;
- int secondary_channel_offset = -1;
-
- memset(csa_ie, 0, sizeof(*csa_ie));
-
- sec_chan_offs = elems->sec_chan_offs;
- wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
-+ ext_chansw_ie = elems->ext_chansw_ie;
-
- if (conn_flags & (IEEE80211_CONN_DISABLE_HT |
- IEEE80211_CONN_DISABLE_40MHZ)) {
-@@ -46,29 +380,30 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- wide_bw_chansw_ie = NULL;
- }
-
-- if (conn_flags & IEEE80211_CONN_DISABLE_VHT)
-- wide_bw_chansw_ie = NULL;
--
-- if (elems->ext_chansw_ie) {
-- if (!ieee80211_operating_class_to_band(
-- elems->ext_chansw_ie->new_operating_class,
-- &new_band)) {
-- sdata_info(sdata,
-- "cannot understand ECSA IE operating class, %d, ignoring\n",
-- elems->ext_chansw_ie->new_operating_class);
-+ if (ext_chansw_ie) {
-+ new_op_class = ext_chansw_ie->new_operating_class;
-+ if (!ieee80211_operating_class_to_band(new_op_class, &new_band)) {
-+ new_op_class = 0;
-+ sdata_info(sdata, "cannot understand ECSA IE "
-+ "operating class, %d, ignoring\n",
-+ ext_chansw_ie->new_operating_class);
-+ } else {
-+ new_chan_no = ext_chansw_ie->new_ch_num;
-+ csa_ie->count = ext_chansw_ie->count;
-+ csa_ie->mode = ext_chansw_ie->mode;
- }
-- new_chan_no = elems->ext_chansw_ie->new_ch_num;
-- csa_ie->count = elems->ext_chansw_ie->count;
-- csa_ie->mode = elems->ext_chansw_ie->mode;
-- } else if (elems->ch_switch_ie) {
-+ }
-+
-+ if (!new_op_class && elems->ch_switch_ie) {
- new_chan_no = elems->ch_switch_ie->new_ch_num;
- csa_ie->count = elems->ch_switch_ie->count;
- csa_ie->mode = elems->ch_switch_ie->mode;
-- } else {
-- /* nothing here we understand */
-- return 1;
- }
-
-+ /* nothing here we understand */
-+ if (!new_chan_no)
-+ return 1;
-+
- /* Mesh Channel Switch Parameters Element */
- if (elems->mesh_chansw_params_ie) {
- csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
-@@ -132,54 +467,119 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- break;
- }
-
-- if (wide_bw_chansw_ie) {
-- u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1;
-- struct ieee80211_vht_operation vht_oper = {
-- .chan_width =
-- wide_bw_chansw_ie->new_channel_width,
-- .center_freq_seg0_idx =
-- wide_bw_chansw_ie->new_center_freq_seg0,
-- .center_freq_seg1_idx = new_seg1,
-- /* .basic_mcs_set doesn't matter */
-- };
-- struct ieee80211_ht_operation ht_oper = {
-- .operation_mode =
-- cpu_to_le16(new_seg1 <<
-- IEEE80211_HT_OP_MODE_CCFS2_SHIFT),
-- };
--
-- /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT,
-- * to the previously parsed chandef
-- */
-- new_vht_chandef = csa_ie->chandef;
-+ if (new_band == NL80211_BAND_6GHZ) {
-+ size = sizeof(struct ieee80211_he_operation) +
-+ sizeof(struct ieee80211_he_6ghz_oper);
-+ he_oper = kzalloc(size, GFP_KERNEL);
-+ if (!he_oper)
-+ return -ENOMEM;
-+
-+ size = sizeof(struct ieee80211_eht_operation) +
-+ sizeof(struct ieee80211_eht_operation_info);
-+ eht_oper = kzalloc(size, GFP_KERNEL);
-+ if (!eht_oper) {
-+ kfree(he_oper);
-+ return -ENOMEM;
-+ }
-+
-+ if (new_op_class && new_op_class != 135 && new_op_class != 137) {
-+ /* There is no way to tell the ccfs1 for op_class 135
-+ * (80P80 MHz) and 137 (320 MHz).
-+ */
-+ op_class_to_6ghz_he_eht_oper(new_op_class, new_chan,
-+ he_oper, eht_oper);
-+ } else if (wide_bw_chansw_ie) {
-+ wbcs_ie_to_6ghz_he_eht_oper(wide_bw_chansw_ie,
-+ new_chan_no, he_oper,
-+ eht_oper);
-+ }
-+
-+ new_chandef = csa_ie->chandef;
-
- /* ignore if parsing fails */
-- if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
-- vht_cap_info,
-- &vht_oper, &ht_oper,
-- &new_vht_chandef))
-- new_vht_chandef.chan = NULL;
-+ if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, eht_oper,
-+ &new_chandef))
-+ new_chandef.chan = NULL;
-+
-+ kfree(he_oper);
-+ kfree(eht_oper);
-+ } else {
-+ sband = sdata->local->hw.wiphy->bands[new_band];
-+ memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
-+ ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
-+
-+ if (!sta_ht_cap.ht_supported ||
-+ !(sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
-+ goto out;
-+
-+ ht_oper = kzalloc(sizeof(*ht_oper), GFP_KERNEL);
-+ if (!ht_oper)
-+ return -ENOMEM;
-+
-+ vht_oper = kzalloc(sizeof(*vht_oper), GFP_KERNEL);
-+ if (!vht_oper) {
-+ kfree(ht_oper);
-+ return -ENOMEM;
-+ }
-+
-+ if (new_op_class && new_op_class != 130) {
-+ /* There is no way to tell the ccfs1 for op_class 130
-+ * (80P80 MHz).
-+ */
-+ op_class_to_ht_vht_oper(new_op_class, new_chan, ht_oper,
-+ vht_oper);
-+ } else if (wide_bw_chansw_ie && new_band == NL80211_BAND_5GHZ &&
-+ sband->vht_cap.vht_supported) {
-+ /* It is assumed that there is no WBCS IE in the beacon
-+ * from a 2 GHz BSS during a channel switch.
-+ */
-+ wbcs_ie_to_ht_vht_oper(new_chan, wide_bw_chansw_ie,
-+ ht_oper, vht_oper);
-+ } else {
-+ kfree(ht_oper);
-+ kfree(vht_oper);
-+ goto out;
-+ }
-+
-+ new_chandef = csa_ie->chandef;
-+
-+ ieee80211_chandef_ht_oper(ht_oper, &new_chandef);
-+
-+ /* ignore if parsing fails */
-+ if (sband->vht_cap.vht_supported &&
-+ !ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
-+ vht_oper, ht_oper, &new_chandef))
-+ new_chandef.chan = NULL;
-+
-+ kfree(ht_oper);
-+ kfree(vht_oper);
-+ }
-+
-+ if (new_chandef.chan) {
-+ if (conn_flags & IEEE80211_CONN_DISABLE_320MHZ &&
-+ new_chandef.width == NL80211_CHAN_WIDTH_320)
-+ ieee80211_chandef_downgrade(&new_chandef);
-
- if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ &&
-- new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
-- ieee80211_chandef_downgrade(&new_vht_chandef);
-+ new_chandef.width == NL80211_CHAN_WIDTH_80P80)
-+ ieee80211_chandef_downgrade(&new_chandef);
-+
- if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ &&
-- new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
-- ieee80211_chandef_downgrade(&new_vht_chandef);
-- }
-+ new_chandef.width == NL80211_CHAN_WIDTH_160)
-+ ieee80211_chandef_downgrade(&new_chandef);
-
-- /* if VHT data is there validate & use it */
-- if (new_vht_chandef.chan) {
-- if (!cfg80211_chandef_compatible(&new_vht_chandef,
-+ if (!cfg80211_chandef_compatible(&new_chandef,
- &csa_ie->chandef)) {
- sdata_info(sdata,
- "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
- bssid);
- return -EINVAL;
- }
-- csa_ie->chandef = new_vht_chandef;
-+
-+ csa_ie->chandef = new_chandef;
- }
-
-+out:
- if (elems->max_channel_switch_time)
- csa_ie->max_switch_time =
- (elems->max_channel_switch_time[0] << 0) |
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0023-mtk-mac80211-add-EHT-BA1024-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0023-mtk-mac80211-add-EHT-BA1024-support.patch
deleted file mode 100644
index d7d7c9f..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0023-mtk-mac80211-add-EHT-BA1024-support.patch
+++ /dev/null
@@ -1,114 +0,0 @@
-From 33ad860265ded8546ac378d721e1b7d0142e069c Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Sun, 25 Dec 2022 22:43:46 +0800
-Subject: [PATCH 23/37] mtk: mac80211: add EHT BA1024 support
-
----
- include/linux/ieee80211.h | 2 ++
- net/mac80211/agg-tx.c | 45 +++++++++++++++++++++++++++++++++++++--
- 2 files changed, 45 insertions(+), 2 deletions(-)
-
-diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
-index e53b73e..2eb145e 100644
---- a/include/linux/ieee80211.h
-+++ b/include/linux/ieee80211.h
-@@ -1343,6 +1343,8 @@ struct ieee80211_mgmt {
- __le16 status;
- __le16 capab;
- __le16 timeout;
-+ /* followed by BA Extension */
-+ u8 variable[0];
- } __packed addba_resp;
- struct{
- u8 action_code;
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 7f66e69..b8b8e22 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -72,10 +72,17 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
-+ struct ieee80211_addba_ext_ie *addba_ext;
-+ u8 *pos;
- u16 capab = 0;
- bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
-
-- skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
-+ if (agg_size >= 1024)
-+ skb = dev_alloc_skb(sizeof(*mgmt) +
-+ 2 + sizeof(struct ieee80211_addba_ext_ie) +
-+ local->hw.extra_tx_headroom);
-+ else
-+ skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
-
- if (!skb)
- return;
-@@ -114,6 +121,15 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- mgmt->u.action.u.addba_req.start_seq_num =
- cpu_to_le16(start_seq_num << 4);
-
-+ if (agg_size >= 1024 ) {
-+ pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
-+ *pos++ = WLAN_EID_ADDBA_EXT;
-+ *pos++ = sizeof(struct ieee80211_addba_ext_ie);
-+ addba_ext = (struct ieee80211_addba_ext_ie *)pos;
-+ addba_ext->data = u8_encode_bits(agg_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT,
-+ IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
-+ }
-+
- ieee80211_tx_skb_tid(sdata, skb, tid, -1);
- }
-
-@@ -481,8 +497,11 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
- sta->ampdu_mlme.addba_req_num[tid]++;
- spin_unlock_bh(&sta->lock);
-
-- if (sta->sta.deflink.he_cap.has_he) {
-+ if (sta->sta.deflink.eht_cap.has_eht) {
- buf_size = local->hw.max_tx_aggregation_subframes;
-+ } else if (sta->sta.deflink.he_cap.has_he) {
-+ buf_size = min_t(u16, local->hw.max_tx_aggregation_subframes,
-+ IEEE80211_MAX_AMPDU_BUF_HE);
- } else {
- /*
- * We really should use what the driver told us it will
-@@ -998,13 +1017,35 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
- {
- struct tid_ampdu_tx *tid_tx;
- struct ieee80211_txq *txq;
-+ struct ieee802_11_elems *elems;
- u16 capab, tid, buf_size;
- bool amsdu;
-+ int ext_ie_len;
-
- capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
- amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK;
- tid = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_TID_MASK);
- buf_size = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
-+ ext_ie_len = len - offsetof(struct ieee80211_mgmt,
-+ u.action.u.addba_resp.variable);
-+
-+ if (ext_ie_len < 0)
-+ goto next;
-+
-+ elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_resp.variable,
-+ ext_ie_len, true, NULL);
-+
-+ if (elems && !elems->parse_error) {
-+ if (sta->sta.deflink.eht_cap.has_eht && elems->addba_ext_ie) {
-+ u8 buf_size_1k = u8_get_bits(elems->addba_ext_ie->data,
-+ IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
-+ buf_size |= buf_size_1k << IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT;
-+ }
-+ }
-+
-+ if (elems)
-+ kfree(elems);
-+next:
- buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
-
- txq = sta->sta.txq[tid];
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0024-mtk-mac80211-add-rate-duration-for-EHT-rate.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0024-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
deleted file mode 100644
index 5db0a2f..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0024-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
+++ /dev/null
@@ -1,440 +0,0 @@
-From f3a735030c257da9f8a4248802270dba37c26eb4 Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Sun, 25 Dec 2022 22:43:46 +0800
-Subject: [PATCH 24/37] mtk: mac80211: add rate duration for EHT rate.
-
----
- net/mac80211/airtime.c | 349 ++++++++++++++++++++++++++++++++++++++++-
- 1 file changed, 346 insertions(+), 3 deletions(-)
-
-diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c
-index e8ebd34..b1de6d0 100644
---- a/net/mac80211/airtime.c
-+++ b/net/mac80211/airtime.c
-@@ -55,10 +55,21 @@
- #define HE_DURATION_S(shift, streams, gi, bps) \
- (HE_DURATION(streams, gi, bps) >> shift)
-
-+/* Transmit duration for the raw data part of an average sized packet */
-+#define EHT_GI_08 HE_GI_08
-+#define EHT_GI_16 HE_GI_16
-+#define EHT_GI_32 HE_GI_32
-+
-+#define EHT_DURATION(streams, gi, bps) \
-+ HE_DURATION(streams, gi, bps)
-+#define EHT_DURATION_S(shift, streams, gi, bps) \
-+ HE_DURATION_S(shift, streams, gi, bps)
-+
- #define BW_20 0
- #define BW_40 1
- #define BW_80 2
- #define BW_160 3
-+#define BW_320 4
-
- /*
- * Define group sort order: HT40 -> SGI -> #streams
-@@ -68,17 +79,26 @@
- #define IEEE80211_VHT_STREAM_GROUPS 8 /* BW(=4) * SGI(=2) */
-
- #define IEEE80211_HE_MAX_STREAMS 8
-+#define IEEE80211_HE_STREAM_GROUPS 12 /* BW(=4) * GI(=3) */
-+
-+#define IEEE80211_EHT_MAX_STREAMS 16
-+#define IEEE80211_EHT_STREAM_GROUPS 15 /* BW(=5) * GI(=3) */
-
- #define IEEE80211_HT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
- IEEE80211_HT_STREAM_GROUPS)
- #define IEEE80211_VHT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
- IEEE80211_VHT_STREAM_GROUPS)
-+#define IEEE80211_HE_GROUPS_NB (IEEE80211_HE_MAX_STREAMS * \
-+ IEEE80211_HE_STREAM_GROUPS)
-+#define IEEE80211_EHT_GROUPS_NB (IEEE80211_EHT_MAX_STREAMS * \
-+ IEEE80211_EHT_STREAM_GROUPS)
-
- #define IEEE80211_HT_GROUP_0 0
- #define IEEE80211_VHT_GROUP_0 (IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB)
- #define IEEE80211_HE_GROUP_0 (IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB)
-+#define IEEE80211_EHT_GROUP_0 (IEEE80211_HE_GROUP_0 + IEEE80211_HE_GROUPS_NB)
-
--#define MCS_GROUP_RATES 12
-+#define MCS_GROUP_RATES 14
-
- #define HT_GROUP_IDX(_streams, _sgi, _ht40) \
- IEEE80211_HT_GROUP_0 + \
-@@ -203,6 +223,59 @@
- #define HE_GROUP(_streams, _gi, _bw) \
- __HE_GROUP(_streams, _gi, _bw, \
- HE_GROUP_SHIFT(_streams, _gi, _bw))
-+
-+#define EHT_BW2VBPS(_bw, r5, r4, r3, r2, r1) \
-+ (_bw == BW_320 ? r5 : _bw == BW_160 ? r4 : _bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
-+
-+#define EHT_GROUP_IDX(_streams, _gi, _bw) \
-+ (IEEE80211_EHT_GROUP_0 + \
-+ IEEE80211_EHT_MAX_STREAMS * 3 * (_bw) + \
-+ IEEE80211_EHT_MAX_STREAMS * (_gi) + \
-+ (_streams) - 1)
-+
-+#define __EHT_GROUP(_streams, _gi, _bw, _s) \
-+ [EHT_GROUP_IDX(_streams, _gi, _bw)] = { \
-+ .shift = _s, \
-+ .duration = { \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 1960, 979, 489, 230, 115)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 3920, 1958, 979, 475, 230)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 5880, 2937, 1468, 705, 345)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 7840, 3916, 1958, 936, 475)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 11760, 5875, 2937, 1411, 705)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 15680, 7833, 3916, 1872, 936)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 17640, 8827, 4406, 2102, 1051)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 19600, 9806, 4896, 2347, 1166)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 23520, 11764, 5875, 2808, 1411)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 26133, 13060, 6523, 3124, 1555)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 29400, 14702, 7344, 3513, 1756)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 32666, 16329, 8164, 3902, 1944)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 35280, 17640, 8820, 4212, 2106)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 39200, 19600, 9800, 4680, 2340)) \
-+ } \
-+}
-+
-+#define EHT_GROUP_SHIFT(_streams, _gi, _bw) \
-+ GROUP_SHIFT(EHT_DURATION(_streams, _gi, \
-+ EHT_BW2VBPS(_bw, 1960, 979, 489, 230, 115)))
-+
-+#define EHT_GROUP(_streams, _gi, _bw) \
-+ __EHT_GROUP(_streams, _gi, _bw, \
-+ EHT_GROUP_SHIFT(_streams, _gi, _bw))
-+
- struct mcs_group {
- u8 shift;
- u16 duration[MCS_GROUP_RATES];
-@@ -376,6 +449,262 @@ static const struct mcs_group airtime_mcs_groups[] = {
- HE_GROUP(6, HE_GI_32, BW_160),
- HE_GROUP(7, HE_GI_32, BW_160),
- HE_GROUP(8, HE_GI_32, BW_160),
-+
-+ /* EHT */
-+ EHT_GROUP( 1, EHT_GI_08, BW_20),
-+ EHT_GROUP( 2, EHT_GI_08, BW_20),
-+ EHT_GROUP( 3, EHT_GI_08, BW_20),
-+ EHT_GROUP( 4, EHT_GI_08, BW_20),
-+ EHT_GROUP( 5, EHT_GI_08, BW_20),
-+ EHT_GROUP( 6, EHT_GI_08, BW_20),
-+ EHT_GROUP( 7, EHT_GI_08, BW_20),
-+ EHT_GROUP( 8, EHT_GI_08, BW_20),
-+ EHT_GROUP( 9, EHT_GI_08, BW_20),
-+ EHT_GROUP(10, EHT_GI_08, BW_20),
-+ EHT_GROUP(11, EHT_GI_08, BW_20),
-+ EHT_GROUP(12, EHT_GI_08, BW_20),
-+ EHT_GROUP(13, EHT_GI_08, BW_20),
-+ EHT_GROUP(14, EHT_GI_08, BW_20),
-+ EHT_GROUP(15, EHT_GI_08, BW_20),
-+ EHT_GROUP(16, EHT_GI_08, BW_20),
-+
-+ EHT_GROUP( 1, EHT_GI_16, BW_20),
-+ EHT_GROUP( 2, EHT_GI_16, BW_20),
-+ EHT_GROUP( 3, EHT_GI_16, BW_20),
-+ EHT_GROUP( 4, EHT_GI_16, BW_20),
-+ EHT_GROUP( 5, EHT_GI_16, BW_20),
-+ EHT_GROUP( 6, EHT_GI_16, BW_20),
-+ EHT_GROUP( 7, EHT_GI_16, BW_20),
-+ EHT_GROUP( 8, EHT_GI_16, BW_20),
-+ EHT_GROUP( 9, EHT_GI_16, BW_20),
-+ EHT_GROUP(10, EHT_GI_16, BW_20),
-+ EHT_GROUP(11, EHT_GI_16, BW_20),
-+ EHT_GROUP(12, EHT_GI_16, BW_20),
-+ EHT_GROUP(13, EHT_GI_16, BW_20),
-+ EHT_GROUP(14, EHT_GI_16, BW_20),
-+ EHT_GROUP(15, EHT_GI_16, BW_20),
-+ EHT_GROUP(16, EHT_GI_16, BW_20),
-+
-+ EHT_GROUP( 1, EHT_GI_32, BW_20),
-+ EHT_GROUP( 2, EHT_GI_32, BW_20),
-+ EHT_GROUP( 3, EHT_GI_32, BW_20),
-+ EHT_GROUP( 4, EHT_GI_32, BW_20),
-+ EHT_GROUP( 5, EHT_GI_32, BW_20),
-+ EHT_GROUP( 6, EHT_GI_32, BW_20),
-+ EHT_GROUP( 7, EHT_GI_32, BW_20),
-+ EHT_GROUP( 8, EHT_GI_32, BW_20),
-+ EHT_GROUP( 9, EHT_GI_32, BW_20),
-+ EHT_GROUP(10, EHT_GI_32, BW_20),
-+ EHT_GROUP(11, EHT_GI_32, BW_20),
-+ EHT_GROUP(12, EHT_GI_32, BW_20),
-+ EHT_GROUP(13, EHT_GI_32, BW_20),
-+ EHT_GROUP(14, EHT_GI_32, BW_20),
-+ EHT_GROUP(15, EHT_GI_32, BW_20),
-+ EHT_GROUP(16, EHT_GI_32, BW_20),
-+
-+ EHT_GROUP( 1, EHT_GI_08, BW_40),
-+ EHT_GROUP( 2, EHT_GI_08, BW_40),
-+ EHT_GROUP( 3, EHT_GI_08, BW_40),
-+ EHT_GROUP( 4, EHT_GI_08, BW_40),
-+ EHT_GROUP( 5, EHT_GI_08, BW_40),
-+ EHT_GROUP( 6, EHT_GI_08, BW_40),
-+ EHT_GROUP( 7, EHT_GI_08, BW_40),
-+ EHT_GROUP( 8, EHT_GI_08, BW_40),
-+ EHT_GROUP( 9, EHT_GI_08, BW_40),
-+ EHT_GROUP(10, EHT_GI_08, BW_40),
-+ EHT_GROUP(11, EHT_GI_08, BW_40),
-+ EHT_GROUP(12, EHT_GI_08, BW_40),
-+ EHT_GROUP(13, EHT_GI_08, BW_40),
-+ EHT_GROUP(14, EHT_GI_08, BW_40),
-+ EHT_GROUP(15, EHT_GI_08, BW_40),
-+ EHT_GROUP(16, EHT_GI_08, BW_40),
-+
-+ EHT_GROUP( 1, EHT_GI_16, BW_40),
-+ EHT_GROUP( 2, EHT_GI_16, BW_40),
-+ EHT_GROUP( 3, EHT_GI_16, BW_40),
-+ EHT_GROUP( 4, EHT_GI_16, BW_40),
-+ EHT_GROUP( 5, EHT_GI_16, BW_40),
-+ EHT_GROUP( 6, EHT_GI_16, BW_40),
-+ EHT_GROUP( 7, EHT_GI_16, BW_40),
-+ EHT_GROUP( 8, EHT_GI_16, BW_40),
-+ EHT_GROUP( 9, EHT_GI_16, BW_40),
-+ EHT_GROUP(10, EHT_GI_16, BW_40),
-+ EHT_GROUP(11, EHT_GI_16, BW_40),
-+ EHT_GROUP(12, EHT_GI_16, BW_40),
-+ EHT_GROUP(13, EHT_GI_16, BW_40),
-+ EHT_GROUP(14, EHT_GI_16, BW_40),
-+ EHT_GROUP(15, EHT_GI_16, BW_40),
-+ EHT_GROUP(16, EHT_GI_16, BW_40),
-+
-+ EHT_GROUP( 1, EHT_GI_32, BW_40),
-+ EHT_GROUP( 2, EHT_GI_32, BW_40),
-+ EHT_GROUP( 3, EHT_GI_32, BW_40),
-+ EHT_GROUP( 4, EHT_GI_32, BW_40),
-+ EHT_GROUP( 5, EHT_GI_32, BW_40),
-+ EHT_GROUP( 6, EHT_GI_32, BW_40),
-+ EHT_GROUP( 7, EHT_GI_32, BW_40),
-+ EHT_GROUP( 8, EHT_GI_32, BW_40),
-+ EHT_GROUP( 9, EHT_GI_32, BW_40),
-+ EHT_GROUP(10, EHT_GI_32, BW_40),
-+ EHT_GROUP(11, EHT_GI_32, BW_40),
-+ EHT_GROUP(12, EHT_GI_32, BW_40),
-+ EHT_GROUP(13, EHT_GI_32, BW_40),
-+ EHT_GROUP(14, EHT_GI_32, BW_40),
-+ EHT_GROUP(15, EHT_GI_32, BW_40),
-+ EHT_GROUP(16, EHT_GI_32, BW_40),
-+
-+ EHT_GROUP( 1, EHT_GI_08, BW_80),
-+ EHT_GROUP( 2, EHT_GI_08, BW_80),
-+ EHT_GROUP( 3, EHT_GI_08, BW_80),
-+ EHT_GROUP( 4, EHT_GI_08, BW_80),
-+ EHT_GROUP( 5, EHT_GI_08, BW_80),
-+ EHT_GROUP( 6, EHT_GI_08, BW_80),
-+ EHT_GROUP( 7, EHT_GI_08, BW_80),
-+ EHT_GROUP( 8, EHT_GI_08, BW_80),
-+ EHT_GROUP( 9, EHT_GI_08, BW_80),
-+ EHT_GROUP(10, EHT_GI_08, BW_80),
-+ EHT_GROUP(11, EHT_GI_08, BW_80),
-+ EHT_GROUP(12, EHT_GI_08, BW_80),
-+ EHT_GROUP(13, EHT_GI_08, BW_80),
-+ EHT_GROUP(14, EHT_GI_08, BW_80),
-+ EHT_GROUP(15, EHT_GI_08, BW_80),
-+ EHT_GROUP(16, EHT_GI_08, BW_80),
-+
-+ EHT_GROUP( 1, EHT_GI_16, BW_80),
-+ EHT_GROUP( 2, EHT_GI_16, BW_80),
-+ EHT_GROUP( 3, EHT_GI_16, BW_80),
-+ EHT_GROUP( 4, EHT_GI_16, BW_80),
-+ EHT_GROUP( 5, EHT_GI_16, BW_80),
-+ EHT_GROUP( 6, EHT_GI_16, BW_80),
-+ EHT_GROUP( 7, EHT_GI_16, BW_80),
-+ EHT_GROUP( 8, EHT_GI_16, BW_80),
-+ EHT_GROUP( 9, EHT_GI_16, BW_80),
-+ EHT_GROUP(10, EHT_GI_16, BW_80),
-+ EHT_GROUP(11, EHT_GI_16, BW_80),
-+ EHT_GROUP(12, EHT_GI_16, BW_80),
-+ EHT_GROUP(13, EHT_GI_16, BW_80),
-+ EHT_GROUP(14, EHT_GI_16, BW_80),
-+ EHT_GROUP(15, EHT_GI_16, BW_80),
-+ EHT_GROUP(16, EHT_GI_16, BW_80),
-+
-+ EHT_GROUP( 1, EHT_GI_32, BW_80),
-+ EHT_GROUP( 2, EHT_GI_32, BW_80),
-+ EHT_GROUP( 3, EHT_GI_32, BW_80),
-+ EHT_GROUP( 4, EHT_GI_32, BW_80),
-+ EHT_GROUP( 5, EHT_GI_32, BW_80),
-+ EHT_GROUP( 6, EHT_GI_32, BW_80),
-+ EHT_GROUP( 7, EHT_GI_32, BW_80),
-+ EHT_GROUP( 8, EHT_GI_32, BW_80),
-+ EHT_GROUP( 9, EHT_GI_32, BW_80),
-+ EHT_GROUP(10, EHT_GI_32, BW_80),
-+ EHT_GROUP(11, EHT_GI_32, BW_80),
-+ EHT_GROUP(12, EHT_GI_32, BW_80),
-+ EHT_GROUP(13, EHT_GI_32, BW_80),
-+ EHT_GROUP(14, EHT_GI_32, BW_80),
-+ EHT_GROUP(15, EHT_GI_32, BW_80),
-+ EHT_GROUP(16, EHT_GI_32, BW_80),
-+
-+ EHT_GROUP( 1, EHT_GI_08, BW_160),
-+ EHT_GROUP( 2, EHT_GI_08, BW_160),
-+ EHT_GROUP( 3, EHT_GI_08, BW_160),
-+ EHT_GROUP( 4, EHT_GI_08, BW_160),
-+ EHT_GROUP( 5, EHT_GI_08, BW_160),
-+ EHT_GROUP( 6, EHT_GI_08, BW_160),
-+ EHT_GROUP( 7, EHT_GI_08, BW_160),
-+ EHT_GROUP( 8, EHT_GI_08, BW_160),
-+ EHT_GROUP( 9, EHT_GI_08, BW_160),
-+ EHT_GROUP(10, EHT_GI_08, BW_160),
-+ EHT_GROUP(11, EHT_GI_08, BW_160),
-+ EHT_GROUP(12, EHT_GI_08, BW_160),
-+ EHT_GROUP(13, EHT_GI_08, BW_160),
-+ EHT_GROUP(14, EHT_GI_08, BW_160),
-+ EHT_GROUP(15, EHT_GI_08, BW_160),
-+ EHT_GROUP(16, EHT_GI_08, BW_160),
-+
-+ EHT_GROUP( 1, EHT_GI_16, BW_160),
-+ EHT_GROUP( 2, EHT_GI_16, BW_160),
-+ EHT_GROUP( 3, EHT_GI_16, BW_160),
-+ EHT_GROUP( 4, EHT_GI_16, BW_160),
-+ EHT_GROUP( 5, EHT_GI_16, BW_160),
-+ EHT_GROUP( 6, EHT_GI_16, BW_160),
-+ EHT_GROUP( 7, EHT_GI_16, BW_160),
-+ EHT_GROUP( 8, EHT_GI_16, BW_160),
-+ EHT_GROUP( 9, EHT_GI_16, BW_160),
-+ EHT_GROUP(10, EHT_GI_16, BW_160),
-+ EHT_GROUP(11, EHT_GI_16, BW_160),
-+ EHT_GROUP(12, EHT_GI_16, BW_160),
-+ EHT_GROUP(13, EHT_GI_16, BW_160),
-+ EHT_GROUP(14, EHT_GI_16, BW_160),
-+ EHT_GROUP(15, EHT_GI_16, BW_160),
-+ EHT_GROUP(16, EHT_GI_16, BW_160),
-+
-+ EHT_GROUP( 1, EHT_GI_32, BW_160),
-+ EHT_GROUP( 2, EHT_GI_32, BW_160),
-+ EHT_GROUP( 3, EHT_GI_32, BW_160),
-+ EHT_GROUP( 4, EHT_GI_32, BW_160),
-+ EHT_GROUP( 5, EHT_GI_32, BW_160),
-+ EHT_GROUP( 6, EHT_GI_32, BW_160),
-+ EHT_GROUP( 7, EHT_GI_32, BW_160),
-+ EHT_GROUP( 8, EHT_GI_32, BW_160),
-+ EHT_GROUP( 9, EHT_GI_32, BW_160),
-+ EHT_GROUP(10, EHT_GI_32, BW_160),
-+ EHT_GROUP(11, EHT_GI_32, BW_160),
-+ EHT_GROUP(12, EHT_GI_32, BW_160),
-+ EHT_GROUP(13, EHT_GI_32, BW_160),
-+ EHT_GROUP(14, EHT_GI_32, BW_160),
-+ EHT_GROUP(15, EHT_GI_32, BW_160),
-+ EHT_GROUP(16, EHT_GI_32, BW_160),
-+
-+ EHT_GROUP( 1, EHT_GI_08, BW_320),
-+ EHT_GROUP( 2, EHT_GI_08, BW_320),
-+ EHT_GROUP( 3, EHT_GI_08, BW_320),
-+ EHT_GROUP( 4, EHT_GI_08, BW_320),
-+ EHT_GROUP( 5, EHT_GI_08, BW_320),
-+ EHT_GROUP( 6, EHT_GI_08, BW_320),
-+ EHT_GROUP( 7, EHT_GI_08, BW_320),
-+ EHT_GROUP( 8, EHT_GI_08, BW_320),
-+ EHT_GROUP( 9, EHT_GI_08, BW_320),
-+ EHT_GROUP(10, EHT_GI_08, BW_320),
-+ EHT_GROUP(11, EHT_GI_08, BW_320),
-+ EHT_GROUP(12, EHT_GI_08, BW_320),
-+ EHT_GROUP(13, EHT_GI_08, BW_320),
-+ EHT_GROUP(14, EHT_GI_08, BW_320),
-+ EHT_GROUP(15, EHT_GI_08, BW_320),
-+ EHT_GROUP(16, EHT_GI_08, BW_320),
-+
-+ EHT_GROUP( 1, EHT_GI_16, BW_320),
-+ EHT_GROUP( 2, EHT_GI_16, BW_320),
-+ EHT_GROUP( 3, EHT_GI_16, BW_320),
-+ EHT_GROUP( 4, EHT_GI_16, BW_320),
-+ EHT_GROUP( 5, EHT_GI_16, BW_320),
-+ EHT_GROUP( 6, EHT_GI_16, BW_320),
-+ EHT_GROUP( 7, EHT_GI_16, BW_320),
-+ EHT_GROUP( 8, EHT_GI_16, BW_320),
-+ EHT_GROUP( 9, EHT_GI_16, BW_320),
-+ EHT_GROUP(10, EHT_GI_16, BW_320),
-+ EHT_GROUP(11, EHT_GI_16, BW_320),
-+ EHT_GROUP(12, EHT_GI_16, BW_320),
-+ EHT_GROUP(13, EHT_GI_16, BW_320),
-+ EHT_GROUP(14, EHT_GI_16, BW_320),
-+ EHT_GROUP(15, EHT_GI_16, BW_320),
-+ EHT_GROUP(16, EHT_GI_16, BW_320),
-+
-+ EHT_GROUP( 1, EHT_GI_32, BW_320),
-+ EHT_GROUP( 2, EHT_GI_32, BW_320),
-+ EHT_GROUP( 3, EHT_GI_32, BW_320),
-+ EHT_GROUP( 4, EHT_GI_32, BW_320),
-+ EHT_GROUP( 5, EHT_GI_32, BW_320),
-+ EHT_GROUP( 6, EHT_GI_32, BW_320),
-+ EHT_GROUP( 7, EHT_GI_32, BW_320),
-+ EHT_GROUP( 8, EHT_GI_32, BW_320),
-+ EHT_GROUP( 9, EHT_GI_32, BW_320),
-+ EHT_GROUP(10, EHT_GI_32, BW_320),
-+ EHT_GROUP(11, EHT_GI_32, BW_320),
-+ EHT_GROUP(12, EHT_GI_32, BW_320),
-+ EHT_GROUP(13, EHT_GI_32, BW_320),
-+ EHT_GROUP(14, EHT_GI_32, BW_320),
-+ EHT_GROUP(15, EHT_GI_32, BW_320),
-+ EHT_GROUP(16, EHT_GI_32, BW_320),
- };
-
- static u32
-@@ -422,6 +751,9 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
- case RATE_INFO_BW_160:
- bw = BW_160;
- break;
-+ case RATE_INFO_BW_320:
-+ bw = BW_320;
-+ break;
- default:
- WARN_ON_ONCE(1);
- return 0;
-@@ -443,11 +775,20 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
- idx = status->rate_idx;
- group = HE_GROUP_IDX(streams, status->he_gi, bw);
- break;
-+ case RX_ENC_EHT:
-+ streams = status->nss;
-+ idx = status->rate_idx;
-+ group = EHT_GROUP_IDX(streams, status->he_gi, bw);
-+ break;
- default:
- WARN_ON_ONCE(1);
- return 0;
- }
-
-+ if (WARN_ON_ONCE((status->encoding != RX_ENC_EHT && streams > 8) ||
-+ (status->encoding == RX_ENC_EHT && streams > 16)))
-+ return 0;
-+
- if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) ||
- (status->encoding == RX_ENC_HE && streams > 8)))
- return 0;
-@@ -517,7 +858,9 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
- stat->nss = ri->nss;
- stat->rate_idx = ri->mcs;
-
-- if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
-+ if (ri->flags & RATE_INFO_FLAGS_EHT_MCS)
-+ stat->encoding = RX_ENC_EHT;
-+ else if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
- stat->encoding = RX_ENC_HE;
- else if (ri->flags & RATE_INFO_FLAGS_VHT_MCS)
- stat->encoding = RX_ENC_VHT;
-@@ -529,7 +872,7 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
- if (ri->flags & RATE_INFO_FLAGS_SHORT_GI)
- stat->enc_flags |= RX_ENC_FLAG_SHORT_GI;
-
-- stat->he_gi = ri->he_gi;
-+ stat->he_gi = (ri->flags & RATE_INFO_FLAGS_EHT_MCS) ? ri->eht_gi : ri->he_gi;
-
- if (stat->encoding != RX_ENC_LEGACY)
- return true;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0025-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0025-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
deleted file mode 100644
index 401c8a8..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0025-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 9e9286abe5cb3ff378b01046d19ddba0944562e1 Mon Sep 17 00:00:00 2001
-From: ye he <ye.he@mediatek.com>
-Date: Wed, 22 Feb 2023 16:09:32 +0800
-Subject: [PATCH 25/37] mtk: mac80211: add send bar action when recieve addba
- rsp
-
-Signed-off-by: ye he <ye.he@mediatek.com>
----
- net/mac80211/agg-tx.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index b8b8e22..285433a 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -1098,7 +1098,8 @@ next:
-
- tid_tx->buf_size = buf_size;
- tid_tx->amsdu = amsdu;
--
-+ ieee80211_send_bar(&sta->sdata->vif, sta->sta.addr,
-+ tid, 0);
- if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))
- ieee80211_agg_tx_operational(local, sta, tid);
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0026-mtk-mac80211-inrease-beacon-loss-count.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0026-mtk-mac80211-inrease-beacon-loss-count.patch
deleted file mode 100644
index 6f6a98b..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0026-mtk-mac80211-inrease-beacon-loss-count.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 2733993c8fb2e6392cddd718c1b95164c49af642 Mon Sep 17 00:00:00 2001
-From: Amit Khatri <amit.khatri@mediatek.com>
-Date: Thu, 6 Apr 2023 21:37:33 +0800
-Subject: [PATCH 26/37] mtk: mac80211: inrease beacon loss count
-
-as per eagle code beacone loss time out is
-4 seconds.
-in 2G connection getting beacon loss logs in routed client
-scenario.
-
-so increasing beacon loss count from 7 to 20
-
-Signed-off-by: Amit Khatri <amit.khatri@mediatek.com>
----
- net/mac80211/mlme.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 6077e89..47a6590 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -61,7 +61,7 @@ MODULE_PARM_DESC(max_probe_tries,
- * probe on beacon miss before declaring the connection lost
- * default to what we want.
- */
--static int beacon_loss_count = 7;
-+static int beacon_loss_count = 20;
- module_param(beacon_loss_count, int, 0644);
- MODULE_PARM_DESC(beacon_loss_count,
- "Number of beacon intervals before we decide beacon was lost.");
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0027-mtk-cfg80211-add-support-for-updating-background-cha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0027-mtk-cfg80211-add-support-for-updating-background-cha.patch
deleted file mode 100644
index 1f46543..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0027-mtk-cfg80211-add-support-for-updating-background-cha.patch
+++ /dev/null
@@ -1,88 +0,0 @@
-From c35a634d57fc0a12d7415c05aaf239e29e0e8357 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 09:49:02 +0800
-Subject: [PATCH 27/37] mtk: cfg80211: add support for updating background
- channel
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 14 ++++++++++++++
- include/uapi/linux/nl80211.h | 6 ++++++
- net/wireless/mlme.c | 12 ++++++++++++
- 3 files changed, 32 insertions(+)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 19c8abe..2e499ed 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -8540,6 +8540,20 @@ void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
- const struct cfg80211_chan_def *csa_chandef,
- bool associated);
-
-+/**
-+ * cfg80211_background_radar_update_channel - notify background chandef has been updated
-+ * @wiphy: the wiphy
-+ * @chandef: the updated chandef
-+ * @expand: whether or not the operating channel should expand its width
-+ * after offchan CAC
-+ *
-+ * Update the background chandef based on driver's decision, and notify the userspace
-+ * that the current channel of background chain should be updated.
-+ */
-+void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef,
-+ bool expand);
-+
- /**
- * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
- * @wiphy: the wiphy
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index 60c6f79..6e96ad9 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -6671,6 +6671,10 @@ enum nl80211_smps_mode {
- * applicable for ETSI dfs domain where pre-CAC is valid for ever.
- * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
- * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
-+ * driver.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
-+ * driver and required to expand main operating channel.
- * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
- * when receiving CSA/assoc resp
- * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-@@ -6683,6 +6687,8 @@ enum nl80211_radar_event {
- NL80211_RADAR_NOP_FINISHED,
- NL80211_RADAR_PRE_CAC_EXPIRED,
- NL80211_RADAR_CAC_STARTED,
-+ NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
-+ NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
- NL80211_RADAR_STA_CAC_SKIPPED,
- NL80211_RADAR_STA_CAC_EXPIRED,
- };
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index e264609..8e77205 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1173,6 +1173,18 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
- return 0;
- }
-
-+void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef,
-+ bool expand)
-+{
-+ enum nl80211_radar_event event;
-+
-+ event = expand ? NL80211_RADAR_BACKGROUND_CHAN_EXPAND :
-+ NL80211_RADAR_BACKGROUND_CHAN_UPDATE;
-+ nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef, event, NULL, GFP_ATOMIC);
-+}
-+EXPORT_SYMBOL(cfg80211_background_radar_update_channel);
-+
- void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
- {
- struct wiphy *wiphy = wdev->wiphy;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0028-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0028-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
deleted file mode 100644
index 9affe14..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0028-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 180f80da3952be05a0177b565559a16dee708925 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 7 Jul 2023 17:17:30 +0800
-Subject: [PATCH 28/37] mtk: mac80211: Allow STA interface to set TX queue
- parameters
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- net/wireless/nl80211.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 2be678c..aed186f 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -3498,6 +3498,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
- }
-
- if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-+ netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
- netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
- result = -EINVAL;
- goto out;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0029-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0029-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
deleted file mode 100644
index 0af68c3..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0029-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
+++ /dev/null
@@ -1,150 +0,0 @@
-From ef1f8c941108eca2665a6caff0cd016c36584d76 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Fri, 23 Jun 2023 05:53:50 +0800
-Subject: [PATCH 29/37] mtk: mac80211: export ieee80211_tpt_led_trig_tx/rx for
- driver
-
-Whenever the H/W path is enabled and traffic is in the binding state,
-mac80211 is not aware of the traffic. Consequently, the LED does not
-blink for that reason.
-
-The ieee80211_tpt_led_trig_tx/rx functions are exported for the driver
-so that we can report the tx and rx bytes from the driver when
-the H/W path is being used.
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
----
- include/net/mac80211.h | 17 +++++++++++++++++
- net/mac80211/led.c | 16 ++++++++++++++++
- net/mac80211/led.h | 17 -----------------
- net/mac80211/rx.c | 2 +-
- net/mac80211/tx.c | 4 ++--
- 5 files changed, 36 insertions(+), 20 deletions(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 361fe92..d641b18 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -4747,6 +4747,8 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
- unsigned int flags,
- const struct ieee80211_tpt_blink *blink_table,
- unsigned int blink_table_len);
-+void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes);
-+void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes);
- #endif
- /**
- * ieee80211_get_tx_led_name - get name of TX LED
-@@ -4857,6 +4859,21 @@ ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags,
- #endif
- }
-
-+static inline void
-+ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
-+{
-+#ifdef CPTCFG_MAC80211_LEDS
-+ __ieee80211_tpt_led_trig_tx(hw, bytes);
-+#endif
-+}
-+
-+static inline void
-+ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
-+{
-+#ifdef CPTCFG_MAC80211_LEDS
-+ __ieee80211_tpt_led_trig_rx(hw, bytes);
-+#endif
-+}
- /**
- * ieee80211_unregister_hw - Unregister a hardware device
- *
-diff --git a/net/mac80211/led.c b/net/mac80211/led.c
-index c60d070..0ecc3ea 100644
---- a/net/mac80211/led.c
-+++ b/net/mac80211/led.c
-@@ -364,6 +364,22 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
- }
- EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
-
-+void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ if (atomic_read(&local->tpt_led_active))
-+ local->tpt_led_trigger->tx_bytes += bytes;
-+}
-+EXPORT_SYMBOL(__ieee80211_tpt_led_trig_tx);
-+
-+void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ if (atomic_read(&local->tpt_led_active))
-+ local->tpt_led_trigger->rx_bytes += bytes;
-+}
-+EXPORT_SYMBOL(__ieee80211_tpt_led_trig_rx);
-+
- static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
- {
- struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
-diff --git a/net/mac80211/led.h b/net/mac80211/led.h
-index 59f5a83..f381790 100644
---- a/net/mac80211/led.h
-+++ b/net/mac80211/led.h
-@@ -65,22 +65,5 @@ static inline void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
- unsigned int types_off)
- {
- }
--#endif
-
--static inline void
--ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, int bytes)
--{
--#ifdef CPTCFG_MAC80211_LEDS
-- if (atomic_read(&local->tpt_led_active))
-- local->tpt_led_trigger->tx_bytes += bytes;
- #endif
--}
--
--static inline void
--ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, int bytes)
--{
--#ifdef CPTCFG_MAC80211_LEDS
-- if (atomic_read(&local->tpt_led_active))
-- local->tpt_led_trigger->rx_bytes += bytes;
--#endif
--}
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index fead07e..1541e9c 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -5401,7 +5401,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
- if (skb) {
- if ((status->flag & RX_FLAG_8023) ||
- ieee80211_is_data_present(hdr->frame_control))
-- ieee80211_tpt_led_trig_rx(local, skb->len);
-+ ieee80211_tpt_led_trig_rx(&local->hw, skb->len);
-
- if (status->flag & RX_FLAG_8023)
- __ieee80211_rx_handle_8023(hw, pubsta, skb, list);
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index 019fc77..c8fade8 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4323,7 +4323,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
- len = 0;
- out:
- if (len)
-- ieee80211_tpt_led_trig_tx(local, len);
-+ ieee80211_tpt_led_trig_tx(&local->hw, len);
- rcu_read_unlock();
- }
-
-@@ -4649,7 +4649,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
- sta->deflink.tx_stats.packets[queue] += skbs;
- sta->deflink.tx_stats.bytes[queue] += len;
-
-- ieee80211_tpt_led_trig_tx(local, len);
-+ ieee80211_tpt_led_trig_tx(&local->hw, len);
-
- ieee80211_tx_8023(sdata, skb, sta, false);
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0030-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0030-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
deleted file mode 100644
index c33da52..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0030-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From 4e785260ea9523efea8deb29f3f7ec7a7dab5128 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Tue, 22 Aug 2023 05:02:53 +0800
-Subject: [PATCH 30/37] mtk: mac80211: add packet count input for
- dev_sw_netstat_rx_add
-
----
- backport-include/linux/netdevice.h | 12 ++++++++----
- drivers/net/usb/qmi_wwan.c | 2 +-
- .../net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c | 2 +-
- .../net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c | 2 +-
- net/mac80211/rx.c | 8 ++++----
- 5 files changed, 15 insertions(+), 11 deletions(-)
-
-diff --git a/backport-include/linux/netdevice.h b/backport-include/linux/netdevice.h
-index 1d2ac66..04d76d7 100644
---- a/backport-include/linux/netdevice.h
-+++ b/backport-include/linux/netdevice.h
-@@ -112,13 +112,15 @@ void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
- #define netif_rx_any_context LINUX_BACKPORT(netif_rx_any_context)
- int netif_rx_any_context(struct sk_buff *skb);
-
--static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
-+static inline void dev_sw_netstats_rx_add(struct net_device *dev,
-+ unsigned int packets,
-+ unsigned int len)
- {
- struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
-
- u64_stats_update_begin(&tstats->syncp);
- tstats->rx_bytes += len;
-- tstats->rx_packets++;
-+ tstats->rx_packets += packets;
- u64_stats_update_end(&tstats->syncp);
- }
-
-@@ -140,13 +142,15 @@ static inline void dev_sw_netstats_tx_add(struct net_device *dev,
-
- #if LINUX_VERSION_IS_LESS(5,10,0)
- #define dev_sw_netstats_rx_add LINUX_BACKPORT(dev_sw_netstats_rx_add)
--static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
-+static inline void dev_sw_netstats_rx_add(struct net_device *dev,
-+ unsigned int packets,
-+ unsigned int len)
- {
- struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
-
- u64_stats_update_begin(&tstats->syncp);
- tstats->rx_bytes += len;
-- tstats->rx_packets++;
-+ tstats->rx_packets += packets;
- u64_stats_update_end(&tstats->syncp);
- }
- #endif /* < 5.10 */
-diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
-index 6c2c1e5..3bc64d8 100644
---- a/drivers/net/usb/qmi_wwan.c
-+++ b/drivers/net/usb/qmi_wwan.c
-@@ -210,7 +210,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
- net->stats.rx_errors++;
- return 0;
- } else {
-- dev_sw_netstats_rx_add(net, pkt_len);
-+ dev_sw_netstats_rx_add(net, 1, pkt_len);
- }
-
- skip:
-diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
-index 8c23a77..8f201ea 100644
---- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
-@@ -756,7 +756,7 @@ static int qtnf_pcie_pearl_rx_poll(struct napi_struct *napi, int budget)
- skb_put(skb, psize);
- ndev = qtnf_classify_skb(bus, skb);
- if (likely(ndev)) {
-- dev_sw_netstats_rx_add(ndev, skb->len);
-+ dev_sw_netstats_rx_add(ndev, 1, skb->len);
- skb->protocol = eth_type_trans(skb, ndev);
- napi_gro_receive(napi, skb);
- } else {
-diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
-index d833625..0a7bb97 100644
---- a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
-@@ -662,7 +662,7 @@ static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
- skb_put(skb, psize);
- ndev = qtnf_classify_skb(bus, skb);
- if (likely(ndev)) {
-- dev_sw_netstats_rx_add(ndev, skb->len);
-+ dev_sw_netstats_rx_add(ndev, 1, skb->len);
- skb->protocol = eth_type_trans(skb, ndev);
- netif_receive_skb(skb);
- } else {
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 1541e9c..7a80945 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -853,7 +853,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
-
- if (skb) {
- skb->dev = sdata->dev;
-- dev_sw_netstats_rx_add(skb->dev, skb->len);
-+ dev_sw_netstats_rx_add(skb->dev, 1, skb->len);
- netif_receive_skb(skb);
- }
- }
-@@ -2631,7 +2631,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
- skb = rx->skb;
- xmit_skb = NULL;
-
-- dev_sw_netstats_rx_add(dev, skb->len);
-+ dev_sw_netstats_rx_add(dev, 1, skb->len);
-
- if (rx->sta) {
- /* The seqno index has the same property as needed
-@@ -4054,7 +4054,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
- }
-
- prev_dev = sdata->dev;
-- dev_sw_netstats_rx_add(sdata->dev, skb->len);
-+ dev_sw_netstats_rx_add(sdata->dev, 1, skb->len);
- }
-
- if (prev_dev) {
-@@ -4762,7 +4762,7 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
-
- skb->dev = fast_rx->dev;
-
-- dev_sw_netstats_rx_add(fast_rx->dev, skb->len);
-+ dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
-
- /* The seqno index has the same property as needed
- * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0031-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0031-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
deleted file mode 100644
index 7690697..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0031-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-From f500b4d7d2eb30f46fe0b75afb7b9bd94f96cf55 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Wed, 16 Aug 2023 07:23:34 +0800
-Subject: [PATCH 31/37] mtk: mac80211: add per-bss flag to support vendors
- counter
-
----
- include/uapi/linux/nl80211.h | 1 +
- net/mac80211/rx.c | 8 ++++++--
- net/mac80211/tx.c | 13 ++++++++++---
- 3 files changed, 17 insertions(+), 5 deletions(-)
-
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index 6e96ad9..b1555bb 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -6471,6 +6471,7 @@ enum nl80211_ext_feature_index {
- NL80211_EXT_FEATURE_PUNCT,
- NL80211_EXT_FEATURE_SECURE_NAN,
- NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
-+ NL80211_EXT_FEATURE_STAS_COUNT,
-
- /* add new features before the definition below */
- NUM_NL80211_EXT_FEATURES,
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 7a80945..0cf8c59 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2631,7 +2631,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
- skb = rx->skb;
- xmit_skb = NULL;
-
-- dev_sw_netstats_rx_add(dev, 1, skb->len);
-+ if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
-+ NL80211_EXT_FEATURE_STAS_COUNT) || !rx->sta)
-+ dev_sw_netstats_rx_add(dev, 1, skb->len);
-
- if (rx->sta) {
- /* The seqno index has the same property as needed
-@@ -4762,7 +4764,9 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
-
- skb->dev = fast_rx->dev;
-
-- dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
-+ if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
-+ NL80211_EXT_FEATURE_STAS_COUNT))
-+ dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
-
- /* The seqno index has the same property as needed
- * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index c8fade8..0f6c960 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3541,7 +3541,9 @@ ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
- if (key)
- info->control.hw_key = &key->conf;
-
-- dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
-+ if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
-+ NL80211_EXT_FEATURE_STAS_COUNT))
-+ dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
-
- if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-@@ -4313,7 +4315,9 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
- goto out;
- }
-
-- dev_sw_netstats_tx_add(dev, 1, skb->len);
-+ if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
-+ NL80211_EXT_FEATURE_STAS_COUNT) || !sta)
-+ dev_sw_netstats_tx_add(dev, 1, skb->len);
-
- ieee80211_xmit(sdata, sta, skb);
- }
-@@ -4645,7 +4649,10 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
- &info->flags, NULL);
-
-- dev_sw_netstats_tx_add(dev, skbs, len);
-+ if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
-+ NL80211_EXT_FEATURE_STAS_COUNT))
-+ dev_sw_netstats_tx_add(dev, skbs, len);
-+
- sta->deflink.tx_stats.packets[queue] += skbs;
- sta->deflink.tx_stats.bytes[queue] += len;
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0032-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0032-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
deleted file mode 100644
index cf32642..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0032-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From c117e28dd0caf91f0d1ce7516ba6aa90f8bb07f4 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 25 Oct 2023 13:37:00 +0800
-Subject: [PATCH 32/37] mtk: mac80211: set eht_support to false when AP is not
- in EHT mode
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- net/mac80211/cfg.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 9b9be1a..cf694bc 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1378,6 +1378,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
- link_conf->eht_su_beamformer = false;
- link_conf->eht_su_beamformee = false;
- link_conf->eht_mu_beamformer = false;
-+ link_conf->eht_support = false;
- }
-
- if (sdata->vif.type == NL80211_IFTYPE_AP &&
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0033-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0033-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
deleted file mode 100644
index eb4281e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0033-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
+++ /dev/null
@@ -1,120 +0,0 @@
-From bc5e57c30a65a64e74ef4d8c355a4a4c83673c73 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Thu, 9 Nov 2023 11:37:37 +0800
-Subject: [PATCH 33/37] mtk: mac80211: Add cert mode to disable ba timeout
-
-Add a switch of certification mode in debugfs as cert_mode. In the case
-we use it to disable BA timeout from STA to prevent crashing STA.
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- include/net/mac80211.h | 6 ++++++
- net/mac80211/agg-tx.c | 5 ++++-
- net/mac80211/debugfs.c | 49 ++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 59 insertions(+), 1 deletion(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index d641b18..ba8343f 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -2910,8 +2910,14 @@ struct ieee80211_hw {
- u32 max_mtu;
- const s8 *tx_power_levels;
- u8 max_txpwr_levels_idx;
-+ bool cert_mode;
- };
-
-+static inline bool ieee80211_is_cert_mode(struct ieee80211_hw *hw)
-+{
-+ return hw->cert_mode;
-+}
-+
- static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
- enum ieee80211_hw_flags flg)
- {
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 285433a..a2f1e14 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -1108,7 +1108,10 @@ next:
- tid_tx->timeout =
- le16_to_cpu(mgmt->u.action.u.addba_resp.timeout);
-
-- if (tid_tx->timeout) {
-+ /* In the case of certification env, testbed STA cannot accept frequent DelBA.
-+ * Therefore, we remove the session timer check here to avoid crashing testbed STA.
-+ */
-+ if (tid_tx->timeout && !ieee80211_is_cert_mode(&local->hw)) {
- mod_timer(&tid_tx->session_timer,
- TU_TO_EXP_TIME(tid_tx->timeout));
- tid_tx->last_tx = jiffies;
-diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
-index 006b46d..8c29786 100644
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -449,6 +449,54 @@ static const struct file_operations reset_ops = {
- };
- #endif
-
-+static ssize_t cert_mode_read(struct file *file,
-+ char __user *user_buf,
-+ size_t count,
-+ loff_t *ppos)
-+{
-+ struct ieee80211_local *local = file->private_data;
-+ char buf[32];
-+ int len = 0;
-+
-+ len = scnprintf(buf, sizeof(buf), "cert_mode: %d\n",
-+ local->hw.cert_mode);
-+
-+ return simple_read_from_buffer(user_buf, count, ppos,
-+ buf, len);
-+}
-+
-+static ssize_t cert_mode_write(struct file *file,
-+ const char __user *user_buf,
-+ size_t count,
-+ loff_t *ppos)
-+{
-+ struct ieee80211_local *local = file->private_data;
-+ char buf[16];
-+
-+ if (count >= sizeof(buf))
-+ return -EINVAL;
-+
-+ if (copy_from_user(buf, user_buf, count))
-+ return -EFAULT;
-+
-+ if (count && buf[count - 1] == '\n')
-+ buf[count - 1] = '\0';
-+ else
-+ buf[count] = '\0';
-+
-+ if (kstrtobool(buf, &local->hw.cert_mode))
-+ return -EINVAL;
-+
-+ return count;
-+}
-+
-+static const struct file_operations cert_mode_ops = {
-+ .write = cert_mode_write,
-+ .read = cert_mode_read,
-+ .open = simple_open,
-+ .llseek = noop_llseek,
-+};
-+
- static const char *hw_flag_names[] = {
- #define FLAG(F) [IEEE80211_HW_##F] = #F
- FLAG(HAS_RATE_CONTROL),
-@@ -680,6 +728,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
- debugfs_create_u32("aql_threshold", 0600,
- phyd, &local->aql_threshold);
-
-+ DEBUGFS_ADD_MODE(cert_mode, 0644);
- statsd = debugfs_create_dir("statistics", phyd);
-
- #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0034-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0034-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch
deleted file mode 100644
index b6e976a..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0034-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch
+++ /dev/null
@@ -1,66 +0,0 @@
-From 618d7f6ad1b398bc21258559c80928febba3dfac Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Wed, 29 Nov 2023 13:51:13 +0800
-Subject: [PATCH 34/37] mtk: mac80211: ACS channel time is reset by ch_restore
-
-Issue:
-There's a chance that the channel time for duty channel is zero in ACS
-scan.
-
-Root cause:
-The chan_stat may be reset when restore to duty channel.
-Mac80211 will notify to hostapd when scan done and then restore to duty
-channel.
-And mt76 will clear scan flag after restore done.
-If hostapd get the chan_stat before channel_restore, will get the
-correct channel time;
-If hostapd get the chan_stat after channel_restore, will get zero
-channel time;
-
-Solution:
-When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
-Mac80211 scan state will be set in scanning, and will be reset after
-scan done and before restore to duty channel.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- include/net/mac80211.h | 7 +++++++
- net/mac80211/util.c | 9 +++++++++
- 2 files changed, 16 insertions(+)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index ba8343f..453466a 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -7485,4 +7485,11 @@ int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links);
- void ieee80211_set_active_links_async(struct ieee80211_vif *vif,
- u16 active_links);
-
-+/**
-+ * ieee80211_get_scanning - get scanning bitmask
-+ *
-+ * @hw: pointer as obtained from ieee80211_alloc_hw()
-+ */
-+unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw);
-+
- #endif /* MAC80211_H */
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index fd82488..f0bb4e8 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -5154,3 +5154,12 @@ void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id)
-
- *len_pos = elem_len;
- }
-+
-+unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+
-+ return local->scanning;
-+}
-+EXPORT_SYMBOL(ieee80211_get_scanning);
-+
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0035-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0035-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
deleted file mode 100644
index 794407f..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0035-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 9ff05c938d2a2b82d022aa8f671d2333cd8a992a Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Thu, 30 Nov 2023 14:01:29 +0800
-Subject: [PATCH 35/37] mtk: mac80211: Fix SMPS action frame cap check
-
-Fix SMPS action frame cap check.
-Due to 6G band doesn't have HT cap, we change cap check into each action
-frame section.
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- net/mac80211/rx.c | 8 +++++---
- 1 file changed, 5 insertions(+), 3 deletions(-)
-
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 0cf8c59..fe3a538 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3493,9 +3493,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
-
- switch (mgmt->u.action.category) {
- case WLAN_CATEGORY_HT:
-- /* reject HT action frames from stations not supporting HT */
-- if (!rx->link_sta->pub->ht_cap.ht_supported)
-- goto invalid;
-
- if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
-@@ -3514,6 +3511,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
- enum ieee80211_smps_mode smps_mode;
- struct sta_opmode_info sta_opmode = {};
-
-+ if (rx->link_sta->pub->he_cap.has_he &&
-+ !(rx->link_sta->pub->he_cap.he_cap_elem.mac_cap_info[5] &
-+ IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS))
-+ goto invalid;
-+
- if (sdata->vif.type != NL80211_IFTYPE_AP &&
- sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
- goto handled;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0036-mtk-mac80211-Add-CSA-action-frame-tx-when-channel-sw.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0036-mtk-mac80211-Add-CSA-action-frame-tx-when-channel-sw.patch
deleted file mode 100644
index a5cf569..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0036-mtk-mac80211-Add-CSA-action-frame-tx-when-channel-sw.patch
+++ /dev/null
@@ -1,117 +0,0 @@
-From 2bb34cb05062f9f53252c5a15304a75141e02660 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Thu, 30 Nov 2023 16:42:59 +0800
-Subject: [PATCH 36/37] mtk: mac80211: Add CSA action frame tx when channel
- switch on AP
-
-Description:
-To meet spec requirement.
-
-802.11-2020
-11.8.8.2 Selecting and advertising a new channel in a non-DMG infrastructure BSS
-11.8.8.6 Selecting and advertising a new channel in a DMG BSS
--
-An AP shall inform associated STAs that the AP is moving to a new channel and shall maintain the association by advertising the switch using Channel Switch Announcement elements in Beacon frames, Probe Response frames, and Channel Switch Announcement frames until the intended channel switch time.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- net/mac80211/cfg.c | 28 ++++++++++++++--------------
- net/mac80211/util.c | 10 +++++++---
- 2 files changed, 21 insertions(+), 17 deletions(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index cf694bc..a2d1688 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -3798,15 +3798,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
- * immediately too. If we would delay the switch
- * until the next TBTT, we would have to set the probe
- * response here.
-- *
-- * TODO: A channel switch with count <= 1 without
-- * sending a CSA action frame is kind of useless,
-- * because the clients won't know we're changing
-- * channels. The action frame must be implemented
-- * either here or in the userspace.
- */
-- if (params->count <= 1)
-- break;
-
- if ((params->n_counter_offsets_beacon >
- IEEE80211_MAX_CNTDWN_COUNTERS_NUM) ||
-@@ -3822,14 +3814,20 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
- csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
- csa.count = params->count;
-
-- err = ieee80211_assign_beacon(sdata, &sdata->deflink,
-- ¶ms->beacon_csa, &csa,
-- NULL, changed);
-- if (err < 0) {
-- ieee80211_free_next_beacon(&sdata->deflink);
-- return err;
-+ /* see comments in the NL80211_IFTYPE_AP block */
-+ if (params->count > 1) {
-+ err = ieee80211_assign_beacon(sdata, &sdata->deflink,
-+ ¶ms->beacon_csa, &csa,
-+ NULL, changed);
-+
-+ if (err < 0) {
-+ ieee80211_free_next_beacon(&sdata->deflink);
-+ return err;
-+ }
- }
-
-+ ieee80211_send_action_csa(sdata, params);
-+
- break;
- case NL80211_IFTYPE_ADHOC:
- if (!sdata->vif.cfg.ibss_joined)
-@@ -3901,6 +3899,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
- }
- #endif
- default:
-+ sdata_info(sdata, "Won't send CSA for vif.type:%d.\n", sdata->vif.type);
- return -EOPNOTSUPP;
- }
-
-@@ -3990,6 +3989,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
-
- err = ieee80211_set_csa_beacon(sdata, params, &changed);
- if (err) {
-+ sdata_info(sdata, "failed to set csa in beacon.\n");
- ieee80211_link_unreserve_chanctx(&sdata->deflink);
- goto out;
- }
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index f0bb4e8..00a20a6 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -4514,8 +4514,11 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
- u8 *pos;
-
- if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
-- sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
-+ sdata->vif.type != NL80211_IFTYPE_AP) {
-+ sdata_info(sdata, "Not allow csa action on vif.type:%d.\n", sdata->vif.type);
- return -EOPNOTSUPP;
-+ }
-
- skb = dev_alloc_skb(local->tx_headroom + hdr_len +
- 5 + /* channel switch announcement element */
-@@ -4532,9 +4535,10 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
-
- eth_broadcast_addr(mgmt->da);
- memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-- if (ieee80211_vif_is_mesh(&sdata->vif)) {
-+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
-+ sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
- memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
-- } else {
-+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0037-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0037-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
deleted file mode 100644
index d0d5004..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0037-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 84a875b2506fb720f902540ba23815e7b6958d92 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 25 Jan 2024 14:07:23 +0800
-Subject: [PATCH 37/37] mtk: mac80211: fix AP mgmt not encrypted in WDS mode
- with PMF on
-
-In ieee80211_tx_prepare(), if tx->sta is still NULL after calling
-sta_info_get(), the skb might be mgmt for WDS peer, so sta_info_get_bss()
-if called to find sta from AP_VLAN, and then interface type & 4-addr
-using is checked.
-
-CR-Id: WCNCR00289305
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- net/mac80211/tx.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index 0f6c960..8f85193 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1241,6 +1241,13 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
- if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) {
- tx->sta = sta_info_get(sdata, hdr->addr1);
- aggr_check = true;
-+
-+ if (!tx->sta) {
-+ tx->sta = sta_info_get_bss(sdata, hdr->addr1);
-+ if (!tx->sta || !tx->sta->sdata->wdev.use_4addr ||
-+ tx->sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
-+ tx->sta = NULL;
-+ }
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
index 8a4fb66..9bb2504 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
@@ -1,63 +1,64 @@
#patch subsys (come from openwrt/lede/target/linux/mediatek)
SRC_URI_append = " \
- file://110-mac80211_keep_keys_on_stop_ap.patch \
- file://120-cfg80211_allow_perm_addr_change.patch \
- file://130-disable_auto_vif.patch \
- file://210-ap_scan.patch \
- file://301-mac80211-sta-randomize-BA-session-dialog-token-alloc.patch \
- file://302-mac80211-minstrel_ht-fix-MINSTREL_FRAC-macro.patch \
- file://303-mac80211-minstrel_ht-reduce-fluctuations-in-rate-pro.patch \
- file://304-mac80211-minstrel_ht-rework-rate-downgrade-code-and-.patch \
- file://305-mac80211-increase-quantum-for-airtime-scheduler.patch \
- file://310-mac80211-split-mesh-fast-tx-cache-into-local-proxied.patch \
- file://312-wifi-cfg80211-annotate-iftype_data-pointer-with-spar.patch \
- file://313-wifi-cfg80211-export-DFS-CAC-time-and-usable-state-h.patch \
- file://314-wifi-mac80211-fix-race-condition-on-enabling-fast-xm.patch \
- file://320-cfg80211-allow-grace-period-for-DFS-available-after-.patch \
- file://330-mac80211-add-AQL-support-for-broadcast-packets.patch \
- file://331-wifi-mac80211-only-call-drv_sta_rc_update-for-upload.patch \
- file://332-wifi-mac80211-check-clear-fast-rx-for-non-4addr-sta-.patch \
- file://400-allow-ibss-mixed.patch \
- file://780-avoid-crashing-missing-band.patch \
- file://782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch \
- file://bp-0001-backports-add-SKB_DROP_REASON-and-kfree_skb_reason-b.patch \
- file://bp-0002-backports-update-kernel-version-check-for-eth_hw_add.patch \
- file://bp-0003-backports-Revert-cfg80211-allow-grace-period-for-DFS.patch \
- file://mtk-0001-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch \
- file://mtk-0002-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch \
- file://mtk-0003-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch \
- file://mtk-0004-mtk-mac80211-add-support-for-runtime-set-inband-disc.patch \
- file://mtk-0005-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch \
- file://mtk-0006-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch \
- file://mtk-0007-mtk-mac80211-check-the-control-channel-before-downgr.patch \
- file://mtk-0008-mtk-mac80211-fix-tx-amsdu-aggregation.patch \
- file://mtk-0009-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch \
- file://mtk-0010-mtk-mac80211-fix-build-error-on-Linux-Kernel-5.4.patch \
- file://mtk-0011-mtk-mac80211-track-obss-color-bitmap.patch \
- file://mtk-0012-mtk-mac80211-ageout-color-bitmap.patch \
- file://mtk-0013-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch \
- file://mtk-0014-mtk-mac80211-support-configurable-addba-resp-time.patch \
- file://mtk-0015-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch \
- file://mtk-0016-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch \
- file://mtk-0017-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch \
- file://mtk-0018-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch \
- file://mtk-0019-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch \
- file://mtk-0020-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch \
- file://mtk-0021-mtk-mac80211-Add-utilities-for-converting-op_class.patch \
- file://mtk-0022-mtk-mac80211-refactor-STA-CSA-parsing-flows.patch \
- file://mtk-0023-mtk-mac80211-add-EHT-BA1024-support.patch \
- file://mtk-0024-mtk-mac80211-add-rate-duration-for-EHT-rate.patch \
- file://mtk-0025-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch \
- file://mtk-0026-mtk-mac80211-inrease-beacon-loss-count.patch \
- file://mtk-0027-mtk-cfg80211-add-support-for-updating-background-cha.patch \
- file://mtk-0028-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch \
- file://mtk-0029-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch \
- file://mtk-0030-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch \
- file://mtk-0031-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch \
- file://mtk-0032-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch \
- file://mtk-0033-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch \
- file://mtk-0034-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch \
- file://mtk-0035-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch \
- file://mtk-0036-mtk-mac80211-Add-CSA-action-frame-tx-when-channel-sw.patch \
- file://mtk-0037-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch \
+ file://0001-sync-backports-patches-build.patch \
+ file://0002-sync-backports-patches-ath.patch \
+ file://0003-sync-backports-patches-ath5k.patch \
+ file://0004-sync-backports-patches-ath9k.patch \
+ file://0005-sync-backports-patches-ath10k.patch \
+ file://0006-sync-backports-patches-rt2x00.patch \
+ file://0007-sync-backports-patches-subsys.patch \
+ file://0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch \
+ file://0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch \
+ file://0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch \
+ file://0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch \
+ file://0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch \
+ file://0013-mtk-mac80211-check-the-control-channel-before-downgr.patch \
+ file://0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch \
+ file://0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch \
+ file://0016-mtk-mac80211-track-obss-color-bitmap.patch \
+ file://0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch \
+ file://0018-mtk-mac80211-support-configurable-addba-resp-time.patch \
+ file://0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch \
+ file://0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch \
+ file://0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch \
+ file://0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch \
+ file://0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch \
+ file://0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch \
+ file://0025-mtk-mac80211-add-EHT-BA1024-support.patch \
+ file://0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch \
+ file://0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch \
+ file://0028-mtk-mac80211-inrease-beacon-loss-count.patch \
+ file://0029-mtk-cfg80211-add-support-for-updating-background-cha.patch \
+ file://0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch \
+ file://0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch \
+ file://0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch \
+ file://0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch \
+ file://0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch \
+ file://0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch \
+ file://0036-backports-update-kernel-version-check-for-eth_hw_add.patch \
+ file://0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch \
+ file://0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch \
+ file://0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch \
+ file://0040-mtk-mac80211-increase-association-timeout-time.patch \
+ file://0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch \
+ file://0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch \
+ file://0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch \
+ file://0044-mtk-wifi-mac80211-add-wds-mlo-support.patch \
+ file://0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch \
+ file://0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch \
+ file://0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch \
+ file://0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch \
+ file://0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch \
+ file://0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch \
+ file://0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch \
+ file://0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch \
+ file://0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch \
+ file://0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch \
+ file://0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch \
+ file://0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch \
+ file://0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch \
+ file://0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch \
+ file://0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch \
+ file://0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch \
+ file://0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch \
"
diff --git a/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb b/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb
index 621c993..479fc4d 100644
--- a/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb
+++ b/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb
@@ -6,10 +6,10 @@
inherit module
-PV = "6.6.15"
+PV = "2024-04-03"
SRC_URI = " \
- http://mirror2.openwrt.org/sources/backports-${PV}.tar.xz \
+ file://backports-${PV}.tar.xz \
file://config \
file://0001-rdkb-fix_build_issue-mac80211-without_depmod.patch;apply=no \
"
@@ -18,10 +18,10 @@
DEPENDS += "virtual/kernel"
DEPENDS += "bison-native coreutils-native flex-native"
-FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-6.x/build:"
+#FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-6.x/build:"
FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-6.x/subsys:"
-require files/patches-6.x/build/build.inc
+#require files/patches-6.x/build/build.inc
require files/patches-6.x/subsys/subsys.inc
SRC_URI_remove = "${@bb.utils.contains('DISTRO_FEATURES', 'flow_offload', '', 'file://mtk-0014-mac80211-mtk-add-fill-receive-path-ops-to-get-wed-id.patch', d)}"
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch
index 93e37fb..87c34b2 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch
@@ -1,7 +1,7 @@
-From 5278bba5f5096bf42e0d808d4c23b2af3d16dd73 Mon Sep 17 00:00:00 2001
+From 4b6925449bf9160932961c506fb8f06db29a5e77 Mon Sep 17 00:00:00 2001
From: Shayne Chen <shayne.chen@mediatek.com>
Date: Tue, 19 Sep 2023 11:21:23 +0800
-Subject: [PATCH 01/17] mtk: Revert "wifi: mt76: mt7996: fill txd by host
+Subject: [PATCH 001/116] mtk: Revert "wifi: mt76: mt7996: fill txd by host
driver"
This reverts commit 325a0c4931990d553487024c4f76c776492bdcc2.
@@ -10,7 +10,7 @@
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/mt7996/mac.c b/mt7996/mac.c
-index bc7111a7..3afdd7eb 100644
+index bc7111a71..3afdd7eb9 100644
--- a/mt7996/mac.c
+++ b/mt7996/mac.c
@@ -938,8 +938,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -41,5 +41,5 @@
if (!key)
txp->fw.flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME);
--
-2.18.0
+2.39.2
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-bp-sync-upstream-changes.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-bp-sync-upstream-changes.patch
new file mode 100644
index 0000000..0937b99
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0002-bp-sync-upstream-changes.patch
@@ -0,0 +1,79 @@
+From aff8d4c7a0312f94429e93edc21b0e6b04899e12 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 21 Mar 2024 12:14:20 +0800
+Subject: [PATCH 002/116] bp: sync upstream changes
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c | 6 +++---
+ mt7615/mcu.c | 2 +-
+ mt7915/mcu.c | 2 +-
+ mt7996/mcu.c | 2 +-
+ 4 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index e7b763bab..64307b967 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1613,8 +1613,8 @@ EXPORT_SYMBOL_GPL(mt76_get_sar_power);
+ static void
+ __mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+- if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif))
+- ieee80211_csa_finish(vif);
++ if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif, 0))
++ ieee80211_csa_finish(vif, 0);
+ }
+
+ void mt76_csa_finish(struct mt76_dev *dev)
+@@ -1638,7 +1638,7 @@ __mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ if (!vif->bss_conf.csa_active)
+ return;
+
+- dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif);
++ dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif, 0);
+ }
+
+ void mt76_csa_check(struct mt76_dev *dev)
+diff --git a/mt7615/mcu.c b/mt7615/mcu.c
+index ae34d019e..c807bd8d9 100644
+--- a/mt7615/mcu.c
++++ b/mt7615/mcu.c
+@@ -353,7 +353,7 @@ static void
+ mt7615_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+ if (vif->bss_conf.csa_active)
+- ieee80211_csa_finish(vif);
++ ieee80211_csa_finish(vif, 0);
+ }
+
+ static void
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index fe54a2f40..24daa0835 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -228,7 +228,7 @@ mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
+ return;
+
+- ieee80211_csa_finish(vif);
++ ieee80211_csa_finish(vif, 0);
+ }
+
+ static void
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index aa4478fdf..4f8e656b2 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -341,7 +341,7 @@ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
+ return;
+
+- ieee80211_csa_finish(vif);
++ ieee80211_csa_finish(vif, 0);
+ }
+
+ static void
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-mtk-wifi-mt76-connac-use-peer-address-for-station-BM.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-mtk-wifi-mt76-connac-use-peer-address-for-station-BM.patch
deleted file mode 100644
index 4067faf..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0002-mtk-wifi-mt76-connac-use-peer-address-for-station-BM.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From b4eb6156a2693382e93c5a2d5d3c564c0a2bb198 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 24 Aug 2023 18:38:11 +0800
-Subject: [PATCH 02/17] mtk: wifi: mt76: connac: use peer address for station
- BMC entry
-
-Set peer address and aid for the BMC wtbl of station interface. For some
-functions such as parsing MU_EDCA parameters from beacon, firmware will
-need peer address to do the correct mapping.
-
-Reported-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt76_connac_mcu.c | 9 ++++++++-
- mt7996/main.c | 3 +++
- 2 files changed, 11 insertions(+), 1 deletion(-)
-
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 368c5f46..fec158ec 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -392,7 +392,14 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
-
- if (!sta) {
- basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC);
-- eth_broadcast_addr(basic->peer_addr);
-+
-+ if (vif->type == NL80211_IFTYPE_STATION &&
-+ !is_zero_ether_addr(vif->bss_conf.bssid)) {
-+ memcpy(basic->peer_addr, vif->bss_conf.bssid, ETH_ALEN);
-+ basic->aid = cpu_to_le16(vif->cfg.aid);
-+ } else {
-+ eth_broadcast_addr(basic->peer_addr);
-+ }
- return;
- }
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 2bf8e8a8..37e40f1d 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -598,6 +598,9 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
- if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
- (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
- (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
-+ /* reset bmc wtbl once BSSID changed */
-+ if (changed & BSS_CHANGED_BSSID)
-+ mt7996_mcu_add_sta(dev, vif, NULL, false);
- mt7996_mcu_add_bss_info(phy, vif, true);
- mt7996_mcu_add_sta(dev, vif, NULL, true);
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-wifi-mt76-mt7996-disable-rx-header-translation-f.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-wifi-mt76-mt7996-disable-rx-header-translation-f.patch
deleted file mode 100644
index 520f6a9..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-wifi-mt76-mt7996-disable-rx-header-translation-f.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From ac159c7469469353fa25787ceb7f25c8e33c59fb Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 5 Sep 2023 17:31:49 +0800
-Subject: [PATCH 03/17] mtk: wifi: mt76: mt7996: disable rx header translation
- for BMC entry
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mcu.c | 9 +++++----
- 1 file changed, 5 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 1356ac14..7f412d6c 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1778,10 +1778,10 @@ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- else
- hdr_trans->from_ds = true;
-
-- wcid = (struct mt76_wcid *)sta->drv_priv;
-- if (!wcid)
-+ if (!sta)
- return;
-
-+ wcid = (struct mt76_wcid *)sta->drv_priv;
- hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
- if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
- hdr_trans->to_ds = true;
-@@ -2154,6 +2154,9 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- if (!enable)
- goto out;
-
-+ /* starec hdr trans */
-+ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
-+
- /* tag order is in accordance with firmware dependency. */
- if (sta) {
- /* starec hdrt mode */
-@@ -2178,8 +2181,6 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- mt7996_mcu_sta_muru_tlv(dev, skb, vif, sta);
- /* starec bfee */
- mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
-- /* starec hdr trans */
-- mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
- }
-
- ret = mt7996_mcu_add_group(dev, vif, sta);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-wifi-mt76-mt7996-let-upper-layer-handle-MGMT-fra.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-wifi-mt76-mt7996-let-upper-layer-handle-MGMT-fra.patch
new file mode 100644
index 0000000..0b35ce2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-wifi-mt76-mt7996-let-upper-layer-handle-MGMT-fra.patch
@@ -0,0 +1,51 @@
+From a764efbef07c65a3b712dc1c5d619c3f22bd6433 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 18 Mar 2024 17:06:58 +0800
+Subject: [PATCH 003/116] mtk: wifi: mt76: mt7996: let upper layer handle MGMT
+ frame protection
+
+The firmware support for management frame protection has limitations:
+- do not support cipher BIP-GMAC-128 and BIP-GMAC-256
+- support cipher BIP-CMAC-128 and BIP-CMAC-256, except action frame with
+ action type 'not robust'.
+
+Therefore, to simplify the logic, do not set the IGTK to firmware and
+let the encryption of management frames be handled by upper layer.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 16115c279..a65a1efca 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -352,10 +352,6 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+
+ /* fall back to sw encryption for unsupported ciphers */
+ switch (key->cipher) {
+- case WLAN_CIPHER_SUITE_AES_CMAC:
+- wcid_keyidx = &wcid->hw_key_idx2;
+- key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+- break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+@@ -363,6 +359,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_SMS4:
+ break;
++ case WLAN_CIPHER_SUITE_AES_CMAC:
++ wcid_keyidx = &wcid->hw_key_idx2;
++ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
++ fallthrough;
++ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ if (key->keyidx == 6 || key->keyidx == 7)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-wifi-mt76-mt7996-set-RCPI-value-in-rate-control-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-wifi-mt76-mt7996-set-RCPI-value-in-rate-control-.patch
deleted file mode 100644
index bcbf32a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-wifi-mt76-mt7996-set-RCPI-value-in-rate-control-.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 8f88d7ea931cb3fcc12f609598f8997e66051bff Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 13 Nov 2023 20:15:39 +0800
-Subject: [PATCH 04/17] mtk: wifi: mt76: mt7996: set RCPI value in rate control
- command
-
-Set RCPI values in mt7996_mcu_sta_rate_ctrl_tlv(), which can make the
-FW rate control be initialized with a better MCS selection table.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mcu.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 7f412d6c..0f1905f2 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1968,6 +1968,7 @@ static void
- mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
- {
-+#define INIT_RCPI 180
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt76_phy *mphy = mvif->phy->mt76;
- struct cfg80211_chan_def *chandef = &mphy->chandef;
-@@ -2065,6 +2066,8 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
- }
- ra->sta_cap = cpu_to_le32(cap);
-+
-+ memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi));
- }
-
- int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch
new file mode 100644
index 0000000..0f821aa
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch
@@ -0,0 +1,35 @@
+From b403f206062aee515c6d0fcabf327a87c7a04fbc Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 10 Apr 2024 14:05:12 +0800
+Subject: [PATCH 004/116] wifi: mt76: mt7996: use hweight16 to get correct
+ tx_ant
+
+The chainmask is u16 so using hweight8 cannot get correct tx_ant.
+
+Without this patch, the tx_ant of band 2 would be -1 and lead to
+the following issue:
+BUG: KASAN: stack-out-of-bounds in mt7996_mcu_add_sta+0x12e0/0x16e0 [mt7996e]
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+CR-Id: WCNCR00240772
+Change-Id: I5ccd634431c6047371e687c7c4bb4f315f2c97e5
+---
+ mt7996/mcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 4f8e656b2..e426d0737 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1653,7 +1653,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mvif->phy;
+- int tx_ant = hweight8(phy->mt76->chainmask) - 1;
++ int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+ struct sta_rec_bf *bf;
+ struct tlv *tlv;
+ const u8 matrix[4][4] = {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-wifi-mt76-mt7996-enable-hw-cso-module.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-wifi-mt76-mt7996-enable-hw-cso-module.patch
deleted file mode 100644
index 2321fa4..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-wifi-mt76-mt7996-enable-hw-cso-module.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-From 01b4af6ea238a69c90d136cdf4684869481d03b7 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Wed, 3 Jan 2024 15:21:44 +0800
-Subject: [PATCH 05/17] mtk: wifi: mt76: mt7996: enable hw cso module
-
-The cso module needs to be enabled. The cso mudule can help identify if the traffic
-is TCP traffic. This can assist the firmware in adjusting algorithms to
-improve overall performance.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt76_connac_mcu.h | 7 +++++++
- mt7996/mcu.c | 15 +++++++++++++++
- 2 files changed, 22 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 2a4aa796..f1cd2e50 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -610,6 +610,12 @@ struct sta_rec_ra_fixed {
- u8 mmps_mode;
- } __packed;
-
-+struct sta_rec_tx_proc {
-+ __le16 tag;
-+ __le16 len;
-+ __le32 flag;
-+} __packed;
-+
- /* wtbl_rec */
-
- struct wtbl_req_hdr {
-@@ -777,6 +783,7 @@ struct wtbl_raw {
- sizeof(struct sta_rec_ra_fixed) + \
- sizeof(struct sta_rec_he_6g_capa) + \
- sizeof(struct sta_rec_pn_info) + \
-+ sizeof(struct sta_rec_tx_proc) + \
- sizeof(struct tlv) + \
- MT76_CONNAC_WTBL_UPDATE_MAX_SIZE)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 0f1905f2..aa054167 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1748,6 +1748,19 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2);
- }
-
-+static void
-+mt7996_mcu_sta_tx_proc_tlv(struct sk_buff *skb)
-+{
-+ struct sta_rec_tx_proc *tx_proc;
-+ struct tlv *tlv;
-+
-+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_TX_PROC, sizeof(*tx_proc));
-+
-+ tx_proc = (struct sta_rec_tx_proc *)tlv;
-+ /* CSO is enabled if this flag exists. */
-+ tx_proc->flag = cpu_to_le32(0);
-+}
-+
- static void
- mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb)
- {
-@@ -2159,6 +2172,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-
- /* starec hdr trans */
- mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
-+ /* starec tx proc */
-+ mt7996_mcu_sta_tx_proc_tlv(skb);
-
- /* tag order is in accordance with firmware dependency. */
- if (sta) {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-wifi-mt76-mt7996-fix-MBSS.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-wifi-mt76-mt7996-fix-MBSS.patch
new file mode 100644
index 0000000..331153f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-wifi-mt76-mt7996-fix-MBSS.patch
@@ -0,0 +1,31 @@
+From eb0640a1313c7f6a3fd579d2e0ad4209808440db Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Thu, 18 Apr 2024 14:19:21 +0800
+Subject: [PATCH 005/116] mtk: wifi: mt76: mt7996: fix MBSS
+
+Refactor 11v mbss unicmd flow
+case1(disable->enable) : when we enable 11v MBSS, we have to add 11v mbss tlv(UNI_BSS_INFO_11V_MBSSID)
+case2(enable->disable) : when we diable 11v MBSS, we should clear 11v mbss tlv (UNI_BSS_INFO_11V_MBSSID-> all value to zero) first,
+otherwise it will cause PSE opration ERR and trigger L1SER. After clear 11v mbss tlv,we have to reset UNI_BSS_INFO_BASIC(from 11v MBSS mode to legacy mode)
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/mcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index e426d0737..f23801901 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -822,7 +822,7 @@ mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ struct bss_info_uni_mbssid *mbssid;
+ struct tlv *tlv;
+
+- if (!vif->bss_conf.bssid_indicator)
++ if (!vif->bss_conf.bssid_indicator && enable)
+ return;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-wifi-mt76-mt7996-fix-non-main-BSS-no-beacon-issu.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-wifi-mt76-mt7996-fix-non-main-BSS-no-beacon-issu.patch
deleted file mode 100644
index 38cb111..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-wifi-mt76-mt7996-fix-non-main-BSS-no-beacon-issu.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 962986bbac91d5a97482f9ae74655ffca70763b1 Mon Sep 17 00:00:00 2001
-From: Henry Yen <henry.yen@mediatek.com>
-Date: Fri, 2 Feb 2024 16:42:24 +0800
-Subject: [PATCH 06/17] mtk: wifi: mt76: mt7996: fix non-main BSS no beacon
- issue for legacy MBSS scenario
-
-Fix non-main BSS no beacon issue for mt7992 legacy MBSS scenario.
-
-There are some design differences between mt7996 and mt7992 in terms of
-MBSS time offset. The original MBSS MCU CMD usage is not applicable to
-mt7992, so we modify the flow to avoid abnormal beaconing behavior
-in MBSS scenario.
-
-Signed-off-by: Henry.Yen <henry.yen@mediatek.com>
-
----
- mt7996/mcu.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index aa054167..6b8a5076 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -819,11 +819,14 @@ mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- struct bss_info_uni_mbssid *mbssid;
- struct tlv *tlv;
-
-+ if (!vif->bss_conf.bssid_indicator)
-+ return;
-+
- tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
-
- mbssid = (struct bss_info_uni_mbssid *)tlv;
-
-- if (enable && vif->bss_conf.bssid_indicator) {
-+ if (enable) {
- mbssid->max_indicator = vif->bss_conf.bssid_indicator;
- mbssid->mbss_idx = vif->bss_conf.bssid_index;
- mbssid->tx_bss_omac_idx = 0;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch
new file mode 100644
index 0000000..82e083c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch
@@ -0,0 +1,148 @@
+From a6f5e8e1df21f5d65fcef533b96b9d26680d5bc3 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 12 Mar 2024 09:07:52 +0800
+Subject: [PATCH 006/116] wifi: mt76: mt7996: fix HE and EHT phy cap
+
+This commit fix he and eht phy capabailties ie. For HE phy cap, fix
+correct beamform capabilities for station vif. For EHT phy cap, remove
+unsupported capabilities.
+
+Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi7 (802.11be) devices")
+Fixes: 348533eb968d ("wifi: mt76: mt7996: add EHT capability init")
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+Change-Id: Ic853ec1b62344ac08787673ae22254f9829e0e6e
+CR-Id: WCNCR00240772
+---
+ mt7996/init.c | 65 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 43 insertions(+), 22 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 9aa97e4a7..c264d5043 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1010,8 +1010,6 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
+ return;
+
+ elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
+- if (vif == NL80211_IFTYPE_AP)
+- elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
+
+ c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ sts - 1) |
+@@ -1019,6 +1017,11 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
+ sts - 1);
+ elem->phy_cap_info[5] |= c;
+
++ if (vif != NL80211_IFTYPE_AP)
++ return;
++
++ elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
++
+ c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB;
+ elem->phy_cap_info[6] |= c;
+@@ -1178,7 +1181,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ IEEE80211_EHT_MAC_CAP0_OM_CONTROL;
+
+ eht_cap_elem->phy_cap_info[0] =
+- IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ |
+ IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
+ IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
+ IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
+@@ -1192,30 +1194,36 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)),
+ IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) |
+ u8_encode_bits(val,
+- IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK) |
+- u8_encode_bits(val,
+- IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
++ IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK);
+
+ eht_cap_elem->phy_cap_info[2] =
+ u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) |
+- u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK) |
+- u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK);
++ u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK);
++
++ if (band == NL80211_BAND_6GHZ) {
++ eht_cap_elem->phy_cap_info[0] |=
++ IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
++
++ eht_cap_elem->phy_cap_info[1] |=
++ u8_encode_bits(val,
++ IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
++
++ eht_cap_elem->phy_cap_info[2] |=
++ u8_encode_bits(sts - 1,
++ IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK);
++ }
+
+ eht_cap_elem->phy_cap_info[3] =
+ IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
+ IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
+ IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
+- IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
+- IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
+- IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
+- IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK;
++ IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK;
+
+ eht_cap_elem->phy_cap_info[4] =
+ u8_encode_bits(min_t(int, sts - 1, 2),
+ IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK);
+
+ eht_cap_elem->phy_cap_info[5] =
+- IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
+ u8_encode_bits(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US,
+ IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK) |
+ u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)),
+@@ -1229,14 +1237,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK) |
+ u8_encode_bits(val, IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK);
+
+- eht_cap_elem->phy_cap_info[7] =
+- IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
+- IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
+- IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ |
+- IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
+- IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ |
+- IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;
+-
+ val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) |
+ u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX);
+ #define SET_EHT_MAX_NSS(_bw, _val) do { \
+@@ -1247,8 +1247,29 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+
+ SET_EHT_MAX_NSS(80, val);
+ SET_EHT_MAX_NSS(160, val);
+- SET_EHT_MAX_NSS(320, val);
++ if (band == NL80211_BAND_6GHZ)
++ SET_EHT_MAX_NSS(320, val);
+ #undef SET_EHT_MAX_NSS
++
++ if (iftype != NL80211_IFTYPE_AP)
++ return;
++
++ eht_cap_elem->phy_cap_info[3] |=
++ IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
++ IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK;
++
++ eht_cap_elem->phy_cap_info[7] =
++ IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
++ IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
++ IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
++ IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ;
++
++ if (band != NL80211_BAND_6GHZ)
++ return;
++
++ eht_cap_elem->phy_cap_info[7] |=
++ IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ |
++ IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;
+ }
+
+ static void
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch
new file mode 100644
index 0000000..d2cd068
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch
@@ -0,0 +1,69 @@
+From 4dde35f480e8bdf961911d07a49c5443cc0c8e6a Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 27 Feb 2024 14:50:20 +0800
+Subject: [PATCH 007/116] mtk: wifi: mt76: mt7996: adjust Beamformee SS
+ capability
+
+This commit includes two changes to adjust beamformee ss capability.
+First, configure the beamformee ss capability for mt7992 chipsets.
+Second, no matter how many antenna numbers is set, always set the
+maximum capability of Beamformee SS that chipsets support.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+CR-Id: WCNCR00240772
+Change-Id: I8a7efedbb02790925affdd3b4d81b623f459589c
+---
+ mt7996/init.c | 23 +++++++++++++++++------
+ 1 file changed, 17 insertions(+), 6 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index c264d5043..ab9445cc3 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -940,8 +940,12 @@ void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy)
+ cap = &phy->mt76->sband_5g.sband.vht_cap.cap;
+
+ *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+- IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+- FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, sts - 1);
++ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
++
++ if (is_mt7996(phy->mt76->dev))
++ *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3);
++ else
++ *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 4);
+
+ *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK |
+ IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+@@ -986,9 +990,15 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
+ IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
+ elem->phy_cap_info[2] |= c;
+
+- c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+- IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
+- IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
++ c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
++
++ if (is_mt7996(phy->mt76->dev))
++ c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
++ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
++ else
++ c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5 |
++ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5;
++
+ elem->phy_cap_info[4] |= c;
+
+ /* do not support NG16 due to spec D4.0 changes subcarrier idx */
+@@ -1185,7 +1195,8 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
+ IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
+
+- val = max_t(u8, sts - 1, 3);
++ /* Set the maximum capability regardless of the antenna configuration. */
++ val = is_mt7992(phy->mt76->dev) ? 4 : 3;
+ eht_cap_elem->phy_cap_info[0] |=
+ u8_encode_bits(u8_get_bits(val, BIT(0)),
+ IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-wifi-mt76-mt7996-initialize-variable-to-avoid-un.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-wifi-mt76-mt7996-initialize-variable-to-avoid-un.patch
deleted file mode 100644
index 33200ad..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-wifi-mt76-mt7996-initialize-variable-to-avoid-un.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 354503529d28d44fc94ea65815da5bf0cbdb79e7 Mon Sep 17 00:00:00 2001
-From: Henry Yen <henry.yen@mediatek.com>
-Date: Fri, 19 Jan 2024 11:11:19 +0800
-Subject: [PATCH 07/17] mtk: wifi: mt76: mt7996: initialize variable to avoid
- unexpected IRQ handling
-
-Initialize the variable to avoid processing unexpected interrupts given from wrong source.
-
-Signed-off-by: Henry.Yen <henry.yen@mediatek.com>
-
----
- mt7996/mmio.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 341fa089..8fe56ed9 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -519,7 +519,7 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
- struct mt7996_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet);
- struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- struct mtk_wed_device *wed_hif2 = &dev->mt76.mmio.wed_hif2;
-- u32 i, intr, mask, intr1;
-+ u32 i, intr, mask, intr1 = 0;
-
- if (dev->hif2 && mtk_wed_device_active(wed_hif2)) {
- mtk_wed_device_irq_set_mask(wed_hif2, 0);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-wifi-mt76-mt7996-enable-ser-query.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-wifi-mt76-mt7996-enable-ser-query.patch
deleted file mode 100644
index e35b141..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-wifi-mt76-mt7996-enable-ser-query.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From f664f6c14d38adc2bf43f4f969ea21eb3daa48a7 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 30 Oct 2023 20:19:41 +0800
-Subject: [PATCH 08/17] mtk: wifi: mt76: mt7996: enable ser query
-
-Do not return -EINVAL when action is UNI_CMD_SER_QUERY for user
-to dump SER information from FW.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- mt7996/mcu.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6b8a5076..0981f592 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3860,6 +3860,8 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
- };
-
- switch (action) {
-+ case UNI_CMD_SER_QUERY:
-+ break;
- case UNI_CMD_SER_SET:
- req.set.mask = cpu_to_le32(val);
- break;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch
new file mode 100644
index 0000000..327a8e3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch
@@ -0,0 +1,34 @@
+From 43f21d17de82755344f60fe7a026bc3566a89dfb Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 18 Mar 2024 11:13:56 +0800
+Subject: [PATCH 008/116] wifi: mt76: mt7992: adjust beamform mcu cmd
+ configuration for mt7992
+
+Adjust the correct beamform mcu cmd configuration for mt7992 chipsets.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+CR-Id: WCNCR00240772
+Change-Id: I2230c3a43d735ab7db114299a005b9cc1e89de41
+---
+ mt7996/mcu.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index f23801901..7928571e0 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3920,8 +3920,9 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en));
+ req_mod_en = (struct bf_mod_en_ctrl *)tlv;
+- req_mod_en->bf_num = 3;
+- req_mod_en->bf_bitmap = GENMASK(2, 0);
++ req_mod_en->bf_num = mt7996_band_valid(dev, MT_BAND2) ? 3 : 2;
++ req_mod_en->bf_bitmap = mt7996_band_valid(dev, MT_BAND2) ?
++ GENMASK(2, 0) : GENMASK(1, 0);
+ break;
+ }
+ default:
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
deleted file mode 100644
index 2cf4eec..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 8e5ba5be6820fa12541785907085f312f13e3e04 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Fri, 17 Nov 2023 11:01:04 +0800
-Subject: [PATCH 09/17] mtk: wifi: mt76: mt7996: Fix TGax HE-4.51.1_24G fail
-
-According to sta capability to decide to enable/disable wed pao when create ppe entry.
-without this patch, TGax HE-4.51.1_24G will test fail
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
----
- mt7996/main.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 37e40f1d..625f87b4 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1447,7 +1447,12 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- path->mtk_wdma.queue = 0;
- path->mtk_wdma.wcid = msta->wcid.idx;
-
-- path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed);
-+ if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) &&
-+ mtk_wed_is_amsdu_supported(wed))
-+ path->mtk_wdma.amsdu = msta->wcid.amsdu;
-+ else
-+ path->mtk_wdma.amsdu = 0;
-+
- ctx->dev = NULL;
-
- return 0;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch
new file mode 100644
index 0000000..90c2dd0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch
@@ -0,0 +1,154 @@
+From e820e86fa2d0b0e0de3c86feb1e0cb4c2ab6e5a2 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 18 Apr 2024 11:16:24 +0800
+Subject: [PATCH 009/116] mtk: wifi: mt76: mt7996: add preamble puncture
+ support for mt7996
+
+Add support configure preamble puncture feature through mcu commands.
+
+Co-developed-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt76_connac_mcu.h | 1 +
+ mt7996/init.c | 1 +
+ mt7996/main.c | 5 +++++
+ mt7996/mcu.c | 40 ++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h | 10 ++++++++++
+ mt7996/mt7996.h | 4 ++++
+ 6 files changed, 61 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 67be14d2a..70def0a3b 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1271,6 +1271,7 @@ enum {
+ MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
+ MCU_UNI_CMD_THERMAL = 0x35,
+ MCU_UNI_CMD_VOW = 0x37,
++ MCU_UNI_CMD_PP = 0x38,
+ MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ MCU_UNI_CMD_RRO = 0x57,
+ MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index ab9445cc3..afe8a0a1a 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -387,6 +387,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
++ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
+
+ if (!mdev->dev->of_node ||
+ !of_property_read_bool(mdev->dev->of_node,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a65a1efca..2e72fd8ed 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -411,6 +411,11 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ int ret;
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
++ ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
++ phy->mt76->chandef.punctured);
++ if (ret)
++ return ret;
++
+ ieee80211_stop_queues(hw);
+ ret = mt7996_set_channel(phy);
+ if (ret)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7928571e0..36f72d586 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4550,3 +4550,43 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT),
+ &cp_mode, sizeof(cp_mode), true);
+ }
++
++int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
++{
++ struct mt7996_dev *dev = phy->dev;
++ bool pp_auto = (mode == PP_FW_MODE);
++ struct {
++ u8 _rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++ u8 mgmt_mode;
++ u8 band_idx;
++ u8 force_bitmap_ctrl;
++ u8 auto_mode;
++ __le16 bitmap;
++ u8 _rsv2[2];
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_CMD_PP_EN_CTRL),
++ .len = cpu_to_le16(sizeof(req) - 4),
++
++ .mgmt_mode = !pp_auto,
++ .band_idx = phy->mt76->band_idx,
++ .force_bitmap_ctrl = (mode == PP_USR_MODE) ? 2 : 0,
++ .auto_mode = pp_auto,
++ .bitmap = cpu_to_le16(bitmap),
++ };
++
++ if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ ||
++ mode > PP_USR_MODE)
++ return 0;
++
++ if (bitmap && phy->punct_bitmap == bitmap)
++ return 0;
++
++ phy->punct_bitmap = bitmap;
++ phy->pp_mode = mode;
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
++ &req, sizeof(req), false);
++}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index a9ba63d14..2052555fe 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -923,6 +923,16 @@ enum {
+ MT7996_SEC_MODE_MAX,
+ };
+
++enum {
++ UNI_CMD_PP_EN_CTRL,
++};
++
++enum pp_mode {
++ PP_DISABLE = 0,
++ PP_FW_MODE,
++ PP_USR_MODE,
++};
++
+ #define MT7996_PATCH_SEC GENMASK(31, 24)
+ #define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8)
+ #define MT7996_PATCH_AES_KEY GENMASK(7, 0)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 177cfff31..58fa6b458 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -237,6 +237,9 @@ struct mt7996_phy {
+ struct mt76_channel_state state_ts;
+
+ bool has_aux_rx;
++
++ u8 pp_mode;
++ u16 punct_bitmap;
+ };
+
+ struct mt7996_dev {
+@@ -614,6 +617,7 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
++int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct dentry *dir);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch
new file mode 100644
index 0000000..d46ffbb
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch
@@ -0,0 +1,27 @@
+From 2a8a93b5c49cb56c5eace22690c7e1544d4e80b1 Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 24 Mar 2023 19:18:53 +0800
+Subject: [PATCH 010/116] mtk: wifi: mt76: mt7996: add driver support for wpa3
+ ocv and bp mt76
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ mt7996/init.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index afe8a0a1a..ab2e17ec4 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -389,6 +389,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
+
++ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
++ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
+ if (!mdev->dev->of_node ||
+ !of_property_read_bool(mdev->dev->of_node,
+ "mediatek,disable-radar-background"))
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-add-eagle-default-bin-of-differ.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-add-eagle-default-bin-of-differ.patch
deleted file mode 100644
index 9c922d6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-add-eagle-default-bin-of-differ.patch
+++ /dev/null
@@ -1,111 +0,0 @@
-From 22607765799dad52bcf304ffd5f393878c4317bf Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 20 Jul 2023 17:27:22 +0800
-Subject: [PATCH 10/17] mtk: wifi: mt76: mt7996: add eagle default bin of
- different sku variants
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 2 ++
- mt7996/init.c | 4 ++++
- mt7996/mt7996.h | 28 ++++++++++++++++++++++++++--
- 3 files changed, 32 insertions(+), 2 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 4a823711..7505a8b7 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -26,6 +26,8 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
- {
- switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
-+ if (dev->chip_sku == MT7996_SKU_404)
-+ return MT7996_EEPROM_DEFAULT_404;
- return MT7996_EEPROM_DEFAULT;
- case 0x7992:
- return MT7992_EEPROM_DEFAULT;
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 9aa97e4a..274863dc 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -897,6 +897,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
- INIT_LIST_HEAD(&dev->wed_rro.poll_list);
- spin_lock_init(&dev->wed_rro.lock);
-
-+ ret = mt7996_get_chip_sku(dev);
-+ if (ret)
-+ return ret;
-+
- ret = mt7996_dma_init(dev);
- if (ret)
- return ret;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 36d1f247..b6df2167 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -40,6 +40,7 @@
- #define MT7992_ROM_PATCH "mediatek/mt7996/mt7992_rom_patch.bin"
-
- #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
-+#define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
- #define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom.bin"
- #define MT7996_EEPROM_SIZE 7680
- #define MT7996_EEPROM_BLOCK_SIZE 16
-@@ -88,6 +89,11 @@ struct mt7996_sta;
- struct mt7996_dfs_pulse;
- struct mt7996_dfs_pattern;
-
-+enum mt7996_sku_type {
-+ MT7996_SKU_404,
-+ MT7996_SKU_444,
-+};
-+
- enum mt7996_ram_type {
- MT7996_RAM_TYPE_WM,
- MT7996_RAM_TYPE_WA,
-@@ -257,6 +263,8 @@ struct mt7996_dev {
- struct cfg80211_chan_def rdd2_chandef;
- struct mt7996_phy *rdd2_phy;
-
-+ u8 chip_sku;
-+
- u16 chainmask;
- u8 chainshift[__MT_MAX_BAND];
- u32 hif_idx;
-@@ -398,6 +406,23 @@ mt7996_phy3(struct mt7996_dev *dev)
- return __mt7996_phy(dev, MT_BAND2);
- }
-
-+static inline int
-+mt7996_get_chip_sku(struct mt7996_dev *dev)
-+{
-+ u32 val = mt76_rr(dev, MT_PAD_GPIO);
-+
-+ /* reserve for future variants */
-+ switch (mt76_chip(&dev->mt76)) {
-+ case 0x7990:
-+ dev->chip_sku = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val) <= 1;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
- static inline bool
- mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- {
-@@ -405,8 +430,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- return band <= MT_BAND1;
-
- /* tri-band support */
-- if (band <= MT_BAND2 &&
-- mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) <= 1)
-+ if (band <= MT_BAND2 && dev->chip_sku)
- return true;
-
- return band == MT_BAND0 || band == MT_BAND2;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-add-kite-fw-default-bin-for-dif.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-add-kite-fw-default-bin-for-dif.patch
deleted file mode 100644
index dd04ff3..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-add-kite-fw-default-bin-for-dif.patch
+++ /dev/null
@@ -1,322 +0,0 @@
-From fcf4d59b7cbd6c298ca90b0eae6aec63544b14d9 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 21 Jul 2023 10:41:28 +0800
-Subject: [PATCH 11/17] mtk: wifi: mt76: mt7996: add kite fw & default bin for
- different sku variants
-
-Add fem type (2i5i, 2i5e, 2e5e, ...)
-Add Kite default bin for each fem type since loading wrong default bin
-will fail to setup interface
-Add eeprom fem type check
-
-Add adie 7976c efuse check
-Efuse offset 0x470 will be set to 0xc after final test if 7976c adie is used
-Chip manufactoring factories may transfer, which leads to different adie chip versions,
-so we add this efuse check to avoid 7976c recognition failure.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-GPIO ADie Combination of BE5040 should be considered as don't care
-instead of 0
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Only check eeprom chip id when fem type (= MT7996_FEM_UNSET) is not determined yet
-Without this fix, mt7996_check_eeprom will return EINVAL in mt7996_eeprom_check_fw_mode
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 38 +++++++++++++++++++++++++++++--
- mt7996/eeprom.h | 1 +
- mt7996/init.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.c | 7 +++++-
- mt7996/mt7996.h | 49 +++++++++++++++++++++++++---------------
- mt7996/regs.h | 7 ++++++
- 6 files changed, 140 insertions(+), 21 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 7505a8b7..3260d1fe 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -9,14 +9,33 @@
-
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
-+#define FEM_INT 0
-+#define FEM_EXT 3
- u8 *eeprom = dev->mt76.eeprom.data;
-+ u8 i, fem[__MT_MAX_BAND], fem_type;
- u16 val = get_unaligned_le16(eeprom);
-
-+ for (i = 0; i < __MT_MAX_BAND; i++)
-+ fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
-+
- switch (val) {
- case 0x7990:
- return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
- case 0x7992:
-- return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
-+ if (dev->fem_type == MT7996_FEM_UNSET)
-+ return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
-+
-+ if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
-+ fem_type = MT7996_FEM_EXT;
-+ else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
-+ fem_type = MT7996_FEM_INT;
-+ else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
-+ fem_type = MT7996_FEM_MIX;
-+ else
-+ return -EINVAL;
-+
-+ return (is_mt7992(&dev->mt76) ? 0 : -EINVAL) |
-+ (dev->fem_type == fem_type ? 0 : -EINVAL);
- default:
- return -EINVAL;
- }
-@@ -30,7 +49,18 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
- return MT7996_EEPROM_DEFAULT_404;
- return MT7996_EEPROM_DEFAULT;
- case 0x7992:
-- return MT7992_EEPROM_DEFAULT;
-+ if (dev->chip_sku == MT7992_SKU_23) {
-+ if (dev->fem_type == MT7996_FEM_INT)
-+ return MT7992_EEPROM_DEFAULT_23;
-+ return MT7992_EEPROM_DEFAULT_23_EXT;
-+ } else if (dev->chip_sku == MT7992_SKU_44) {
-+ if (dev->fem_type == MT7996_FEM_INT)
-+ return MT7992_EEPROM_DEFAULT;
-+ else if (dev->fem_type == MT7996_FEM_MIX)
-+ return MT7992_EEPROM_DEFAULT_MIX;
-+ return MT7992_EEPROM_DEFAULT_EXT;
-+ }
-+ return MT7992_EEPROM_DEFAULT_24;
- default:
- return MT7996_EEPROM_DEFAULT;
- }
-@@ -221,6 +251,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- {
- int ret;
-
-+ ret = mt7996_get_chip_sku(dev);
-+ if (ret)
-+ return ret;
-+
- ret = mt7996_eeprom_load(dev);
- if (ret < 0) {
- if (ret != -EINVAL)
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 412d6e2f..72c38ad3 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -29,6 +29,7 @@ enum mt7996_eeprom_field {
- #define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(2, 0)
- #define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(5, 3)
- #define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0)
-+#define MT_EE_WIFI_PA_LNA_CONFIG GENMASK(1, 0)
-
- #define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3)
- #define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(2, 0)
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 274863dc..0e3cdc05 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -882,6 +882,65 @@ out:
- #endif
- }
-
-+int mt7996_get_chip_sku(struct mt7996_dev *dev)
-+{
-+#define MT7976C_CHIP_VER 0x8a10
-+#define MT7976C_HL_CHIP_VER 0x8b00
-+#define MT7976C_PS_CHIP_VER 0x8c10
-+#define MT7976C_EFUSE_OFFSET 0x470
-+#define MT7976C_EFUSE_VALUE 0xc
-+ u32 regval, val = mt76_rr(dev, MT_PAD_GPIO);
-+ u16 adie_chip_id, adie_chip_ver;
-+ u8 adie_comb, adie_num, adie_idx = 0;
-+
-+ switch (mt76_chip(&dev->mt76)) {
-+ case 0x7990:
-+ adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
-+ if (adie_comb <= 1)
-+ dev->chip_sku = MT7996_SKU_444;
-+ else
-+ dev->chip_sku = MT7996_SKU_404;
-+ break;
-+ case 0x7992:
-+ adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB_7992, val);
-+ adie_num = FIELD_GET(MT_PAD_GPIO_ADIE_NUM_7992, val);
-+ adie_idx = !adie_num;
-+ if (adie_num)
-+ dev->chip_sku = MT7992_SKU_23;
-+ else if (adie_comb)
-+ dev->chip_sku = MT7992_SKU_44;
-+ else
-+ dev->chip_sku = MT7992_SKU_24;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) {
-+ u8 buf[MT7996_EEPROM_BLOCK_SIZE];
-+ u8 idx = MT7976C_EFUSE_OFFSET % MT7996_EEPROM_BLOCK_SIZE;
-+ bool is_7976c;
-+
-+ mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), ®val, false);
-+ adie_chip_id = FIELD_GET(MT_ADIE_CHIP_ID_MASK, regval);
-+ adie_chip_ver = FIELD_GET(MT_ADIE_VERSION_MASK, regval);
-+ mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf);
-+ is_7976c = (adie_chip_ver == MT7976C_CHIP_VER) ||
-+ (adie_chip_ver == MT7976C_HL_CHIP_VER) ||
-+ (adie_chip_ver == MT7976C_PS_CHIP_VER) ||
-+ (buf[idx] == MT7976C_EFUSE_VALUE);
-+ if (adie_chip_id == 0x7975 || (adie_chip_id == 0x7976 && is_7976c) ||
-+ adie_chip_id == 0x7979)
-+ dev->fem_type = MT7996_FEM_INT;
-+ else if (adie_chip_id == 0x7977 && adie_comb == 1)
-+ dev->fem_type = MT7996_FEM_MIX;
-+ else
-+ dev->fem_type = MT7996_FEM_EXT;
-+ }
-+
-+ return 0;
-+}
-+
- static int mt7996_init_hardware(struct mt7996_dev *dev)
- {
- int ret, idx;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 0981f592..5aefecb0 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -14,7 +14,12 @@
- char *_fw; \
- switch (mt76_chip(&(_dev)->mt76)) { \
- case 0x7992: \
-- _fw = MT7992_##name; \
-+ if ((_dev)->chip_sku == MT7992_SKU_23) \
-+ _fw = MT7992_##name##_23; \
-+ else if ((_dev)->chip_sku == MT7992_SKU_24) \
-+ _fw = MT7992_##name##_24; \
-+ else \
-+ _fw = MT7992_##name; \
- break; \
- case 0x7990: \
- default: \
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b6df2167..7e5ec212 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -39,9 +39,24 @@
- #define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin"
- #define MT7992_ROM_PATCH "mediatek/mt7996/mt7992_rom_patch.bin"
-
-+#define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin"
-+#define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin"
-+#define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin"
-+#define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin"
-+
-+#define MT7992_FIRMWARE_WA_23 "mediatek/mt7996/mt7992_wa_23.bin"
-+#define MT7992_FIRMWARE_WM_23 "mediatek/mt7996/mt7992_wm_23.bin"
-+#define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin"
-+#define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
-+
- #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
- #define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
--#define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom.bin"
-+#define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom_2i5i.bin"
-+#define MT7992_EEPROM_DEFAULT_EXT "mediatek/mt7996/mt7992_eeprom_2e5e.bin"
-+#define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin"
-+#define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
-+#define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
-+#define MT7992_EEPROM_DEFAULT_23_EXT "mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
- #define MT7996_EEPROM_SIZE 7680
- #define MT7996_EEPROM_BLOCK_SIZE 16
- #define MT7996_TOKEN_SIZE 16384
-@@ -89,11 +104,24 @@ struct mt7996_sta;
- struct mt7996_dfs_pulse;
- struct mt7996_dfs_pattern;
-
-+enum mt7996_fem_type {
-+ MT7996_FEM_UNSET,
-+ MT7996_FEM_EXT,
-+ MT7996_FEM_INT,
-+ MT7996_FEM_MIX,
-+};
-+
- enum mt7996_sku_type {
- MT7996_SKU_404,
- MT7996_SKU_444,
- };
-
-+enum mt7992_sku_type {
-+ MT7992_SKU_23,
-+ MT7992_SKU_24,
-+ MT7992_SKU_44,
-+};
-+
- enum mt7996_ram_type {
- MT7996_RAM_TYPE_WM,
- MT7996_RAM_TYPE_WA,
-@@ -264,6 +292,7 @@ struct mt7996_dev {
- struct mt7996_phy *rdd2_phy;
-
- u8 chip_sku;
-+ u8 fem_type;
-
- u16 chainmask;
- u8 chainshift[__MT_MAX_BAND];
-@@ -406,23 +435,6 @@ mt7996_phy3(struct mt7996_dev *dev)
- return __mt7996_phy(dev, MT_BAND2);
- }
-
--static inline int
--mt7996_get_chip_sku(struct mt7996_dev *dev)
--{
-- u32 val = mt76_rr(dev, MT_PAD_GPIO);
--
-- /* reserve for future variants */
-- switch (mt76_chip(&dev->mt76)) {
-- case 0x7990:
-- dev->chip_sku = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val) <= 1;
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- return 0;
--}
--
- static inline bool
- mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- {
-@@ -461,6 +473,7 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
- int n_desc, int ring_base, struct mtk_wed_device *wed);
- void mt7996_init_txpower(struct mt7996_phy *phy);
- int mt7996_txbf_init(struct mt7996_dev *dev);
-+int mt7996_get_chip_sku(struct mt7996_dev *dev);
- void mt7996_reset(struct mt7996_dev *dev);
- int mt7996_run(struct ieee80211_hw *hw);
- int mt7996_mcu_init(struct mt7996_dev *dev);
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 47b429d8..cf12c5e0 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -662,6 +662,13 @@ enum offs_rev {
-
- #define MT_PAD_GPIO 0x700056f0
- #define MT_PAD_GPIO_ADIE_COMB GENMASK(16, 15)
-+#define MT_PAD_GPIO_ADIE_COMB_7992 GENMASK(17, 16)
-+#define MT_PAD_GPIO_ADIE_NUM_7992 BIT(15)
-+
-+/* ADIE */
-+#define MT_ADIE_CHIP_ID(_idx) (0x0f00002c + ((_idx) << 28))
-+#define MT_ADIE_VERSION_MASK GENMASK(15, 0)
-+#define MT_ADIE_CHIP_ID_MASK GENMASK(31, 16)
-
- #define MT_HW_REV 0x70010204
- #define MT_HW_REV1 0x8a00
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-enable-ser-query.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-enable-ser-query.patch
new file mode 100644
index 0000000..e6eb857
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-enable-ser-query.patch
@@ -0,0 +1,30 @@
+From 87fdddbf88629d4c0bf6dc966107fb70df157a5a Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 30 Oct 2023 20:19:41 +0800
+Subject: [PATCH 011/116] mtk: wifi: mt76: mt7996: enable ser query
+
+Do not return -EINVAL when action is UNI_CMD_SER_QUERY for user
+to dump SER information from FW.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mcu.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 36f72d586..10637226c 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3865,6 +3865,8 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
+ };
+
+ switch (action) {
++ case UNI_CMD_SER_QUERY:
++ break;
+ case UNI_CMD_SER_SET:
+ req.set.mask = cpu_to_le32(val);
+ break;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch
deleted file mode 100644
index 0959871..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 35f210176257373e9b71821ca3246b10f1bb1b2a Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Tue, 14 Nov 2023 10:13:24 +0800
-Subject: [PATCH 12/17] mtk: wifi: mt76: mt7996: ACS channel time too long on
- duty channel
-
-Step and issue:
-1. Set channel to 36 in hostapd config;
-2. Bootup;
-3. Enable ACS through UCI command and reload;
-4. Check hostapd log, channel 36 channel_time is much longer than other channels.
-
-Root cause:
-The reset chan_stat condition missed duty channel.
-
-Solution:
-When scan start, need to reset chan_stat in each channel switch.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
-
-Issue:
-There's a chance that the channel time for duty channel is zero in ACS
-scan.
-
-Root cause:
-The chan_stat may be reset when restore to duty channel.
-Mac80211 will notify to hostapd when scan done and then restore to duty
-channel.
-And mt76 will clear scan flag after restore done.
-If hostapd get the chan_stat before channel_restore, will get the
-correct channel time;
-If hostapd get the chan_stat after channel_restore, will get zero
-channel time;
-
-Solution:
-When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
-Mac80211 scan state will be set in scanning, and will be reset after
-scan done and before restore to duty channel.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- mac80211.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index b603d40c..6e8ac6f4 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -928,6 +928,7 @@ void mt76_set_channel(struct mt76_phy *phy)
- struct cfg80211_chan_def *chandef = &hw->conf.chandef;
- bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
- int timeout = HZ / 5;
-+ unsigned long was_scanning = ieee80211_get_scanning(hw);
-
- wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
- mt76_update_survey(phy);
-@@ -942,7 +943,7 @@ void mt76_set_channel(struct mt76_phy *phy)
- if (!offchannel)
- phy->main_chan = chandef->chan;
-
-- if (chandef->chan != phy->main_chan)
-+ if (chandef->chan != phy->main_chan || was_scanning)
- memset(phy->chan_state, 0, sizeof(*phy->chan_state));
- }
- EXPORT_SYMBOL_GPL(mt76_set_channel);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch
new file mode 100644
index 0000000..e99403e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch
@@ -0,0 +1,43 @@
+From d33a316c3d868e7ee5b34eac5c8cd0d624b37dc3 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 18 Apr 2024 17:21:22 +0800
+Subject: [PATCH 012/116] mtk: wifi: mt76: mt7996: set key flag
+ 'IEEE80211_KEY_FLAG_GENERATE_MMIE' for other ciphers
+
+When beacon protection is enabled, FW checks MMIE tag & length in the
+beacon in every cipher mode. Therefore mt76 needs to set the flag
+'IEEE80211_KEY_GENERATE_MMIE' on so that MAC80211 generates and initializes
+MMIE for us.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 2e72fd8ed..8b8037f54 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -360,14 +360,14 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ case WLAN_CIPHER_SUITE_SMS4:
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+- wcid_keyidx = &wcid->hw_key_idx2;
+- key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+- fallthrough;
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+- if (key->keyidx == 6 || key->keyidx == 7)
++ if (key->keyidx == 6 || key->keyidx == 7) {
++ wcid_keyidx = &wcid->hw_key_idx2;
++ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+ break;
++ }
+ fallthrough;
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
new file mode 100644
index 0000000..68798c3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
@@ -0,0 +1,36 @@
+From d0bb7153ee8c546e2dc582a77b49c2d7f0f8c7c0 Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Fri, 17 Nov 2023 11:01:04 +0800
+Subject: [PATCH 013/116] mtk: wifi: mt76: mt7996: Fix TGax HE-4.51.1_24G fail
+
+According to sta capability to decide to enable/disable wed pao when create ppe entry.
+without this patch, TGax HE-4.51.1_24G will test fail
+
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+CR-Id: WCNCR00259516
+Change-Id: Ic51473fcc24757887f0f9f81a31b6f01dee2c845
+---
+ mt7996/main.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8b8037f54..f553704ce 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1463,7 +1463,12 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ path->mtk_wdma.queue = 0;
+ path->mtk_wdma.wcid = msta->wcid.idx;
+
+- path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed);
++ if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) &&
++ mtk_wed_is_amsdu_supported(wed))
++ path->mtk_wdma.amsdu = msta->wcid.amsdu;
++ else
++ path->mtk_wdma.amsdu = 0;
++
+ ctx->dev = NULL;
+
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch
deleted file mode 100644
index 25d942c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From cd699c6764e92f89aef0c75fa0c5a5c69402bcf6 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Thu, 26 Oct 2023 10:08:10 +0800
-Subject: [PATCH 13/17] mtk: wifi: mt76: mt7996: Fixed null pointer dereference
- issue
-
-Without this patch, when the station is still in Authentication stage and
-sends a "Notify bandwidth change action frame" to AP at the same time,
-there will be a race condition that causes a crash to occur because the AP
-access "msta->vif" that has not been fully initialized.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Money Wang <money.wang@mediatek.com>
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- mt7996/main.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 625f87b4..ad2c6a9d 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1063,9 +1063,16 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta,
- u32 changed)
- {
-+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt7996_phy *phy = mt7996_hw_phy(hw);
- struct mt7996_dev *dev = phy->dev;
-
-+ if (!msta->vif) {
-+ dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
-+ sta->addr, msta->wcid.idx);
-+ return;
-+ }
-+
- mt7996_sta_rc_work(&changed, sta);
- ieee80211_queue_work(hw, &dev->rc_work);
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch
deleted file mode 100644
index 8839a71..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 5cb8c91ff8f579d192f93e258314199fc33355c8 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 30 Oct 2023 11:06:19 +0800
-Subject: [PATCH 14/17] mtk: wifi: mt76: add sanity check to prevent kernel
- crash
-
-wcid may not be initialized when mac80211 calls mt76.tx and it would lead to
-kernel crash.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- tx.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/tx.c b/tx.c
-index 5cf6edee..ab42f69b 100644
---- a/tx.c
-+++ b/tx.c
-@@ -345,6 +345,14 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
-
- info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx);
-
-+ if (!wcid->tx_pending.prev || !wcid->tx_pending.next) {
-+ dev_warn(phy->dev->dev, "Un-initialized STA %pM wcid %d in mt76_tx\n",
-+ sta->addr, wcid->idx);
-+
-+ ieee80211_free_txskb(phy->hw, skb);
-+ return;
-+ }
-+
- spin_lock_bh(&wcid->tx_pending.lock);
- __skb_queue_tail(&wcid->tx_pending, skb);
- spin_unlock_bh(&wcid->tx_pending.lock);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch
new file mode 100644
index 0000000..6cbec2f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch
@@ -0,0 +1,329 @@
+From a4eb2ef1c0960faae461ab4585f4572eda428ed2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 20 Jul 2023 17:27:22 +0800
+Subject: [PATCH 014/116] mtk: wifi: mt76: mt7996: add support for different
+ variants
+
+Add fem type (2i5i, 2i5e, 2e5e, ...)
+Add Kite default bin for each fem type since loading wrong default bin
+will fail to setup interface
+Add eeprom fem type check
+
+Add adie 7976c efuse check
+Efuse offset 0x470 will be set to 0xc after final test if 7976c adie is used
+Chip manufactoring factories may transfer, which leads to different adie chip versions,
+so we add this efuse check to avoid 7976c recognition failure.
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I98caec6675670e3d1c0ee953bef2aeb71c3cf74e
+
+GPIO ADie Combination of BE5040 should be considered as don't care
+instead of 0
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Only check eeprom chip id when fem type (= MT7996_FEM_UNSET) is not determined yet
+Without this fix, mt7996_check_eeprom will return EINVAL in mt7996_eeprom_check_fw_mode
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 40 +++++++++++++++++++++++++++++--
+ mt7996/eeprom.h | 1 +
+ mt7996/init.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.c | 7 +++++-
+ mt7996/mt7996.h | 43 ++++++++++++++++++++++++++++++---
+ mt7996/regs.h | 7 ++++++
+ 6 files changed, 155 insertions(+), 6 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 4a8237118..3260d1fef 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -9,14 +9,33 @@
+
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
++#define FEM_INT 0
++#define FEM_EXT 3
+ u8 *eeprom = dev->mt76.eeprom.data;
++ u8 i, fem[__MT_MAX_BAND], fem_type;
+ u16 val = get_unaligned_le16(eeprom);
+
++ for (i = 0; i < __MT_MAX_BAND; i++)
++ fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
++
+ switch (val) {
+ case 0x7990:
+ return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
+ case 0x7992:
+- return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
++ if (dev->fem_type == MT7996_FEM_UNSET)
++ return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
++
++ if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
++ fem_type = MT7996_FEM_EXT;
++ else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
++ fem_type = MT7996_FEM_INT;
++ else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
++ fem_type = MT7996_FEM_MIX;
++ else
++ return -EINVAL;
++
++ return (is_mt7992(&dev->mt76) ? 0 : -EINVAL) |
++ (dev->fem_type == fem_type ? 0 : -EINVAL);
+ default:
+ return -EINVAL;
+ }
+@@ -26,9 +45,22 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ {
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7990:
++ if (dev->chip_sku == MT7996_SKU_404)
++ return MT7996_EEPROM_DEFAULT_404;
+ return MT7996_EEPROM_DEFAULT;
+ case 0x7992:
+- return MT7992_EEPROM_DEFAULT;
++ if (dev->chip_sku == MT7992_SKU_23) {
++ if (dev->fem_type == MT7996_FEM_INT)
++ return MT7992_EEPROM_DEFAULT_23;
++ return MT7992_EEPROM_DEFAULT_23_EXT;
++ } else if (dev->chip_sku == MT7992_SKU_44) {
++ if (dev->fem_type == MT7996_FEM_INT)
++ return MT7992_EEPROM_DEFAULT;
++ else if (dev->fem_type == MT7996_FEM_MIX)
++ return MT7992_EEPROM_DEFAULT_MIX;
++ return MT7992_EEPROM_DEFAULT_EXT;
++ }
++ return MT7992_EEPROM_DEFAULT_24;
+ default:
+ return MT7996_EEPROM_DEFAULT;
+ }
+@@ -219,6 +251,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ int ret;
+
++ ret = mt7996_get_chip_sku(dev);
++ if (ret)
++ return ret;
++
+ ret = mt7996_eeprom_load(dev);
+ if (ret < 0) {
+ if (ret != -EINVAL)
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 412d6e2f8..72c38ad3b 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -29,6 +29,7 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0)
++#define MT_EE_WIFI_PA_LNA_CONFIG GENMASK(1, 0)
+
+ #define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(2, 0)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index ab2e17ec4..d58335a37 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -885,6 +885,65 @@ out:
+ #endif
+ }
+
++int mt7996_get_chip_sku(struct mt7996_dev *dev)
++{
++#define MT7976C_CHIP_VER 0x8a10
++#define MT7976C_HL_CHIP_VER 0x8b00
++#define MT7976C_PS_CHIP_VER 0x8c10
++#define MT7976C_EFUSE_OFFSET 0x470
++#define MT7976C_EFUSE_VALUE 0xc
++ u32 regval, val = mt76_rr(dev, MT_PAD_GPIO);
++ u16 adie_chip_id, adie_chip_ver;
++ u8 adie_comb, adie_num, adie_idx = 0;
++
++ switch (mt76_chip(&dev->mt76)) {
++ case 0x7990:
++ adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
++ if (adie_comb <= 1)
++ dev->chip_sku = MT7996_SKU_444;
++ else
++ dev->chip_sku = MT7996_SKU_404;
++ break;
++ case 0x7992:
++ adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB_7992, val);
++ adie_num = FIELD_GET(MT_PAD_GPIO_ADIE_NUM_7992, val);
++ adie_idx = !adie_num;
++ if (adie_num)
++ dev->chip_sku = MT7992_SKU_23;
++ else if (adie_comb)
++ dev->chip_sku = MT7992_SKU_44;
++ else
++ dev->chip_sku = MT7992_SKU_24;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) {
++ u8 buf[MT7996_EEPROM_BLOCK_SIZE];
++ u8 idx = MT7976C_EFUSE_OFFSET % MT7996_EEPROM_BLOCK_SIZE;
++ bool is_7976c;
++
++ mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), ®val, false);
++ adie_chip_id = FIELD_GET(MT_ADIE_CHIP_ID_MASK, regval);
++ adie_chip_ver = FIELD_GET(MT_ADIE_VERSION_MASK, regval);
++ mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf);
++ is_7976c = (adie_chip_ver == MT7976C_CHIP_VER) ||
++ (adie_chip_ver == MT7976C_HL_CHIP_VER) ||
++ (adie_chip_ver == MT7976C_PS_CHIP_VER) ||
++ (buf[idx] == MT7976C_EFUSE_VALUE);
++ if (adie_chip_id == 0x7975 || (adie_chip_id == 0x7976 && is_7976c) ||
++ adie_chip_id == 0x7979)
++ dev->fem_type = MT7996_FEM_INT;
++ else if (adie_chip_id == 0x7977 && adie_comb == 1)
++ dev->fem_type = MT7996_FEM_MIX;
++ else
++ dev->fem_type = MT7996_FEM_EXT;
++ }
++
++ return 0;
++}
++
+ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ {
+ int ret, idx;
+@@ -900,6 +959,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ INIT_LIST_HEAD(&dev->wed_rro.poll_list);
+ spin_lock_init(&dev->wed_rro.lock);
+
++ ret = mt7996_get_chip_sku(dev);
++ if (ret)
++ return ret;
++
+ ret = mt7996_dma_init(dev);
+ if (ret)
+ return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 10637226c..9e94e03d8 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -14,7 +14,12 @@
+ char *_fw; \
+ switch (mt76_chip(&(_dev)->mt76)) { \
+ case 0x7992: \
+- _fw = MT7992_##name; \
++ if ((_dev)->chip_sku == MT7992_SKU_23) \
++ _fw = MT7992_##name##_23; \
++ else if ((_dev)->chip_sku == MT7992_SKU_24) \
++ _fw = MT7992_##name##_24; \
++ else \
++ _fw = MT7992_##name; \
+ break; \
+ case 0x7990: \
+ default: \
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 58fa6b458..b7197dcb7 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -39,8 +39,24 @@
+ #define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin"
+ #define MT7992_ROM_PATCH "mediatek/mt7996/mt7992_rom_patch.bin"
+
++#define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin"
++#define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin"
++#define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin"
++#define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin"
++
++#define MT7992_FIRMWARE_WA_23 "mediatek/mt7996/mt7992_wa_23.bin"
++#define MT7992_FIRMWARE_WM_23 "mediatek/mt7996/mt7992_wm_23.bin"
++#define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin"
++#define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
++
+ #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
+-#define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom.bin"
++#define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
++#define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom_2i5i.bin"
++#define MT7992_EEPROM_DEFAULT_EXT "mediatek/mt7996/mt7992_eeprom_2e5e.bin"
++#define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin"
++#define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
++#define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
++#define MT7992_EEPROM_DEFAULT_23_EXT "mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
+ #define MT7996_EEPROM_SIZE 7680
+ #define MT7996_EEPROM_BLOCK_SIZE 16
+ #define MT7996_TOKEN_SIZE 16384
+@@ -89,6 +105,24 @@ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+ struct mt7996_dfs_pattern;
+
++enum mt7996_fem_type {
++ MT7996_FEM_UNSET,
++ MT7996_FEM_EXT,
++ MT7996_FEM_INT,
++ MT7996_FEM_MIX,
++};
++
++enum mt7996_sku_type {
++ MT7996_SKU_404,
++ MT7996_SKU_444,
++};
++
++enum mt7992_sku_type {
++ MT7992_SKU_23,
++ MT7992_SKU_24,
++ MT7992_SKU_44,
++};
++
+ enum mt7996_ram_type {
+ MT7996_RAM_TYPE_WM,
+ MT7996_RAM_TYPE_WA,
+@@ -261,6 +295,9 @@ struct mt7996_dev {
+ struct cfg80211_chan_def rdd2_chandef;
+ struct mt7996_phy *rdd2_phy;
+
++ u8 chip_sku;
++ u8 fem_type;
++
+ u16 chainmask;
+ u8 chainshift[__MT_MAX_BAND];
+ u32 hif_idx;
+@@ -409,8 +446,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
+ return band <= MT_BAND1;
+
+ /* tri-band support */
+- if (band <= MT_BAND2 &&
+- mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) <= 1)
++ if (band <= MT_BAND2 && dev->chip_sku)
+ return true;
+
+ return band == MT_BAND0 || band == MT_BAND2;
+@@ -441,6 +477,7 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
+ int n_desc, int ring_base, struct mtk_wed_device *wed);
+ void mt7996_init_txpower(struct mt7996_phy *phy);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
++int mt7996_get_chip_sku(struct mt7996_dev *dev);
+ void mt7996_reset(struct mt7996_dev *dev);
+ int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 47b429d8b..cf12c5e02 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -662,6 +662,13 @@ enum offs_rev {
+
+ #define MT_PAD_GPIO 0x700056f0
+ #define MT_PAD_GPIO_ADIE_COMB GENMASK(16, 15)
++#define MT_PAD_GPIO_ADIE_COMB_7992 GENMASK(17, 16)
++#define MT_PAD_GPIO_ADIE_NUM_7992 BIT(15)
++
++/* ADIE */
++#define MT_ADIE_CHIP_ID(_idx) (0x0f00002c + ((_idx) << 28))
++#define MT_ADIE_VERSION_MASK GENMASK(15, 0)
++#define MT_ADIE_CHIP_ID_MASK GENMASK(31, 16)
+
+ #define MT_HW_REV 0x70010204
+ #define MT_HW_REV1 0x8a00
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch
new file mode 100644
index 0000000..778e91f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch
@@ -0,0 +1,70 @@
+From 615fa2bf42b12776d01445f009f766d3c35dabf7 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Tue, 14 Nov 2023 10:13:24 +0800
+Subject: [PATCH 015/116] mtk: wifi: mt76: mt7996: ACS channel time too long on
+ duty channel
+
+Step and issue:
+1. Set channel to 36 in hostapd config;
+2. Bootup;
+3. Enable ACS through UCI command and reload;
+4. Check hostapd log, channel 36 channel_time is much longer than other channels.
+
+Root cause:
+The reset chan_stat condition missed duty channel.
+
+Solution:
+When scan start, need to reset chan_stat in each channel switch.
+
+CR-Id: WCNCR00351054
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+
+Issue:
+There's a chance that the channel time for duty channel is zero in ACS
+scan.
+
+Root cause:
+The chan_stat may be reset when restore to duty channel.
+Mac80211 will notify to hostapd when scan done and then restore to duty
+channel.
+And mt76 will clear scan flag after restore done.
+If hostapd get the chan_stat before channel_restore, will get the
+correct channel time;
+If hostapd get the chan_stat after channel_restore, will get zero
+channel time;
+
+Solution:
+When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
+Mac80211 scan state will be set in scanning, and will be reset after
+scan done and before restore to duty channel.
+
+CR-Id: WCNCR00357653
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ mac80211.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 64307b967..993e155b9 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -927,6 +927,7 @@ void mt76_set_channel(struct mt76_phy *phy)
+ struct cfg80211_chan_def *chandef = &hw->conf.chandef;
+ bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
+ int timeout = HZ / 5;
++ unsigned long was_scanning = ieee80211_get_scanning(hw);
+
+ wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
+ mt76_update_survey(phy);
+@@ -941,7 +942,7 @@ void mt76_set_channel(struct mt76_phy *phy)
+ if (!offchannel)
+ phy->main_chan = chandef->chan;
+
+- if (chandef->chan != phy->main_chan)
++ if (chandef->chan != phy->main_chan || was_scanning)
+ memset(phy->chan_state, 0, sizeof(*phy->chan_state));
+ }
+ EXPORT_SYMBOL_GPL(mt76_set_channel);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
deleted file mode 100644
index 66ee8cf..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
+++ /dev/null
@@ -1,597 +0,0 @@
-From 5a195822d50b588206b18344df61b36bc7e26e6e Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Fri, 19 May 2023 14:16:50 +0800
-Subject: [PATCH 15/17] mtk: wifi: mt76: mt7996: add firmware WA's coredump.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- mt7996/coredump.c | 180 ++++++++++++++++++++++++++++++----------------
- mt7996/coredump.h | 35 ++++++---
- mt7996/mac.c | 31 +++++---
- mt7996/mcu.c | 5 ++
- mt7996/mt7996.h | 7 +-
- mt7996/regs.h | 7 +-
- 6 files changed, 182 insertions(+), 83 deletions(-)
-
-diff --git a/mt7996/coredump.c b/mt7996/coredump.c
-index ccab0d7b..60b88085 100644
---- a/mt7996/coredump.c
-+++ b/mt7996/coredump.c
-@@ -7,11 +7,11 @@
- #include <linux/utsname.h>
- #include "coredump.h"
-
--static bool coredump_memdump;
-+static bool coredump_memdump = true;
- module_param(coredump_memdump, bool, 0644);
- MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
-
--static const struct mt7996_mem_region mt7996_mem_regions[] = {
-+static const struct mt7996_mem_region mt7996_wm_mem_regions[] = {
- {
- .start = 0x00800000,
- .len = 0x0004ffff,
-@@ -44,27 +44,55 @@ static const struct mt7996_mem_region mt7996_mem_regions[] = {
- },
- };
-
-+static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
-+ {
-+ .start = 0xE0000000,
-+ .len = 0x0000ffff,
-+ .name = "CRAM",
-+ },
-+ {
-+ .start = 0xE0010000,
-+ .len = 0x000117ff,
-+ .name = "CRAM2",
-+ },
-+ {
-+ .start = 0x10000000,
-+ .len = 0x0001bfff,
-+ .name = "ILM",
-+ },
-+ {
-+ .start = 0x10200000,
-+ .len = 0x00063fff,
-+ .name = "DLM",
-+ },
-+};
-+
- const struct mt7996_mem_region*
--mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
-+mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
- {
- switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
- case 0x7991:
-- *num = ARRAY_SIZE(mt7996_mem_regions);
-- return &mt7996_mem_regions[0];
-+ if (type == MT7996_RAM_TYPE_WA) {
-+ *num = ARRAY_SIZE(mt7996_wa_mem_regions);
-+ return &mt7996_wa_mem_regions[0];
-+ }
-+
-+ *num = ARRAY_SIZE(mt7996_wm_mem_regions);
-+ return &mt7996_wm_mem_regions[0];
- default:
- return NULL;
- }
- }
-
--static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
-+static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev, u8 type)
- {
- const struct mt7996_mem_region *mem_region;
- size_t size = 0;
- u32 num;
- int i;
-
-- mem_region = mt7996_coredump_get_mem_layout(dev, &num);
-+ mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
- if (!mem_region)
- return 0;
-
-@@ -81,14 +109,13 @@ static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
- return size;
- }
-
--struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
-+struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
- {
-- struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
-+ struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
-
- lockdep_assert_held(&dev->dump_mutex);
-
-- if (coredump_memdump &&
-- !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500))
-+ if (!coredump_memdump)
- return NULL;
-
- guid_gen(&crash_data->guid);
-@@ -98,12 +125,15 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
- }
-
- static void
--mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
-+mt7996_coredump_fw_state(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
- bool *exception)
- {
-- u32 count;
-+ u32 count, reg = MT_FW_WM_DUMP_STATE;
-+
-+ if (type == MT7996_RAM_TYPE_WA)
-+ reg = MT_FW_WA_DUMP_STATE;
-
-- count = mt76_rr(dev, MT_FW_ASSERT_CNT);
-+ count = mt76_rr(dev, reg);
-
- /* normal mode: driver can manually trigger assert for detail info */
- if (!count)
-@@ -115,53 +145,59 @@ mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
- }
-
- static void
--mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump,
-+mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
- bool exception)
- {
-- u32 oldest, i, idx;
-+ u32 reg, i, offset = 0, val = MT7996_RAM_TYPE_WM;
-
-- strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current));
-+ if (type == MT7996_RAM_TYPE_WA) {
-+ offset = MT_MCU_WA_EXCP_BASE - MT_MCU_WM_EXCP_BASE;
-+ val = MT7996_RAM_TYPE_WA;
-+ }
-
-- /* 0: WM PC log output */
-- mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0);
-+ /* 0: WM PC log output, 1: WA PC log output */
-+ mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, val);
- /* choose 33th PC log buffer to read current PC index */
- mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f);
-
- /* read current PC */
-- dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
-+ for (i = 0; i < 10; i++)
-+ dump->pc_cur[i] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
-
- /* stop call stack record */
- if (!exception) {
-- mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
-- mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
-+ mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
-+ mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
- }
-
-- oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL,
-- GENMASK(20, 16)) + 2;
-- for (i = 0; i < 16; i++) {
-- idx = ((oldest + 2 * i + 1) % 32);
-- dump->pc_stack[i + 1] =
-- mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4);
-+ /* read PC log */
-+ dump->pc_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_PC_CTRL + offset);
-+ dump->pc_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS,
-+ dump->pc_dbg_ctrl);
-+ for (i = 0; i < 32; i++) {
-+ reg = MT_MCU_WM_EXCP_PC_LOG + i * 4 + offset;
-+ dump->pc_stack[i] = mt76_rr(dev, reg);
- }
-
-- oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL,
-- GENMASK(20, 16)) + 2;
-- for (i = 0; i < 16; i++) {
-- idx = ((oldest + 2 * i + 1) % 32);
-- dump->lr_stack[i] =
-- mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4);
-+ /* read LR log */
-+ dump->lr_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_LR_CTRL + offset);
-+ dump->lr_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS,
-+ dump->lr_dbg_ctrl);
-+ for (i = 0; i < 32; i++) {
-+ reg = MT_MCU_WM_EXCP_LR_LOG + i * 4 + offset;
-+ dump->lr_stack[i] = mt76_rr(dev, reg);
- }
-
- /* start call stack record */
- if (!exception) {
-- mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
-- mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
-+ mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
-+ mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
- }
- }
-
--static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
-+static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
- {
-- struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
-+ struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
- struct mt7996_coredump *dump;
- struct mt7996_coredump_mem *dump_mem;
- size_t len, sofar = 0, hdr_len = sizeof(*dump);
-@@ -186,20 +222,31 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
-
- dump = (struct mt7996_coredump *)(buf);
- dump->len = len;
-+ dump->hdr_len = hdr_len;
-
- /* plain text */
- strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
- strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
-+ strscpy(dump->fw_type, ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"),
-+ sizeof(dump->fw_type));
- strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
- sizeof(dump->fw_ver));
-+ strscpy(dump->fw_patch_date, dev->patch_build_date,
-+ sizeof(dump->fw_patch_date));
-+ strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WM],
-+ dev->ram_build_date[MT7996_RAM_TYPE_WM],
-+ MT7996_BUILD_TIME_LEN);
-+ strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WA],
-+ dev->ram_build_date[MT7996_RAM_TYPE_WA],
-+ MT7996_BUILD_TIME_LEN);
-
- guid_copy(&dump->guid, &crash_data->guid);
- dump->tv_sec = crash_data->timestamp.tv_sec;
- dump->tv_nsec = crash_data->timestamp.tv_nsec;
- dump->device_id = mt76_chip(&dev->mt76);
-
-- mt7996_coredump_fw_state(dev, dump, &exception);
-- mt7996_coredump_fw_stack(dev, dump, exception);
-+ mt7996_coredump_fw_state(dev, type, dump, &exception);
-+ mt7996_coredump_fw_stack(dev, type, dump, exception);
-
- /* gather memory content */
- dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
-@@ -213,17 +260,19 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
- return dump;
- }
-
--int mt7996_coredump_submit(struct mt7996_dev *dev)
-+int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- {
- struct mt7996_coredump *dump;
-
-- dump = mt7996_coredump_build(dev);
-+ dump = mt7996_coredump_build(dev, type);
- if (!dump) {
- dev_warn(dev->mt76.dev, "no crash dump data found\n");
- return -ENODATA;
- }
-
- dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
-+ dev_info(dev->mt76.dev, "%s coredump completed\n",
-+ wiphy_name(dev->mt76.hw->wiphy));
-
- return 0;
- }
-@@ -231,23 +280,26 @@ int mt7996_coredump_submit(struct mt7996_dev *dev)
- int mt7996_coredump_register(struct mt7996_dev *dev)
- {
- struct mt7996_crash_data *crash_data;
-+ int i;
-
-- crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
-- if (!crash_data)
-- return -ENOMEM;
-+ for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
-+ crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
-+ if (!crash_data)
-+ return -ENOMEM;
-
-- dev->coredump.crash_data = crash_data;
-+ dev->coredump.crash_data[i] = crash_data;
-
-- if (coredump_memdump) {
-- crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev);
-- if (!crash_data->memdump_buf_len)
-- /* no memory content */
-- return 0;
-+ if (coredump_memdump) {
-+ crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
-+ if (!crash_data->memdump_buf_len)
-+ /* no memory content */
-+ return 0;
-
-- crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
-- if (!crash_data->memdump_buf) {
-- vfree(crash_data);
-- return -ENOMEM;
-+ crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
-+ if (!crash_data->memdump_buf) {
-+ vfree(crash_data);
-+ return -ENOMEM;
-+ }
- }
- }
-
-@@ -256,13 +308,17 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
-
- void mt7996_coredump_unregister(struct mt7996_dev *dev)
- {
-- if (dev->coredump.crash_data->memdump_buf) {
-- vfree(dev->coredump.crash_data->memdump_buf);
-- dev->coredump.crash_data->memdump_buf = NULL;
-- dev->coredump.crash_data->memdump_buf_len = 0;
-- }
-+ int i;
-
-- vfree(dev->coredump.crash_data);
-- dev->coredump.crash_data = NULL;
-+ for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
-+ if (dev->coredump.crash_data[i]->memdump_buf) {
-+ vfree(dev->coredump.crash_data[i]->memdump_buf);
-+ dev->coredump.crash_data[i]->memdump_buf = NULL;
-+ dev->coredump.crash_data[i]->memdump_buf_len = 0;
-+ }
-+
-+ vfree(dev->coredump.crash_data[i]);
-+ dev->coredump.crash_data[i] = NULL;
-+ }
- }
-
-diff --git a/mt7996/coredump.h b/mt7996/coredump.h
-index af2ba219..01ed3731 100644
---- a/mt7996/coredump.h
-+++ b/mt7996/coredump.h
-@@ -6,10 +6,13 @@
-
- #include "mt7996.h"
-
-+#define MT7996_COREDUMP_MAX (MT7996_RAM_TYPE_WA + 1)
-+
- struct mt7996_coredump {
- char magic[16];
-
- u32 len;
-+ u32 hdr_len;
-
- guid_t guid;
-
-@@ -21,17 +24,28 @@ struct mt7996_coredump {
- char kernel[64];
- /* firmware version */
- char fw_ver[ETHTOOL_FWVERS_LEN];
-+ char fw_patch_date[MT7996_BUILD_TIME_LEN];
-+ char fw_ram_date[MT7996_COREDUMP_MAX][MT7996_BUILD_TIME_LEN];
-
- u32 device_id;
-
-+ /* fw type */
-+ char fw_type[8];
-+
- /* exception state */
- char fw_state[12];
-
- /* program counters */
-- char pc_current[16];
-- u32 pc_stack[17];
-- /* link registers */
-- u32 lr_stack[16];
-+ u32 pc_dbg_ctrl;
-+ u32 pc_cur_idx;
-+ u32 pc_cur[10];
-+ /* PC registers */
-+ u32 pc_stack[32];
-+
-+ u32 lr_dbg_ctrl;
-+ u32 lr_cur_idx;
-+ /* LR registers */
-+ u32 lr_stack[32];
-
- /* memory content */
- u8 data[];
-@@ -43,6 +57,7 @@ struct mt7996_coredump_mem {
- } __packed;
-
- struct mt7996_mem_hdr {
-+ char name[64];
- u32 start;
- u32 len;
- u8 data[];
-@@ -58,27 +73,27 @@ struct mt7996_mem_region {
- #ifdef CONFIG_DEV_COREDUMP
-
- const struct mt7996_mem_region *
--mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num);
--struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev);
--int mt7996_coredump_submit(struct mt7996_dev *dev);
-+mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
-+struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
-+int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
- int mt7996_coredump_register(struct mt7996_dev *dev);
- void mt7996_coredump_unregister(struct mt7996_dev *dev);
-
- #else /* CONFIG_DEV_COREDUMP */
-
- static inline const struct mt7996_mem_region *
--mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
-+mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
- {
- return NULL;
- }
-
--static inline int mt7996_coredump_submit(struct mt7996_dev *dev)
-+static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- {
- return 0;
- }
-
- static inline struct
--mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
-+mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
- {
- return NULL;
- }
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 3afdd7eb..d88bbfb2 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1998,28 +1998,25 @@ void mt7996_mac_reset_work(struct work_struct *work)
- }
-
- /* firmware coredump */
--void mt7996_mac_dump_work(struct work_struct *work)
-+void mt7996_mac_fw_coredump(struct mt7996_dev *dev, u8 type)
- {
- const struct mt7996_mem_region *mem_region;
- struct mt7996_crash_data *crash_data;
-- struct mt7996_dev *dev;
- struct mt7996_mem_hdr *hdr;
- size_t buf_len;
- int i;
- u32 num;
- u8 *buf;
-
-- dev = container_of(work, struct mt7996_dev, dump_work);
--
- mutex_lock(&dev->dump_mutex);
-
-- crash_data = mt7996_coredump_new(dev);
-+ crash_data = mt7996_coredump_new(dev, type);
- if (!crash_data) {
- mutex_unlock(&dev->dump_mutex);
-- goto skip_coredump;
-+ return;
- }
-
-- mem_region = mt7996_coredump_get_mem_layout(dev, &num);
-+ mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
- if (!mem_region || !crash_data->memdump_buf_len) {
- mutex_unlock(&dev->dump_mutex);
- goto skip_memdump;
-@@ -2029,6 +2026,9 @@ void mt7996_mac_dump_work(struct work_struct *work)
- buf_len = crash_data->memdump_buf_len;
-
- /* dumping memory content... */
-+ dev_info(dev->mt76.dev, "%s start coredump for %s\n",
-+ wiphy_name(dev->mt76.hw->wiphy),
-+ ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"));
- memset(buf, 0, buf_len);
- for (i = 0; i < num; i++) {
- if (mem_region->len > buf_len) {
-@@ -2045,6 +2045,7 @@ void mt7996_mac_dump_work(struct work_struct *work)
- mt7996_memcpy_fromio(dev, buf, mem_region->start,
- mem_region->len);
-
-+ strscpy(hdr->name, mem_region->name, sizeof(mem_region->name));
- hdr->start = mem_region->start;
- hdr->len = mem_region->len;
-
-@@ -2061,8 +2062,20 @@ void mt7996_mac_dump_work(struct work_struct *work)
- mutex_unlock(&dev->dump_mutex);
-
- skip_memdump:
-- mt7996_coredump_submit(dev);
--skip_coredump:
-+ mt7996_coredump_submit(dev, type);
-+}
-+
-+void mt7996_mac_dump_work(struct work_struct *work)
-+{
-+ struct mt7996_dev *dev;
-+
-+ dev = container_of(work, struct mt7996_dev, dump_work);
-+ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
-+ mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
-+
-+ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
-+ mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
-+
- queue_work(dev->mt76.wq, &dev->reset_work);
- }
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 5aefecb0..88fb196d 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2702,6 +2702,8 @@ static int mt7996_load_patch(struct mt7996_dev *dev)
-
- dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
- be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
-+ memcpy(dev->patch_build_date, hdr->build_date,
-+ sizeof(dev->patch_build_date));
-
- for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
- struct mt7996_patch_sec *sec;
-@@ -2828,6 +2830,9 @@ static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type,
- }
-
- hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
-+ memcpy(dev->ram_build_date[ram_type],
-+ hdr->build_date,
-+ sizeof(dev->ram_build_date[ram_type]));
- dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n",
- fw_type, hdr->fw_ver, hdr->build_date);
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 7e5ec212..e1610d3b 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -83,6 +83,8 @@
- #define MT7996_CRIT_TEMP 110
- #define MT7996_MAX_TEMP 120
-
-+#define MT7996_BUILD_TIME_LEN 24
-+
- #define MT7996_RRO_MAX_SESSION 1024
- #define MT7996_RRO_WINDOW_MAX_LEN 1024
- #define MT7996_RRO_ADDR_ELEM_LEN 128
-@@ -126,6 +128,7 @@ enum mt7996_ram_type {
- MT7996_RAM_TYPE_WM,
- MT7996_RAM_TYPE_WA,
- MT7996_RAM_TYPE_DSP,
-+ __MT7996_RAM_TYPE_MAX,
- };
-
- enum mt7996_txq_id {
-@@ -316,9 +319,11 @@ struct mt7996_dev {
- struct mutex dump_mutex;
- #ifdef CONFIG_DEV_COREDUMP
- struct {
-- struct mt7996_crash_data *crash_data;
-+ struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
- } coredump;
- #endif
-+ char patch_build_date[MT7996_BUILD_TIME_LEN];
-+ char ram_build_date[__MT7996_RAM_TYPE_MAX][MT7996_BUILD_TIME_LEN];
-
- struct list_head sta_rc_list;
- struct list_head twt_list;
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index cf12c5e0..4c20a67d 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -597,7 +597,8 @@ enum offs_rev {
-
- /* FW MODE SYNC */
- #define MT_FW_ASSERT_CNT 0x02208274
--#define MT_FW_DUMP_STATE 0x02209e90
-+#define MT_FW_WM_DUMP_STATE 0x02209e90
-+#define MT_FW_WA_DUMP_STATE 0x7C05B080
-
- #define MT_SWDEF_BASE 0x00401400
-
-@@ -714,11 +715,15 @@ enum offs_rev {
- #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29)
-
- /* CONN MCU EXCP CON */
-+#define MT_MCU_WA_EXCP_BASE 0x890d0000
- #define MT_MCU_WM_EXCP_BASE 0x89050000
-+
- #define MT_MCU_WM_EXCP(ofs) (MT_MCU_WM_EXCP_BASE + (ofs))
- #define MT_MCU_WM_EXCP_PC_CTRL MT_MCU_WM_EXCP(0x100)
-+#define MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS GENMASK(20, 16)
- #define MT_MCU_WM_EXCP_PC_LOG MT_MCU_WM_EXCP(0x104)
- #define MT_MCU_WM_EXCP_LR_CTRL MT_MCU_WM_EXCP(0x200)
-+#define MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS GENMASK(20, 16)
- #define MT_MCU_WM_EXCP_LR_LOG MT_MCU_WM_EXCP(0x204)
-
- /* CONN AFE CTL CON */
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch
new file mode 100644
index 0000000..646da41
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch
@@ -0,0 +1,44 @@
+From 8cb027bb01c80130b08a9a6a5b4a0fe49a42995e Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Thu, 26 Oct 2023 10:08:10 +0800
+Subject: [PATCH 016/116] mtk: wifi: mt76: mt7996: Fixed null pointer
+ dereference issue
+
+Without this patch, when the station is still in Authentication stage and
+sends a "Notify bandwidth change action frame" to AP at the same time,
+there will be a race condition that causes a crash to occur because the AP
+access "msta->vif" that has not been fully initialized.
+
+CR-ID: WCNCR00240597
+Change-Id: Ie17fbdd8ab11651a9ae0c30faac0b5ad82176e95
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Money Wang <money.wang@mediatek.com>
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ mt7996/main.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f553704ce..4fc1dd9a9 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1079,9 +1079,16 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u32 changed)
+ {
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
+
++ if (!msta->vif) {
++ dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
++ sta->addr, msta->wcid.idx);
++ return;
++ }
++
+ mt7996_sta_rc_work(&changed, sta);
+ ieee80211_queue_work(hw, &dev->rc_work);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch
deleted file mode 100644
index 2ed6aca..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch
+++ /dev/null
@@ -1,97 +0,0 @@
-From 86aed1e2968dcb516b60cad4361048c2df7a8119 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Fri, 22 Sep 2023 10:32:37 +0800
-Subject: [PATCH 16/17] mtk: wifi: mt76: mt7996: add preamble puncture support
- for mt7996
-
-Add support configure preamble puncture feature through mcu commands.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt76_connac_mcu.h | 1 +
- mt7996/mcu.c | 30 ++++++++++++++++++++++++++++++
- mt7996/mcu.h | 4 ++++
- mt7996/mt7996.h | 2 ++
- 4 files changed, 37 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index f1cd2e50..482782f7 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1270,6 +1270,7 @@ enum {
- MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
- MCU_UNI_CMD_THERMAL = 0x35,
- MCU_UNI_CMD_VOW = 0x37,
-+ MCU_UNI_CMD_PP = 0x38,
- MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
- MCU_UNI_CMD_RRO = 0x57,
- MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 88fb196d..b9939e0c 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4540,3 +4540,33 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- return mt76_mcu_skb_send_msg(&dev->mt76, skb,
- MCU_WM_UNI_CMD(TXPOWER), true);
- }
-+
-+int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, bool auto_mode,
-+ u8 force_bitmap_ctrl, u16 bitmap)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ bool mgmt_mode;
-+ u8 band_idx;
-+ u8 force_bitmap_ctrl;
-+ bool auto_mode;
-+ __le16 bitmap;
-+ u8 _rsv2[2];
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_CMD_PP_EN_CTRL),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+
-+ .mgmt_mode = !auto_mode,
-+ .band_idx = phy->mt76->band_idx,
-+ .force_bitmap_ctrl = force_bitmap_ctrl,
-+ .auto_mode = auto_mode,
-+ .bitmap = cpu_to_le16(bitmap),
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
-+ &req, sizeof(req), false);
-+}
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index a9ba63d1..238c4c53 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -923,6 +923,10 @@ enum {
- MT7996_SEC_MODE_MAX,
- };
-
-+enum {
-+ UNI_CMD_PP_EN_CTRL,
-+};
-+
- #define MT7996_PATCH_SEC GENMASK(31, 24)
- #define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8)
- #define MT7996_PATCH_AES_KEY GENMASK(7, 0)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index e1610d3b..4176e51a 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -654,6 +654,8 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
- int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-+int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, bool auto_mode, u8 force_bitmap,
-+ u16 bitmap);
- #ifdef CONFIG_MAC80211_DEBUGFS
- void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, struct dentry *dir);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch
new file mode 100644
index 0000000..27ef133
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch
@@ -0,0 +1,38 @@
+From 49a1a916113cf23ad38ef6e3f3cfd0279f7abea4 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 30 Oct 2023 11:06:19 +0800
+Subject: [PATCH 017/116] mtk: wifi: mt76: add sanity check to prevent kernel
+ crash
+
+wcid may not be initialized when mac80211 calls mt76.tx and it would lead to
+kernel crash.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I90004271c6e91620c6991195dd332780ce28380e
+---
+ tx.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/tx.c b/tx.c
+index 5cf6edee4..ab42f69b8 100644
+--- a/tx.c
++++ b/tx.c
+@@ -345,6 +345,14 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
+
+ info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx);
+
++ if (!wcid->tx_pending.prev || !wcid->tx_pending.next) {
++ dev_warn(phy->dev->dev, "Un-initialized STA %pM wcid %d in mt76_tx\n",
++ sta->addr, wcid->idx);
++
++ ieee80211_free_txskb(phy->hw, skb);
++ return;
++ }
++
+ spin_lock_bh(&wcid->tx_pending.lock);
+ __skb_queue_tail(&wcid->tx_pending, skb);
+ spin_unlock_bh(&wcid->tx_pending.lock);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-mt7996-add-sanity-check-for-NAPI-sched.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-mt7996-add-sanity-check-for-NAPI-sched.patch
deleted file mode 100644
index dbfb384..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-mt7996-add-sanity-check-for-NAPI-sched.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 19ea1497e7aea34be6e8851a1aca75e3eb58b58f Mon Sep 17 00:00:00 2001
-From: "Henry.Yen" <henry.yen@mediatek.com>
-Date: Tue, 16 Jan 2024 11:30:02 +0800
-Subject: [PATCH 17/17] mtk: wifi: mt76: mt7996: add sanity check for NAPI
- schedule
-
-Add sanity check for NAPI schedule.
-
-It's observed that host driver might occasionally receive
-interrupts from unexpected Rx ring, whose Rx NAPI hasn't been
-prepared yet. Under such situation, __napi_poll crash issue
-would occur, so we add sanity check to prevent it.
-
-If without this patch, we might encounter kernel crash issue
-especially in WED-on & RRO-on software path.
-
-Signed-off-by: Henry.Yen <henry.yen@mediatek.com>
-
----
- mt7996/mmio.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 8fe56ed9..367a204d 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -560,7 +560,7 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
- napi_schedule(&dev->mt76.tx_napi);
-
- for (i = 0; i < __MT_RXQ_MAX; i++) {
-- if ((intr & MT_INT_RX(i)))
-+ if ((intr & MT_INT_RX(i)) && dev->mt76.napi[i].poll)
- napi_schedule(&dev->mt76.napi[i]);
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
new file mode 100644
index 0000000..ac443b0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
@@ -0,0 +1,598 @@
+From 88fd1708523b4d5a2f8c821f6aa768b2f2a6a033 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 19 May 2023 14:16:50 +0800
+Subject: [PATCH 018/116] mtk: wifi: mt76: mt7996: add firmware WA's coredump.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Change-Id: I51f115b4ae15bc0f871f93652570d72511dbf880
+---
+ mt7996/coredump.c | 180 ++++++++++++++++++++++++++++++----------------
+ mt7996/coredump.h | 35 ++++++---
+ mt7996/mac.c | 31 +++++---
+ mt7996/mcu.c | 5 ++
+ mt7996/mt7996.h | 7 +-
+ mt7996/regs.h | 7 +-
+ 6 files changed, 182 insertions(+), 83 deletions(-)
+
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index ccab0d7b9..60b88085c 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -7,11 +7,11 @@
+ #include <linux/utsname.h>
+ #include "coredump.h"
+
+-static bool coredump_memdump;
++static bool coredump_memdump = true;
+ module_param(coredump_memdump, bool, 0644);
+ MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
+
+-static const struct mt7996_mem_region mt7996_mem_regions[] = {
++static const struct mt7996_mem_region mt7996_wm_mem_regions[] = {
+ {
+ .start = 0x00800000,
+ .len = 0x0004ffff,
+@@ -44,27 +44,55 @@ static const struct mt7996_mem_region mt7996_mem_regions[] = {
+ },
+ };
+
++static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
++ {
++ .start = 0xE0000000,
++ .len = 0x0000ffff,
++ .name = "CRAM",
++ },
++ {
++ .start = 0xE0010000,
++ .len = 0x000117ff,
++ .name = "CRAM2",
++ },
++ {
++ .start = 0x10000000,
++ .len = 0x0001bfff,
++ .name = "ILM",
++ },
++ {
++ .start = 0x10200000,
++ .len = 0x00063fff,
++ .name = "DLM",
++ },
++};
++
+ const struct mt7996_mem_region*
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7990:
+ case 0x7991:
+- *num = ARRAY_SIZE(mt7996_mem_regions);
+- return &mt7996_mem_regions[0];
++ if (type == MT7996_RAM_TYPE_WA) {
++ *num = ARRAY_SIZE(mt7996_wa_mem_regions);
++ return &mt7996_wa_mem_regions[0];
++ }
++
++ *num = ARRAY_SIZE(mt7996_wm_mem_regions);
++ return &mt7996_wm_mem_regions[0];
+ default:
+ return NULL;
+ }
+ }
+
+-static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
++static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev, u8 type)
+ {
+ const struct mt7996_mem_region *mem_region;
+ size_t size = 0;
+ u32 num;
+ int i;
+
+- mem_region = mt7996_coredump_get_mem_layout(dev, &num);
++ mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
+ if (!mem_region)
+ return 0;
+
+@@ -81,14 +109,13 @@ static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
+ return size;
+ }
+
+-struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
++struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+- struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
++ struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+
+ lockdep_assert_held(&dev->dump_mutex);
+
+- if (coredump_memdump &&
+- !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500))
++ if (!coredump_memdump)
+ return NULL;
+
+ guid_gen(&crash_data->guid);
+@@ -98,12 +125,15 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
+ }
+
+ static void
+-mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
++mt7996_coredump_fw_state(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
+ bool *exception)
+ {
+- u32 count;
++ u32 count, reg = MT_FW_WM_DUMP_STATE;
++
++ if (type == MT7996_RAM_TYPE_WA)
++ reg = MT_FW_WA_DUMP_STATE;
+
+- count = mt76_rr(dev, MT_FW_ASSERT_CNT);
++ count = mt76_rr(dev, reg);
+
+ /* normal mode: driver can manually trigger assert for detail info */
+ if (!count)
+@@ -115,53 +145,59 @@ mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
+ }
+
+ static void
+-mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump,
++mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
+ bool exception)
+ {
+- u32 oldest, i, idx;
++ u32 reg, i, offset = 0, val = MT7996_RAM_TYPE_WM;
+
+- strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current));
++ if (type == MT7996_RAM_TYPE_WA) {
++ offset = MT_MCU_WA_EXCP_BASE - MT_MCU_WM_EXCP_BASE;
++ val = MT7996_RAM_TYPE_WA;
++ }
+
+- /* 0: WM PC log output */
+- mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0);
++ /* 0: WM PC log output, 1: WA PC log output */
++ mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, val);
+ /* choose 33th PC log buffer to read current PC index */
+ mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f);
+
+ /* read current PC */
+- dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
++ for (i = 0; i < 10; i++)
++ dump->pc_cur[i] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
+
+ /* stop call stack record */
+ if (!exception) {
+- mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
+- mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
++ mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
++ mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
+ }
+
+- oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL,
+- GENMASK(20, 16)) + 2;
+- for (i = 0; i < 16; i++) {
+- idx = ((oldest + 2 * i + 1) % 32);
+- dump->pc_stack[i + 1] =
+- mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4);
++ /* read PC log */
++ dump->pc_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_PC_CTRL + offset);
++ dump->pc_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS,
++ dump->pc_dbg_ctrl);
++ for (i = 0; i < 32; i++) {
++ reg = MT_MCU_WM_EXCP_PC_LOG + i * 4 + offset;
++ dump->pc_stack[i] = mt76_rr(dev, reg);
+ }
+
+- oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL,
+- GENMASK(20, 16)) + 2;
+- for (i = 0; i < 16; i++) {
+- idx = ((oldest + 2 * i + 1) % 32);
+- dump->lr_stack[i] =
+- mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4);
++ /* read LR log */
++ dump->lr_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_LR_CTRL + offset);
++ dump->lr_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS,
++ dump->lr_dbg_ctrl);
++ for (i = 0; i < 32; i++) {
++ reg = MT_MCU_WM_EXCP_LR_LOG + i * 4 + offset;
++ dump->lr_stack[i] = mt76_rr(dev, reg);
+ }
+
+ /* start call stack record */
+ if (!exception) {
+- mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
+- mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
++ mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
++ mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
+ }
+ }
+
+-static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
++static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
+ {
+- struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
++ struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ struct mt7996_coredump *dump;
+ struct mt7996_coredump_mem *dump_mem;
+ size_t len, sofar = 0, hdr_len = sizeof(*dump);
+@@ -186,20 +222,31 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
+
+ dump = (struct mt7996_coredump *)(buf);
+ dump->len = len;
++ dump->hdr_len = hdr_len;
+
+ /* plain text */
+ strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
+ strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
++ strscpy(dump->fw_type, ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"),
++ sizeof(dump->fw_type));
+ strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
+ sizeof(dump->fw_ver));
++ strscpy(dump->fw_patch_date, dev->patch_build_date,
++ sizeof(dump->fw_patch_date));
++ strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WM],
++ dev->ram_build_date[MT7996_RAM_TYPE_WM],
++ MT7996_BUILD_TIME_LEN);
++ strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WA],
++ dev->ram_build_date[MT7996_RAM_TYPE_WA],
++ MT7996_BUILD_TIME_LEN);
+
+ guid_copy(&dump->guid, &crash_data->guid);
+ dump->tv_sec = crash_data->timestamp.tv_sec;
+ dump->tv_nsec = crash_data->timestamp.tv_nsec;
+ dump->device_id = mt76_chip(&dev->mt76);
+
+- mt7996_coredump_fw_state(dev, dump, &exception);
+- mt7996_coredump_fw_stack(dev, dump, exception);
++ mt7996_coredump_fw_state(dev, type, dump, &exception);
++ mt7996_coredump_fw_stack(dev, type, dump, exception);
+
+ /* gather memory content */
+ dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
+@@ -213,17 +260,19 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
+ return dump;
+ }
+
+-int mt7996_coredump_submit(struct mt7996_dev *dev)
++int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ struct mt7996_coredump *dump;
+
+- dump = mt7996_coredump_build(dev);
++ dump = mt7996_coredump_build(dev, type);
+ if (!dump) {
+ dev_warn(dev->mt76.dev, "no crash dump data found\n");
+ return -ENODATA;
+ }
+
+ dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
++ dev_info(dev->mt76.dev, "%s coredump completed\n",
++ wiphy_name(dev->mt76.hw->wiphy));
+
+ return 0;
+ }
+@@ -231,23 +280,26 @@ int mt7996_coredump_submit(struct mt7996_dev *dev)
+ int mt7996_coredump_register(struct mt7996_dev *dev)
+ {
+ struct mt7996_crash_data *crash_data;
++ int i;
+
+- crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
+- if (!crash_data)
+- return -ENOMEM;
++ for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
++ crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
++ if (!crash_data)
++ return -ENOMEM;
+
+- dev->coredump.crash_data = crash_data;
++ dev->coredump.crash_data[i] = crash_data;
+
+- if (coredump_memdump) {
+- crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev);
+- if (!crash_data->memdump_buf_len)
+- /* no memory content */
+- return 0;
++ if (coredump_memdump) {
++ crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
++ if (!crash_data->memdump_buf_len)
++ /* no memory content */
++ return 0;
+
+- crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
+- if (!crash_data->memdump_buf) {
+- vfree(crash_data);
+- return -ENOMEM;
++ crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
++ if (!crash_data->memdump_buf) {
++ vfree(crash_data);
++ return -ENOMEM;
++ }
+ }
+ }
+
+@@ -256,13 +308,17 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
+
+ void mt7996_coredump_unregister(struct mt7996_dev *dev)
+ {
+- if (dev->coredump.crash_data->memdump_buf) {
+- vfree(dev->coredump.crash_data->memdump_buf);
+- dev->coredump.crash_data->memdump_buf = NULL;
+- dev->coredump.crash_data->memdump_buf_len = 0;
+- }
++ int i;
+
+- vfree(dev->coredump.crash_data);
+- dev->coredump.crash_data = NULL;
++ for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
++ if (dev->coredump.crash_data[i]->memdump_buf) {
++ vfree(dev->coredump.crash_data[i]->memdump_buf);
++ dev->coredump.crash_data[i]->memdump_buf = NULL;
++ dev->coredump.crash_data[i]->memdump_buf_len = 0;
++ }
++
++ vfree(dev->coredump.crash_data[i]);
++ dev->coredump.crash_data[i] = NULL;
++ }
+ }
+
+diff --git a/mt7996/coredump.h b/mt7996/coredump.h
+index af2ba219b..01ed3731c 100644
+--- a/mt7996/coredump.h
++++ b/mt7996/coredump.h
+@@ -6,10 +6,13 @@
+
+ #include "mt7996.h"
+
++#define MT7996_COREDUMP_MAX (MT7996_RAM_TYPE_WA + 1)
++
+ struct mt7996_coredump {
+ char magic[16];
+
+ u32 len;
++ u32 hdr_len;
+
+ guid_t guid;
+
+@@ -21,17 +24,28 @@ struct mt7996_coredump {
+ char kernel[64];
+ /* firmware version */
+ char fw_ver[ETHTOOL_FWVERS_LEN];
++ char fw_patch_date[MT7996_BUILD_TIME_LEN];
++ char fw_ram_date[MT7996_COREDUMP_MAX][MT7996_BUILD_TIME_LEN];
+
+ u32 device_id;
+
++ /* fw type */
++ char fw_type[8];
++
+ /* exception state */
+ char fw_state[12];
+
+ /* program counters */
+- char pc_current[16];
+- u32 pc_stack[17];
+- /* link registers */
+- u32 lr_stack[16];
++ u32 pc_dbg_ctrl;
++ u32 pc_cur_idx;
++ u32 pc_cur[10];
++ /* PC registers */
++ u32 pc_stack[32];
++
++ u32 lr_dbg_ctrl;
++ u32 lr_cur_idx;
++ /* LR registers */
++ u32 lr_stack[32];
+
+ /* memory content */
+ u8 data[];
+@@ -43,6 +57,7 @@ struct mt7996_coredump_mem {
+ } __packed;
+
+ struct mt7996_mem_hdr {
++ char name[64];
+ u32 start;
+ u32 len;
+ u8 data[];
+@@ -58,27 +73,27 @@ struct mt7996_mem_region {
+ #ifdef CONFIG_DEV_COREDUMP
+
+ const struct mt7996_mem_region *
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num);
+-struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev);
+-int mt7996_coredump_submit(struct mt7996_dev *dev);
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
++struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
++int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
+ int mt7996_coredump_register(struct mt7996_dev *dev);
+ void mt7996_coredump_unregister(struct mt7996_dev *dev);
+
+ #else /* CONFIG_DEV_COREDUMP */
+
+ static inline const struct mt7996_mem_region *
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+ return NULL;
+ }
+
+-static inline int mt7996_coredump_submit(struct mt7996_dev *dev)
++static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ return 0;
+ }
+
+ static inline struct
+-mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
++mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+ return NULL;
+ }
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3afdd7eb9..d88bbfb24 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1998,28 +1998,25 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ }
+
+ /* firmware coredump */
+-void mt7996_mac_dump_work(struct work_struct *work)
++void mt7996_mac_fw_coredump(struct mt7996_dev *dev, u8 type)
+ {
+ const struct mt7996_mem_region *mem_region;
+ struct mt7996_crash_data *crash_data;
+- struct mt7996_dev *dev;
+ struct mt7996_mem_hdr *hdr;
+ size_t buf_len;
+ int i;
+ u32 num;
+ u8 *buf;
+
+- dev = container_of(work, struct mt7996_dev, dump_work);
+-
+ mutex_lock(&dev->dump_mutex);
+
+- crash_data = mt7996_coredump_new(dev);
++ crash_data = mt7996_coredump_new(dev, type);
+ if (!crash_data) {
+ mutex_unlock(&dev->dump_mutex);
+- goto skip_coredump;
++ return;
+ }
+
+- mem_region = mt7996_coredump_get_mem_layout(dev, &num);
++ mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
+ if (!mem_region || !crash_data->memdump_buf_len) {
+ mutex_unlock(&dev->dump_mutex);
+ goto skip_memdump;
+@@ -2029,6 +2026,9 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ buf_len = crash_data->memdump_buf_len;
+
+ /* dumping memory content... */
++ dev_info(dev->mt76.dev, "%s start coredump for %s\n",
++ wiphy_name(dev->mt76.hw->wiphy),
++ ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"));
+ memset(buf, 0, buf_len);
+ for (i = 0; i < num; i++) {
+ if (mem_region->len > buf_len) {
+@@ -2045,6 +2045,7 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ mt7996_memcpy_fromio(dev, buf, mem_region->start,
+ mem_region->len);
+
++ strscpy(hdr->name, mem_region->name, sizeof(mem_region->name));
+ hdr->start = mem_region->start;
+ hdr->len = mem_region->len;
+
+@@ -2061,8 +2062,20 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ mutex_unlock(&dev->dump_mutex);
+
+ skip_memdump:
+- mt7996_coredump_submit(dev);
+-skip_coredump:
++ mt7996_coredump_submit(dev, type);
++}
++
++void mt7996_mac_dump_work(struct work_struct *work)
++{
++ struct mt7996_dev *dev;
++
++ dev = container_of(work, struct mt7996_dev, dump_work);
++ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
++ mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
++
++ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
++ mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
++
+ queue_work(dev->mt76.wq, &dev->reset_work);
+ }
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 9e94e03d8..0656b4a51 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2704,6 +2704,8 @@ static int mt7996_load_patch(struct mt7996_dev *dev)
+
+ dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
+ be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
++ memcpy(dev->patch_build_date, hdr->build_date,
++ sizeof(dev->patch_build_date));
+
+ for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
+ struct mt7996_patch_sec *sec;
+@@ -2830,6 +2832,9 @@ static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type,
+ }
+
+ hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
++ memcpy(dev->ram_build_date[ram_type],
++ hdr->build_date,
++ sizeof(dev->ram_build_date[ram_type]));
+ dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n",
+ fw_type, hdr->fw_ver, hdr->build_date);
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index b7197dcb7..e12ad318d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -84,6 +84,8 @@
+ #define MT7996_CRIT_TEMP 110
+ #define MT7996_MAX_TEMP 120
+
++#define MT7996_BUILD_TIME_LEN 24
++
+ #define MT7996_RRO_MAX_SESSION 1024
+ #define MT7996_RRO_WINDOW_MAX_LEN 1024
+ #define MT7996_RRO_ADDR_ELEM_LEN 128
+@@ -127,6 +129,7 @@ enum mt7996_ram_type {
+ MT7996_RAM_TYPE_WM,
+ MT7996_RAM_TYPE_WA,
+ MT7996_RAM_TYPE_DSP,
++ __MT7996_RAM_TYPE_MAX,
+ };
+
+ enum mt7996_txq_id {
+@@ -320,9 +323,11 @@ struct mt7996_dev {
+ struct mutex dump_mutex;
+ #ifdef CONFIG_DEV_COREDUMP
+ struct {
+- struct mt7996_crash_data *crash_data;
++ struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
+ } coredump;
+ #endif
++ char patch_build_date[MT7996_BUILD_TIME_LEN];
++ char ram_build_date[__MT7996_RAM_TYPE_MAX][MT7996_BUILD_TIME_LEN];
+
+ struct list_head sta_rc_list;
+ struct list_head twt_list;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index cf12c5e02..4c20a67d7 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -597,7 +597,8 @@ enum offs_rev {
+
+ /* FW MODE SYNC */
+ #define MT_FW_ASSERT_CNT 0x02208274
+-#define MT_FW_DUMP_STATE 0x02209e90
++#define MT_FW_WM_DUMP_STATE 0x02209e90
++#define MT_FW_WA_DUMP_STATE 0x7C05B080
+
+ #define MT_SWDEF_BASE 0x00401400
+
+@@ -714,11 +715,15 @@ enum offs_rev {
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29)
+
+ /* CONN MCU EXCP CON */
++#define MT_MCU_WA_EXCP_BASE 0x890d0000
+ #define MT_MCU_WM_EXCP_BASE 0x89050000
++
+ #define MT_MCU_WM_EXCP(ofs) (MT_MCU_WM_EXCP_BASE + (ofs))
+ #define MT_MCU_WM_EXCP_PC_CTRL MT_MCU_WM_EXCP(0x100)
++#define MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS GENMASK(20, 16)
+ #define MT_MCU_WM_EXCP_PC_LOG MT_MCU_WM_EXCP(0x104)
+ #define MT_MCU_WM_EXCP_LR_CTRL MT_MCU_WM_EXCP(0x200)
++#define MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS GENMASK(20, 16)
+ #define MT_MCU_WM_EXCP_LR_LOG MT_MCU_WM_EXCP(0x204)
+
+ /* CONN AFE CTL CON */
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-wifi-mt76-mt7996-for-build-pass.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-wifi-mt76-mt7996-for-build-pass.patch
new file mode 100644
index 0000000..dd21584
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-wifi-mt76-mt7996-for-build-pass.patch
@@ -0,0 +1,137 @@
+From db4bbc138ecda8790cc3e6b0d513278ff900a4cc Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 3 Nov 2022 00:27:17 +0800
+Subject: [PATCH 019/116] mtk: wifi: mt76: mt7996: for build pass
+
+Change-Id: Ieb44c33ee6e6a2e6058c1ef528404c1a1cbcfdaf
+---
+ debugfs.c | 3 +++
+ dma.c | 2 +-
+ mcu.c | 1 +
+ mt7615/mcu.c | 1 +
+ mt76_connac_mcu.c | 1 +
+ mt7915/mcu.c | 1 +
+ mt7996/dma.c | 4 ++--
+ mt7996/eeprom.c | 1 +
+ mt7996/mcu.c | 1 +
+ 9 files changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/debugfs.c b/debugfs.c
+index c4649ba04..ac5207e5e 100644
+--- a/debugfs.c
++++ b/debugfs.c
+@@ -33,8 +33,11 @@ mt76_napi_threaded_set(void *data, u64 val)
+ if (!mt76_is_mmio(dev))
+ return -EOPNOTSUPP;
+
++#if 0
++ /* need to backport patch from networking stack */
+ if (dev->napi_dev.threaded != val)
+ return dev_set_threaded(&dev->napi_dev, val);
++#endif
+
+ return 0;
+ }
+diff --git a/dma.c b/dma.c
+index f4f88c444..560446395 100644
+--- a/dma.c
++++ b/dma.c
+@@ -883,7 +883,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ !(dev->drv->rx_check(dev, data, len)))
+ goto free_frag;
+
+- skb = napi_build_skb(data, q->buf_size);
++ skb = build_skb(data, q->buf_size);
+ if (!skb)
+ goto free_frag;
+
+diff --git a/mcu.c b/mcu.c
+index a8cafa39a..fa4b05441 100644
+--- a/mcu.c
++++ b/mcu.c
+@@ -4,6 +4,7 @@
+ */
+
+ #include "mt76.h"
++#include <linux/moduleparam.h>
+
+ struct sk_buff *
+ __mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data,
+diff --git a/mt7615/mcu.c b/mt7615/mcu.c
+index c807bd8d9..a9310660b 100644
+--- a/mt7615/mcu.c
++++ b/mt7615/mcu.c
+@@ -10,6 +10,7 @@
+ #include "mcu.h"
+ #include "mac.h"
+ #include "eeprom.h"
++#include <linux/moduleparam.h>
+
+ static bool prefer_offload_fw = true;
+ module_param(prefer_offload_fw, bool, 0644);
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index b35acf86e..1e34e0ae3 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -4,6 +4,7 @@
+ #include <linux/firmware.h>
+ #include "mt76_connac2_mac.h"
+ #include "mt76_connac_mcu.h"
++#include <linux/module.h>
+
+ int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option)
+ {
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index 24daa0835..2d017396c 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -6,6 +6,7 @@
+ #include "mcu.h"
+ #include "mac.h"
+ #include "eeprom.h"
++#include <linux/moduleparam.h>
+
+ #define fw_name(_dev, name, ...) ({ \
+ char *_fw; \
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 73e633d0d..759a58e8e 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -641,8 +641,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ if (ret < 0)
+ return ret;
+
+- netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+- mt7996_poll_tx);
++ netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
++ mt7996_poll_tx, NAPI_POLL_WEIGHT);
+ napi_enable(&dev->mt76.tx_napi);
+
+ mt7996_dma_enable(dev, false);
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 3260d1fef..121a3c958 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -138,6 +138,7 @@ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
++ cap = 0x4b249248; /* internal hardcode */
+ if (cap) {
+ dev->has_eht = !(cap & MODE_HE_ONLY);
+ dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0656b4a51..57cfa1494 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5,6 +5,7 @@
+
+ #include <linux/firmware.h>
+ #include <linux/fs.h>
++#include <linux/moduleparam.h>
+ #include "mt7996.h"
+ #include "mcu.h"
+ #include "mac.h"
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-wifi-mt76-mt7996-add-debug-tool.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-wifi-mt76-mt7996-add-debug-tool.patch
new file mode 100644
index 0000000..828eee7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-wifi-mt76-mt7996-add-debug-tool.patch
@@ -0,0 +1,5421 @@
+From 6b19a7a6cfa1095afbf622419d085c54e11d05b3 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 24 Mar 2023 14:02:32 +0800
+Subject: [PATCH 020/116] mtk: wifi: mt76: mt7996: add debug tool
+
+Change-Id: Ie10390b01f17db893dbfbf3221bf63a4bd1fe38f
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Add PSM bit in sta_info
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I591b558a9eec2fbd46d166c9bb1580a94e22072c
+
+Remove the duplicate function in mtk_debugfs.c & mtk_debug_i.c
+Only enable mt7996_mcu_fw_log_2_host function in mcu.c
+
+CR-ID: WCNCR00240597
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+
+Support more ids category NDPA/NDP TXD/FBK and debug log recommended by
+CTD members.
+
+This commit equals to run the follwoing commands on Logan driver:
+command:
+1. iwpriv ra0 set fw_dbg=1:84
+2. iwpriv ra0 set fw_dbg=2:84
+3. iwpriv ra0 set fw_dbg=1:101
+
+CR-Id: WCNCR00261410
+Change-Id: Ifddd4db86982d39f2d39d198b8f5d3e7028983c2
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+mtk: wifi: mt76: mt7996: add wtbl_info support for mt7992
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: mt7996: add mt7992 & mt7996 CR debug offset revision
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: mt7992: refactor code for FW log
+
+Refactor code for FW log.
+
+CR-Id: WCNCR00298425
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Change-Id: I00c760b31009142848e32b1249d305800585e7fd
+
+mtk: wifi: mt76: mt7996: support disable muru debug info when recording fwlog
+
+When we record fwlog, we will also enable recording muru debug info log by
+default. However, in certain test scenarios, this can result in
+recording too many logs, causing inconvenience during issue analysis.
+Therefore, this commit adds an debug option, fw_debug_muru_disable, in
+debugfs. User can modify this option to enable/disable recording muru
+debug info log.
+
+[Usage]
+Set:
+$ echo val > debugfs/fw_debug_muru_disable
+Get:
+$ cat debugfs/fw_debug_muru_disable
+
+val can be the following values:
+0 = enable recording muru debug info (Default value)
+1 = disable recording muru debug info
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+mtk: wifi: mt76: mt7996: add adie id & ver dump
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76.h | 2 +
+ mt7996/Makefile | 4 +
+ mt7996/coredump.c | 10 +-
+ mt7996/coredump.h | 7 +
+ mt7996/debugfs.c | 128 ++-
+ mt7996/mac.c | 3 +
+ mt7996/mt7996.h | 13 +
+ mt7996/mtk_debug.h | 2286 ++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_debugfs.c | 2507 ++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c | 39 +
+ mt7996/mtk_mcu.h | 19 +
+ tools/fwlog.c | 25 +-
+ 12 files changed, 5018 insertions(+), 25 deletions(-)
+ create mode 100644 mt7996/mtk_debug.h
+ create mode 100644 mt7996/mtk_debugfs.c
+ create mode 100644 mt7996/mtk_mcu.c
+ create mode 100644 mt7996/mtk_mcu.h
+
+diff --git a/mt76.h b/mt76.h
+index 2cbea731e..599787db2 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -399,6 +399,8 @@ struct mt76_txwi_cache {
+ struct sk_buff *skb;
+ void *ptr;
+ };
++
++ unsigned long jiffies;
+ };
+
+ struct mt76_rx_tid {
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index 07c8b555c..a056b40e0 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -1,4 +1,6 @@
+ # SPDX-License-Identifier: ISC
++EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
++EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
+
+ obj-$(CONFIG_MT7996E) += mt7996e.o
+
+@@ -6,3 +8,5 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+ debugfs.o mmio.o
+
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
++
++mt7996e-y += mtk_debugfs.o mtk_mcu.o
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index 60b88085c..a7f91b56d 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -195,7 +195,7 @@ mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump
+ }
+ }
+
+-static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
++struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
+ {
+ struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ struct mt7996_coredump *dump;
+@@ -206,7 +206,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+
+ len = hdr_len;
+
+- if (coredump_memdump && crash_data->memdump_buf_len)
++ if (full_dump && coredump_memdump && crash_data->memdump_buf_len)
+ len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
+
+ sofar += hdr_len;
+@@ -248,6 +248,9 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ mt7996_coredump_fw_state(dev, type, dump, &exception);
+ mt7996_coredump_fw_stack(dev, type, dump, exception);
+
++ if (!full_dump)
++ goto skip_dump_mem;
++
+ /* gather memory content */
+ dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
+ dump_mem->len = crash_data->memdump_buf_len;
+@@ -255,6 +258,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ memcpy(dump_mem->data, crash_data->memdump_buf,
+ crash_data->memdump_buf_len);
+
++skip_dump_mem:
+ mutex_unlock(&dev->dump_mutex);
+
+ return dump;
+@@ -264,7 +268,7 @@ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ struct mt7996_coredump *dump;
+
+- dump = mt7996_coredump_build(dev, type);
++ dump = mt7996_coredump_build(dev, type, true);
+ if (!dump) {
+ dev_warn(dev->mt76.dev, "no crash dump data found\n");
+ return -ENODATA;
+diff --git a/mt7996/coredump.h b/mt7996/coredump.h
+index 01ed3731c..93cd84a03 100644
+--- a/mt7996/coredump.h
++++ b/mt7996/coredump.h
+@@ -75,6 +75,7 @@ struct mt7996_mem_region {
+ const struct mt7996_mem_region *
+ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
+ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
++struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump);
+ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
+ int mt7996_coredump_register(struct mt7996_dev *dev);
+ void mt7996_coredump_unregister(struct mt7996_dev *dev);
+@@ -92,6 +93,12 @@ static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ return 0;
+ }
+
++static inline struct
++mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
++{
++ return NULL;
++}
++
+ static inline struct
+ mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 62c03d088..344c759c0 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -295,11 +295,39 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ DEBUG_SPL,
+ DEBUG_RPT_RX,
+ DEBUG_RPT_RA = 68,
+- } debug;
++ DEBUG_IDS_SND = 84,
++ DEBUG_IDS_PP = 93,
++ DEBUG_IDS_RA = 94,
++ DEBUG_IDS_BF = 95,
++ DEBUG_IDS_SR = 96,
++ DEBUG_IDS_RU = 97,
++ DEBUG_IDS_MUMIMO = 98,
++ DEBUG_IDS_ERR_LOG = 101,
++ };
++ u8 debug_category[] = {
++ DEBUG_TXCMD,
++ DEBUG_CMD_RPT_TX,
++ DEBUG_CMD_RPT_TRIG,
++ DEBUG_SPL,
++ DEBUG_RPT_RX,
++ DEBUG_RPT_RA,
++ DEBUG_IDS_SND,
++ DEBUG_IDS_PP,
++ DEBUG_IDS_RA,
++ DEBUG_IDS_BF,
++ DEBUG_IDS_SR,
++ DEBUG_IDS_RU,
++ DEBUG_IDS_MUMIMO,
++ DEBUG_IDS_ERR_LOG,
++ };
+ bool tx, rx, en;
+ int ret;
++ u8 i;
+
+ dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0;
++#ifdef CONFIG_MTK_DEBUG
++ dev->fw_debug_wm = val;
++#endif
+
+ if (dev->fw_debug_bin)
+ val = MCU_FW_LOG_RELAY;
+@@ -314,18 +342,21 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ if (ret)
+ return ret;
+
+- for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) {
+- if (debug == 67)
+- continue;
+-
+- if (debug == DEBUG_RPT_RX)
++ for (i = 0; i < ARRAY_SIZE(debug_category); i++) {
++ if (debug_category[i] == DEBUG_RPT_RX)
+ val = en && rx;
+ else
+ val = en && tx;
+
+- ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val);
++ ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], val);
+ if (ret)
+ return ret;
++
++ if (debug_category[i] == DEBUG_IDS_SND && en) {
++ ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], 2);
++ if (ret)
++ return ret;
++ }
+ }
+
+ return 0;
+@@ -397,6 +428,39 @@ remove_buf_file_cb(struct dentry *f)
+ return 0;
+ }
+
++static int
++mt7996_fw_debug_muru_set(void *data)
++{
++ struct mt7996_dev *dev = data;
++ enum {
++ DEBUG_BSRP_STATUS = 256,
++ DEBUG_TX_DATA_BYTE_CONUT,
++ DEBUG_RX_DATA_BYTE_CONUT,
++ DEBUG_RX_TOTAL_BYTE_CONUT,
++ DEBUG_INVALID_TID_BSR,
++ DEBUG_UL_LONG_TERM_PPDU_TYPE,
++ DEBUG_DL_LONG_TERM_PPDU_TYPE,
++ DEBUG_PPDU_CLASS_TRIG_ONOFF,
++ DEBUG_AIRTIME_BUSY_STATUS,
++ DEBUG_UL_OFDMA_MIMO_STATUS,
++ DEBUG_RU_CANDIDATE,
++ DEBUG_MEC_UPDATE_AMSDU,
++ } debug;
++ int ret;
++
++ if (dev->fw_debug_muru_disable)
++ return 0;
++
++ for (debug = DEBUG_BSRP_STATUS; debug <= DEBUG_MEC_UPDATE_AMSDU; debug++) {
++ ret = mt7996_mcu_muru_dbg_info(dev, debug,
++ dev->fw_debug_bin & BIT(0));
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
+ static int
+ mt7996_fw_debug_bin_set(void *data, u64 val)
+ {
+@@ -405,17 +469,23 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
+ .remove_buf_file = remove_buf_file_cb,
+ };
+ struct mt7996_dev *dev = data;
++ int ret;
+
+- if (!dev->relay_fwlog)
++ if (!dev->relay_fwlog) {
+ dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir,
+ 1500, 512, &relay_cb, NULL);
+- if (!dev->relay_fwlog)
+- return -ENOMEM;
++ if (!dev->relay_fwlog)
++ return -ENOMEM;
++ }
+
+ dev->fw_debug_bin = val;
+
+ relay_reset(dev->relay_fwlog);
+
++ ret = mt7996_fw_debug_muru_set(dev);
++ if (ret)
++ return ret;
++
+ return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
+ }
+
+@@ -785,6 +855,30 @@ mt7996_rf_regval_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get,
+ mt7996_rf_regval_set, "0x%08llx\n");
+
++static int
++mt7996_fw_debug_muru_disable_set(void *data, u64 val)
++{
++ struct mt7996_dev *dev = data;
++
++ dev->fw_debug_muru_disable = !!val;
++
++ return 0;
++}
++
++static int
++mt7996_fw_debug_muru_disable_get(void *data, u64 *val)
++{
++ struct mt7996_dev *dev = data;
++
++ *val = dev->fw_debug_muru_disable;
++
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
++ mt7996_fw_debug_muru_disable_get,
++ mt7996_fw_debug_muru_disable_set, "%lld\n");
++
+ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -820,10 +914,17 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
+ mt7996_rdd_monitor);
+ }
++ debugfs_create_file("fw_debug_muru_disable", 0600, dir, dev,
++ &fops_fw_debug_muru_disable);
+
+ if (phy == &dev->phy)
+ dev->debugfs_dir = dir;
+
++#ifdef CONFIG_MTK_DEBUG
++ debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
++ mt7996_mtk_init_debugfs(phy, dir);
++#endif
++
+ return 0;
+ }
+
+@@ -835,7 +936,11 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
+ unsigned long flags;
+ void *dest;
+
++ if (!dev->relay_fwlog)
++ return;
++
+ spin_lock_irqsave(&lock, flags);
++
+ dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
+ if (dest) {
+ *(u32 *)dest = hdrlen + len;
+@@ -868,9 +973,6 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+ .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR),
+ };
+
+- if (!dev->relay_fwlog)
+- return;
+-
+ hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++);
+ hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0)));
+ hdr.len = *(__le16 *)data;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index d88bbfb24..1f53d2303 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -936,6 +936,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ id = mt76_token_consume(mdev, &t);
+ if (id < 0)
+ return id;
++#ifdef CONFIG_MTK_DEBUG
++ t->jiffies = jiffies;
++#endif
+
+ pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
+ memset(txwi_ptr, 0, MT_TXD_SIZE);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index e12ad318d..696e16fa1 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -362,6 +362,7 @@ struct mt7996_dev {
+ u8 fw_debug_wa;
+ u8 fw_debug_bin;
+ u16 fw_debug_seq;
++ bool fw_debug_muru_disable;
+
+ struct dentry *debugfs_dir;
+ struct rchan *relay_fwlog;
+@@ -374,6 +375,17 @@ struct mt7996_dev {
+ spinlock_t reg_lock;
+
+ u8 wtbl_size_group;
++
++#ifdef CONFIG_MTK_DEBUG
++ u16 wlan_idx;
++ struct {
++ u8 sku_disable;
++ u32 fw_dbg_module;
++ u8 fw_dbg_lv;
++ u32 bcn_total_cnt[__MT_MAX_BAND];
++ } dbg;
++ const struct mt7996_dbg_reg_desc *dbg_reg;
++#endif
+ };
+
+ enum {
+@@ -670,6 +682,7 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+
+ #ifdef CONFIG_MTK_DEBUG
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
++int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
+new file mode 100644
+index 000000000..27d8f1cb2
+--- /dev/null
++++ b/mt7996/mtk_debug.h
+@@ -0,0 +1,2286 @@
++#ifndef __MTK_DEBUG_H
++#define __MTK_DEBUG_H
++
++#ifdef CONFIG_MTK_DEBUG
++#define NO_SHIFT_DEFINE 0xFFFFFFFF
++#define BITS(m, n) (~(BIT(m)-1) & ((BIT(n) - 1) | BIT(n)))
++
++#define GET_FIELD(_field, _reg) \
++ ({ \
++ (((_reg) & (_field##_MASK)) >> (_field##_SHIFT)); \
++ })
++
++#define __DBG_OFFS(id) (dev->dbg_reg->offs_rev[(id)])
++
++enum dbg_offs_rev {
++ AGG_AALCR2,
++ AGG_AALCR3,
++ AGG_AALCR4,
++ AGG_AALCR5,
++ AGG_AALCR6,
++ AGG_AALCR7,
++ MIB_TDRCR0,
++ MIB_TDRCR1,
++ MIB_TDRCR2,
++ MIB_TDRCR3,
++ MIB_TDRCR4,
++ MIB_RSCR26,
++ MIB_TSCR18,
++ MIB_TRDR0,
++ MIB_TRDR2,
++ MIB_TRDR3,
++ MIB_TRDR4,
++ MIB_TRDR5,
++ MIB_TRDR6,
++ MIB_TRDR7,
++ MIB_TRDR8,
++ MIB_TRDR9,
++ MIB_TRDR10,
++ MIB_TRDR11,
++ MIB_TRDR12,
++ MIB_TRDR13,
++ MIB_TRDR14,
++ MIB_TRDR15,
++ MIB_MSR0,
++ MIB_MSR1,
++ MIB_MSR2,
++ MIB_MCTR5,
++ MIB_MCTR6,
++ __MT_DBG_OFFS_REV_MAX,
++};
++
++static const u32 mt7996_dbg_offs[] = {
++ [AGG_AALCR2] = 0x128,
++ [AGG_AALCR3] = 0x12c,
++ [AGG_AALCR4] = 0x130,
++ [AGG_AALCR5] = 0x134,
++ [AGG_AALCR6] = 0x138,
++ [AGG_AALCR7] = 0x13c,
++ [MIB_TDRCR0] = 0x728,
++ [MIB_TDRCR1] = 0x72c,
++ [MIB_TDRCR2] = 0x730,
++ [MIB_TDRCR3] = 0x734,
++ [MIB_TDRCR4] = 0x738,
++ [MIB_RSCR26] = 0x950,
++ [MIB_TSCR18] = 0xa1c,
++ [MIB_TRDR0] = 0xa24,
++ [MIB_TRDR2] = 0xa2c,
++ [MIB_TRDR3] = 0xa30,
++ [MIB_TRDR4] = 0xa34,
++ [MIB_TRDR5] = 0xa38,
++ [MIB_TRDR6] = 0xa3c,
++ [MIB_TRDR7] = 0xa40,
++ [MIB_TRDR8] = 0xa44,
++ [MIB_TRDR9] = 0xa48,
++ [MIB_TRDR10] = 0xa4c,
++ [MIB_TRDR11] = 0xa50,
++ [MIB_TRDR12] = 0xa54,
++ [MIB_TRDR13] = 0xa58,
++ [MIB_TRDR14] = 0xa5c,
++ [MIB_TRDR15] = 0xa60,
++ [MIB_MSR0] = 0xa64,
++ [MIB_MSR1] = 0xa68,
++ [MIB_MSR2] = 0xa6c,
++ [MIB_MCTR5] = 0xa70,
++ [MIB_MCTR6] = 0xa74,
++};
++
++static const u32 mt7992_dbg_offs[] = {
++ [AGG_AALCR2] = 0x12c,
++ [AGG_AALCR3] = 0x130,
++ [AGG_AALCR4] = 0x134,
++ [AGG_AALCR5] = 0x138,
++ [AGG_AALCR6] = 0x13c,
++ [AGG_AALCR7] = 0x140,
++ [MIB_TDRCR0] = 0x768,
++ [MIB_TDRCR1] = 0x76c,
++ [MIB_TDRCR2] = 0x770,
++ [MIB_TDRCR3] = 0x774,
++ [MIB_TDRCR4] = 0x778,
++ [MIB_RSCR26] = 0x994,
++ [MIB_TSCR18] = 0xb18,
++ [MIB_TRDR0] = 0xb20,
++ [MIB_TRDR2] = 0xb28,
++ [MIB_TRDR3] = 0xb2c,
++ [MIB_TRDR4] = 0xb30,
++ [MIB_TRDR5] = 0xb34,
++ [MIB_TRDR6] = 0xb38,
++ [MIB_TRDR7] = 0xb3c,
++ [MIB_TRDR8] = 0xb40,
++ [MIB_TRDR9] = 0xb44,
++ [MIB_TRDR10] = 0xb48,
++ [MIB_TRDR11] = 0xb4c,
++ [MIB_TRDR12] = 0xb50,
++ [MIB_TRDR13] = 0xb54,
++ [MIB_TRDR14] = 0xb58,
++ [MIB_TRDR15] = 0xb5c,
++ [MIB_MSR0] = 0xb60,
++ [MIB_MSR1] = 0xb64,
++ [MIB_MSR2] = 0xb68,
++ [MIB_MCTR5] = 0xb6c,
++ [MIB_MCTR6] = 0xb70,
++};
++
++/* used to differentiate between generations */
++struct mt7996_dbg_reg_desc {
++ const u32 id;
++ const u32 *offs_rev;
++};
++
++/* AGG */
++#define BN0_WF_AGG_TOP_BASE 0x820e2000
++#define BN1_WF_AGG_TOP_BASE 0x820f2000
++#define IP1_BN0_WF_AGG_TOP_BASE 0x830e2000
++
++#define BN0_WF_AGG_TOP_SCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x0) // 2000
++#define BN0_WF_AGG_TOP_SCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x4) // 2004
++#define BN0_WF_AGG_TOP_SCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x8) // 2008
++#define BN0_WF_AGG_TOP_BCR_ADDR (BN0_WF_AGG_TOP_BASE + 0xc) // 200C
++#define BN0_WF_AGG_TOP_BWCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x10) // 2010
++#define BN0_WF_AGG_TOP_ARCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x14) // 2014
++#define BN0_WF_AGG_TOP_ARUCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x18) // 2018
++#define BN0_WF_AGG_TOP_ARDCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x1c) // 201C
++#define BN0_WF_AGG_TOP_AALCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x20) // 2020
++#define BN0_WF_AGG_TOP_AALCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x24) // 2024
++#define BN0_WF_AGG_TOP_PCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x28) // 2028
++#define BN0_WF_AGG_TOP_PCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2c) // 202C
++#define BN0_WF_AGG_TOP_TTCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x30) // 2030
++#define BN0_WF_AGG_TOP_TTCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x34) // 2034
++#define BN0_WF_AGG_TOP_ACR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x38) // 2038
++#define BN0_WF_AGG_TOP_ACR4_ADDR (BN0_WF_AGG_TOP_BASE + 0x3c) // 203C
++#define BN0_WF_AGG_TOP_ACR5_ADDR (BN0_WF_AGG_TOP_BASE + 0x40) // 2040
++#define BN0_WF_AGG_TOP_ACR6_ADDR (BN0_WF_AGG_TOP_BASE + 0x44) // 2044
++#define BN0_WF_AGG_TOP_ACR8_ADDR (BN0_WF_AGG_TOP_BASE + 0x4c) // 204C
++#define BN0_WF_AGG_TOP_MRCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x50) // 2050
++#define BN0_WF_AGG_TOP_MMPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x54) // 2054
++#define BN0_WF_AGG_TOP_GFPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x58) // 2058
++#define BN0_WF_AGG_TOP_VHTPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x5c) // 205C
++#define BN0_WF_AGG_TOP_HEPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x60) // 2060
++#define BN0_WF_AGG_TOP_CTCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x64) // 2064
++#define BN0_WF_AGG_TOP_ATCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0x68) // 2068
++#define BN0_WF_AGG_TOP_SRCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x6c) // 206C
++#define BN0_WF_AGG_TOP_VBCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x70) // 2070
++#define BN0_WF_AGG_TOP_TCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x74) // 2074
++#define BN0_WF_AGG_TOP_SRHS_ADDR (BN0_WF_AGG_TOP_BASE + 0x78) // 2078
++#define BN0_WF_AGG_TOP_DBRCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x7c) // 207C
++#define BN0_WF_AGG_TOP_DBRCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x80) // 2080
++#define BN0_WF_AGG_TOP_CTETCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x84) // 2084
++#define BN0_WF_AGG_TOP_WPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x88) // 2088
++#define BN0_WF_AGG_TOP_PLRPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x8c) // 208C
++#define BN0_WF_AGG_TOP_CECR_ADDR (BN0_WF_AGG_TOP_BASE + 0x90) // 2090
++#define BN0_WF_AGG_TOP_OMRCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x94) // 2094
++#define BN0_WF_AGG_TOP_OMRCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x98) // 2098
++#define BN0_WF_AGG_TOP_OMRCR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x9c) // 209C
++#define BN0_WF_AGG_TOP_OMRCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0xa0) // 20A0
++#define BN0_WF_AGG_TOP_TMCR_ADDR (BN0_WF_AGG_TOP_BASE + 0xa4) // 20A4
++#define BN0_WF_AGG_TOP_TWTCR_ADDR (BN0_WF_AGG_TOP_BASE + 0xa8) // 20A8
++#define BN0_WF_AGG_TOP_TWTSTACR_ADDR (BN0_WF_AGG_TOP_BASE + 0xac) // 20AC
++#define BN0_WF_AGG_TOP_TWTE0TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xb0) // 20B0
++#define BN0_WF_AGG_TOP_TWTE1TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xb4) // 20B4
++#define BN0_WF_AGG_TOP_TWTE2TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xb8) // 20B8
++#define BN0_WF_AGG_TOP_TWTE3TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xbc) // 20BC
++#define BN0_WF_AGG_TOP_TWTE4TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xc0) // 20C0
++#define BN0_WF_AGG_TOP_TWTE5TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xc4) // 20C4
++#define BN0_WF_AGG_TOP_TWTE6TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xc8) // 20C8
++#define BN0_WF_AGG_TOP_TWTE7TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xcc) // 20CC
++#define BN0_WF_AGG_TOP_TWTE8TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xd0) // 20D0
++#define BN0_WF_AGG_TOP_TWTE9TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xd4) // 20D4
++#define BN0_WF_AGG_TOP_TWTEATB_ADDR (BN0_WF_AGG_TOP_BASE + 0xd8) // 20D8
++#define BN0_WF_AGG_TOP_TWTEBTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xdc) // 20DC
++#define BN0_WF_AGG_TOP_TWTECTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xe0) // 20E0
++#define BN0_WF_AGG_TOP_TWTEDTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xe4) // 20E4
++#define BN0_WF_AGG_TOP_TWTEETB_ADDR (BN0_WF_AGG_TOP_BASE + 0xe8) // 20E8
++#define BN0_WF_AGG_TOP_TWTEFTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xec) // 20EC
++#define BN0_WF_AGG_TOP_ATCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x108) // 2108
++#define BN0_WF_AGG_TOP_ATCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x10c) // 210C
++#define BN0_WF_AGG_TOP_TCCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x110) // 2110
++#define BN0_WF_AGG_TOP_TFCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x114) // 2114
++#define BN0_WF_AGG_TOP_MUCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x118) // 2118
++#define BN0_WF_AGG_TOP_MUCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x11c) // 211C
++#define BN0_WF_AGG_TOP_AALCR2_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR2))
++#define BN0_WF_AGG_TOP_AALCR3_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR3))
++#define BN0_WF_AGG_TOP_AALCR4_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR4))
++#define BN0_WF_AGG_TOP_AALCR5_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR5))
++#define BN0_WF_AGG_TOP_AALCR6_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR6))
++#define BN0_WF_AGG_TOP_AALCR7_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR7))
++#define BN0_WF_AGG_TOP_CSDCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x150) // 2150
++#define BN0_WF_AGG_TOP_CSDCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x154) // 2154
++#define BN0_WF_AGG_TOP_CSDCR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x158) // 2158
++#define BN0_WF_AGG_TOP_CSDCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0x15c) // 215C
++#define BN0_WF_AGG_TOP_CSDCR4_ADDR (BN0_WF_AGG_TOP_BASE + 0x160) // 2160
++#define BN0_WF_AGG_TOP_DYNSCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x178) // 2178
++#define BN0_WF_AGG_TOP_DYNSSCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x198) // 2198
++#define BN0_WF_AGG_TOP_TCDCNT0_ADDR (BN0_WF_AGG_TOP_BASE + 0x2c8) // 22C8
++#define BN0_WF_AGG_TOP_TCDCNT1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2cc) // 22CC
++#define BN0_WF_AGG_TOP_TCSR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x2d0) // 22D0
++#define BN0_WF_AGG_TOP_TCSR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2d4) // 22D4
++#define BN0_WF_AGG_TOP_TCSR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x2d8) // 22D8
++#define BN0_WF_AGG_TOP_DCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x2e4) // 22E4
++#define BN0_WF_AGG_TOP_SMDCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x2e8) // 22E8
++#define BN0_WF_AGG_TOP_TXCMDSMCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x2ec) // 22EC
++#define BN0_WF_AGG_TOP_SMCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x2f0) // 22F0
++#define BN0_WF_AGG_TOP_SMCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2f4) // 22F4
++#define BN0_WF_AGG_TOP_SMCR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x2f8) // 22F8
++#define BN0_WF_AGG_TOP_SMCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0x2fc) // 22FC
++
++#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR0_ADDR
++#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK 0x03FF0000 // AC01_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR0_ADDR
++#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK 0x000003FF // AC00_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT 0
++
++#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR1_ADDR
++#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK 0x03FF0000 // AC03_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR1_ADDR
++#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK 0x000003FF // AC02_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT 0
++
++#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR2_ADDR
++#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK 0x03FF0000 // AC11_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR2_ADDR
++#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK 0x000003FF // AC10_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT 0
++
++#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR3_ADDR
++#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK 0x03FF0000 // AC13_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR3_ADDR
++#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK 0x000003FF // AC12_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT 0
++
++#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR4_ADDR
++#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK 0x03FF0000 // AC21_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR4_ADDR
++#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK 0x000003FF // AC20_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT 0
++
++#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR5_ADDR
++#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK 0x03FF0000 // AC23_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR5_ADDR
++#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK 0x000003FF // AC22_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT 0
++
++#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR6_ADDR
++#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK 0x03FF0000 // AC31_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR6_ADDR
++#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK 0x000003FF // AC30_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT 0
++#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR7_ADDR
++#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK 0x03FF0000 // AC33_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT 16
++#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR7_ADDR
++#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK 0x000003FF // AC32_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT 0
++
++/* DMA */
++struct queue_desc {
++ u32 hw_desc_base;
++ u16 ring_size;
++ char *const ring_info;
++};
++
++// HOST DMA
++#define WF_WFDMA_HOST_DMA0_BASE 0xd4000
++
++#define WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x200) /* 4200 */
++#define WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0X204) /* 4204 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x208) /* 4208 */
++
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR \
++ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK \
++ 0x00000008 /* RX_DMA_BUSY[3] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR \
++ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK \
++ 0x00000004 /* RX_DMA_EN[2] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR \
++ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK \
++ 0x00000002 /* TX_DMA_BUSY[1] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR \
++ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK \
++ 0x00000001 /* TX_DMA_EN[0] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
++
++
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x300) /* 4300 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x304) /* 4304 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x308) /* 4308 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x30c) /* 430C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x310) /* 4310 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x314) /* 4314 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x318) /* 4318 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x31c) /* 431C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x320) /* 4320 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x324) /* 4324 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x328) /* 4328 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x32c) /* 432C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x330) /* 4330 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x334) /* 4334 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x338) /* 4338 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x33c) /* 433C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x340) /* 4340 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x344) /* 4344 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x348) /* 4348 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x34c) /* 434C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x350) /* 4350 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x354) /* 4354 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x358) /* 4358 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x35c) /* 435C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x360) /* 4360 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x364) /* 4364 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x368) /* 4368 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x36c) /* 436C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x400) /* 4400 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x404) /* 4404 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x408) /* 4408 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x40c) /* 440C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x410) /* 4410 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x414) /* 4414 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x418) /* 4418 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x41c) /* 441C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x420) /* 4420 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x424) /* 4424 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x428) /* 4428 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x42c) /* 442C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x430) /* 4430 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x434) /* 4434 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x438) /* 4438 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x43c) /* 443C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x440) /* 4440 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x444) /* 4444 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x448) /* 4448 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x44c) /* 444C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x450) /* 4450 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x454) /* 4454 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x458) /* 4458 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x45c) /* 445c */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x460) // 4460
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x464) // 4464
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x468) // 4468
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x46c) // 446C
++
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x500) /* 4500 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x504) /* 4504 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x508) /* 4508 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x50c) /* 450C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x510) /* 4510 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x514) /* 4514 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x518) /* 4518 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x51c) /* 451C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x520) /* 4520 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x524) /* 4524 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x528) /* 4528 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x52C) /* 452C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x530) /* 4530 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x534) /* 4534 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x538) /* 4538 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x53C) /* 453C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x540) /* 4540 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x544) /* 4544 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x548) /* 4548 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x54c) /* 454C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x550) /* 4550 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x554) /* 4554 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x558) /* 4558 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x55c) /* 455C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x560) /* 4560 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x564) /* 4564 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x568) /* 4568 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x56c) /* 456C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x570) /* 4570 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x574) /* 4574 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x578) /* 4578 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x57c) /* 457C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x580) /* 4580 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x584) /* 4584 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x588) /* 4588 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x58c) /* 458C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x590) /* 4590 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL1_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x594) /* 4594 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL2_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x598) /* 4598 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL3_ADDR \
++ (WF_WFDMA_HOST_DMA0_BASE + 0x59c) /* 459C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5a0) // 45A0
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5a4) // 45A4
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5a8) // 45A8
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5ac) // 45AC
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5b0) // 45B0
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5b4) // 45B4
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5b8) // 45B8
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5bc) // 45BC
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5C0) // 45C0
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5C4) // 45C4
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5C8) // 45C8
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5CC) // 45CC
++
++// HOST PCIE1 DMA
++#define WF_WFDMA_HOST_DMA0_PCIE1_BASE 0xd8000
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x200) // 8200
++#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0X204) // 8204
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x208) // 8208
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_PDMA_BT_SIZE_SHFT 4
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK 0x00000008
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK 0x00000004
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK 0x00000002
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK 0x00000001
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x450) // 8450
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x454) // 8454
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x458) // 8458
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x45c) // 845C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x460) // 8460
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x464) // 8464
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x468) // 8468
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x46c) // 846C
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x530) // 8530
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x534) // 8534
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x538) // 8538
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x53C) // 853C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x550) // 8550
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x554) // 8554
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x558) // 8558
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x55c) // 855C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x560) // 8560
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x564) // 8564
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x568) // 8568
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x56c) // 856C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x570) // 8570
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
++//MCU DMA
++//#define WF_WFDMA_MCU_DMA0_BASE 0x02000
++#define WF_WFDMA_MCU_DMA0_BASE 0x54000000
++
++#define WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x200) // 0200
++#define WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0X204) // 0204
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x208) // 0208
++
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK 0x00000008 // RX_DMA_BUSY[3]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK 0x00000004 // RX_DMA_EN[2]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK 0x00000002 // TX_DMA_BUSY[1]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK 0x00000001 // TX_DMA_EN[0]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
++
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x300) // 0300
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x304) // 0304
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x308) // 0308
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x30c) // 030C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x310) // 0310
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x314) // 0314
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x318) // 0318
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x31c) // 031C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x320) // 0320
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x324) // 0324
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x328) // 0328
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x32c) // 032C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x330) // 0330
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x334) // 0334
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x338) // 0338
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x33c) // 033C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x340) // 0340
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x344) // 0344
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x348) // 0348
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x34c) // 034C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x350) // 0350
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x354) // 0354
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x358) // 0358
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x35c) // 035C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x360) // 0360
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x364) // 0364
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x368) // 0368
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x36c) // 036C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x370) // 0370
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x374) // 0374
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x378) // 0378
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x37c) // 037C
++
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x500) // 0500
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x504) // 0504
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x508) // 0508
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x50c) // 050C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x510) // 0510
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x514) // 0514
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x518) // 0518
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x51c) // 051C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x520) // 0520
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x524) // 0524
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x528) // 0528
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x52C) // 052C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x530) // 0530
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x534) // 0534
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x538) // 0538
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x53C) // 053C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x540) // 0540
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x544) // 0544
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x548) // 0548
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x54C) // 054C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x550) // 0550
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x554) // 0554
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x558) // 0558
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x55C) // 055C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x560) // 0560
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x564) // 0564
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x568) // 0568
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x56c) // 056C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x570) // 0570
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x574) // 0574
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x578) // 0578
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x57c) // 057C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x580) // 0580
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x584) // 0584
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x588) // 0588
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x58c) // 058C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x590) // 0590
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x594) // 0594
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x598) // 0598
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x59c) // 059C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5A0) // 05A0
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5A4) // 05A4
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5A8) // 05A8
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5Ac) // 05AC
++
++// MEM DMA
++#define WF_WFDMA_MEM_DMA_BASE 0x58000000
++
++#define WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x200) // 0200
++#define WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR (WF_WFDMA_MEM_DMA_BASE + 0X204) // 0204
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x208) // 0208
++
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK 0x00000008 // RX_DMA_BUSY[3]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK 0x00000004 // RX_DMA_EN[2]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK 0x00000002 // TX_DMA_BUSY[1]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK 0x00000001 // TX_DMA_EN[0]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
++
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x300) // 0300
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x304) // 0304
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x308) // 0308
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x30c) // 030C
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x310) // 0310
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x314) // 0314
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x318) // 0318
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x31c) // 031C
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x320) // 0320
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x324) // 0324
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x328) // 0328
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x32c) // 032C
++
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x500) // 0500
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x504) // 0504
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x508) // 0508
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x50c) // 050C
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x510) // 0510
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x514) // 0514
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x518) // 0518
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x51c) // 051C
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x520) // 0520
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x524) // 0524
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x528) // 0528
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x52C) // 052C
++
++/* MIB */
++#define WF_UMIB_TOP_BASE 0x820cd000
++#define BN0_WF_MIB_TOP_BASE 0x820ed000
++#define BN1_WF_MIB_TOP_BASE 0x820fd000
++#define IP1_BN0_WF_MIB_TOP_BASE 0x830ed000
++
++#define WF_UMIB_TOP_B0BROCR_ADDR (WF_UMIB_TOP_BASE + 0x484) // D484
++#define WF_UMIB_TOP_B0BRBCR_ADDR (WF_UMIB_TOP_BASE + 0x4D4) // D4D4
++#define WF_UMIB_TOP_B0BRDCR_ADDR (WF_UMIB_TOP_BASE + 0x524) // D524
++#define WF_UMIB_TOP_B1BROCR_ADDR (WF_UMIB_TOP_BASE + 0x5E8) // D5E8
++#define WF_UMIB_TOP_B2BROCR_ADDR (WF_UMIB_TOP_BASE + 0x74C) // D74C
++
++#define BN0_WF_MIB_TOP_M0SCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x000) // D000
++#define BN0_WF_MIB_TOP_M0SDR6_ADDR (BN0_WF_MIB_TOP_BASE + 0x020) // D020
++#define BN0_WF_MIB_TOP_M0SDR9_ADDR (BN0_WF_MIB_TOP_BASE + 0x024) // D024
++#define BN0_WF_MIB_TOP_M0SDR18_ADDR (BN0_WF_MIB_TOP_BASE + 0x030) // D030
++#define BN0_WF_MIB_TOP_BTOCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x400) // D400
++#define BN0_WF_MIB_TOP_BTBCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x450) // D450
++#define BN0_WF_MIB_TOP_BTDCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x590) // D590
++#define BN0_WF_MIB_TOP_BTCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x5A0) // D5A0
++#define BN0_WF_MIB_TOP_RVSR0_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RVSR0))
++
++#define BN0_WF_MIB_TOP_TSCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x6B0) // D6B0
++#define BN0_WF_MIB_TOP_TSCR3_ADDR (BN0_WF_MIB_TOP_BASE + 0x6BC) // D6BC
++#define BN0_WF_MIB_TOP_TSCR4_ADDR (BN0_WF_MIB_TOP_BASE + 0x6C0) // D6C0
++#define BN0_WF_MIB_TOP_TSCR5_ADDR (BN0_WF_MIB_TOP_BASE + 0x6C4) // D6C4
++#define BN0_WF_MIB_TOP_TSCR6_ADDR (BN0_WF_MIB_TOP_BASE + 0x6C8) // D6C8
++#define BN0_WF_MIB_TOP_TSCR7_ADDR (BN0_WF_MIB_TOP_BASE + 0x6D0) // D6D0
++#define BN0_WF_MIB_TOP_TSCR8_ADDR (BN0_WF_MIB_TOP_BASE + 0x6CC) // D6CC
++
++#define BN0_WF_MIB_TOP_TBCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x6EC) // D6EC
++#define BN0_WF_MIB_TOP_TBCR1_ADDR (BN0_WF_MIB_TOP_BASE + 0x6F0) // D6F0
++#define BN0_WF_MIB_TOP_TBCR2_ADDR (BN0_WF_MIB_TOP_BASE + 0x6F4) // D6F4
++#define BN0_WF_MIB_TOP_TBCR3_ADDR (BN0_WF_MIB_TOP_BASE + 0x6F8) // D6F8
++#define BN0_WF_MIB_TOP_TBCR4_ADDR (BN0_WF_MIB_TOP_BASE + 0x6FC) // D6FC
++
++#define BN0_WF_MIB_TOP_TDRCR0_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR0))
++#define BN0_WF_MIB_TOP_TDRCR1_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR1))
++#define BN0_WF_MIB_TOP_TDRCR2_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR2))
++#define BN0_WF_MIB_TOP_TDRCR3_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR3))
++#define BN0_WF_MIB_TOP_TDRCR4_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR4))
++
++#define BN0_WF_MIB_TOP_BTSCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x5E0) // D5E0
++#define BN0_WF_MIB_TOP_BTSCR1_ADDR (BN0_WF_MIB_TOP_BASE + 0x5F0) // D5F0
++#define BN0_WF_MIB_TOP_BTSCR2_ADDR (BN0_WF_MIB_TOP_BASE + 0x600) // D600
++#define BN0_WF_MIB_TOP_BTSCR3_ADDR (BN0_WF_MIB_TOP_BASE + 0x610) // D610
++#define BN0_WF_MIB_TOP_BTSCR4_ADDR (BN0_WF_MIB_TOP_BASE + 0x620) // D620
++#define BN0_WF_MIB_TOP_BTSCR5_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR5))
++#define BN0_WF_MIB_TOP_BTSCR6_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR6))
++
++#define BN0_WF_MIB_TOP_RSCR1_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR1))
++#define BN0_WF_MIB_TOP_BSCR2_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BSCR2))
++#define BN0_WF_MIB_TOP_TSCR18_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TSCR18))
++
++#define BN0_WF_MIB_TOP_MSR0_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR0))
++#define BN0_WF_MIB_TOP_MSR1_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR1))
++#define BN0_WF_MIB_TOP_MSR2_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR2))
++#define BN0_WF_MIB_TOP_MCTR5_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR5))
++#define BN0_WF_MIB_TOP_MCTR6_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR6))
++
++#define BN0_WF_MIB_TOP_RSCR26_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_RSCR26))
++#define BN0_WF_MIB_TOP_RSCR27_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR27))
++#define BN0_WF_MIB_TOP_RSCR28_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR28))
++#define BN0_WF_MIB_TOP_RSCR31_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR31))
++#define BN0_WF_MIB_TOP_RSCR33_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR33))
++#define BN0_WF_MIB_TOP_RSCR35_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR35))
++#define BN0_WF_MIB_TOP_RSCR36_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR36))
++
++#define BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK 0xFFFFFFFF // AMPDU_MPDU_COUNT[31..0]
++#define BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK 0xFFFFFFFF // AMPDU_ACKED_COUNT[31..0]
++#define BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK 0x0000FFFF // CHANNEL_IDLE_COUNT[15..0]
++#define BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK 0x00FFFFFF // CCA_NAV_TX_TIME[23..0]
++#define BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK 0xFFFFFFFF // RX_MDRDY_COUNT[31..0]
++#define BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK 0xFFFFFFFF // CCK_MDRDY_TIME[31..0]
++#define BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK 0xFFFFFFFF // OFDM_LG_MIXED_VHT_MDRDY_TIME[31..0]
++#define BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK 0xFFFFFFFF // OFDM_GREEN_MDRDY_TIME[31..0]
++#define BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK 0xFFFFFFFF // P_CCA_TIME[31..0]
++#define BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK 0xFFFFFFFF // S_CCA_TIME[31..0]
++#define BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK 0x00FFFFFF // P_ED_TIME[23..0]
++#define BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK 0xFFFFFFFF // BEACONTXCOUNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK 0xFFFFFFFF // TX_20MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK 0xFFFFFFFF // TX_40MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK 0xFFFFFFFF // TX_80MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK 0xFFFFFFFF // TX_160MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK 0xFFFFFFFF // TX_320MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK 0xFFFFFFFF // MUBF_TX_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK 0xFFFFFFFF // VEC_MISS_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK 0xFFFFFFFF // DELIMITER_FAIL_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK 0xFFFFFFFF // RX_FCS_ERROR_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK 0xFFFFFFFF // RX_FIFO_FULL_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK 0xFFFFFFFF // RX_LEN_MISMATCH[31..0]
++#define BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK 0xFFFFFFFF // RX_MPDU_COUNT[31..0]
++#define BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK 0xFFFFFFFF // RTSTXCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK 0xFFFFFFFF // RTSRETRYCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK 0xFFFFFFFF // BAMISSCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK 0xFFFFFFFF // ACKFAILCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK 0xFFFFFFFF // FRAMERETRYCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK 0xFFFFFFFF // FRAMERETRY2COUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK 0xFFFFFFFF // FRAMERETRY3COUNTn[31..0]
++#define BN0_WF_MIB_TOP_TRARC0_ADDR (BN0_WF_MIB_TOP_BASE + 0x0B0) // D0B0
++#define BN0_WF_MIB_TOP_TRARC1_ADDR (BN0_WF_MIB_TOP_BASE + 0x0B4) // D0B4
++#define BN0_WF_MIB_TOP_TRARC2_ADDR (BN0_WF_MIB_TOP_BASE + 0x0B8) // D0B8
++#define BN0_WF_MIB_TOP_TRARC3_ADDR (BN0_WF_MIB_TOP_BASE + 0x0BC) // D0BC
++#define BN0_WF_MIB_TOP_TRARC4_ADDR (BN0_WF_MIB_TOP_BASE + 0x0C0) // D0C0
++#define BN0_WF_MIB_TOP_TRARC5_ADDR (BN0_WF_MIB_TOP_BASE + 0x0C4) // D0C4
++#define BN0_WF_MIB_TOP_TRARC6_ADDR (BN0_WF_MIB_TOP_BASE + 0x0C8) // D0C8
++#define BN0_WF_MIB_TOP_TRARC7_ADDR (BN0_WF_MIB_TOP_BASE + 0x0CC) // D0CC
++
++#define BN0_WF_MIB_TOP_TRDR0_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR0))
++#define BN0_WF_MIB_TOP_TRDR1_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_TRDR1))
++#define BN0_WF_MIB_TOP_TRDR2_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR2))
++#define BN0_WF_MIB_TOP_TRDR3_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR3))
++#define BN0_WF_MIB_TOP_TRDR4_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR4))
++#define BN0_WF_MIB_TOP_TRDR5_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR5))
++#define BN0_WF_MIB_TOP_TRDR6_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR6))
++#define BN0_WF_MIB_TOP_TRDR7_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR7))
++#define BN0_WF_MIB_TOP_TRDR8_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR8))
++#define BN0_WF_MIB_TOP_TRDR9_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR9))
++#define BN0_WF_MIB_TOP_TRDR10_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR10))
++#define BN0_WF_MIB_TOP_TRDR11_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR11))
++#define BN0_WF_MIB_TOP_TRDR12_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR12))
++#define BN0_WF_MIB_TOP_TRDR13_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR13))
++#define BN0_WF_MIB_TOP_TRDR14_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR14))
++#define BN0_WF_MIB_TOP_TRDR15_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR15))
++
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_ADDR BN0_WF_MIB_TOP_TRARC0_ADDR
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK 0x03FF0000 // AGG_RANG_SEL_1[25..16]
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT 16
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_ADDR BN0_WF_MIB_TOP_TRARC0_ADDR
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK 0x000003FF // AGG_RANG_SEL_0[9..0]
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT 0
++
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_ADDR BN0_WF_MIB_TOP_TRARC1_ADDR
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK 0x03FF0000 // AGG_RANG_SEL_3[25..16]
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT 16
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_ADDR BN0_WF_MIB_TOP_TRARC1_ADDR
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK 0x000003FF // AGG_RANG_SEL_2[9..0]
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT 0
++
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_ADDR BN0_WF_MIB_TOP_TRARC2_ADDR
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK 0x03FF0000 // AGG_RANG_SEL_5[25..16]
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT 16
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_ADDR BN0_WF_MIB_TOP_TRARC2_ADDR
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK 0x000003FF // AGG_RANG_SEL_4[9..0]
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT 0
++
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_ADDR BN0_WF_MIB_TOP_TRARC3_ADDR
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK 0x03FF0000 // AGG_RANG_SEL_7[25..16]
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT 16
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_ADDR BN0_WF_MIB_TOP_TRARC3_ADDR
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK 0x000003FF // AGG_RANG_SEL_6[9..0]
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT 0
++
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_ADDR BN0_WF_MIB_TOP_TRARC4_ADDR
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK 0x03FF0000 // AGG_RANG_SEL_9[25..16]
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT 16
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_ADDR BN0_WF_MIB_TOP_TRARC4_ADDR
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK 0x000003FF // AGG_RANG_SEL_8[9..0]
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT 0
++
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_ADDR BN0_WF_MIB_TOP_TRARC5_ADDR
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK 0x03FF0000 // AGG_RANG_SEL_11[25..16]
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT 16
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_ADDR BN0_WF_MIB_TOP_TRARC5_ADDR
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK 0x000003FF // AGG_RANG_SEL_10[9..0]
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT 0
++
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_ADDR BN0_WF_MIB_TOP_TRARC6_ADDR
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK 0x03FF0000 // AGG_RANG_SEL_13[25..16]
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT 16
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_ADDR BN0_WF_MIB_TOP_TRARC6_ADDR
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK 0x000003FF // AGG_RANG_SEL_12[9..0]
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT 0
++
++#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_ADDR BN0_WF_MIB_TOP_TRARC7_ADDR
++#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK 0x000003FF // AGG_RANG_SEL_14[9..0]
++#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT 0
++
++/* RRO TOP */
++#define WF_RRO_TOP_BASE 0xA000 /*0x820C2000 */
++#define WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR (WF_RRO_TOP_BASE + 0x40) // 2040
++ //
++/* WTBL */
++enum mt7996_wtbl_type {
++ WTBL_TYPE_LMAC, /* WTBL in LMAC */
++ WTBL_TYPE_UMAC, /* WTBL in UMAC */
++ WTBL_TYPE_KEY, /* Key Table */
++ MAX_NUM_WTBL_TYPE
++};
++
++struct berse_wtbl_parse {
++ u8 *name;
++ u32 mask;
++ u32 shift;
++ u8 new_line;
++};
++
++enum muar_idx {
++ MUAR_INDEX_OWN_MAC_ADDR_0 = 0,
++ MUAR_INDEX_OWN_MAC_ADDR_1,
++ MUAR_INDEX_OWN_MAC_ADDR_2,
++ MUAR_INDEX_OWN_MAC_ADDR_3,
++ MUAR_INDEX_OWN_MAC_ADDR_4,
++ MUAR_INDEX_OWN_MAC_ADDR_BC_MC = 0xE,
++ MUAR_INDEX_UNMATCHED = 0xF,
++ MUAR_INDEX_OWN_MAC_ADDR_11 = 0x11,
++ MUAR_INDEX_OWN_MAC_ADDR_12,
++ MUAR_INDEX_OWN_MAC_ADDR_13,
++ MUAR_INDEX_OWN_MAC_ADDR_14,
++ MUAR_INDEX_OWN_MAC_ADDR_15,
++ MUAR_INDEX_OWN_MAC_ADDR_16,
++ MUAR_INDEX_OWN_MAC_ADDR_17,
++ MUAR_INDEX_OWN_MAC_ADDR_18,
++ MUAR_INDEX_OWN_MAC_ADDR_19,
++ MUAR_INDEX_OWN_MAC_ADDR_1A,
++ MUAR_INDEX_OWN_MAC_ADDR_1B,
++ MUAR_INDEX_OWN_MAC_ADDR_1C,
++ MUAR_INDEX_OWN_MAC_ADDR_1D,
++ MUAR_INDEX_OWN_MAC_ADDR_1E,
++ MUAR_INDEX_OWN_MAC_ADDR_1F,
++ MUAR_INDEX_OWN_MAC_ADDR_20,
++ MUAR_INDEX_OWN_MAC_ADDR_21,
++ MUAR_INDEX_OWN_MAC_ADDR_22,
++ MUAR_INDEX_OWN_MAC_ADDR_23,
++ MUAR_INDEX_OWN_MAC_ADDR_24,
++ MUAR_INDEX_OWN_MAC_ADDR_25,
++ MUAR_INDEX_OWN_MAC_ADDR_26,
++ MUAR_INDEX_OWN_MAC_ADDR_27,
++ MUAR_INDEX_OWN_MAC_ADDR_28,
++ MUAR_INDEX_OWN_MAC_ADDR_29,
++ MUAR_INDEX_OWN_MAC_ADDR_2A,
++ MUAR_INDEX_OWN_MAC_ADDR_2B,
++ MUAR_INDEX_OWN_MAC_ADDR_2C,
++ MUAR_INDEX_OWN_MAC_ADDR_2D,
++ MUAR_INDEX_OWN_MAC_ADDR_2E,
++ MUAR_INDEX_OWN_MAC_ADDR_2F
++};
++
++enum cipher_suit {
++ IGTK_CIPHER_SUIT_NONE = 0,
++ IGTK_CIPHER_SUIT_BIP,
++ IGTK_CIPHER_SUIT_BIP_256
++};
++
++#define LWTBL_LEN_IN_DW 36
++#define UWTBL_LEN_IN_DW 16
++
++#define MT_DBG_WTBL_BASE 0x820D8000
++
++#define MT_DBG_WTBLON_TOP_BASE 0x820d4000
++#define MT_DBG_WTBLON_TOP_WDUCR_ADDR (MT_DBG_WTBLON_TOP_BASE + 0x0370) // 4370
++#define MT_DBG_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0)
++
++#define MT_DBG_UWTBL_TOP_BASE 0x820c4000
++#define MT_DBG_UWTBL_TOP_WDUCR_ADDR (MT_DBG_UWTBL_TOP_BASE + 0x0104) // 4104
++#define MT_DBG_UWTBL_TOP_WDUCR_GROUP GENMASK(5, 0)
++#define MT_DBG_UWTBL_TOP_WDUCR_TARGET BIT(31)
++
++#define LWTBL_IDX2BASE_ID GENMASK(14, 8)
++#define LWTBL_IDX2BASE_DW GENMASK(7, 2)
++#define LWTBL_IDX2BASE(_id, _dw) (MT_DBG_WTBL_BASE | \
++ FIELD_PREP(LWTBL_IDX2BASE_ID, _id) | \
++ FIELD_PREP(LWTBL_IDX2BASE_DW, _dw))
++
++#define UWTBL_IDX2BASE_ID GENMASK(12, 6)
++#define UWTBL_IDX2BASE_DW GENMASK(5, 2)
++#define UWTBL_IDX2BASE(_id, _dw) (MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
++ FIELD_PREP(UWTBL_IDX2BASE_ID, _id) | \
++ FIELD_PREP(UWTBL_IDX2BASE_DW, _dw))
++
++#define KEYTBL_IDX2BASE_KEY GENMASK(12, 6)
++#define KEYTBL_IDX2BASE_DW GENMASK(5, 2)
++#define KEYTBL_IDX2BASE(_key, _dw) (MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
++ FIELD_PREP(KEYTBL_IDX2BASE_KEY, _key) | \
++ FIELD_PREP(KEYTBL_IDX2BASE_DW, _dw))
++
++// UMAC WTBL
++// DW0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__DW 0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__ADDR 0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__MASK 0x0000ffff // 15- 0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__SHIFT 0
++#define WF_UWTBL_OWN_MLD_ID_DW 0
++#define WF_UWTBL_OWN_MLD_ID_ADDR 0
++#define WF_UWTBL_OWN_MLD_ID_MASK 0x003f0000 // 21-16
++#define WF_UWTBL_OWN_MLD_ID_SHIFT 16
++// DW1
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__DW 1
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__ADDR 4
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__MASK 0xffffffff // 31- 0
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__SHIFT 0
++// DW2
++#define WF_UWTBL_PN_31_0__DW 2
++#define WF_UWTBL_PN_31_0__ADDR 8
++#define WF_UWTBL_PN_31_0__MASK 0xffffffff // 31- 0
++#define WF_UWTBL_PN_31_0__SHIFT 0
++// DW3
++#define WF_UWTBL_PN_47_32__DW 3
++#define WF_UWTBL_PN_47_32__ADDR 12
++#define WF_UWTBL_PN_47_32__MASK 0x0000ffff // 15- 0
++#define WF_UWTBL_PN_47_32__SHIFT 0
++#define WF_UWTBL_COM_SN_DW 3
++#define WF_UWTBL_COM_SN_ADDR 12
++#define WF_UWTBL_COM_SN_MASK 0x0fff0000 // 27-16
++#define WF_UWTBL_COM_SN_SHIFT 16
++// DW4
++#define WF_UWTBL_TID0_SN_DW 4
++#define WF_UWTBL_TID0_SN_ADDR 16
++#define WF_UWTBL_TID0_SN_MASK 0x00000fff // 11- 0
++#define WF_UWTBL_TID0_SN_SHIFT 0
++#define WF_UWTBL_RX_BIPN_31_0__DW 4
++#define WF_UWTBL_RX_BIPN_31_0__ADDR 16
++#define WF_UWTBL_RX_BIPN_31_0__MASK 0xffffffff // 31- 0
++#define WF_UWTBL_RX_BIPN_31_0__SHIFT 0
++#define WF_UWTBL_TID1_SN_DW 4
++#define WF_UWTBL_TID1_SN_ADDR 16
++#define WF_UWTBL_TID1_SN_MASK 0x00fff000 // 23-12
++#define WF_UWTBL_TID1_SN_SHIFT 12
++#define WF_UWTBL_TID2_SN_7_0__DW 4
++#define WF_UWTBL_TID2_SN_7_0__ADDR 16
++#define WF_UWTBL_TID2_SN_7_0__MASK 0xff000000 // 31-24
++#define WF_UWTBL_TID2_SN_7_0__SHIFT 24
++// DW5
++#define WF_UWTBL_TID2_SN_11_8__DW 5
++#define WF_UWTBL_TID2_SN_11_8__ADDR 20
++#define WF_UWTBL_TID2_SN_11_8__MASK 0x0000000f // 3- 0
++#define WF_UWTBL_TID2_SN_11_8__SHIFT 0
++#define WF_UWTBL_RX_BIPN_47_32__DW 5
++#define WF_UWTBL_RX_BIPN_47_32__ADDR 20
++#define WF_UWTBL_RX_BIPN_47_32__MASK 0x0000ffff // 15- 0
++#define WF_UWTBL_RX_BIPN_47_32__SHIFT 0
++#define WF_UWTBL_TID3_SN_DW 5
++#define WF_UWTBL_TID3_SN_ADDR 20
++#define WF_UWTBL_TID3_SN_MASK 0x0000fff0 // 15- 4
++#define WF_UWTBL_TID3_SN_SHIFT 4
++#define WF_UWTBL_TID4_SN_DW 5
++#define WF_UWTBL_TID4_SN_ADDR 20
++#define WF_UWTBL_TID4_SN_MASK 0x0fff0000 // 27-16
++#define WF_UWTBL_TID4_SN_SHIFT 16
++#define WF_UWTBL_TID5_SN_3_0__DW 5
++#define WF_UWTBL_TID5_SN_3_0__ADDR 20
++#define WF_UWTBL_TID5_SN_3_0__MASK 0xf0000000 // 31-28
++#define WF_UWTBL_TID5_SN_3_0__SHIFT 28
++// DW6
++#define WF_UWTBL_TID5_SN_11_4__DW 6
++#define WF_UWTBL_TID5_SN_11_4__ADDR 24
++#define WF_UWTBL_TID5_SN_11_4__MASK 0x000000ff // 7- 0
++#define WF_UWTBL_TID5_SN_11_4__SHIFT 0
++#define WF_UWTBL_KEY_LOC2_DW 6
++#define WF_UWTBL_KEY_LOC2_ADDR 24
++#define WF_UWTBL_KEY_LOC2_MASK 0x00001fff // 12- 0
++#define WF_UWTBL_KEY_LOC2_SHIFT 0
++#define WF_UWTBL_TID6_SN_DW 6
++#define WF_UWTBL_TID6_SN_ADDR 24
++#define WF_UWTBL_TID6_SN_MASK 0x000fff00 // 19- 8
++#define WF_UWTBL_TID6_SN_SHIFT 8
++#define WF_UWTBL_TID7_SN_DW 6
++#define WF_UWTBL_TID7_SN_ADDR 24
++#define WF_UWTBL_TID7_SN_MASK 0xfff00000 // 31-20
++#define WF_UWTBL_TID7_SN_SHIFT 20
++// DW7
++#define WF_UWTBL_KEY_LOC0_DW 7
++#define WF_UWTBL_KEY_LOC0_ADDR 28
++#define WF_UWTBL_KEY_LOC0_MASK 0x00001fff // 12- 0
++#define WF_UWTBL_KEY_LOC0_SHIFT 0
++#define WF_UWTBL_KEY_LOC1_DW 7
++#define WF_UWTBL_KEY_LOC1_ADDR 28
++#define WF_UWTBL_KEY_LOC1_MASK 0x1fff0000 // 28-16
++#define WF_UWTBL_KEY_LOC1_SHIFT 16
++// DW8
++#define WF_UWTBL_AMSDU_CFG_DW 8
++#define WF_UWTBL_AMSDU_CFG_ADDR 32
++#define WF_UWTBL_AMSDU_CFG_MASK 0x00000fff // 11- 0
++#define WF_UWTBL_AMSDU_CFG_SHIFT 0
++#define WF_UWTBL_SEC_ADDR_MODE_DW 8
++#define WF_UWTBL_SEC_ADDR_MODE_ADDR 32
++#define WF_UWTBL_SEC_ADDR_MODE_MASK 0x00300000 // 21-20
++#define WF_UWTBL_SEC_ADDR_MODE_SHIFT 20
++#define WF_UWTBL_WMM_Q_DW 8
++#define WF_UWTBL_WMM_Q_ADDR 32
++#define WF_UWTBL_WMM_Q_MASK 0x06000000 // 26-25
++#define WF_UWTBL_WMM_Q_SHIFT 25
++#define WF_UWTBL_QOS_DW 8
++#define WF_UWTBL_QOS_ADDR 32
++#define WF_UWTBL_QOS_MASK 0x08000000 // 27-27
++#define WF_UWTBL_QOS_SHIFT 27
++#define WF_UWTBL_HT_DW 8
++#define WF_UWTBL_HT_ADDR 32
++#define WF_UWTBL_HT_MASK 0x10000000 // 28-28
++#define WF_UWTBL_HT_SHIFT 28
++#define WF_UWTBL_HDRT_MODE_DW 8
++#define WF_UWTBL_HDRT_MODE_ADDR 32
++#define WF_UWTBL_HDRT_MODE_MASK 0x20000000 // 29-29
++#define WF_UWTBL_HDRT_MODE_SHIFT 29
++// DW9
++#define WF_UWTBL_RELATED_IDX0_DW 9
++#define WF_UWTBL_RELATED_IDX0_ADDR 36
++#define WF_UWTBL_RELATED_IDX0_MASK 0x00000fff // 11- 0
++#define WF_UWTBL_RELATED_IDX0_SHIFT 0
++#define WF_UWTBL_RELATED_BAND0_DW 9
++#define WF_UWTBL_RELATED_BAND0_ADDR 36
++#define WF_UWTBL_RELATED_BAND0_MASK 0x00003000 // 13-12
++#define WF_UWTBL_RELATED_BAND0_SHIFT 12
++#define WF_UWTBL_PRIMARY_MLD_BAND_DW 9
++#define WF_UWTBL_PRIMARY_MLD_BAND_ADDR 36
++#define WF_UWTBL_PRIMARY_MLD_BAND_MASK 0x0000c000 // 15-14
++#define WF_UWTBL_PRIMARY_MLD_BAND_SHIFT 14
++#define WF_UWTBL_RELATED_IDX1_DW 9
++#define WF_UWTBL_RELATED_IDX1_ADDR 36
++#define WF_UWTBL_RELATED_IDX1_MASK 0x0fff0000 // 27-16
++#define WF_UWTBL_RELATED_IDX1_SHIFT 16
++#define WF_UWTBL_RELATED_BAND1_DW 9
++#define WF_UWTBL_RELATED_BAND1_ADDR 36
++#define WF_UWTBL_RELATED_BAND1_MASK 0x30000000 // 29-28
++#define WF_UWTBL_RELATED_BAND1_SHIFT 28
++#define WF_UWTBL_SECONDARY_MLD_BAND_DW 9
++#define WF_UWTBL_SECONDARY_MLD_BAND_ADDR 36
++#define WF_UWTBL_SECONDARY_MLD_BAND_MASK 0xc0000000 // 31-30
++#define WF_UWTBL_SECONDARY_MLD_BAND_SHIFT 30
++
++/* LMAC WTBL */
++// DW0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__DW 0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__ADDR 0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__MASK \
++ 0x0000ffff // 15- 0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__SHIFT 0
++#define WF_LWTBL_MUAR_DW 0
++#define WF_LWTBL_MUAR_ADDR 0
++#define WF_LWTBL_MUAR_MASK \
++ 0x003f0000 // 21-16
++#define WF_LWTBL_MUAR_SHIFT 16
++#define WF_LWTBL_RCA1_DW 0
++#define WF_LWTBL_RCA1_ADDR 0
++#define WF_LWTBL_RCA1_MASK \
++ 0x00400000 // 22-22
++#define WF_LWTBL_RCA1_SHIFT 22
++#define WF_LWTBL_KID_DW 0
++#define WF_LWTBL_KID_ADDR 0
++#define WF_LWTBL_KID_MASK \
++ 0x01800000 // 24-23
++#define WF_LWTBL_KID_SHIFT 23
++#define WF_LWTBL_RCID_DW 0
++#define WF_LWTBL_RCID_ADDR 0
++#define WF_LWTBL_RCID_MASK \
++ 0x02000000 // 25-25
++#define WF_LWTBL_RCID_SHIFT 25
++#define WF_LWTBL_BAND_DW 0
++#define WF_LWTBL_BAND_ADDR 0
++#define WF_LWTBL_BAND_MASK \
++ 0x0c000000 // 27-26
++#define WF_LWTBL_BAND_SHIFT 26
++#define WF_LWTBL_RV_DW 0
++#define WF_LWTBL_RV_ADDR 0
++#define WF_LWTBL_RV_MASK \
++ 0x10000000 // 28-28
++#define WF_LWTBL_RV_SHIFT 28
++#define WF_LWTBL_RCA2_DW 0
++#define WF_LWTBL_RCA2_ADDR 0
++#define WF_LWTBL_RCA2_MASK \
++ 0x20000000 // 29-29
++#define WF_LWTBL_RCA2_SHIFT 29
++#define WF_LWTBL_WPI_FLAG_DW 0
++#define WF_LWTBL_WPI_FLAG_ADDR 0
++#define WF_LWTBL_WPI_FLAG_MASK \
++ 0x40000000 // 30-30
++#define WF_LWTBL_WPI_FLAG_SHIFT 30
++// DW1
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__DW 1
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__ADDR 4
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__MASK \
++ 0xffffffff // 31- 0
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__SHIFT 0
++// DW2
++#define WF_LWTBL_AID_DW 2
++#define WF_LWTBL_AID_ADDR 8
++#define WF_LWTBL_AID_MASK \
++ 0x00000fff // 11- 0
++#define WF_LWTBL_AID_SHIFT 0
++#define WF_LWTBL_GID_SU_DW 2
++#define WF_LWTBL_GID_SU_ADDR 8
++#define WF_LWTBL_GID_SU_MASK \
++ 0x00001000 // 12-12
++#define WF_LWTBL_GID_SU_SHIFT 12
++#define WF_LWTBL_SPP_EN_DW 2
++#define WF_LWTBL_SPP_EN_ADDR 8
++#define WF_LWTBL_SPP_EN_MASK \
++ 0x00002000 // 13-13
++#define WF_LWTBL_SPP_EN_SHIFT 13
++#define WF_LWTBL_WPI_EVEN_DW 2
++#define WF_LWTBL_WPI_EVEN_ADDR 8
++#define WF_LWTBL_WPI_EVEN_MASK \
++ 0x00004000 // 14-14
++#define WF_LWTBL_WPI_EVEN_SHIFT 14
++#define WF_LWTBL_AAD_OM_DW 2
++#define WF_LWTBL_AAD_OM_ADDR 8
++#define WF_LWTBL_AAD_OM_MASK \
++ 0x00008000 // 15-15
++#define WF_LWTBL_AAD_OM_SHIFT 15
++/* kite DW2 field bit 13-14 */
++#define WF_LWTBL_DUAL_PTEC_EN_DW 2
++#define WF_LWTBL_DUAL_PTEC_EN_ADDR 8
++#define WF_LWTBL_DUAL_PTEC_EN_MASK \
++ 0x00002000 // 13-13
++#define WF_LWTBL_DUAL_PTEC_EN_SHIFT 13
++#define WF_LWTBL_DUAL_CTS_CAP_DW 2
++#define WF_LWTBL_DUAL_CTS_CAP_ADDR 8
++#define WF_LWTBL_DUAL_CTS_CAP_MASK \
++ 0x00004000 // 14-14
++#define WF_LWTBL_DUAL_CTS_CAP_SHIFT 14
++#define WF_LWTBL_CIPHER_SUIT_PGTK_DW 2
++#define WF_LWTBL_CIPHER_SUIT_PGTK_ADDR 8
++#define WF_LWTBL_CIPHER_SUIT_PGTK_MASK \
++ 0x001f0000 // 20-16
++#define WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT 16
++#define WF_LWTBL_FD_DW 2
++#define WF_LWTBL_FD_ADDR 8
++#define WF_LWTBL_FD_MASK \
++ 0x00200000 // 21-21
++#define WF_LWTBL_FD_SHIFT 21
++#define WF_LWTBL_TD_DW 2
++#define WF_LWTBL_TD_ADDR 8
++#define WF_LWTBL_TD_MASK \
++ 0x00400000 // 22-22
++#define WF_LWTBL_TD_SHIFT 22
++#define WF_LWTBL_SW_DW 2
++#define WF_LWTBL_SW_ADDR 8
++#define WF_LWTBL_SW_MASK \
++ 0x00800000 // 23-23
++#define WF_LWTBL_SW_SHIFT 23
++#define WF_LWTBL_UL_DW 2
++#define WF_LWTBL_UL_ADDR 8
++#define WF_LWTBL_UL_MASK \
++ 0x01000000 // 24-24
++#define WF_LWTBL_UL_SHIFT 24
++#define WF_LWTBL_TX_PS_DW 2
++#define WF_LWTBL_TX_PS_ADDR 8
++#define WF_LWTBL_TX_PS_MASK \
++ 0x02000000 // 25-25
++#define WF_LWTBL_TX_PS_SHIFT 25
++#define WF_LWTBL_QOS_DW 2
++#define WF_LWTBL_QOS_ADDR 8
++#define WF_LWTBL_QOS_MASK \
++ 0x04000000 // 26-26
++#define WF_LWTBL_QOS_SHIFT 26
++#define WF_LWTBL_HT_DW 2
++#define WF_LWTBL_HT_ADDR 8
++#define WF_LWTBL_HT_MASK \
++ 0x08000000 // 27-27
++#define WF_LWTBL_HT_SHIFT 27
++#define WF_LWTBL_VHT_DW 2
++#define WF_LWTBL_VHT_ADDR 8
++#define WF_LWTBL_VHT_MASK \
++ 0x10000000 // 28-28
++#define WF_LWTBL_VHT_SHIFT 28
++#define WF_LWTBL_HE_DW 2
++#define WF_LWTBL_HE_ADDR 8
++#define WF_LWTBL_HE_MASK \
++ 0x20000000 // 29-29
++#define WF_LWTBL_HE_SHIFT 29
++#define WF_LWTBL_EHT_DW 2
++#define WF_LWTBL_EHT_ADDR 8
++#define WF_LWTBL_EHT_MASK \
++ 0x40000000 // 30-30
++#define WF_LWTBL_EHT_SHIFT 30
++#define WF_LWTBL_MESH_DW 2
++#define WF_LWTBL_MESH_ADDR 8
++#define WF_LWTBL_MESH_MASK \
++ 0x80000000 // 31-31
++#define WF_LWTBL_MESH_SHIFT 31
++// DW3
++#define WF_LWTBL_WMM_Q_DW 3
++#define WF_LWTBL_WMM_Q_ADDR 12
++#define WF_LWTBL_WMM_Q_MASK \
++ 0x00000003 // 1- 0
++#define WF_LWTBL_WMM_Q_SHIFT 0
++#define WF_LWTBL_EHT_SIG_MCS_DW 3
++#define WF_LWTBL_EHT_SIG_MCS_ADDR 12
++#define WF_LWTBL_EHT_SIG_MCS_MASK \
++ 0x0000000c // 3- 2
++#define WF_LWTBL_EHT_SIG_MCS_SHIFT 2
++#define WF_LWTBL_HDRT_MODE_DW 3
++#define WF_LWTBL_HDRT_MODE_ADDR 12
++#define WF_LWTBL_HDRT_MODE_MASK \
++ 0x00000010 // 4- 4
++#define WF_LWTBL_HDRT_MODE_SHIFT 4
++#define WF_LWTBL_BEAM_CHG_DW 3
++#define WF_LWTBL_BEAM_CHG_ADDR 12
++#define WF_LWTBL_BEAM_CHG_MASK \
++ 0x00000020 // 5- 5
++#define WF_LWTBL_BEAM_CHG_SHIFT 5
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_DW 3
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_ADDR 12
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK \
++ 0x000000c0 // 7- 6
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT 6
++#define WF_LWTBL_PFMU_IDX_DW 3
++#define WF_LWTBL_PFMU_IDX_ADDR 12
++#define WF_LWTBL_PFMU_IDX_MASK \
++ 0x0000ff00 // 15- 8
++#define WF_LWTBL_PFMU_IDX_SHIFT 8
++#define WF_LWTBL_ULPF_IDX_DW 3
++#define WF_LWTBL_ULPF_IDX_ADDR 12
++#define WF_LWTBL_ULPF_IDX_MASK \
++ 0x00ff0000 // 23-16
++#define WF_LWTBL_ULPF_IDX_SHIFT 16
++#define WF_LWTBL_RIBF_DW 3
++#define WF_LWTBL_RIBF_ADDR 12
++#define WF_LWTBL_RIBF_MASK \
++ 0x01000000 // 24-24
++#define WF_LWTBL_RIBF_SHIFT 24
++#define WF_LWTBL_ULPF_DW 3
++#define WF_LWTBL_ULPF_ADDR 12
++#define WF_LWTBL_ULPF_MASK \
++ 0x02000000 // 25-25
++#define WF_LWTBL_ULPF_SHIFT 25
++#define WF_LWTBL_BYPASS_TXSMM_DW 3
++#define WF_LWTBL_BYPASS_TXSMM_ADDR 12
++#define WF_LWTBL_BYPASS_TXSMM_MASK \
++ 0x04000000 // 26-26
++#define WF_LWTBL_BYPASS_TXSMM_SHIFT 26
++#define WF_LWTBL_TBF_HT_DW 3
++#define WF_LWTBL_TBF_HT_ADDR 12
++#define WF_LWTBL_TBF_HT_MASK \
++ 0x08000000 // 27-27
++#define WF_LWTBL_TBF_HT_SHIFT 27
++#define WF_LWTBL_TBF_VHT_DW 3
++#define WF_LWTBL_TBF_VHT_ADDR 12
++#define WF_LWTBL_TBF_VHT_MASK \
++ 0x10000000 // 28-28
++#define WF_LWTBL_TBF_VHT_SHIFT 28
++#define WF_LWTBL_TBF_HE_DW 3
++#define WF_LWTBL_TBF_HE_ADDR 12
++#define WF_LWTBL_TBF_HE_MASK \
++ 0x20000000 // 29-29
++#define WF_LWTBL_TBF_HE_SHIFT 29
++#define WF_LWTBL_TBF_EHT_DW 3
++#define WF_LWTBL_TBF_EHT_ADDR 12
++#define WF_LWTBL_TBF_EHT_MASK \
++ 0x40000000 // 30-30
++#define WF_LWTBL_TBF_EHT_SHIFT 30
++#define WF_LWTBL_IGN_FBK_DW 3
++#define WF_LWTBL_IGN_FBK_ADDR 12
++#define WF_LWTBL_IGN_FBK_MASK \
++ 0x80000000 // 31-31
++#define WF_LWTBL_IGN_FBK_SHIFT 31
++// DW4
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_MASK \
++ 0x00000007 // 2- 0
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT 0
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_MASK \
++ 0x00000038 // 5- 3
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT 3
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_MASK \
++ 0x000001c0 // 8- 6
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT 6
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_MASK \
++ 0x00000e00 // 11- 9
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT 9
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_MASK \
++ 0x00007000 // 14-12
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT 12
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_MASK \
++ 0x00038000 // 17-15
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT 15
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_MASK \
++ 0x001c0000 // 20-18
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT 18
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_DW 4
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_ADDR 16
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_MASK \
++ 0x00e00000 // 23-21
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT 21
++#define WF_LWTBL_PE_DW 4
++#define WF_LWTBL_PE_ADDR 16
++#define WF_LWTBL_PE_MASK \
++ 0x03000000 // 25-24
++#define WF_LWTBL_PE_SHIFT 24
++#define WF_LWTBL_DIS_RHTR_DW 4
++#define WF_LWTBL_DIS_RHTR_ADDR 16
++#define WF_LWTBL_DIS_RHTR_MASK \
++ 0x04000000 // 26-26
++#define WF_LWTBL_DIS_RHTR_SHIFT 26
++#define WF_LWTBL_LDPC_HT_DW 4
++#define WF_LWTBL_LDPC_HT_ADDR 16
++#define WF_LWTBL_LDPC_HT_MASK \
++ 0x08000000 // 27-27
++#define WF_LWTBL_LDPC_HT_SHIFT 27
++#define WF_LWTBL_LDPC_VHT_DW 4
++#define WF_LWTBL_LDPC_VHT_ADDR 16
++#define WF_LWTBL_LDPC_VHT_MASK \
++ 0x10000000 // 28-28
++#define WF_LWTBL_LDPC_VHT_SHIFT 28
++#define WF_LWTBL_LDPC_HE_DW 4
++#define WF_LWTBL_LDPC_HE_ADDR 16
++#define WF_LWTBL_LDPC_HE_MASK \
++ 0x20000000 // 29-29
++#define WF_LWTBL_LDPC_HE_SHIFT 29
++#define WF_LWTBL_LDPC_EHT_DW 4
++#define WF_LWTBL_LDPC_EHT_ADDR 16
++#define WF_LWTBL_LDPC_EHT_MASK \
++ 0x40000000 // 30-30
++#define WF_LWTBL_LDPC_EHT_SHIFT 30
++#define WF_LWTBL_BA_MODE_DW 4
++#define WF_LWTBL_BA_MODE_ADDR 16
++#define WF_LWTBL_BA_MODE_MASK \
++ 0x80000000 // 31-31
++#define WF_LWTBL_BA_MODE_SHIFT 31
++// DW5
++#define WF_LWTBL_AF_DW 5
++#define WF_LWTBL_AF_ADDR 20
++#define WF_LWTBL_AF_MASK \
++ 0x00000007 // 2- 0
++#define WF_LWTBL_AF_MASK_7992 \
++ 0x0000000f // 3- 0
++#define WF_LWTBL_AF_SHIFT 0
++#define WF_LWTBL_AF_HE_DW 5
++#define WF_LWTBL_AF_HE_ADDR 20
++#define WF_LWTBL_AF_HE_MASK \
++ 0x00000018 // 4- 3
++#define WF_LWTBL_AF_HE_SHIFT 3
++#define WF_LWTBL_RTS_DW 5
++#define WF_LWTBL_RTS_ADDR 20
++#define WF_LWTBL_RTS_MASK \
++ 0x00000020 // 5- 5
++#define WF_LWTBL_RTS_SHIFT 5
++#define WF_LWTBL_SMPS_DW 5
++#define WF_LWTBL_SMPS_ADDR 20
++#define WF_LWTBL_SMPS_MASK \
++ 0x00000040 // 6- 6
++#define WF_LWTBL_SMPS_SHIFT 6
++#define WF_LWTBL_DYN_BW_DW 5
++#define WF_LWTBL_DYN_BW_ADDR 20
++#define WF_LWTBL_DYN_BW_MASK \
++ 0x00000080 // 7- 7
++#define WF_LWTBL_DYN_BW_SHIFT 7
++#define WF_LWTBL_MMSS_DW 5
++#define WF_LWTBL_MMSS_ADDR 20
++#define WF_LWTBL_MMSS_MASK \
++ 0x00000700 // 10- 8
++#define WF_LWTBL_MMSS_SHIFT 8
++#define WF_LWTBL_USR_DW 5
++#define WF_LWTBL_USR_ADDR 20
++#define WF_LWTBL_USR_MASK \
++ 0x00000800 // 11-11
++#define WF_LWTBL_USR_SHIFT 11
++#define WF_LWTBL_SR_R_DW 5
++#define WF_LWTBL_SR_R_ADDR 20
++#define WF_LWTBL_SR_R_MASK \
++ 0x00007000 // 14-12
++#define WF_LWTBL_SR_R_SHIFT 12
++#define WF_LWTBL_SR_ABORT_DW 5
++#define WF_LWTBL_SR_ABORT_ADDR 20
++#define WF_LWTBL_SR_ABORT_MASK \
++ 0x00008000 // 15-15
++#define WF_LWTBL_SR_ABORT_SHIFT 15
++#define WF_LWTBL_TX_POWER_OFFSET_DW 5
++#define WF_LWTBL_TX_POWER_OFFSET_ADDR 20
++#define WF_LWTBL_TX_POWER_OFFSET_MASK \
++ 0x003f0000 // 21-16
++#define WF_LWTBL_TX_POWER_OFFSET_SHIFT 16
++#define WF_LWTBL_LTF_EHT_DW 5
++#define WF_LWTBL_LTF_EHT_ADDR 20
++#define WF_LWTBL_LTF_EHT_MASK \
++ 0x00c00000 // 23-22
++#define WF_LWTBL_LTF_EHT_SHIFT 22
++#define WF_LWTBL_GI_EHT_DW 5
++#define WF_LWTBL_GI_EHT_ADDR 20
++#define WF_LWTBL_GI_EHT_MASK \
++ 0x03000000 // 25-24
++#define WF_LWTBL_GI_EHT_SHIFT 24
++#define WF_LWTBL_DOPPL_DW 5
++#define WF_LWTBL_DOPPL_ADDR 20
++#define WF_LWTBL_DOPPL_MASK \
++ 0x04000000 // 26-26
++#define WF_LWTBL_DOPPL_SHIFT 26
++#define WF_LWTBL_TXOP_PS_CAP_DW 5
++#define WF_LWTBL_TXOP_PS_CAP_ADDR 20
++#define WF_LWTBL_TXOP_PS_CAP_MASK \
++ 0x08000000 // 27-27
++#define WF_LWTBL_TXOP_PS_CAP_SHIFT 27
++#define WF_LWTBL_DU_I_PSM_DW 5
++#define WF_LWTBL_DU_I_PSM_ADDR 20
++#define WF_LWTBL_DU_I_PSM_MASK \
++ 0x10000000 // 28-28
++#define WF_LWTBL_DU_I_PSM_SHIFT 28
++#define WF_LWTBL_I_PSM_DW 5
++#define WF_LWTBL_I_PSM_ADDR 20
++#define WF_LWTBL_I_PSM_MASK \
++ 0x20000000 // 29-29
++#define WF_LWTBL_I_PSM_SHIFT 29
++#define WF_LWTBL_PSM_DW 5
++#define WF_LWTBL_PSM_ADDR 20
++#define WF_LWTBL_PSM_MASK \
++ 0x40000000 // 30-30
++#define WF_LWTBL_PSM_SHIFT 30
++#define WF_LWTBL_SKIP_TX_DW 5
++#define WF_LWTBL_SKIP_TX_ADDR 20
++#define WF_LWTBL_SKIP_TX_MASK \
++ 0x80000000 // 31-31
++#define WF_LWTBL_SKIP_TX_SHIFT 31
++// DW6
++#define WF_LWTBL_CBRN_DW 6
++#define WF_LWTBL_CBRN_ADDR 24
++#define WF_LWTBL_CBRN_MASK \
++ 0x00000007 // 2- 0
++#define WF_LWTBL_CBRN_SHIFT 0
++#define WF_LWTBL_DBNSS_EN_DW 6
++#define WF_LWTBL_DBNSS_EN_ADDR 24
++#define WF_LWTBL_DBNSS_EN_MASK \
++ 0x00000008 // 3- 3
++#define WF_LWTBL_DBNSS_EN_SHIFT 3
++#define WF_LWTBL_BAF_EN_DW 6
++#define WF_LWTBL_BAF_EN_ADDR 24
++#define WF_LWTBL_BAF_EN_MASK \
++ 0x00000010 // 4- 4
++#define WF_LWTBL_BAF_EN_SHIFT 4
++#define WF_LWTBL_RDGBA_DW 6
++#define WF_LWTBL_RDGBA_ADDR 24
++#define WF_LWTBL_RDGBA_MASK \
++ 0x00000020 // 5- 5
++#define WF_LWTBL_RDGBA_SHIFT 5
++#define WF_LWTBL_R_DW 6
++#define WF_LWTBL_R_ADDR 24
++#define WF_LWTBL_R_MASK \
++ 0x00000040 // 6- 6
++#define WF_LWTBL_R_SHIFT 6
++#define WF_LWTBL_SPE_IDX_DW 6
++#define WF_LWTBL_SPE_IDX_ADDR 24
++#define WF_LWTBL_SPE_IDX_MASK \
++ 0x00000f80 // 11- 7
++#define WF_LWTBL_SPE_IDX_SHIFT 7
++#define WF_LWTBL_G2_DW 6
++#define WF_LWTBL_G2_ADDR 24
++#define WF_LWTBL_G2_MASK \
++ 0x00001000 // 12-12
++#define WF_LWTBL_G2_SHIFT 12
++#define WF_LWTBL_G4_DW 6
++#define WF_LWTBL_G4_ADDR 24
++#define WF_LWTBL_G4_MASK \
++ 0x00002000 // 13-13
++#define WF_LWTBL_G4_SHIFT 13
++#define WF_LWTBL_G8_DW 6
++#define WF_LWTBL_G8_ADDR 24
++#define WF_LWTBL_G8_MASK \
++ 0x00004000 // 14-14
++#define WF_LWTBL_G8_SHIFT 14
++#define WF_LWTBL_G16_DW 6
++#define WF_LWTBL_G16_ADDR 24
++#define WF_LWTBL_G16_MASK \
++ 0x00008000 // 15-15
++#define WF_LWTBL_G16_SHIFT 15
++#define WF_LWTBL_G2_LTF_DW 6
++#define WF_LWTBL_G2_LTF_ADDR 24
++#define WF_LWTBL_G2_LTF_MASK \
++ 0x00030000 // 17-16
++#define WF_LWTBL_G2_LTF_SHIFT 16
++#define WF_LWTBL_G4_LTF_DW 6
++#define WF_LWTBL_G4_LTF_ADDR 24
++#define WF_LWTBL_G4_LTF_MASK \
++ 0x000c0000 // 19-18
++#define WF_LWTBL_G4_LTF_SHIFT 18
++#define WF_LWTBL_G8_LTF_DW 6
++#define WF_LWTBL_G8_LTF_ADDR 24
++#define WF_LWTBL_G8_LTF_MASK \
++ 0x00300000 // 21-20
++#define WF_LWTBL_G8_LTF_SHIFT 20
++#define WF_LWTBL_G16_LTF_DW 6
++#define WF_LWTBL_G16_LTF_ADDR 24
++#define WF_LWTBL_G16_LTF_MASK \
++ 0x00c00000 // 23-22
++#define WF_LWTBL_G16_LTF_SHIFT 22
++#define WF_LWTBL_G2_HE_DW 6
++#define WF_LWTBL_G2_HE_ADDR 24
++#define WF_LWTBL_G2_HE_MASK \
++ 0x03000000 // 25-24
++#define WF_LWTBL_G2_HE_SHIFT 24
++#define WF_LWTBL_G4_HE_DW 6
++#define WF_LWTBL_G4_HE_ADDR 24
++#define WF_LWTBL_G4_HE_MASK \
++ 0x0c000000 // 27-26
++#define WF_LWTBL_G4_HE_SHIFT 26
++#define WF_LWTBL_G8_HE_DW 6
++#define WF_LWTBL_G8_HE_ADDR 24
++#define WF_LWTBL_G8_HE_MASK \
++ 0x30000000 // 29-28
++#define WF_LWTBL_G8_HE_SHIFT 28
++#define WF_LWTBL_G16_HE_DW 6
++#define WF_LWTBL_G16_HE_ADDR 24
++#define WF_LWTBL_G16_HE_MASK \
++ 0xc0000000 // 31-30
++#define WF_LWTBL_G16_HE_SHIFT 30
++// DW7
++#define WF_LWTBL_BA_WIN_SIZE0_DW 7
++#define WF_LWTBL_BA_WIN_SIZE0_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE0_MASK \
++ 0x0000000f // 3- 0
++#define WF_LWTBL_BA_WIN_SIZE0_SHIFT 0
++#define WF_LWTBL_BA_WIN_SIZE1_DW 7
++#define WF_LWTBL_BA_WIN_SIZE1_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE1_MASK \
++ 0x000000f0 // 7- 4
++#define WF_LWTBL_BA_WIN_SIZE1_SHIFT 4
++#define WF_LWTBL_BA_WIN_SIZE2_DW 7
++#define WF_LWTBL_BA_WIN_SIZE2_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE2_MASK \
++ 0x00000f00 // 11- 8
++#define WF_LWTBL_BA_WIN_SIZE2_SHIFT 8
++#define WF_LWTBL_BA_WIN_SIZE3_DW 7
++#define WF_LWTBL_BA_WIN_SIZE3_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE3_MASK \
++ 0x0000f000 // 15-12
++#define WF_LWTBL_BA_WIN_SIZE3_SHIFT 12
++#define WF_LWTBL_BA_WIN_SIZE4_DW 7
++#define WF_LWTBL_BA_WIN_SIZE4_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE4_MASK \
++ 0x000f0000 // 19-16
++#define WF_LWTBL_BA_WIN_SIZE4_SHIFT 16
++#define WF_LWTBL_BA_WIN_SIZE5_DW 7
++#define WF_LWTBL_BA_WIN_SIZE5_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE5_MASK \
++ 0x00f00000 // 23-20
++#define WF_LWTBL_BA_WIN_SIZE5_SHIFT 20
++#define WF_LWTBL_BA_WIN_SIZE6_DW 7
++#define WF_LWTBL_BA_WIN_SIZE6_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE6_MASK \
++ 0x0f000000 // 27-24
++#define WF_LWTBL_BA_WIN_SIZE6_SHIFT 24
++#define WF_LWTBL_BA_WIN_SIZE7_DW 7
++#define WF_LWTBL_BA_WIN_SIZE7_ADDR 28
++#define WF_LWTBL_BA_WIN_SIZE7_MASK \
++ 0xf0000000 // 31-28
++#define WF_LWTBL_BA_WIN_SIZE7_SHIFT 28
++// DW8
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_DW 8
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_ADDR 32
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_MASK \
++ 0x0000001f // 4- 0
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT 0
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_DW 8
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_ADDR 32
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_MASK \
++ 0x000003e0 // 9- 5
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT 5
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_DW 8
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_ADDR 32
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_MASK \
++ 0x00007c00 // 14-10
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT 10
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_DW 8
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_ADDR 32
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_MASK \
++ 0x000f8000 // 19-15
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT 15
++#define WF_LWTBL_PARTIAL_AID_DW 8
++#define WF_LWTBL_PARTIAL_AID_ADDR 32
++#define WF_LWTBL_PARTIAL_AID_MASK \
++ 0x1ff00000 // 28-20
++#define WF_LWTBL_PARTIAL_AID_SHIFT 20
++#define WF_LWTBL_CHK_PER_DW 8
++#define WF_LWTBL_CHK_PER_ADDR 32
++#define WF_LWTBL_CHK_PER_MASK \
++ 0x80000000 // 31-31
++#define WF_LWTBL_CHK_PER_SHIFT 31
++// DW9
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_DW 9
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_ADDR 36
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_MASK \
++ 0x00003fff // 13- 0
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT 0
++#define WF_LWTBL_PRITX_SW_MODE_DW 9
++#define WF_LWTBL_PRITX_SW_MODE_ADDR 36
++#define WF_LWTBL_PRITX_SW_MODE_MASK \
++ 0x00008000 // 15-15
++#define WF_LWTBL_PRITX_SW_MODE_SHIFT 15
++#define WF_LWTBL_PRITX_SW_MODE_MASK_7992 \
++ 0x00004000 // 14-14
++#define WF_LWTBL_PRITX_SW_MODE_SHIFT_7992 14
++#define WF_LWTBL_PRITX_ERSU_DW 9
++#define WF_LWTBL_PRITX_ERSU_ADDR 36
++#define WF_LWTBL_PRITX_ERSU_MASK \
++ 0x00010000 // 16-16
++#define WF_LWTBL_PRITX_ERSU_SHIFT 16
++#define WF_LWTBL_PRITX_ERSU_MASK_7992 \
++ 0x00008000 // 15-15
++#define WF_LWTBL_PRITX_ERSU_SHIFT_7992 15
++#define WF_LWTBL_PRITX_PLR_DW 9
++#define WF_LWTBL_PRITX_PLR_ADDR 36
++#define WF_LWTBL_PRITX_PLR_MASK \
++ 0x00020000 // 17-17
++#define WF_LWTBL_PRITX_PLR_SHIFT 17
++#define WF_LWTBL_PRITX_PLR_MASK_7992 \
++ 0x00030000 // 17-16
++#define WF_LWTBL_PRITX_PLR_SHIFT_7992 16
++#define WF_LWTBL_PRITX_DCM_DW 9
++#define WF_LWTBL_PRITX_DCM_ADDR 36
++#define WF_LWTBL_PRITX_DCM_MASK \
++ 0x00040000 // 18-18
++#define WF_LWTBL_PRITX_DCM_SHIFT 18
++#define WF_LWTBL_PRITX_ER106T_DW 9
++#define WF_LWTBL_PRITX_ER106T_ADDR 36
++#define WF_LWTBL_PRITX_ER106T_MASK \
++ 0x00080000 // 19-19
++#define WF_LWTBL_PRITX_ER106T_SHIFT 19
++#define WF_LWTBL_FCAP_DW 9
++#define WF_LWTBL_FCAP_ADDR 36
++#define WF_LWTBL_FCAP_MASK \
++ 0x00700000 // 22-20
++#define WF_LWTBL_FCAP_SHIFT 20
++#define WF_LWTBL_MPDU_FAIL_CNT_DW 9
++#define WF_LWTBL_MPDU_FAIL_CNT_ADDR 36
++#define WF_LWTBL_MPDU_FAIL_CNT_MASK \
++ 0x03800000 // 25-23
++#define WF_LWTBL_MPDU_FAIL_CNT_SHIFT 23
++#define WF_LWTBL_MPDU_OK_CNT_DW 9
++#define WF_LWTBL_MPDU_OK_CNT_ADDR 36
++#define WF_LWTBL_MPDU_OK_CNT_MASK \
++ 0x1c000000 // 28-26
++#define WF_LWTBL_MPDU_OK_CNT_SHIFT 26
++#define WF_LWTBL_RATE_IDX_DW 9
++#define WF_LWTBL_RATE_IDX_ADDR 36
++#define WF_LWTBL_RATE_IDX_MASK \
++ 0xe0000000 // 31-29
++#define WF_LWTBL_RATE_IDX_SHIFT 29
++// DW10
++#define WF_LWTBL_RATE1_DW 10
++#define WF_LWTBL_RATE1_ADDR 40
++#define WF_LWTBL_RATE1_MASK \
++ 0x00007fff // 14- 0
++#define WF_LWTBL_RATE1_SHIFT 0
++#define WF_LWTBL_RATE2_DW 10
++#define WF_LWTBL_RATE2_ADDR 40
++#define WF_LWTBL_RATE2_MASK \
++ 0x7fff0000 // 30-16
++#define WF_LWTBL_RATE2_SHIFT 16
++// DW11
++#define WF_LWTBL_RATE3_DW 11
++#define WF_LWTBL_RATE3_ADDR 44
++#define WF_LWTBL_RATE3_MASK \
++ 0x00007fff // 14- 0
++#define WF_LWTBL_RATE3_SHIFT 0
++#define WF_LWTBL_RATE4_DW 11
++#define WF_LWTBL_RATE4_ADDR 44
++#define WF_LWTBL_RATE4_MASK \
++ 0x7fff0000 // 30-16
++#define WF_LWTBL_RATE4_SHIFT 16
++// DW12
++#define WF_LWTBL_RATE5_DW 12
++#define WF_LWTBL_RATE5_ADDR 48
++#define WF_LWTBL_RATE5_MASK \
++ 0x00007fff // 14- 0
++#define WF_LWTBL_RATE5_SHIFT 0
++#define WF_LWTBL_RATE6_DW 12
++#define WF_LWTBL_RATE6_ADDR 48
++#define WF_LWTBL_RATE6_MASK \
++ 0x7fff0000 // 30-16
++#define WF_LWTBL_RATE6_SHIFT 16
++// DW13
++#define WF_LWTBL_RATE7_DW 13
++#define WF_LWTBL_RATE7_ADDR 52
++#define WF_LWTBL_RATE7_MASK \
++ 0x00007fff // 14- 0
++#define WF_LWTBL_RATE7_SHIFT 0
++#define WF_LWTBL_RATE8_DW 13
++#define WF_LWTBL_RATE8_ADDR 52
++#define WF_LWTBL_RATE8_MASK \
++ 0x7fff0000 // 30-16
++#define WF_LWTBL_RATE8_SHIFT 16
++// DW14
++#define WF_LWTBL_RATE1_TX_CNT_DW 14
++#define WF_LWTBL_RATE1_TX_CNT_ADDR 56
++#define WF_LWTBL_RATE1_TX_CNT_MASK \
++ 0x0000ffff // 15- 0
++#define WF_LWTBL_RATE1_TX_CNT_SHIFT 0
++#define WF_LWTBL_CIPHER_SUIT_IGTK_DW 14
++#define WF_LWTBL_CIPHER_SUIT_IGTK_ADDR 56
++#define WF_LWTBL_CIPHER_SUIT_IGTK_MASK \
++ 0x00003000 // 13-12
++#define WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT 12
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_DW 14
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_ADDR 56
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_MASK \
++ 0x0000c000 // 15-14
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT 14
++#define WF_LWTBL_RATE1_FAIL_CNT_DW 14
++#define WF_LWTBL_RATE1_FAIL_CNT_ADDR 56
++#define WF_LWTBL_RATE1_FAIL_CNT_MASK \
++ 0xffff0000 // 31-16
++#define WF_LWTBL_RATE1_FAIL_CNT_SHIFT 16
++// DW15
++#define WF_LWTBL_RATE2_OK_CNT_DW 15
++#define WF_LWTBL_RATE2_OK_CNT_ADDR 60
++#define WF_LWTBL_RATE2_OK_CNT_MASK \
++ 0x0000ffff // 15- 0
++#define WF_LWTBL_RATE2_OK_CNT_SHIFT 0
++#define WF_LWTBL_RATE3_OK_CNT_DW 15
++#define WF_LWTBL_RATE3_OK_CNT_ADDR 60
++#define WF_LWTBL_RATE3_OK_CNT_MASK \
++ 0xffff0000 // 31-16
++#define WF_LWTBL_RATE3_OK_CNT_SHIFT 16
++// DW16
++#define WF_LWTBL_CURRENT_BW_TX_CNT_DW 16
++#define WF_LWTBL_CURRENT_BW_TX_CNT_ADDR 64
++#define WF_LWTBL_CURRENT_BW_TX_CNT_MASK \
++ 0x0000ffff // 15- 0
++#define WF_LWTBL_CURRENT_BW_TX_CNT_SHIFT 0
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_DW 16
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_ADDR 64
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_MASK \
++ 0xffff0000 // 31-16
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_SHIFT 16
++// DW17
++#define WF_LWTBL_OTHER_BW_TX_CNT_DW 17
++#define WF_LWTBL_OTHER_BW_TX_CNT_ADDR 68
++#define WF_LWTBL_OTHER_BW_TX_CNT_MASK \
++ 0x0000ffff // 15- 0
++#define WF_LWTBL_OTHER_BW_TX_CNT_SHIFT 0
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_DW 17
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_ADDR 68
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_MASK \
++ 0xffff0000 // 31-16
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_SHIFT 16
++// DW18
++#define WF_LWTBL_RTS_OK_CNT_DW 18
++#define WF_LWTBL_RTS_OK_CNT_ADDR 72
++#define WF_LWTBL_RTS_OK_CNT_MASK \
++ 0x0000ffff // 15- 0
++#define WF_LWTBL_RTS_OK_CNT_SHIFT 0
++#define WF_LWTBL_RTS_FAIL_CNT_DW 18
++#define WF_LWTBL_RTS_FAIL_CNT_ADDR 72
++#define WF_LWTBL_RTS_FAIL_CNT_MASK \
++ 0xffff0000 // 31-16
++#define WF_LWTBL_RTS_FAIL_CNT_SHIFT 16
++// DW19
++#define WF_LWTBL_DATA_RETRY_CNT_DW 19
++#define WF_LWTBL_DATA_RETRY_CNT_ADDR 76
++#define WF_LWTBL_DATA_RETRY_CNT_MASK \
++ 0x0000ffff // 15- 0
++#define WF_LWTBL_DATA_RETRY_CNT_SHIFT 0
++#define WF_LWTBL_MGNT_RETRY_CNT_DW 19
++#define WF_LWTBL_MGNT_RETRY_CNT_ADDR 76
++#define WF_LWTBL_MGNT_RETRY_CNT_MASK \
++ 0xffff0000 // 31-16
++#define WF_LWTBL_MGNT_RETRY_CNT_SHIFT 16
++// DW20
++#define WF_LWTBL_AC0_CTT_CDT_CRB_DW 20
++#define WF_LWTBL_AC0_CTT_CDT_CRB_ADDR 80
++#define WF_LWTBL_AC0_CTT_CDT_CRB_MASK \
++ 0xffffffff // 31- 0
++#define WF_LWTBL_AC0_CTT_CDT_CRB_SHIFT 0
++// DW21
++// DO NOT process repeat field(adm[0])
++// DW22
++#define WF_LWTBL_AC1_CTT_CDT_CRB_DW 22
++#define WF_LWTBL_AC1_CTT_CDT_CRB_ADDR 88
++#define WF_LWTBL_AC1_CTT_CDT_CRB_MASK \
++ 0xffffffff // 31- 0
++#define WF_LWTBL_AC1_CTT_CDT_CRB_SHIFT 0
++// DW23
++// DO NOT process repeat field(adm[1])
++// DW24
++#define WF_LWTBL_AC2_CTT_CDT_CRB_DW 24
++#define WF_LWTBL_AC2_CTT_CDT_CRB_ADDR 96
++#define WF_LWTBL_AC2_CTT_CDT_CRB_MASK \
++ 0xffffffff // 31- 0
++#define WF_LWTBL_AC2_CTT_CDT_CRB_SHIFT 0
++// DW25
++// DO NOT process repeat field(adm[2])
++// DW26
++#define WF_LWTBL_AC3_CTT_CDT_CRB_DW 26
++#define WF_LWTBL_AC3_CTT_CDT_CRB_ADDR 104
++#define WF_LWTBL_AC3_CTT_CDT_CRB_MASK \
++ 0xffffffff // 31- 0
++#define WF_LWTBL_AC3_CTT_CDT_CRB_SHIFT 0
++// DW27
++// DO NOT process repeat field(adm[3])
++// DW28
++#define WF_LWTBL_RELATED_IDX0_DW 28
++#define WF_LWTBL_RELATED_IDX0_ADDR 112
++#define WF_LWTBL_RELATED_IDX0_MASK \
++ 0x00000fff // 11- 0
++#define WF_LWTBL_RELATED_IDX0_SHIFT 0
++#define WF_LWTBL_RELATED_BAND0_DW 28
++#define WF_LWTBL_RELATED_BAND0_ADDR 112
++#define WF_LWTBL_RELATED_BAND0_MASK \
++ 0x00003000 // 13-12
++#define WF_LWTBL_RELATED_BAND0_SHIFT 12
++#define WF_LWTBL_PRIMARY_MLD_BAND_DW 28
++#define WF_LWTBL_PRIMARY_MLD_BAND_ADDR 112
++#define WF_LWTBL_PRIMARY_MLD_BAND_MASK \
++ 0x0000c000 // 15-14
++#define WF_LWTBL_PRIMARY_MLD_BAND_SHIFT 14
++#define WF_LWTBL_RELATED_IDX1_DW 28
++#define WF_LWTBL_RELATED_IDX1_ADDR 112
++#define WF_LWTBL_RELATED_IDX1_MASK \
++ 0x0fff0000 // 27-16
++#define WF_LWTBL_RELATED_IDX1_SHIFT 16
++#define WF_LWTBL_RELATED_BAND1_DW 28
++#define WF_LWTBL_RELATED_BAND1_ADDR 112
++#define WF_LWTBL_RELATED_BAND1_MASK \
++ 0x30000000 // 29-28
++#define WF_LWTBL_RELATED_BAND1_SHIFT 28
++#define WF_LWTBL_SECONDARY_MLD_BAND_DW 28
++#define WF_LWTBL_SECONDARY_MLD_BAND_ADDR 112
++#define WF_LWTBL_SECONDARY_MLD_BAND_MASK \
++ 0xc0000000 // 31-30
++#define WF_LWTBL_SECONDARY_MLD_BAND_SHIFT 30
++// DW29
++#define WF_LWTBL_DISPATCH_POLICY0_DW 29
++#define WF_LWTBL_DISPATCH_POLICY0_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY0_MASK \
++ 0x00000003 // 1- 0
++#define WF_LWTBL_DISPATCH_POLICY0_SHIFT 0
++#define WF_LWTBL_DISPATCH_POLICY1_DW 29
++#define WF_LWTBL_DISPATCH_POLICY1_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY1_MASK \
++ 0x0000000c // 3- 2
++#define WF_LWTBL_DISPATCH_POLICY1_SHIFT 2
++#define WF_LWTBL_DISPATCH_POLICY2_DW 29
++#define WF_LWTBL_DISPATCH_POLICY2_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY2_MASK \
++ 0x00000030 // 5- 4
++#define WF_LWTBL_DISPATCH_POLICY2_SHIFT 4
++#define WF_LWTBL_DISPATCH_POLICY3_DW 29
++#define WF_LWTBL_DISPATCH_POLICY3_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY3_MASK \
++ 0x000000c0 // 7- 6
++#define WF_LWTBL_DISPATCH_POLICY3_SHIFT 6
++#define WF_LWTBL_DISPATCH_POLICY4_DW 29
++#define WF_LWTBL_DISPATCH_POLICY4_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY4_MASK \
++ 0x00000300 // 9- 8
++#define WF_LWTBL_DISPATCH_POLICY4_SHIFT 8
++#define WF_LWTBL_DISPATCH_POLICY5_DW 29
++#define WF_LWTBL_DISPATCH_POLICY5_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY5_MASK \
++ 0x00000c00 // 11-10
++#define WF_LWTBL_DISPATCH_POLICY5_SHIFT 10
++#define WF_LWTBL_DISPATCH_POLICY6_DW 29
++#define WF_LWTBL_DISPATCH_POLICY6_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY6_MASK \
++ 0x00003000 // 13-12
++#define WF_LWTBL_DISPATCH_POLICY6_SHIFT 12
++#define WF_LWTBL_DISPATCH_POLICY7_DW 29
++#define WF_LWTBL_DISPATCH_POLICY7_ADDR 116
++#define WF_LWTBL_DISPATCH_POLICY7_MASK \
++ 0x0000c000 // 15-14
++#define WF_LWTBL_DISPATCH_POLICY7_SHIFT 14
++#define WF_LWTBL_OWN_MLD_ID_DW 29
++#define WF_LWTBL_OWN_MLD_ID_ADDR 116
++#define WF_LWTBL_OWN_MLD_ID_MASK \
++ 0x003f0000 // 21-16
++#define WF_LWTBL_OWN_MLD_ID_SHIFT 16
++#define WF_LWTBL_EMLSR0_DW 29
++#define WF_LWTBL_EMLSR0_ADDR 116
++#define WF_LWTBL_EMLSR0_MASK \
++ 0x00400000 // 22-22
++#define WF_LWTBL_EMLSR0_SHIFT 22
++#define WF_LWTBL_EMLMR0_DW 29
++#define WF_LWTBL_EMLMR0_ADDR 116
++#define WF_LWTBL_EMLMR0_MASK \
++ 0x00800000 // 23-23
++#define WF_LWTBL_EMLMR0_SHIFT 23
++#define WF_LWTBL_EMLSR1_DW 29
++#define WF_LWTBL_EMLSR1_ADDR 116
++#define WF_LWTBL_EMLSR1_MASK \
++ 0x01000000 // 24-24
++#define WF_LWTBL_EMLSR1_SHIFT 24
++#define WF_LWTBL_EMLMR1_DW 29
++#define WF_LWTBL_EMLMR1_ADDR 116
++#define WF_LWTBL_EMLMR1_MASK \
++ 0x02000000 // 25-25
++#define WF_LWTBL_EMLMR1_SHIFT 25
++#define WF_LWTBL_EMLSR2_DW 29
++#define WF_LWTBL_EMLSR2_ADDR 116
++#define WF_LWTBL_EMLSR2_MASK \
++ 0x04000000 // 26-26
++#define WF_LWTBL_EMLSR2_SHIFT 26
++#define WF_LWTBL_EMLMR2_DW 29
++#define WF_LWTBL_EMLMR2_ADDR 116
++#define WF_LWTBL_EMLMR2_MASK \
++ 0x08000000 // 27-27
++#define WF_LWTBL_EMLMR2_SHIFT 27
++#define WF_LWTBL_STR_BITMAP_DW 29
++#define WF_LWTBL_STR_BITMAP_ADDR 116
++#define WF_LWTBL_STR_BITMAP_MASK \
++ 0xe0000000 // 31-29
++#define WF_LWTBL_STR_BITMAP_SHIFT 29
++// DW30
++#define WF_LWTBL_DISPATCH_ORDER_DW 30
++#define WF_LWTBL_DISPATCH_ORDER_ADDR 120
++#define WF_LWTBL_DISPATCH_ORDER_MASK \
++ 0x0000007f // 6- 0
++#define WF_LWTBL_DISPATCH_ORDER_SHIFT 0
++#define WF_LWTBL_DISPATCH_RATIO_DW 30
++#define WF_LWTBL_DISPATCH_RATIO_ADDR 120
++#define WF_LWTBL_DISPATCH_RATIO_MASK \
++ 0x00003f80 // 13- 7
++#define WF_LWTBL_DISPATCH_RATIO_SHIFT 7
++#define WF_LWTBL_LINK_MGF_DW 30
++#define WF_LWTBL_LINK_MGF_ADDR 120
++#define WF_LWTBL_LINK_MGF_MASK \
++ 0xffff0000 // 31-16
++#define WF_LWTBL_LINK_MGF_SHIFT 16
++// DW31
++#define WF_LWTBL_BFTX_TB_DW 31
++#define WF_LWTBL_BFTX_TB_ADDR 124
++#define WF_LWTBL_BFTX_TB_MASK \
++ 0x00800000 // 23-23
++#define WF_LWTBL_DROP_DW 31
++#define WF_LWTBL_DROP_ADDR 124
++#define WF_LWTBL_DROP_MASK \
++ 0x01000000 // 24-24
++#define WF_LWTBL_DROP_SHIFT 24
++#define WF_LWTBL_CASCAD_DW 31
++#define WF_LWTBL_CASCAD_ADDR 124
++#define WF_LWTBL_CASCAD_MASK \
++ 0x02000000 // 25-25
++#define WF_LWTBL_CASCAD_SHIFT 25
++#define WF_LWTBL_ALL_ACK_DW 31
++#define WF_LWTBL_ALL_ACK_ADDR 124
++#define WF_LWTBL_ALL_ACK_MASK \
++ 0x04000000 // 26-26
++#define WF_LWTBL_ALL_ACK_SHIFT 26
++#define WF_LWTBL_MPDU_SIZE_DW 31
++#define WF_LWTBL_MPDU_SIZE_ADDR 124
++#define WF_LWTBL_MPDU_SIZE_MASK \
++ 0x18000000 // 28-27
++#define WF_LWTBL_MPDU_SIZE_SHIFT 27
++#define WF_LWTBL_RXD_DUP_MODE_DW 31
++#define WF_LWTBL_RXD_DUP_MODE_ADDR 124
++#define WF_LWTBL_RXD_DUP_MODE_MASK \
++ 0x60000000 // 30-29
++#define WF_LWTBL_RXD_DUP_MODE_SHIFT 29
++#define WF_LWTBL_ACK_EN_DW 31
++#define WF_LWTBL_ACK_EN_ADDR 128
++#define WF_LWTBL_ACK_EN_MASK \
++ 0x80000000 // 31-31
++#define WF_LWTBL_ACK_EN_SHIFT 31
++// DW32
++#define WF_LWTBL_OM_INFO_DW 32
++#define WF_LWTBL_OM_INFO_ADDR 128
++#define WF_LWTBL_OM_INFO_MASK \
++ 0x00000fff // 11- 0
++#define WF_LWTBL_OM_INFO_SHIFT 0
++#define WF_LWTBL_OM_INFO_EHT_DW 32
++#define WF_LWTBL_OM_INFO_EHT_ADDR 128
++#define WF_LWTBL_OM_INFO_EHT_MASK \
++ 0x0000f000 // 15-12
++#define WF_LWTBL_OM_INFO_EHT_SHIFT 12
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_DW 32
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_ADDR 128
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK \
++ 0x00010000 // 16-16
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_SHIFT 16
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_DW 32
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_ADDR 128
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_MASK \
++ 0x1ffe0000 // 28-17
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT 17
++// DW33
++#define WF_LWTBL_USER_RSSI_DW 33
++#define WF_LWTBL_USER_RSSI_ADDR 132
++#define WF_LWTBL_USER_RSSI_MASK \
++ 0x000001ff // 8- 0
++#define WF_LWTBL_USER_RSSI_SHIFT 0
++#define WF_LWTBL_USER_SNR_DW 33
++#define WF_LWTBL_USER_SNR_ADDR 132
++#define WF_LWTBL_USER_SNR_MASK \
++ 0x00007e00 // 14- 9
++#define WF_LWTBL_USER_SNR_SHIFT 9
++#define WF_LWTBL_RAPID_REACTION_RATE_DW 33
++#define WF_LWTBL_RAPID_REACTION_RATE_ADDR 132
++#define WF_LWTBL_RAPID_REACTION_RATE_MASK \
++ 0x0fff0000 // 27-16
++#define WF_LWTBL_RAPID_REACTION_RATE_SHIFT 16
++#define WF_LWTBL_HT_AMSDU_DW 33
++#define WF_LWTBL_HT_AMSDU_ADDR 132
++#define WF_LWTBL_HT_AMSDU_MASK \
++ 0x40000000 // 30-30
++#define WF_LWTBL_HT_AMSDU_SHIFT 30
++#define WF_LWTBL_AMSDU_CROSS_LG_DW 33
++#define WF_LWTBL_AMSDU_CROSS_LG_ADDR 132
++#define WF_LWTBL_AMSDU_CROSS_LG_MASK \
++ 0x80000000 // 31-31
++#define WF_LWTBL_AMSDU_CROSS_LG_SHIFT 31
++// DW34
++#define WF_LWTBL_RESP_RCPI0_DW 34
++#define WF_LWTBL_RESP_RCPI0_ADDR 136
++#define WF_LWTBL_RESP_RCPI0_MASK \
++ 0x000000ff // 7- 0
++#define WF_LWTBL_RESP_RCPI0_SHIFT 0
++#define WF_LWTBL_RESP_RCPI1_DW 34
++#define WF_LWTBL_RESP_RCPI1_ADDR 136
++#define WF_LWTBL_RESP_RCPI1_MASK \
++ 0x0000ff00 // 15- 8
++#define WF_LWTBL_RESP_RCPI1_SHIFT 8
++#define WF_LWTBL_RESP_RCPI2_DW 34
++#define WF_LWTBL_RESP_RCPI2_ADDR 136
++#define WF_LWTBL_RESP_RCPI2_MASK \
++ 0x00ff0000 // 23-16
++#define WF_LWTBL_RESP_RCPI2_SHIFT 16
++#define WF_LWTBL_RESP_RCPI3_DW 34
++#define WF_LWTBL_RESP_RCPI3_ADDR 136
++#define WF_LWTBL_RESP_RCPI3_MASK \
++ 0xff000000 // 31-24
++#define WF_LWTBL_RESP_RCPI3_SHIFT 24
++// DW35
++#define WF_LWTBL_SNR_RX0_DW 35
++#define WF_LWTBL_SNR_RX0_ADDR 140
++#define WF_LWTBL_SNR_RX0_MASK \
++ 0x0000003f // 5- 0
++#define WF_LWTBL_SNR_RX0_SHIFT 0
++#define WF_LWTBL_SNR_RX1_DW 35
++#define WF_LWTBL_SNR_RX1_ADDR 140
++#define WF_LWTBL_SNR_RX1_MASK \
++ 0x00000fc0 // 11- 6
++#define WF_LWTBL_SNR_RX1_SHIFT 6
++#define WF_LWTBL_SNR_RX2_DW 35
++#define WF_LWTBL_SNR_RX2_ADDR 140
++#define WF_LWTBL_SNR_RX2_MASK \
++ 0x0003f000 // 17-12
++#define WF_LWTBL_SNR_RX2_SHIFT 12
++#define WF_LWTBL_SNR_RX3_DW 35
++#define WF_LWTBL_SNR_RX3_ADDR 140
++#define WF_LWTBL_SNR_RX3_MASK \
++ 0x00fc0000 // 23-18
++#define WF_LWTBL_SNR_RX3_SHIFT 18
++
++/* WTBL Group - Packet Number */
++/* DW 2 */
++#define WTBL_PN0_MASK BITS(0, 7)
++#define WTBL_PN0_OFFSET 0
++#define WTBL_PN1_MASK BITS(8, 15)
++#define WTBL_PN1_OFFSET 8
++#define WTBL_PN2_MASK BITS(16, 23)
++#define WTBL_PN2_OFFSET 16
++#define WTBL_PN3_MASK BITS(24, 31)
++#define WTBL_PN3_OFFSET 24
++
++/* DW 3 */
++#define WTBL_PN4_MASK BITS(0, 7)
++#define WTBL_PN4_OFFSET 0
++#define WTBL_PN5_MASK BITS(8, 15)
++#define WTBL_PN5_OFFSET 8
++
++/* DW 4 */
++#define WTBL_BIPN0_MASK BITS(0, 7)
++#define WTBL_BIPN0_OFFSET 0
++#define WTBL_BIPN1_MASK BITS(8, 15)
++#define WTBL_BIPN1_OFFSET 8
++#define WTBL_BIPN2_MASK BITS(16, 23)
++#define WTBL_BIPN2_OFFSET 16
++#define WTBL_BIPN3_MASK BITS(24, 31)
++#define WTBL_BIPN3_OFFSET 24
++
++/* DW 5 */
++#define WTBL_BIPN4_MASK BITS(0, 7)
++#define WTBL_BIPN4_OFFSET 0
++#define WTBL_BIPN5_MASK BITS(8, 15)
++#define WTBL_BIPN5_OFFSET 8
++
++/* UWTBL DW 6 */
++#define WTBL_AMSDU_LEN_MASK BITS(0, 5)
++#define WTBL_AMSDU_LEN_OFFSET 0
++#define WTBL_AMSDU_NUM_MASK BITS(6, 10)
++#define WTBL_AMSDU_NUM_OFFSET 6
++#define WTBL_AMSDU_EN_MASK BIT(11)
++#define WTBL_AMSDU_EN_OFFSET 11
++
++/* UWTBL DW 8 */
++#define WTBL_SEC_ADDR_MODE_MASK BITS(20, 21)
++#define WTBL_SEC_ADDR_MODE_OFFSET 20
++
++/* LWTBL Rate field */
++#define WTBL_RATE_TX_RATE_MASK BITS(0, 5)
++#define WTBL_RATE_TX_RATE_OFFSET 0
++#define WTBL_RATE_TX_MODE_MASK BITS(6, 9)
++#define WTBL_RATE_TX_MODE_OFFSET 6
++#define WTBL_RATE_NSTS_MASK BITS(10, 13)
++#define WTBL_RATE_NSTS_OFFSET 10
++#define WTBL_RATE_STBC_MASK BIT(14)
++#define WTBL_RATE_STBC_OFFSET 14
++
++/***** WTBL(LMAC) DW Offset *****/
++/* LMAC WTBL Group - Peer Unique Information */
++#define WTBL_GROUP_PEER_INFO_DW_0 0
++#define WTBL_GROUP_PEER_INFO_DW_1 1
++
++/* WTBL Group - TxRx Capability/Information */
++#define WTBL_GROUP_TRX_CAP_DW_2 2
++#define WTBL_GROUP_TRX_CAP_DW_3 3
++#define WTBL_GROUP_TRX_CAP_DW_4 4
++#define WTBL_GROUP_TRX_CAP_DW_5 5
++#define WTBL_GROUP_TRX_CAP_DW_6 6
++#define WTBL_GROUP_TRX_CAP_DW_7 7
++#define WTBL_GROUP_TRX_CAP_DW_8 8
++#define WTBL_GROUP_TRX_CAP_DW_9 9
++
++/* WTBL Group - Auto Rate Table*/
++#define WTBL_GROUP_AUTO_RATE_1_2 10
++#define WTBL_GROUP_AUTO_RATE_3_4 11
++#define WTBL_GROUP_AUTO_RATE_5_6 12
++#define WTBL_GROUP_AUTO_RATE_7_8 13
++
++/* WTBL Group - Tx Counter */
++#define WTBL_GROUP_TX_CNT_LINE_1 14
++#define WTBL_GROUP_TX_CNT_LINE_2 15
++#define WTBL_GROUP_TX_CNT_LINE_3 16
++#define WTBL_GROUP_TX_CNT_LINE_4 17
++#define WTBL_GROUP_TX_CNT_LINE_5 18
++#define WTBL_GROUP_TX_CNT_LINE_6 19
++
++/* WTBL Group - Admission Control Counter */
++#define WTBL_GROUP_ADM_CNT_LINE_1 20
++#define WTBL_GROUP_ADM_CNT_LINE_2 21
++#define WTBL_GROUP_ADM_CNT_LINE_3 22
++#define WTBL_GROUP_ADM_CNT_LINE_4 23
++#define WTBL_GROUP_ADM_CNT_LINE_5 24
++#define WTBL_GROUP_ADM_CNT_LINE_6 25
++#define WTBL_GROUP_ADM_CNT_LINE_7 26
++#define WTBL_GROUP_ADM_CNT_LINE_8 27
++
++/* WTBL Group -MLO Info */
++#define WTBL_GROUP_MLO_INFO_LINE_1 28
++#define WTBL_GROUP_MLO_INFO_LINE_2 29
++#define WTBL_GROUP_MLO_INFO_LINE_3 30
++
++/* WTBL Group -RESP Info */
++#define WTBL_GROUP_RESP_INFO_DW_31 31
++
++/* WTBL Group -RX DUP Info */
++#define WTBL_GROUP_RX_DUP_INFO_DW_32 32
++
++/* WTBL Group - Rx Statistics Counter */
++#define WTBL_GROUP_RX_STAT_CNT_LINE_1 33
++#define WTBL_GROUP_RX_STAT_CNT_LINE_2 34
++#define WTBL_GROUP_RX_STAT_CNT_LINE_3 35
++
++/* UWTBL Group - HW AMSDU */
++#define UWTBL_HW_AMSDU_DW WF_UWTBL_AMSDU_CFG_DW
++
++/* LWTBL DW 4 */
++#define WTBL_DIS_RHTR WF_LWTBL_DIS_RHTR_MASK
++
++/* UWTBL DW 5 */
++#define WTBL_KEY_LINK_DW_KEY_LOC0_MASK BITS(0, 10)
++#define WTBL_PSM WF_LWTBL_PSM_MASK
++
++/* Need to sync with FW define */
++#define INVALID_KEY_ENTRY WTBL_KEY_LINK_DW_KEY_LOC0_MASK
++
++// RATE
++#define WTBL_RATE_TX_RATE_MASK BITS(0, 5)
++#define WTBL_RATE_TX_RATE_OFFSET 0
++#define WTBL_RATE_TX_MODE_MASK BITS(6, 9)
++#define WTBL_RATE_TX_MODE_OFFSET 6
++#define WTBL_RATE_NSTS_MASK BITS(10, 13)
++#define WTBL_RATE_NSTS_OFFSET 10
++#define WTBL_RATE_STBC_MASK BIT(14)
++#define WTBL_RATE_STBC_OFFSET 14
++#endif
++
++#endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+new file mode 100644
+index 000000000..8baf27c76
+--- /dev/null
++++ b/mt7996/mtk_debugfs.c
+@@ -0,0 +1,2507 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2023 MediaTek Inc.
++ */
++#include "mt7996.h"
++#include "../mt76.h"
++#include "mcu.h"
++#include "mac.h"
++#include "eeprom.h"
++#include "mtk_debug.h"
++#include "mtk_mcu.h"
++#include "coredump.h"
++
++#ifdef CONFIG_MTK_DEBUG
++
++/* AGG INFO */
++static int
++mt7996_agginfo_read_per_band(struct seq_file *s, int band_idx)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u64 total_burst, total_ampdu, ampdu_cnt[16];
++ u32 value, idx, row_idx, col_idx, start_range, agg_rang_sel[16], burst_cnt[16], band_offset = 0;
++ u8 partial_str[16] = {}, full_str[64] = {};
++
++ switch (band_idx) {
++ case 0:
++ band_offset = 0;
++ break;
++ case 1:
++ band_offset = BN1_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
++ break;
++ case 2:
++ band_offset = IP1_BN0_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
++ break;
++ default:
++ return 0;
++ }
++
++ seq_printf(s, "Band %d AGG Status\n", band_idx);
++ seq_printf(s, "===============================\n");
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR0_ADDR + band_offset);
++ seq_printf(s, "AC00 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC01 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT);
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR1_ADDR + band_offset);
++ seq_printf(s, "AC02 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC03 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT);
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR2_ADDR + band_offset);
++ seq_printf(s, "AC10 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC11 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT);
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR3_ADDR + band_offset);
++ seq_printf(s, "AC12 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC13 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT);
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR4_ADDR + band_offset);
++ seq_printf(s, "AC20 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC21 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT);
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR5_ADDR + band_offset);
++ seq_printf(s, "AC22 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC23 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT);
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR6_ADDR + band_offset);
++ seq_printf(s, "AC30 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC31 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT);
++ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR7_ADDR + band_offset);
++ seq_printf(s, "AC32 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT);
++ seq_printf(s, "AC33 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT);
++
++ switch (band_idx) {
++ case 0:
++ band_offset = 0;
++ break;
++ case 1:
++ band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++ break;
++ case 2:
++ band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++ break;
++ default:
++ return 0;
++ }
++
++ seq_printf(s, "===AMPDU Related Counters===\n");
++
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC0_ADDR + band_offset);
++ agg_rang_sel[0] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT;
++ agg_rang_sel[1] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT;
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC1_ADDR + band_offset);
++ agg_rang_sel[2] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT;
++ agg_rang_sel[3] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT;
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC2_ADDR + band_offset);
++ agg_rang_sel[4] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT;
++ agg_rang_sel[5] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT;
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC3_ADDR + band_offset);
++ agg_rang_sel[6] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT;
++ agg_rang_sel[7] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT;
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC4_ADDR + band_offset);
++ agg_rang_sel[8] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT;
++ agg_rang_sel[9] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT;
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC5_ADDR + band_offset);
++ agg_rang_sel[10] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT;
++ agg_rang_sel[11] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT;
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC6_ADDR + band_offset);
++ agg_rang_sel[12] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT;
++ agg_rang_sel[13] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT;
++ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC7_ADDR + band_offset);
++ agg_rang_sel[14] = (value & BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK) >> BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT;
++
++ burst_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR0_ADDR + band_offset);
++ burst_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR1_ADDR + band_offset);
++ burst_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR2_ADDR + band_offset);
++ burst_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR3_ADDR + band_offset);
++ burst_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR4_ADDR + band_offset);
++ burst_cnt[5] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR5_ADDR + band_offset);
++ burst_cnt[6] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR6_ADDR + band_offset);
++ burst_cnt[7] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR7_ADDR + band_offset);
++ burst_cnt[8] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR8_ADDR + band_offset);
++ burst_cnt[9] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR9_ADDR + band_offset);
++ burst_cnt[10] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR10_ADDR + band_offset);
++ burst_cnt[11] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR11_ADDR + band_offset);
++ burst_cnt[12] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR12_ADDR + band_offset);
++ burst_cnt[13] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR13_ADDR + band_offset);
++ burst_cnt[14] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR14_ADDR + band_offset);
++ burst_cnt[15] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR15_ADDR + band_offset);
++
++ start_range = 1;
++ total_burst = 0;
++ total_ampdu = 0;
++ agg_rang_sel[15] = 1023;
++
++ /* Need to add 1 after read from AGG_RANG_SEL CR */
++ for (idx = 0; idx < 16; idx++) {
++ agg_rang_sel[idx]++;
++ total_burst += burst_cnt[idx];
++
++ if (start_range == agg_rang_sel[idx])
++ ampdu_cnt[idx] = (u64) start_range * burst_cnt[idx];
++ else
++ ampdu_cnt[idx] = (u64) ((start_range + agg_rang_sel[idx]) >> 1) * burst_cnt[idx];
++
++ start_range = agg_rang_sel[idx] + 1;
++ total_ampdu += ampdu_cnt[idx];
++ }
++
++ start_range = 1;
++ sprintf(full_str, "%13s ", "Tx Agg Range:");
++
++ for (row_idx = 0; row_idx < 4; row_idx++) {
++ for (col_idx = 0; col_idx < 4; col_idx++, idx++) {
++ idx = 4 * row_idx + col_idx;
++
++ if (start_range == agg_rang_sel[idx])
++ sprintf(partial_str, "%d", agg_rang_sel[idx]);
++ else
++ sprintf(partial_str, "%d~%d", start_range, agg_rang_sel[idx]);
++
++ start_range = agg_rang_sel[idx] + 1;
++ sprintf(full_str + strlen(full_str), "%-11s ", partial_str);
++ }
++
++ idx = 4 * row_idx;
++
++ seq_printf(s, "%s\n", full_str);
++ seq_printf(s, "%13s 0x%-9x 0x%-9x 0x%-9x 0x%-9x\n",
++ row_idx ? "" : "Burst count:",
++ burst_cnt[idx], burst_cnt[idx + 1],
++ burst_cnt[idx + 2], burst_cnt[idx + 3]);
++
++ if (total_burst != 0) {
++ if (row_idx == 0)
++ sprintf(full_str, "%13s ",
++ "Burst ratio:");
++ else
++ sprintf(full_str, "%13s ", "");
++
++ for (col_idx = 0; col_idx < 4; col_idx++) {
++ u64 count = (u64) burst_cnt[idx + col_idx] * 100;
++
++ sprintf(partial_str, "(%llu%%)",
++ div64_u64(count, total_burst));
++ sprintf(full_str + strlen(full_str),
++ "%-11s ", partial_str);
++ }
++
++ seq_printf(s, "%s\n", full_str);
++
++ if (row_idx == 0)
++ sprintf(full_str, "%13s ",
++ "MDPU ratio:");
++ else
++ sprintf(full_str, "%13s ", "");
++
++ for (col_idx = 0; col_idx < 4; col_idx++) {
++ u64 count = ampdu_cnt[idx + col_idx] * 100;
++
++ sprintf(partial_str, "(%llu%%)",
++ div64_u64(count, total_ampdu));
++ sprintf(full_str + strlen(full_str),
++ "%-11s ", partial_str);
++ }
++
++ seq_printf(s, "%s\n", full_str);
++ }
++
++ sprintf(full_str, "%13s ", "");
++ }
++
++ return 0;
++}
++
++static int mt7996_agginfo_read_band0(struct seq_file *s, void *data)
++{
++ mt7996_agginfo_read_per_band(s, MT_BAND0);
++ return 0;
++}
++
++static int mt7996_agginfo_read_band1(struct seq_file *s, void *data)
++{
++ mt7996_agginfo_read_per_band(s, MT_BAND1);
++ return 0;
++}
++
++static int mt7996_agginfo_read_band2(struct seq_file *s, void *data)
++{
++ mt7996_agginfo_read_per_band(s, MT_BAND2);
++ return 0;
++}
++
++/* AMSDU INFO */
++static int mt7996_amsdu_result_read(struct seq_file *s, void *data)
++{
++#define HW_MSDU_CNT_ADDR 0xf400
++#define HW_MSDU_NUM_MAX 33
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u32 ple_stat[HW_MSDU_NUM_MAX] = {0}, total_amsdu = 0;
++ u8 i;
++
++ for (i = 0; i < HW_MSDU_NUM_MAX; i++)
++ ple_stat[i] = mt76_rr(dev, HW_MSDU_CNT_ADDR + i * 0x04);
++
++ seq_printf(s, "TXD counter status of MSDU:\n");
++
++ for (i = 0; i < HW_MSDU_NUM_MAX; i++)
++ total_amsdu += ple_stat[i];
++
++ for (i = 0; i < HW_MSDU_NUM_MAX; i++) {
++ seq_printf(s, "AMSDU pack count of %d MSDU in TXD: 0x%x ", i, ple_stat[i]);
++ if (total_amsdu != 0)
++ seq_printf(s, "(%d%%)\n", ple_stat[i] * 100 / total_amsdu);
++ else
++ seq_printf(s, "\n");
++ }
++
++ return 0;
++}
++
++/* DBG MODLE */
++static int
++mt7996_fw_debug_module_set(void *data, u64 module)
++{
++ struct mt7996_dev *dev = data;
++
++ dev->dbg.fw_dbg_module = module;
++ return 0;
++}
++
++static int
++mt7996_fw_debug_module_get(void *data, u64 *module)
++{
++ struct mt7996_dev *dev = data;
++
++ *module = dev->dbg.fw_dbg_module;
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_module, mt7996_fw_debug_module_get,
++ mt7996_fw_debug_module_set, "%lld\n");
++
++static int
++mt7996_fw_debug_level_set(void *data, u64 level)
++{
++ struct mt7996_dev *dev = data;
++
++ dev->dbg.fw_dbg_lv = level;
++ mt7996_mcu_fw_dbg_ctrl(dev, dev->dbg.fw_dbg_module, dev->dbg.fw_dbg_lv);
++ return 0;
++}
++
++static int
++mt7996_fw_debug_level_get(void *data, u64 *level)
++{
++ struct mt7996_dev *dev = data;
++
++ *level = dev->dbg.fw_dbg_lv;
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_level, mt7996_fw_debug_level_get,
++ mt7996_fw_debug_level_set, "%lld\n");
++
++/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_set */
++static int
++mt7996_wa_set(void *data, u64 val)
++{
++ struct mt7996_dev *dev = data;
++ u32 arg1, arg2, arg3;
++
++ arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
++ arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
++ arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
++
++ return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
++ arg1, arg2, arg3);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_set, NULL, mt7996_wa_set,
++ "0x%llx\n");
++
++/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_query */
++static int
++mt7996_wa_query(void *data, u64 val)
++{
++ struct mt7996_dev *dev = data;
++ u32 arg1, arg2, arg3;
++
++ arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
++ arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
++ arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
++
++ return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY),
++ arg1, arg2, arg3);
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_query, NULL, mt7996_wa_query,
++ "0x%llx\n");
++
++static int mt7996_dump_version(struct seq_file *s, void *data)
++{
++#define MAX_ADIE_NUM 3
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u32 regval;
++ u16 adie_chip_id, adie_chip_ver;
++ int adie_idx;
++ static const char * const fem_type[] = {
++ [MT7996_FEM_UNSET] = "N/A",
++ [MT7996_FEM_EXT] = "eFEM",
++ [MT7996_FEM_INT] = "iFEM",
++ [MT7996_FEM_MIX] = "mixed FEM",
++ };
++
++ seq_printf(s, "Version: 4.3.24.3\n");
++
++ if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
++ return 0;
++
++ seq_printf(s, "Rom Patch Build Time: %.16s\n", dev->patch_build_date);
++ seq_printf(s, "WM Patch Build Time: %.15s, Mode: %s\n",
++ dev->ram_build_date[MT7996_RAM_TYPE_WM],
++ dev->testmode_enable ? "Testmode" : "Normal mode");
++ seq_printf(s, "WA Patch Build Time: %.15s\n",
++ dev->ram_build_date[MT7996_RAM_TYPE_WA]);
++ seq_printf(s, "DSP Patch Build Time: %.15s\n",
++ dev->ram_build_date[MT7996_RAM_TYPE_DSP]);
++ for (adie_idx = 0; adie_idx < MAX_ADIE_NUM; adie_idx++) {
++ mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), ®val, false);
++ adie_chip_id = FIELD_GET(MT_ADIE_CHIP_ID_MASK, regval);
++ adie_chip_ver = FIELD_GET(MT_ADIE_VERSION_MASK, regval);
++ if (adie_chip_id)
++ seq_printf(s, "Adie %d: ID = 0x%04x, Ver = 0x%04x\n",
++ adie_idx, adie_chip_id, adie_chip_ver);
++ else
++ seq_printf(s, "Adie %d: ID = N/A, Ver = N/A\n", adie_idx);
++ }
++ seq_printf(s, "FEM type: %s\n", fem_type[dev->fem_type]);
++
++ return 0;
++}
++
++/* fw wm call trace info dump */
++void mt7996_show_lp_history(struct seq_file *s, u32 type)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ struct mt7996_crash_data *crash_data;
++ struct mt7996_coredump *dump;
++ u64 now = 0;
++ int i = 0;
++ u8 fw_type = !!type;
++
++ mutex_lock(&dev->dump_mutex);
++
++ crash_data = mt7996_coredump_new(dev, fw_type);
++ if (!crash_data) {
++ mutex_unlock(&dev->dump_mutex);
++ seq_printf(s, "the coredump is disable!\n");
++ return;
++ }
++ mutex_unlock(&dev->dump_mutex);
++
++ dump = mt7996_coredump_build(dev, fw_type, false);
++ if (!dump) {
++ seq_printf(s, "no call stack data found!\n");
++ return;
++ }
++
++ seq_printf(s, "\x1b[32m%s log output\x1b[0m\n", dump->fw_type);
++ seq_printf(s, "\x1b[32mfw status: %s\n", dump->fw_state);
++ mt7996_dump_version(s, NULL);
++ /* PC log */
++ now = jiffies;
++ for (i = 0; i < 10; i++)
++ seq_printf(s, "\tCurrent PC=%x\n", dump->pc_cur[i]);
++
++ seq_printf(s, "PC log contorl=0x%x(T=%llu)(latest PC index = 0x%x)\n",
++ dump->pc_dbg_ctrl, now, dump->pc_cur_idx);
++ for (i = 0; i < 32; i++)
++ seq_printf(s, "\tPC log(%d)=0x%08x\n", i, dump->pc_stack[i]);
++
++ /* LR log */
++ now = jiffies;
++ seq_printf(s, "\nLR log contorl=0x%x(T=%llu)(latest LR index = 0x%x)\n",
++ dump->lr_dbg_ctrl, now, dump->lr_cur_idx);
++ for (i = 0; i < 32; i++)
++ seq_printf(s, "\tLR log(%d)=0x%08x\n", i, dump->lr_stack[i]);
++
++ vfree(dump);
++}
++
++static int mt7996_fw_wa_info_read(struct seq_file *s, void *data)
++{
++ seq_printf(s, "======[ShowPcLpHistory]======\n");
++ mt7996_show_lp_history(s, MT7996_RAM_TYPE_WA);
++ seq_printf(s, "======[End ShowPcLpHistory]==\n");
++
++ return 0;
++}
++
++static int mt7996_fw_wm_info_read(struct seq_file *s, void *data)
++{
++ seq_printf(s, "======[ShowPcLpHistory]======\n");
++ mt7996_show_lp_history(s, MT7996_RAM_TYPE_WM);
++ seq_printf(s, "======[End ShowPcLpHistory]==\n");
++
++ return 0;
++}
++
++/* dma info dump */
++static void
++dump_dma_tx_ring_info(struct seq_file *s, struct mt7996_dev *dev, char *str1, char *str2, u32 ring_base)
++{
++ u32 base, cnt, cidx, didx, queue_cnt;
++
++ base= mt76_rr(dev, ring_base);
++ cnt = mt76_rr(dev, ring_base + 4);
++ cidx = mt76_rr(dev, ring_base + 8);
++ didx = mt76_rr(dev, ring_base + 12);
++ queue_cnt = (cidx >= didx) ? (cidx - didx) : (cidx - didx + cnt);
++
++ seq_printf(s, "%20s %6s %10x %15x %10x %10x %10x\n", str1, str2, base, cnt, cidx, didx, queue_cnt);
++}
++
++static void
++dump_dma_rx_ring_info(struct seq_file *s, struct mt7996_dev *dev, char *str1, char *str2, u32 ring_base)
++{
++ u32 base, ctrl1, cnt, cidx, didx, queue_cnt;
++
++ base= mt76_rr(dev, ring_base);
++ ctrl1 = mt76_rr(dev, ring_base + 4);
++ cidx = mt76_rr(dev, ring_base + 8) & 0xfff;
++ didx = mt76_rr(dev, ring_base + 12) & 0xfff;
++ cnt = ctrl1 & 0xfff;
++ queue_cnt = (didx > cidx) ? (didx - cidx - 1) : (didx - cidx + cnt - 1);
++
++ seq_printf(s, "%20s %6s %10x %10x(%3x) %10x %10x %10x\n",
++ str1, str2, base, ctrl1, cnt, cidx, didx, queue_cnt);
++}
++
++static void
++mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
++{
++ u32 sys_ctrl[10];
++
++ /* HOST DMA0 information */
++ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR);
++ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR);
++ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR);
++
++ seq_printf(s, "HOST_DMA Configuration\n");
++ seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
++ "DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
++ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++ "DMA0", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++
++ if (dev->hif2) {
++ /* HOST DMA1 information */
++ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR);
++ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR);
++ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR);
++
++ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++ "DMA0P1", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++ }
++
++ seq_printf(s, "HOST_DMA0 Ring Configuration\n");
++ seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
++ "Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
++ dump_dma_tx_ring_info(s, dev, "T0:TXD0(H2MAC)", "STA",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T1:TXD1(H2MAC)", "STA",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T2:TXD2(H2MAC)", "STA",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T3:", "STA",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T4:", "STA",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T5:", "STA",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T6:", "STA",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T16:FWDL", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T17:Cmd(H2WM)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T18:TXD0(H2WA)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T19:TXD1(H2WA)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T20:Cmd(H2WA)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T22:TXD3(H2WA)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR);
++
++
++ dump_dma_rx_ring_info(s, dev, "R0:Event(WM2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R1:Event(WA2H)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R2:TxDone0(WA2H)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R4:Data0(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R12:MSDU_PG2(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "IND:IND_CMD(MAC2H)", "Both",
++ WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR);
++
++ if (dev->hif2) {
++ seq_printf(s, "HOST_DMA0 PCIe1 Ring Configuration\n");
++ seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
++ "Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
++ dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
++
++ dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
++ }
++
++ /* MCU DMA information */
++ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR);
++ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR);
++ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR);
++
++ seq_printf(s, "MCU_DMA Configuration\n");
++ seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
++ "DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
++ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++ "DMA0", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
++ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++
++ seq_printf(s, "MCU_DMA0 Ring Configuration\n");
++ seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
++ "Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
++ dump_dma_tx_ring_info(s, dev, "T0:Event(WM2H)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T1:Event(WA2H)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T2:TxDone0(WA2H)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T3:TxDone1(WA2H)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T4:TXD(WM2MAC)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T5:TXCMD(WM2MAC)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T6:TXD(WA2MAC)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R0:FWDL", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R1:Cmd(H2WM)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R2:TXD0(H2WA)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R3:TXD1(H2WA)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R4:Cmd(H2WA)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R5:Data0(MAC2WM)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R6:TxDone(MAC2WM)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R7:SPL/RPT(MAC2WM)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R8:TxDone(MAC2WA)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R9:Data1(MAC2WM)", "Both",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R10:TXD2(H2WA)", "AP",
++ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
++
++ /* MEM DMA information */
++ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR);
++ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR);
++ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR);
++
++ seq_printf(s, "MEM_DMA Configuration\n");
++ seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
++ "DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
++ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++ "MEM", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
++ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++
++ seq_printf(s, "MEM_DMA Ring Configuration\n");
++ seq_printf(s, "%20s %6s %10s %10s %10s %10s %10s\n",
++ "Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
++ dump_dma_tx_ring_info(s, dev, "T0:CmdEvent(WM2WA)", "AP",
++ WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR);
++ dump_dma_tx_ring_info(s, dev, "T1:CmdEvent(WA2WM)", "AP",
++ WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R0:CmdEvent(WM2WA)", "AP",
++ WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR);
++ dump_dma_rx_ring_info(s, dev, "R1:CmdEvent(WA2WM)", "AP",
++ WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR);
++}
++
++static int mt7996_trinfo_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ mt7996_show_dma_info(s, dev);
++ return 0;
++}
++
++/* MIB INFO */
++static int mt7996_mibinfo_read_per_band(struct seq_file *s, int band_idx)
++{
++#define BSS_NUM 4
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u8 bss_nums = BSS_NUM;
++ u32 idx;
++ u32 mac_val, band_offset = 0, band_offset_umib = 0;
++ u32 msdr6, msdr9, msdr18;
++ u32 rvsr0, rscr26, rscr35, mctr5, mctr6, msr0, msr1, msr2;
++ u32 tbcr0, tbcr1, tbcr2, tbcr3, tbcr4;
++ u32 btscr[7];
++ u32 tdrcr[5];
++ u32 mbtocr[16], mbtbcr[16], mbrocr[16], mbrbcr[16];
++ u32 btcr, btbcr, brocr, brbcr, btdcr, brdcr;
++ u32 mu_cnt[5];
++ u32 ampdu_cnt[3];
++ u64 per;
++
++ switch (band_idx) {
++ case 0:
++ band_offset = 0;
++ band_offset_umib = 0;
++ break;
++ case 1:
++ band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++ band_offset_umib = WF_UMIB_TOP_B1BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
++ break;
++ case 2:
++ band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++ band_offset_umib = WF_UMIB_TOP_B2BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
++ break;
++ default:
++ return true;
++ }
++
++ seq_printf(s, "Band %d MIB Status\n", band_idx);
++ seq_printf(s, "===============================\n");
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_M0SCR0_ADDR + band_offset);
++ seq_printf(s, "MIB Status Control=0x%x\n", mac_val);
++
++ msdr6 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR6_ADDR + band_offset);
++ rvsr0 = mt76_rr(dev, BN0_WF_MIB_TOP_RVSR0_ADDR + band_offset);
++ rscr35 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR35_ADDR + band_offset);
++ msdr9 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR9_ADDR + band_offset);
++ rscr26 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR26_ADDR + band_offset);
++ mctr5 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR5_ADDR + band_offset);
++ mctr6 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR6_ADDR + band_offset);
++ msdr18 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR18_ADDR + band_offset);
++ msr0 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR0_ADDR + band_offset);
++ msr1 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR1_ADDR + band_offset);
++ msr2 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR2_ADDR + band_offset);
++ ampdu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR0_ADDR + band_offset);
++ ampdu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR3_ADDR + band_offset);
++ ampdu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR4_ADDR + band_offset);
++ ampdu_cnt[1] &= BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK;
++ ampdu_cnt[2] &= BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK;
++
++ seq_printf(s, "===Phy/Timing Related Counters===\n");
++ seq_printf(s, "\tChannelIdleCnt=0x%x\n",
++ msdr6 & BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK);
++ seq_printf(s, "\tCCA_NAV_Tx_Time=0x%x\n",
++ msdr9 & BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK);
++ seq_printf(s, "\tRx_MDRDY_CNT=0x%x\n",
++ rscr26 & BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK);
++ seq_printf(s, "\tCCK_MDRDY_TIME=0x%x, OFDM_MDRDY_TIME=0x%x",
++ msr0 & BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK,
++ msr1 & BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK);
++ seq_printf(s, ", OFDM_GREEN_MDRDY_TIME=0x%x\n",
++ msr2 & BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK);
++ seq_printf(s, "\tPrim CCA Time=0x%x\n",
++ mctr5 & BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK);
++ seq_printf(s, "\tSec CCA Time=0x%x\n",
++ mctr6 & BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK);
++ seq_printf(s, "\tPrim ED Time=0x%x\n",
++ msdr18 & BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK);
++
++ seq_printf(s, "===Tx Related Counters(Generic)===\n");
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR18_ADDR + band_offset);
++ dev->dbg.bcn_total_cnt[band_idx] +=
++ (mac_val & BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK);
++ seq_printf(s, "\tBeaconTxCnt=0x%x\n", dev->dbg.bcn_total_cnt[band_idx]);
++ dev->dbg.bcn_total_cnt[band_idx] = 0;
++
++ tbcr0 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR0_ADDR + band_offset);
++ seq_printf(s, "\tTx 20MHz Cnt=0x%x\n",
++ tbcr0 & BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK);
++ tbcr1 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR1_ADDR + band_offset);
++ seq_printf(s, "\tTx 40MHz Cnt=0x%x\n",
++ tbcr1 & BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK);
++ tbcr2 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR2_ADDR + band_offset);
++ seq_printf(s, "\tTx 80MHz Cnt=0x%x\n",
++ tbcr2 & BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK);
++ tbcr3 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR3_ADDR + band_offset);
++ seq_printf(s, "\tTx 160MHz Cnt=0x%x\n",
++ tbcr3 & BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK);
++ tbcr4 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR4_ADDR + band_offset);
++ seq_printf(s, "\tTx 320MHz Cnt=0x%x\n",
++ tbcr4 & BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK);
++ seq_printf(s, "\tAMPDU Cnt=0x%x\n", ampdu_cnt[0]);
++ seq_printf(s, "\tAMPDU MPDU Cnt=0x%x\n", ampdu_cnt[1]);
++ seq_printf(s, "\tAMPDU MPDU Ack Cnt=0x%x\n", ampdu_cnt[2]);
++ per = (ampdu_cnt[2] == 0 ?
++ 0 : 1000 * (ampdu_cnt[1] - ampdu_cnt[2]) / ampdu_cnt[1]);
++ seq_printf(s, "\tAMPDU MPDU PER=%llu.%1llu%%\n", per / 10, per % 10);
++
++ seq_printf(s, "===MU Related Counters===\n");
++ mu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BSCR2_ADDR + band_offset);
++ mu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR5_ADDR + band_offset);
++ mu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR6_ADDR + band_offset);
++ mu_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR8_ADDR + band_offset);
++ mu_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR7_ADDR + band_offset);
++
++ seq_printf(s, "\tMUBF_TX_COUNT=0x%x\n",
++ mu_cnt[0] & BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK);
++ seq_printf(s, "\tMU_TX_MPDU_COUNT(Ok+Fail)=0x%x\n", mu_cnt[1]);
++ seq_printf(s, "\tMU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[2]);
++ seq_printf(s, "\tMU_TO_MU_FAIL_PPDU_COUNT=0x%x\n", mu_cnt[3]);
++ seq_printf(s, "\tSU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[4]);
++
++ seq_printf(s, "===Rx Related Counters(Generic)===\n");
++ seq_printf(s, "\tVector Mismacth Cnt=0x%x\n",
++ rvsr0 & BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK);
++ seq_printf(s, "\tDelimiter Fail Cnt=0x%x\n",
++ rscr35 & BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK);
++
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR1_ADDR + band_offset);
++ seq_printf(s, "\tRxFCSErrCnt=0x%x\n",
++ (mac_val & BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK));
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR33_ADDR + band_offset);
++ seq_printf(s, "\tRxFifoFullCnt=0x%x\n",
++ (mac_val & BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK));
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR36_ADDR + band_offset);
++ seq_printf(s, "\tRxLenMismatch=0x%x\n",
++ (mac_val & BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK));
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR31_ADDR + band_offset);
++ seq_printf(s, "\tRxMPDUCnt=0x%x\n",
++ (mac_val & BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK));
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR27_ADDR + band_offset);
++ seq_printf(s, "\tRx AMPDU Cnt=0x%x\n", mac_val);
++ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR28_ADDR + band_offset);
++ seq_printf(s, "\tRx Total ByteCnt=0x%x\n", mac_val);
++
++
++ /* Per-BSS T/RX Counters */
++ seq_printf(s, "===Per-BSS Related Tx/Rx Counters===\n");
++ seq_printf(s, "BSS Idx TxCnt/DataCnt TxByteCnt RxOkCnt/DataCnt RxByteCnt\n");
++ for (idx = 0; idx < bss_nums; idx++) {
++ btcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTCR_ADDR + band_offset + idx * 4);
++ btdcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTDCR_ADDR + band_offset + idx * 4);
++ btbcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + idx * 4);
++
++ brocr = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + idx * 4);
++ brdcr = mt76_rr(dev, WF_UMIB_TOP_B0BRDCR_ADDR + band_offset_umib + idx * 4);
++ brbcr = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + idx * 4);
++
++ seq_printf(s, "%d\t 0x%x/0x%x\t 0x%x \t 0x%x/0x%x \t 0x%x\n",
++ idx, btcr, btdcr, btbcr, brocr, brdcr, brbcr);
++ }
++
++ seq_printf(s, "===Per-BSS Related MIB Counters===\n");
++ seq_printf(s, "BSS Idx RTSTx/RetryCnt BAMissCnt AckFailCnt FrmRetry1/2/3Cnt\n");
++
++ /* Per-BSS TX Status */
++ for (idx = 0; idx < bss_nums; idx++) {
++ btscr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR5_ADDR + band_offset + idx * 4);
++ btscr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR6_ADDR + band_offset + idx * 4);
++ btscr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR0_ADDR + band_offset + idx * 4);
++ btscr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR1_ADDR + band_offset + idx * 4);
++ btscr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR2_ADDR + band_offset + idx * 4);
++ btscr[5] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR3_ADDR + band_offset + idx * 4);
++ btscr[6] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR4_ADDR + band_offset + idx * 4);
++
++ seq_printf(s, "%d:\t0x%x/0x%x 0x%x \t 0x%x \t 0x%x/0x%x/0x%x\n",
++ idx, (btscr[0] & BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK),
++ (btscr[1] & BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK),
++ (btscr[2] & BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK),
++ (btscr[3] & BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK),
++ (btscr[4] & BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK),
++ (btscr[5] & BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK),
++ (btscr[6] & BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK));
++ }
++
++ /* Dummy delimiter insertion result */
++ seq_printf(s, "===Dummy delimiter insertion result===\n");
++ tdrcr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR0_ADDR + band_offset);
++ tdrcr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR1_ADDR + band_offset);
++ tdrcr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR2_ADDR + band_offset);
++ tdrcr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR3_ADDR + band_offset);
++ tdrcr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR4_ADDR + band_offset);
++
++ seq_printf(s, "Range0 = %d\t Range1 = %d\t Range2 = %d\t Range3 = %d\t Range4 = %d\n",
++ tdrcr[0],
++ tdrcr[1],
++ tdrcr[2],
++ tdrcr[3],
++ tdrcr[4]);
++
++ /* Per-MBSS T/RX Counters */
++ seq_printf(s, "===Per-MBSS Related Tx/Rx Counters===\n");
++ seq_printf(s, "MBSSIdx TxOkCnt TxByteCnt RxOkCnt RxByteCnt\n");
++
++ for (idx = 0; idx < 16; idx++) {
++ mbtocr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTOCR_ADDR + band_offset + (bss_nums + idx) * 4);
++ mbtbcr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + (bss_nums + idx) * 4);
++
++ mbrocr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
++ mbrbcr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
++ }
++
++ for (idx = 0; idx < 16; idx++) {
++ seq_printf(s, "%d\t 0x%x\t 0x%x \t 0x%x \t 0x%x\n",
++ idx, mbtocr[idx], mbtbcr[idx], mbrocr[idx], mbrbcr[idx]);
++ }
++
++ return 0;
++}
++
++static int mt7996_mibinfo_band0(struct seq_file *s, void *data)
++{
++ mt7996_mibinfo_read_per_band(s, MT_BAND0);
++ return 0;
++}
++
++static int mt7996_mibinfo_band1(struct seq_file *s, void *data)
++{
++ mt7996_mibinfo_read_per_band(s, MT_BAND1);
++ return 0;
++}
++
++static int mt7996_mibinfo_band2(struct seq_file *s, void *data)
++{
++ mt7996_mibinfo_read_per_band(s, MT_BAND2);
++ return 0;
++}
++
++/* WTBL INFO */
++static int
++mt7996_wtbl_read_raw(struct mt7996_dev *dev, u16 idx,
++ enum mt7996_wtbl_type type, u16 start_dw,
++ u16 len, void *buf)
++{
++ u32 *dest_cpy = (u32 *)buf;
++ u32 size_dw = len;
++ u32 src = 0;
++
++ if (!buf)
++ return 0xFF;
++
++ if (type == WTBL_TYPE_LMAC) {
++ mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
++ FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
++ src = LWTBL_IDX2BASE(idx, start_dw);
++ } else if (type == WTBL_TYPE_UMAC) {
++ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++ src = UWTBL_IDX2BASE(idx, start_dw);
++ } else if (type == WTBL_TYPE_KEY) {
++ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ MT_DBG_UWTBL_TOP_WDUCR_TARGET |
++ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++ src = KEYTBL_IDX2BASE(idx, start_dw);
++ }
++
++ while (size_dw--) {
++ *dest_cpy++ = mt76_rr(dev, src);
++ src += 4;
++ };
++
++ return 0;
++}
++
++#if 0
++static int
++mt7996_wtbl_write_raw(struct mt7996_dev *dev, u16 idx,
++ enum mt7996_wtbl_type type, u16 start_dw,
++ u32 val)
++{
++ u32 addr = 0;
++
++ if (type == WTBL_TYPE_LMAC) {
++ mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
++ FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
++ addr = LWTBL_IDX2BASE(idx, start_dw);
++ } else if (type == WTBL_TYPE_UMAC) {
++ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++ addr = UWTBL_IDX2BASE(idx, start_dw);
++ } else if (type == WTBL_TYPE_KEY) {
++ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ MT_DBG_UWTBL_TOP_WDUCR_TARGET |
++ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++ addr = KEYTBL_IDX2BASE(idx, start_dw);
++ }
++
++ mt76_wr(dev, addr, val);
++
++ return 0;
++}
++#endif
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW0[] = {
++ {"MUAR_IDX", WF_LWTBL_MUAR_MASK, WF_LWTBL_MUAR_SHIFT,false},
++ {"RCA1", WF_LWTBL_RCA1_MASK, NO_SHIFT_DEFINE, false},
++ {"KID", WF_LWTBL_KID_MASK, WF_LWTBL_KID_SHIFT, false},
++ {"RCID", WF_LWTBL_RCID_MASK, NO_SHIFT_DEFINE, false},
++ {"BAND", WF_LWTBL_BAND_MASK, WF_LWTBL_BAND_SHIFT,false},
++ {"RV", WF_LWTBL_RV_MASK, NO_SHIFT_DEFINE, false},
++ {"RCA2", WF_LWTBL_RCA2_MASK, NO_SHIFT_DEFINE, false},
++ {"WPI_FLAG", WF_LWTBL_WPI_FLAG_MASK, NO_SHIFT_DEFINE,true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw0_1(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LinkAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
++ lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
++
++ /* LMAC WTBL DW 0 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 0/1\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_PEER_INFO_DW_0*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW0[i].name) {
++
++ if (WTBL_LMAC_DW0[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW0[i].name,
++ (dw_value & WTBL_LMAC_DW0[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW0[i].name,
++ (dw_value & WTBL_LMAC_DW0[i].mask) >> WTBL_LMAC_DW0[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse *WTBL_LMAC_DW2;
++static const struct berse_wtbl_parse WTBL_LMAC_DW2_7996[] = {
++ {"AID", WF_LWTBL_AID_MASK, WF_LWTBL_AID_SHIFT, false},
++ {"GID_SU", WF_LWTBL_GID_SU_MASK, NO_SHIFT_DEFINE, false},
++ {"SPP_EN", WF_LWTBL_SPP_EN_MASK, NO_SHIFT_DEFINE, false},
++ {"WPI_EVEN", WF_LWTBL_WPI_EVEN_MASK, NO_SHIFT_DEFINE, false},
++ {"AAD_OM", WF_LWTBL_AAD_OM_MASK, NO_SHIFT_DEFINE, false},
++ {"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT, true},
++ {"FROM_DS", WF_LWTBL_FD_MASK, NO_SHIFT_DEFINE, false},
++ {"TO_DS", WF_LWTBL_TD_MASK, NO_SHIFT_DEFINE, false},
++ {"SW", WF_LWTBL_SW_MASK, NO_SHIFT_DEFINE, false},
++ {"UL", WF_LWTBL_UL_MASK, NO_SHIFT_DEFINE, false},
++ {"TX_POWER_SAVE", WF_LWTBL_TX_PS_MASK, NO_SHIFT_DEFINE, true},
++ {"QOS", WF_LWTBL_QOS_MASK, NO_SHIFT_DEFINE, false},
++ {"HT", WF_LWTBL_HT_MASK, NO_SHIFT_DEFINE, false},
++ {"VHT", WF_LWTBL_VHT_MASK, NO_SHIFT_DEFINE, false},
++ {"HE", WF_LWTBL_HE_MASK, NO_SHIFT_DEFINE, false},
++ {"EHT", WF_LWTBL_EHT_MASK, NO_SHIFT_DEFINE, false},
++ {"MESH", WF_LWTBL_MESH_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW2_7992[] = {
++ {"AID", WF_LWTBL_AID_MASK, WF_LWTBL_AID_SHIFT, false},
++ {"GID_SU", WF_LWTBL_GID_SU_MASK, NO_SHIFT_DEFINE, false},
++ {"DUAL_PTEC_EN", WF_LWTBL_DUAL_PTEC_EN_MASK, NO_SHIFT_DEFINE, false},
++ {"DUAL_CTS_CAP", WF_LWTBL_DUAL_CTS_CAP_MASK, NO_SHIFT_DEFINE, false},
++ {"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT, true},
++ {"FROM_DS", WF_LWTBL_FD_MASK, NO_SHIFT_DEFINE, false},
++ {"TO_DS", WF_LWTBL_TD_MASK, NO_SHIFT_DEFINE, false},
++ {"SW", WF_LWTBL_SW_MASK, NO_SHIFT_DEFINE, false},
++ {"UL", WF_LWTBL_UL_MASK, NO_SHIFT_DEFINE, false},
++ {"TX_POWER_SAVE", WF_LWTBL_TX_PS_MASK, NO_SHIFT_DEFINE, true},
++ {"QOS", WF_LWTBL_QOS_MASK, NO_SHIFT_DEFINE, false},
++ {"HT", WF_LWTBL_HT_MASK, NO_SHIFT_DEFINE, false},
++ {"VHT", WF_LWTBL_VHT_MASK, NO_SHIFT_DEFINE, false},
++ {"HE", WF_LWTBL_HE_MASK, NO_SHIFT_DEFINE, false},
++ {"EHT", WF_LWTBL_EHT_MASK, NO_SHIFT_DEFINE, false},
++ {"MESH", WF_LWTBL_MESH_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw2(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 2 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 2\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW2[i].name) {
++
++ if (WTBL_LMAC_DW2[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW2[i].name,
++ (dw_value & WTBL_LMAC_DW2[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW2[i].name,
++ (dw_value & WTBL_LMAC_DW2[i].mask) >> WTBL_LMAC_DW2[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW3[] = {
++ {"WMM_Q", WF_LWTBL_WMM_Q_MASK, WF_LWTBL_WMM_Q_SHIFT, false},
++ {"EHT_SIG_MCS", WF_LWTBL_EHT_SIG_MCS_MASK, WF_LWTBL_EHT_SIG_MCS_SHIFT, false},
++ {"HDRT_MODE", WF_LWTBL_HDRT_MODE_MASK, NO_SHIFT_DEFINE, false},
++ {"BEAM_CHG", WF_LWTBL_BEAM_CHG_MASK, NO_SHIFT_DEFINE, false},
++ {"EHT_LTF_SYM_NUM", WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK, WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT, true},
++ {"PFMU_IDX", WF_LWTBL_PFMU_IDX_MASK, WF_LWTBL_PFMU_IDX_SHIFT, false},
++ {"ULPF_IDX", WF_LWTBL_ULPF_IDX_MASK, WF_LWTBL_ULPF_IDX_SHIFT, false},
++ {"RIBF", WF_LWTBL_RIBF_MASK, NO_SHIFT_DEFINE, false},
++ {"ULPF", WF_LWTBL_ULPF_MASK, NO_SHIFT_DEFINE, false},
++ {"BYPASS_TXSMM", WF_LWTBL_BYPASS_TXSMM_MASK, NO_SHIFT_DEFINE, true},
++ {"TBF_HT", WF_LWTBL_TBF_HT_MASK, NO_SHIFT_DEFINE, false},
++ {"TBF_VHT", WF_LWTBL_TBF_VHT_MASK, NO_SHIFT_DEFINE, false},
++ {"TBF_HE", WF_LWTBL_TBF_HE_MASK, NO_SHIFT_DEFINE, false},
++ {"TBF_EHT", WF_LWTBL_TBF_EHT_MASK, NO_SHIFT_DEFINE, false},
++ {"IGN_FBK", WF_LWTBL_IGN_FBK_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw3(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 3 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 3\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_3*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW3[i].name) {
++
++ if (WTBL_LMAC_DW3[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW3[i].name,
++ (dw_value & WTBL_LMAC_DW3[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW3[i].name,
++ (dw_value & WTBL_LMAC_DW3[i].mask) >> WTBL_LMAC_DW3[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW4[] = {
++ {"NEGOTIATED_WINSIZE0", WF_LWTBL_NEGOTIATED_WINSIZE0_MASK, WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT, false},
++ {"WINSIZE1", WF_LWTBL_NEGOTIATED_WINSIZE1_MASK, WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT, false},
++ {"WINSIZE2", WF_LWTBL_NEGOTIATED_WINSIZE2_MASK, WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT, false},
++ {"WINSIZE3", WF_LWTBL_NEGOTIATED_WINSIZE3_MASK, WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT, true},
++ {"WINSIZE4", WF_LWTBL_NEGOTIATED_WINSIZE4_MASK, WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT, false},
++ {"WINSIZE5", WF_LWTBL_NEGOTIATED_WINSIZE5_MASK, WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT, false},
++ {"WINSIZE6", WF_LWTBL_NEGOTIATED_WINSIZE6_MASK, WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT, false},
++ {"WINSIZE7", WF_LWTBL_NEGOTIATED_WINSIZE7_MASK, WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT, true},
++ {"PE", WF_LWTBL_PE_MASK, WF_LWTBL_PE_SHIFT, false},
++ {"DIS_RHTR", WF_LWTBL_DIS_RHTR_MASK, NO_SHIFT_DEFINE, false},
++ {"LDPC_HT", WF_LWTBL_LDPC_HT_MASK, NO_SHIFT_DEFINE, false},
++ {"LDPC_VHT", WF_LWTBL_LDPC_VHT_MASK, NO_SHIFT_DEFINE, false},
++ {"LDPC_HE", WF_LWTBL_LDPC_HE_MASK, NO_SHIFT_DEFINE, false},
++ {"LDPC_EHT", WF_LWTBL_LDPC_EHT_MASK, NO_SHIFT_DEFINE, true},
++ {"BA_MODE", WF_LWTBL_BA_MODE_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw4(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 4 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 4\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_4*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW4[i].name) {
++ if (WTBL_LMAC_DW4[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW4[i].name,
++ (dw_value & WTBL_LMAC_DW4[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW4[i].name,
++ (dw_value & WTBL_LMAC_DW4[i].mask) >> WTBL_LMAC_DW4[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse *WTBL_LMAC_DW5;
++static const struct berse_wtbl_parse WTBL_LMAC_DW5_7996[] = {
++ {"AF", WF_LWTBL_AF_MASK, WF_LWTBL_AF_SHIFT, false},
++ {"AF_HE", WF_LWTBL_AF_HE_MASK, WF_LWTBL_AF_HE_SHIFT,false},
++ {"RTS", WF_LWTBL_RTS_MASK, NO_SHIFT_DEFINE, false},
++ {"SMPS", WF_LWTBL_SMPS_MASK, NO_SHIFT_DEFINE, false},
++ {"DYN_BW", WF_LWTBL_DYN_BW_MASK, NO_SHIFT_DEFINE, true},
++ {"MMSS", WF_LWTBL_MMSS_MASK, WF_LWTBL_MMSS_SHIFT,false},
++ {"USR", WF_LWTBL_USR_MASK, NO_SHIFT_DEFINE, false},
++ {"SR_RATE", WF_LWTBL_SR_R_MASK, WF_LWTBL_SR_R_SHIFT,false},
++ {"SR_ABORT", WF_LWTBL_SR_ABORT_MASK, NO_SHIFT_DEFINE, true},
++ {"TX_POWER_OFFSET", WF_LWTBL_TX_POWER_OFFSET_MASK, WF_LWTBL_TX_POWER_OFFSET_SHIFT, false},
++ {"LTF_EHT", WF_LWTBL_LTF_EHT_MASK, WF_LWTBL_LTF_EHT_SHIFT, false},
++ {"GI_EHT", WF_LWTBL_GI_EHT_MASK, WF_LWTBL_GI_EHT_SHIFT, false},
++ {"DOPPL", WF_LWTBL_DOPPL_MASK, NO_SHIFT_DEFINE, false},
++ {"TXOP_PS_CAP", WF_LWTBL_TXOP_PS_CAP_MASK, NO_SHIFT_DEFINE, false},
++ {"DONOT_UPDATE_I_PSM", WF_LWTBL_DU_I_PSM_MASK, NO_SHIFT_DEFINE, true},
++ {"I_PSM", WF_LWTBL_I_PSM_MASK, NO_SHIFT_DEFINE, false},
++ {"PSM", WF_LWTBL_PSM_MASK, NO_SHIFT_DEFINE, false},
++ {"SKIP_TX", WF_LWTBL_SKIP_TX_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW5_7992[] = {
++ {"AF", WF_LWTBL_AF_MASK_7992, WF_LWTBL_AF_SHIFT, false},
++ {"RTS", WF_LWTBL_RTS_MASK, NO_SHIFT_DEFINE, false},
++ {"SMPS", WF_LWTBL_SMPS_MASK, NO_SHIFT_DEFINE, false},
++ {"DYN_BW", WF_LWTBL_DYN_BW_MASK, NO_SHIFT_DEFINE, true},
++ {"MMSS", WF_LWTBL_MMSS_MASK, WF_LWTBL_MMSS_SHIFT,false},
++ {"USR", WF_LWTBL_USR_MASK, NO_SHIFT_DEFINE, false},
++ {"SR_RATE", WF_LWTBL_SR_R_MASK, WF_LWTBL_SR_R_SHIFT,false},
++ {"SR_ABORT", WF_LWTBL_SR_ABORT_MASK, NO_SHIFT_DEFINE, true},
++ {"TX_POWER_OFFSET", WF_LWTBL_TX_POWER_OFFSET_MASK, WF_LWTBL_TX_POWER_OFFSET_SHIFT, false},
++ {"LTF_EHT", WF_LWTBL_LTF_EHT_MASK, WF_LWTBL_LTF_EHT_SHIFT, false},
++ {"GI_EHT", WF_LWTBL_GI_EHT_MASK, WF_LWTBL_GI_EHT_SHIFT, false},
++ {"DOPPL", WF_LWTBL_DOPPL_MASK, NO_SHIFT_DEFINE, false},
++ {"TXOP_PS_CAP", WF_LWTBL_TXOP_PS_CAP_MASK, NO_SHIFT_DEFINE, false},
++ {"DONOT_UPDATE_I_PSM", WF_LWTBL_DU_I_PSM_MASK, NO_SHIFT_DEFINE, true},
++ {"I_PSM", WF_LWTBL_I_PSM_MASK, NO_SHIFT_DEFINE, false},
++ {"PSM", WF_LWTBL_PSM_MASK, NO_SHIFT_DEFINE, false},
++ {"SKIP_TX", WF_LWTBL_SKIP_TX_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw5(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 5 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 5\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW5[i].name) {
++ if (WTBL_LMAC_DW5[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW5[i].name,
++ (dw_value & WTBL_LMAC_DW5[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW5[i].name,
++ (dw_value & WTBL_LMAC_DW5[i].mask) >> WTBL_LMAC_DW5[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW6[] = {
++ {"CBRN", WF_LWTBL_CBRN_MASK, WF_LWTBL_CBRN_SHIFT, false},
++ {"DBNSS_EN", WF_LWTBL_DBNSS_EN_MASK, NO_SHIFT_DEFINE, false},
++ {"BAF_EN", WF_LWTBL_BAF_EN_MASK, NO_SHIFT_DEFINE, false},
++ {"RDGBA", WF_LWTBL_RDGBA_MASK, NO_SHIFT_DEFINE, false},
++ {"RDG", WF_LWTBL_R_MASK, NO_SHIFT_DEFINE, false},
++ {"SPE_IDX", WF_LWTBL_SPE_IDX_MASK, WF_LWTBL_SPE_IDX_SHIFT, true},
++ {"G2", WF_LWTBL_G2_MASK, NO_SHIFT_DEFINE, false},
++ {"G4", WF_LWTBL_G4_MASK, NO_SHIFT_DEFINE, false},
++ {"G8", WF_LWTBL_G8_MASK, NO_SHIFT_DEFINE, false},
++ {"G16", WF_LWTBL_G16_MASK, NO_SHIFT_DEFINE, true},
++ {"G2_LTF", WF_LWTBL_G2_LTF_MASK, WF_LWTBL_G2_LTF_SHIFT, false},
++ {"G4_LTF", WF_LWTBL_G4_LTF_MASK, WF_LWTBL_G4_LTF_SHIFT, false},
++ {"G8_LTF", WF_LWTBL_G8_LTF_MASK, WF_LWTBL_G8_LTF_SHIFT, false},
++ {"G16_LTF", WF_LWTBL_G16_LTF_MASK, WF_LWTBL_G16_LTF_SHIFT, true},
++ {"G2_HE", WF_LWTBL_G2_HE_MASK, WF_LWTBL_G2_HE_SHIFT, false},
++ {"G4_HE", WF_LWTBL_G4_HE_MASK, WF_LWTBL_G4_HE_SHIFT, false},
++ {"G8_HE", WF_LWTBL_G8_HE_MASK, WF_LWTBL_G8_HE_SHIFT, false},
++ {"G16_HE", WF_LWTBL_G16_HE_MASK, WF_LWTBL_G16_HE_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw6(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 6 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 6\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_6*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW6[i].name) {
++ if (WTBL_LMAC_DW6[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW6[i].name,
++ (dw_value & WTBL_LMAC_DW6[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW6[i].name,
++ (dw_value & WTBL_LMAC_DW6[i].mask) >> WTBL_LMAC_DW6[i].shift);
++ i++;
++ }
++}
++
++static void parse_fmac_lwtbl_dw7(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ int i = 0;
++
++ /* LMAC WTBL DW 7 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 7\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_7*4]);
++ dw_value = *addr;
++
++ for (i = 0; i < 8; i++) {
++ seq_printf(s, "\tBA_WIN_SIZE%u:%lu\n", i, ((dw_value & BITS(i*4, i*4+3)) >> i*4));
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW8[] = {
++ {"RTS_FAIL_CNT_AC0", WF_LWTBL_AC0_RTS_FAIL_CNT_MASK, WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT, false},
++ {"AC1", WF_LWTBL_AC1_RTS_FAIL_CNT_MASK, WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT, false},
++ {"AC2", WF_LWTBL_AC2_RTS_FAIL_CNT_MASK, WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT, false},
++ {"AC3", WF_LWTBL_AC3_RTS_FAIL_CNT_MASK, WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT, true},
++ {"PARTIAL_AID", WF_LWTBL_PARTIAL_AID_MASK, WF_LWTBL_PARTIAL_AID_SHIFT, false},
++ {"CHK_PER", WF_LWTBL_CHK_PER_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw8(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 8 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 8\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_8*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW8[i].name) {
++ if (WTBL_LMAC_DW8[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW8[i].name,
++ (dw_value & WTBL_LMAC_DW8[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW8[i].name,
++ (dw_value & WTBL_LMAC_DW8[i].mask) >> WTBL_LMAC_DW8[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse *WTBL_LMAC_DW9;
++static const struct berse_wtbl_parse WTBL_LMAC_DW9_7996[] = {
++ {"RX_AVG_MPDU_SIZE", WF_LWTBL_RX_AVG_MPDU_SIZE_MASK, WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT, false},
++ {"PRITX_SW_MODE", WF_LWTBL_PRITX_SW_MODE_MASK, NO_SHIFT_DEFINE, false},
++ {"PRITX_ERSU", WF_LWTBL_PRITX_ERSU_MASK, NO_SHIFT_DEFINE, false},
++ {"PRITX_PLR", WF_LWTBL_PRITX_PLR_MASK, NO_SHIFT_DEFINE, true},
++ {"PRITX_DCM", WF_LWTBL_PRITX_DCM_MASK, NO_SHIFT_DEFINE, false},
++ {"PRITX_ER106T", WF_LWTBL_PRITX_ER106T_MASK, NO_SHIFT_DEFINE, true},
++ /* {"FCAP(0:20 1:~40)", WTBL_FCAP_20_TO_160_MHZ, WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
++ {"MPDU_FAIL_CNT", WF_LWTBL_MPDU_FAIL_CNT_MASK, WF_LWTBL_MPDU_FAIL_CNT_SHIFT, false},
++ {"MPDU_OK_CNT", WF_LWTBL_MPDU_OK_CNT_MASK, WF_LWTBL_MPDU_OK_CNT_SHIFT, false},
++ {"RATE_IDX", WF_LWTBL_RATE_IDX_MASK, WF_LWTBL_RATE_IDX_SHIFT, true},
++ {NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW9_7992[] = {
++ {"RX_AVG_MPDU_SIZE", WF_LWTBL_RX_AVG_MPDU_SIZE_MASK, WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT, false},
++ {"PRITX_SW_MODE", WF_LWTBL_PRITX_SW_MODE_MASK_7992, NO_SHIFT_DEFINE, false},
++ {"PRITX_ERSU", WF_LWTBL_PRITX_ERSU_MASK_7992, NO_SHIFT_DEFINE, false},
++ {"PRITX_PLR", WF_LWTBL_PRITX_PLR_MASK_7992, NO_SHIFT_DEFINE, true},
++ {"PRITX_DCM", WF_LWTBL_PRITX_DCM_MASK, NO_SHIFT_DEFINE, false},
++ {"PRITX_ER106T", WF_LWTBL_PRITX_ER106T_MASK, NO_SHIFT_DEFINE, true},
++ /* {"FCAP(0:20 1:~40)", WTBL_FCAP_20_TO_160_MHZ, WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
++ {"MPDU_FAIL_CNT", WF_LWTBL_MPDU_FAIL_CNT_MASK, WF_LWTBL_MPDU_FAIL_CNT_SHIFT, false},
++ {"MPDU_OK_CNT", WF_LWTBL_MPDU_OK_CNT_MASK, WF_LWTBL_MPDU_OK_CNT_SHIFT, false},
++ {"RATE_IDX", WF_LWTBL_RATE_IDX_MASK, WF_LWTBL_RATE_IDX_SHIFT, true},
++ {NULL,}
++};
++
++char *fcap_name[] = {"20MHz", "20/40MHz", "20/40/80MHz", "20/40/80/160/80+80MHz", "20/40/80/160/80+80/320MHz"};
++
++static void parse_fmac_lwtbl_dw9(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 9 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 9\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_9*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW9[i].name) {
++ if (WTBL_LMAC_DW9[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW9[i].name,
++ (dw_value & WTBL_LMAC_DW9[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW9[i].name,
++ (dw_value & WTBL_LMAC_DW9[i].mask) >> WTBL_LMAC_DW9[i].shift);
++ i++;
++ }
++
++ /* FCAP parser */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "FCAP:%s\n", fcap_name[(dw_value & WF_LWTBL_FCAP_MASK) >> WF_LWTBL_FCAP_SHIFT]);
++}
++
++#define HW_TX_RATE_TO_MODE(_x) (((_x) & WTBL_RATE_TX_MODE_MASK) >> WTBL_RATE_TX_MODE_OFFSET)
++#define HW_TX_RATE_TO_MCS(_x, _mode) ((_x) & WTBL_RATE_TX_RATE_MASK >> WTBL_RATE_TX_RATE_OFFSET)
++#define HW_TX_RATE_TO_NSS(_x) (((_x) & WTBL_RATE_NSTS_MASK) >> WTBL_RATE_NSTS_OFFSET)
++#define HW_TX_RATE_TO_STBC(_x) (((_x) & WTBL_RATE_STBC_MASK) >> WTBL_RATE_STBC_OFFSET)
++
++#define MAX_TX_MODE 16
++static char *HW_TX_MODE_STR[] = {"CCK", "OFDM", "HT-Mix", "HT-GF", "VHT",
++ "N/A", "N/A", "N/A",
++ "HE_SU", "HE_EXT_SU", "HE_TRIG", "HE_MU",
++ "N/A",
++ "EHT_EXT_SU", "EHT_TRIG", "EHT_MU",
++ "N/A"};
++static char *HW_TX_RATE_CCK_STR[] = {"1M", "2Mlong", "5.5Mlong", "11Mlong", "N/A", "2Mshort", "5.5Mshort", "11Mshort", "N/A"};
++static char *HW_TX_RATE_OFDM_STR[] = {"6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"};
++
++static char *hw_rate_ofdm_str(uint16_t ofdm_idx)
++{
++ switch (ofdm_idx) {
++ case 11: /* 6M */
++ return HW_TX_RATE_OFDM_STR[0];
++
++ case 15: /* 9M */
++ return HW_TX_RATE_OFDM_STR[1];
++
++ case 10: /* 12M */
++ return HW_TX_RATE_OFDM_STR[2];
++
++ case 14: /* 18M */
++ return HW_TX_RATE_OFDM_STR[3];
++
++ case 9: /* 24M */
++ return HW_TX_RATE_OFDM_STR[4];
++
++ case 13: /* 36M */
++ return HW_TX_RATE_OFDM_STR[5];
++
++ case 8: /* 48M */
++ return HW_TX_RATE_OFDM_STR[6];
++
++ case 12: /* 54M */
++ return HW_TX_RATE_OFDM_STR[7];
++
++ default:
++ return HW_TX_RATE_OFDM_STR[8];
++ }
++}
++
++static char *hw_rate_str(u8 mode, uint16_t rate_idx)
++{
++ if (mode == 0)
++ return rate_idx < 8 ? HW_TX_RATE_CCK_STR[rate_idx] : HW_TX_RATE_CCK_STR[8];
++ else if (mode == 1)
++ return hw_rate_ofdm_str(rate_idx);
++ else
++ return "MCS";
++}
++
++static void
++parse_rate(struct seq_file *s, uint16_t rate_idx, uint16_t txrate)
++{
++ uint16_t txmode, mcs, nss, stbc;
++
++ txmode = HW_TX_RATE_TO_MODE(txrate);
++ mcs = HW_TX_RATE_TO_MCS(txrate, txmode);
++ nss = HW_TX_RATE_TO_NSS(txrate);
++ stbc = HW_TX_RATE_TO_STBC(txrate);
++
++ seq_printf(s, "\tRate%d(0x%x):TxMode=%d(%s), TxRate=%d(%s), Nsts=%d, STBC=%d\n",
++ rate_idx + 1, txrate,
++ txmode, (txmode < MAX_TX_MODE ? HW_TX_MODE_STR[txmode] : HW_TX_MODE_STR[MAX_TX_MODE]),
++ mcs, hw_rate_str(txmode, mcs), nss, stbc);
++}
++
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW10[] = {
++ {"RATE1", WF_LWTBL_RATE1_MASK, WF_LWTBL_RATE1_SHIFT},
++ {"RATE2", WF_LWTBL_RATE2_MASK, WF_LWTBL_RATE2_SHIFT},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw10(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 10 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 10\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_1_2*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW10[i].name) {
++ parse_rate(s, i, (dw_value & WTBL_LMAC_DW10[i].mask) >> WTBL_LMAC_DW10[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW11[] = {
++ {"RATE3", WF_LWTBL_RATE3_MASK, WF_LWTBL_RATE3_SHIFT},
++ {"RATE4", WF_LWTBL_RATE4_MASK, WF_LWTBL_RATE4_SHIFT},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw11(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 11 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 11\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_3_4*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW11[i].name) {
++ parse_rate(s, i+2, (dw_value & WTBL_LMAC_DW11[i].mask) >> WTBL_LMAC_DW11[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW12[] = {
++ {"RATE5", WF_LWTBL_RATE5_MASK, WF_LWTBL_RATE5_SHIFT},
++ {"RATE6", WF_LWTBL_RATE6_MASK, WF_LWTBL_RATE6_SHIFT},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw12(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 12 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 12\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_5_6*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW12[i].name) {
++ parse_rate(s, i+4, (dw_value & WTBL_LMAC_DW12[i].mask) >> WTBL_LMAC_DW12[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW13[] = {
++ {"RATE7", WF_LWTBL_RATE7_MASK, WF_LWTBL_RATE7_SHIFT},
++ {"RATE8", WF_LWTBL_RATE8_MASK, WF_LWTBL_RATE8_SHIFT},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw13(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 13 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 13\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_7_8*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW13[i].name) {
++ parse_rate(s, i+6, (dw_value & WTBL_LMAC_DW13[i].mask) >> WTBL_LMAC_DW13[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW14_BMC[] = {
++ {"CIPHER_IGTK", WF_LWTBL_CIPHER_SUIT_IGTK_MASK, WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT, false},
++ {"CIPHER_BIGTK", WF_LWTBL_CIPHER_SUIT_BIGTK_MASK, WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT, true},
++ {NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW14[] = {
++ {"RATE1_TX_CNT", WF_LWTBL_RATE1_TX_CNT_MASK, WF_LWTBL_RATE1_TX_CNT_SHIFT, false},
++ {"RATE1_FAIL_CNT", WF_LWTBL_RATE1_FAIL_CNT_MASK, WF_LWTBL_RATE1_FAIL_CNT_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw14(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr, *muar_addr = 0;
++ u32 dw_value, muar_dw_value = 0;
++ u16 i = 0;
++
++ /* DUMP DW14 for BMC entry only */
++ muar_addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
++ muar_dw_value = *muar_addr;
++ if (((muar_dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT)
++ == MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
++ /* LMAC WTBL DW 14 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 14\n");
++ addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW14_BMC[i].name) {
++ if (WTBL_LMAC_DW14_BMC[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14_BMC[i].name,
++ (dw_value & WTBL_LMAC_DW14_BMC[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14_BMC[i].name,
++ (dw_value & WTBL_LMAC_DW14_BMC[i].mask) >> WTBL_LMAC_DW14_BMC[i].shift);
++ i++;
++ }
++ } else {
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 14\n");
++ addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW14[i].name) {
++ if (WTBL_LMAC_DW14[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14[i].name,
++ (dw_value & WTBL_LMAC_DW14[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14[i].name,
++ (dw_value & WTBL_LMAC_DW14[i].mask) >> WTBL_LMAC_DW14[i].shift);
++ i++;
++ }
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW28[] = {
++ {"RELATED_IDX0", WF_LWTBL_RELATED_IDX0_MASK, WF_LWTBL_RELATED_IDX0_SHIFT, false},
++ {"RELATED_BAND0", WF_LWTBL_RELATED_BAND0_MASK, WF_LWTBL_RELATED_BAND0_SHIFT, false},
++ {"PRI_MLD_BAND", WF_LWTBL_PRIMARY_MLD_BAND_MASK, WF_LWTBL_PRIMARY_MLD_BAND_SHIFT, true},
++ {"RELATED_IDX1", WF_LWTBL_RELATED_IDX1_MASK, WF_LWTBL_RELATED_IDX1_SHIFT, false},
++ {"RELATED_BAND1", WF_LWTBL_RELATED_BAND1_MASK, WF_LWTBL_RELATED_BAND1_SHIFT, false},
++ {"SEC_MLD_BAND", WF_LWTBL_SECONDARY_MLD_BAND_MASK, WF_LWTBL_SECONDARY_MLD_BAND_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw28(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 28 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 28\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_1*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW28[i].name) {
++ if (WTBL_LMAC_DW28[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW28[i].name,
++ (dw_value & WTBL_LMAC_DW28[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW28[i].name,
++ (dw_value & WTBL_LMAC_DW28[i].mask) >>
++ WTBL_LMAC_DW28[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW29[] = {
++ {"DISPATCH_POLICY_MLD_TID0", WF_LWTBL_DISPATCH_POLICY0_MASK, WF_LWTBL_DISPATCH_POLICY0_SHIFT, false},
++ {"MLD_TID1", WF_LWTBL_DISPATCH_POLICY1_MASK, WF_LWTBL_DISPATCH_POLICY1_SHIFT, false},
++ {"MLD_TID2", WF_LWTBL_DISPATCH_POLICY2_MASK, WF_LWTBL_DISPATCH_POLICY2_SHIFT, false},
++ {"MLD_TID3", WF_LWTBL_DISPATCH_POLICY3_MASK, WF_LWTBL_DISPATCH_POLICY3_SHIFT, true},
++ {"MLD_TID4", WF_LWTBL_DISPATCH_POLICY4_MASK, WF_LWTBL_DISPATCH_POLICY4_SHIFT, false},
++ {"MLD_TID5", WF_LWTBL_DISPATCH_POLICY5_MASK, WF_LWTBL_DISPATCH_POLICY5_SHIFT, false},
++ {"MLD_TID6", WF_LWTBL_DISPATCH_POLICY6_MASK, WF_LWTBL_DISPATCH_POLICY6_SHIFT, false},
++ {"MLD_TID7", WF_LWTBL_DISPATCH_POLICY7_MASK, WF_LWTBL_DISPATCH_POLICY7_SHIFT, true},
++ {"OMLD_ID", WF_LWTBL_OWN_MLD_ID_MASK, WF_LWTBL_OWN_MLD_ID_SHIFT, false},
++ {"EMLSR0", WF_LWTBL_EMLSR0_MASK, NO_SHIFT_DEFINE, false},
++ {"EMLMR0", WF_LWTBL_EMLMR0_MASK, NO_SHIFT_DEFINE, false},
++ {"EMLSR1", WF_LWTBL_EMLSR1_MASK, NO_SHIFT_DEFINE, false},
++ {"EMLMR1", WF_LWTBL_EMLMR1_MASK, NO_SHIFT_DEFINE, true},
++ {"EMLSR2", WF_LWTBL_EMLSR2_MASK, NO_SHIFT_DEFINE, false},
++ {"EMLMR2", WF_LWTBL_EMLMR2_MASK, NO_SHIFT_DEFINE, false},
++ {"STR_BITMAP", WF_LWTBL_STR_BITMAP_MASK, WF_LWTBL_STR_BITMAP_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw29(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 29 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 29\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_2*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW29[i].name) {
++ if (WTBL_LMAC_DW29[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW29[i].name,
++ (dw_value & WTBL_LMAC_DW29[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW29[i].name,
++ (dw_value & WTBL_LMAC_DW29[i].mask) >>
++ WTBL_LMAC_DW29[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW30[] = {
++ {"DISPATCH_ORDER", WF_LWTBL_DISPATCH_ORDER_MASK, WF_LWTBL_DISPATCH_ORDER_SHIFT, false},
++ {"DISPATCH_RATIO", WF_LWTBL_DISPATCH_RATIO_MASK, WF_LWTBL_DISPATCH_RATIO_SHIFT, false},
++ {"LINK_MGF", WF_LWTBL_LINK_MGF_MASK, WF_LWTBL_LINK_MGF_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw30(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 30 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 30\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_3*4]);
++ dw_value = *addr;
++
++
++ while (WTBL_LMAC_DW30[i].name) {
++ if (WTBL_LMAC_DW30[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW30[i].name,
++ (dw_value & WTBL_LMAC_DW30[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW30[i].name,
++ (dw_value & WTBL_LMAC_DW30[i].mask) >> WTBL_LMAC_DW30[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW31[] = {
++ {"BFTX_TB", WF_LWTBL_BFTX_TB_MASK, NO_SHIFT_DEFINE, false},
++ {"DROP", WF_LWTBL_DROP_MASK, NO_SHIFT_DEFINE, false},
++ {"CASCAD", WF_LWTBL_CASCAD_MASK, NO_SHIFT_DEFINE, false},
++ {"ALL_ACK", WF_LWTBL_ALL_ACK_MASK, NO_SHIFT_DEFINE, false},
++ {"MPDU_SIZE", WF_LWTBL_MPDU_SIZE_MASK, WF_LWTBL_MPDU_SIZE_SHIFT, false},
++ {"RXD_DUP_MODE", WF_LWTBL_RXD_DUP_MODE_MASK, WF_LWTBL_RXD_DUP_MODE_SHIFT, true},
++ {"ACK_EN", WF_LWTBL_ACK_EN_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw31(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 31 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 31\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_RESP_INFO_DW_31*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW31[i].name) {
++ if (WTBL_LMAC_DW31[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW31[i].name,
++ (dw_value & WTBL_LMAC_DW31[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW31[i].name,
++ (dw_value & WTBL_LMAC_DW31[i].mask) >>
++ WTBL_LMAC_DW31[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW32[] = {
++ {"OM_INFO", WF_LWTBL_OM_INFO_MASK, WF_LWTBL_OM_INFO_SHIFT, false},
++ {"OM_INFO_EHT", WF_LWTBL_OM_INFO_EHT_MASK, WF_LWTBL_OM_INFO_EHT_SHIFT, false},
++ {"RXD_DUP_FOR_OM_CHG", WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK, NO_SHIFT_DEFINE, false},
++ {"RXD_DUP_WHITE_LIST", WF_LWTBL_RXD_DUP_WHITE_LIST_MASK, WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT, false},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw32(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 32 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 32\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_DUP_INFO_DW_32*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW32[i].name) {
++ if (WTBL_LMAC_DW32[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW32[i].name,
++ (dw_value & WTBL_LMAC_DW32[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW32[i].name,
++ (dw_value & WTBL_LMAC_DW32[i].mask) >>
++ WTBL_LMAC_DW32[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW33[] = {
++ {"USER_RSSI", WF_LWTBL_USER_RSSI_MASK, WF_LWTBL_USER_RSSI_SHIFT, false},
++ {"USER_SNR", WF_LWTBL_USER_SNR_MASK, WF_LWTBL_USER_SNR_SHIFT, false},
++ {"RAPID_REACTION_RATE", WF_LWTBL_RAPID_REACTION_RATE_MASK, WF_LWTBL_RAPID_REACTION_RATE_SHIFT, true},
++ {"HT_AMSDU(Read Only)", WF_LWTBL_HT_AMSDU_MASK, NO_SHIFT_DEFINE, false},
++ {"AMSDU_CROSS_LG(Read Only)", WF_LWTBL_AMSDU_CROSS_LG_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw33(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 33 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 33\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_1*4]);
++ dw_value = *addr;
++
++ while (WTBL_LMAC_DW33[i].name) {
++ if (WTBL_LMAC_DW33[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW33[i].name,
++ (dw_value & WTBL_LMAC_DW33[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW33[i].name,
++ (dw_value & WTBL_LMAC_DW33[i].mask) >>
++ WTBL_LMAC_DW33[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW34[] = {
++ {"RESP_RCPI0", WF_LWTBL_RESP_RCPI0_MASK, WF_LWTBL_RESP_RCPI0_SHIFT, false},
++ {"RCPI1", WF_LWTBL_RESP_RCPI1_MASK, WF_LWTBL_RESP_RCPI1_SHIFT, false},
++ {"RCPI2", WF_LWTBL_RESP_RCPI2_MASK, WF_LWTBL_RESP_RCPI2_SHIFT, false},
++ {"RCPI3", WF_LWTBL_RESP_RCPI3_MASK, WF_LWTBL_RESP_RCPI3_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw34(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 34 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 34\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_2*4]);
++ dw_value = *addr;
++
++
++ while (WTBL_LMAC_DW34[i].name) {
++ if (WTBL_LMAC_DW34[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW34[i].name,
++ (dw_value & WTBL_LMAC_DW34[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW34[i].name,
++ (dw_value & WTBL_LMAC_DW34[i].mask) >>
++ WTBL_LMAC_DW34[i].shift);
++ i++;
++ }
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW35[] = {
++ {"SNR 0", WF_LWTBL_SNR_RX0_MASK, WF_LWTBL_SNR_RX0_SHIFT, false},
++ {"SNR 1", WF_LWTBL_SNR_RX1_MASK, WF_LWTBL_SNR_RX1_SHIFT, false},
++ {"SNR 2", WF_LWTBL_SNR_RX2_MASK, WF_LWTBL_SNR_RX2_SHIFT, false},
++ {"SNR 3", WF_LWTBL_SNR_RX3_MASK, WF_LWTBL_SNR_RX3_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_lwtbl_dw35(struct seq_file *s, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ /* LMAC WTBL DW 35 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "LWTBL DW 35\n");
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_3*4]);
++ dw_value = *addr;
++
++
++ while (WTBL_LMAC_DW35[i].name) {
++ if (WTBL_LMAC_DW35[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW35[i].name,
++ (dw_value & WTBL_LMAC_DW35[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW35[i].name,
++ (dw_value & WTBL_LMAC_DW35[i].mask) >>
++ WTBL_LMAC_DW35[i].shift);
++ i++;
++ }
++}
++
++static void parse_fmac_lwtbl_rx_stats(struct seq_file *s, u8 *lwtbl)
++{
++ parse_fmac_lwtbl_dw33(s, lwtbl);
++ parse_fmac_lwtbl_dw34(s, lwtbl);
++ parse_fmac_lwtbl_dw35(s, lwtbl);
++}
++
++static void parse_fmac_lwtbl_mlo_info(struct seq_file *s, u8 *lwtbl)
++{
++ parse_fmac_lwtbl_dw28(s, lwtbl);
++ parse_fmac_lwtbl_dw29(s, lwtbl);
++ parse_fmac_lwtbl_dw30(s, lwtbl);
++}
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW9[] = {
++ {"RELATED_IDX0", WF_UWTBL_RELATED_IDX0_MASK, WF_UWTBL_RELATED_IDX0_SHIFT, false},
++ {"RELATED_BAND0", WF_UWTBL_RELATED_BAND0_MASK, WF_UWTBL_RELATED_BAND0_SHIFT, false},
++ {"PRI_MLD_BAND", WF_UWTBL_PRIMARY_MLD_BAND_MASK, WF_UWTBL_PRIMARY_MLD_BAND_SHIFT, true},
++ {"RELATED_IDX1", WF_UWTBL_RELATED_IDX1_MASK, WF_UWTBL_RELATED_IDX1_SHIFT, false},
++ {"RELATED_BAND1", WF_UWTBL_RELATED_BAND1_MASK, WF_UWTBL_RELATED_BAND1_SHIFT, false},
++ {"SEC_MLD_BAND", WF_UWTBL_SECONDARY_MLD_BAND_MASK, WF_UWTBL_SECONDARY_MLD_BAND_SHIFT, true},
++ {NULL,}
++};
++
++static void parse_fmac_uwtbl_mlo_info(struct seq_file *s, u8 *uwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ seq_printf(s, "\t\n");
++ seq_printf(s, "MldAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
++ uwtbl[4], uwtbl[5], uwtbl[6], uwtbl[7], uwtbl[0], uwtbl[1]);
++
++ /* UMAC WTBL DW 0 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "UWTBL DW 0\n");
++ addr = (u32 *)&(uwtbl[WF_UWTBL_OWN_MLD_ID_DW*4]);
++ dw_value = *addr;
++
++ seq_printf(s, "\t%s:%u\n", "OMLD_ID",
++ (dw_value & WF_UWTBL_OWN_MLD_ID_MASK) >> WF_UWTBL_OWN_MLD_ID_SHIFT);
++
++ /* UMAC WTBL DW 9 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "UWTBL DW 9\n");
++ addr = (u32 *)&(uwtbl[WF_UWTBL_RELATED_IDX0_DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_UMAC_DW9[i].name) {
++
++ if (WTBL_UMAC_DW9[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW9[i].name,
++ (dw_value & WTBL_UMAC_DW9[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW9[i].name,
++ (dw_value & WTBL_UMAC_DW9[i].mask) >>
++ WTBL_UMAC_DW9[i].shift);
++ i++;
++ }
++}
++
++static bool
++is_wtbl_bigtk_exist(u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++
++ addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
++ dw_value = *addr;
++ if (((dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT) ==
++ MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
++ addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_BIGTK_DW*4]);
++ dw_value = *addr;
++ if (((dw_value & WF_LWTBL_CIPHER_SUIT_BIGTK_MASK) >>
++ WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT) != IGTK_CIPHER_SUIT_NONE)
++ return true;
++ }
++
++ return false;
++}
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW2[] = {
++ {"PN0", WTBL_PN0_MASK, WTBL_PN0_OFFSET, false},
++ {"PN1", WTBL_PN1_MASK, WTBL_PN1_OFFSET, false},
++ {"PN2", WTBL_PN2_MASK, WTBL_PN2_OFFSET, true},
++ {"PN3", WTBL_PN3_MASK, WTBL_PN3_OFFSET, false},
++ {NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW3[] = {
++ {"PN4", WTBL_PN4_MASK, WTBL_PN4_OFFSET, false},
++ {"PN5", WTBL_PN5_MASK, WTBL_PN5_OFFSET, true},
++ {"COM_SN", WF_UWTBL_COM_SN_MASK, WF_UWTBL_COM_SN_SHIFT, true},
++ {NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW4_BIPN[] = {
++ {"BIPN0", WTBL_BIPN0_MASK, WTBL_BIPN0_OFFSET, false},
++ {"BIPN1", WTBL_BIPN1_MASK, WTBL_BIPN1_OFFSET, false},
++ {"BIPN2", WTBL_BIPN2_MASK, WTBL_BIPN2_OFFSET, true},
++ {"BIPN3", WTBL_BIPN3_MASK, WTBL_BIPN3_OFFSET, false},
++ {NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW5_BIPN[] = {
++ {"BIPN4", WTBL_BIPN4_MASK, WTBL_BIPN4_OFFSET, false},
++ {"BIPN5", WTBL_BIPN5_MASK, WTBL_BIPN5_OFFSET, true},
++ {NULL,}
++};
++
++static void parse_fmac_uwtbl_pn(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u16 i = 0;
++
++ seq_printf(s, "\t\n");
++ seq_printf(s, "UWTBL PN\n");
++
++ /* UMAC WTBL DW 2/3 */
++ addr = (u32 *)&(uwtbl[WF_UWTBL_PN_31_0__DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_UMAC_DW2[i].name) {
++ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW2[i].name,
++ (dw_value & WTBL_UMAC_DW2[i].mask) >>
++ WTBL_UMAC_DW2[i].shift);
++ i++;
++ }
++
++ i = 0;
++ addr = (u32 *)&(uwtbl[WF_UWTBL_PN_47_32__DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_UMAC_DW3[i].name) {
++ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW3[i].name,
++ (dw_value & WTBL_UMAC_DW3[i].mask) >>
++ WTBL_UMAC_DW3[i].shift);
++ i++;
++ }
++
++
++ /* UMAC WTBL DW 4/5 for BIGTK */
++ if (is_wtbl_bigtk_exist(lwtbl) == true) {
++ i = 0;
++ addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_31_0__DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_UMAC_DW4_BIPN[i].name) {
++ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW4_BIPN[i].name,
++ (dw_value & WTBL_UMAC_DW4_BIPN[i].mask) >>
++ WTBL_UMAC_DW4_BIPN[i].shift);
++ i++;
++ }
++
++ i = 0;
++ addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_47_32__DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_UMAC_DW5_BIPN[i].name) {
++ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW5_BIPN[i].name,
++ (dw_value & WTBL_UMAC_DW5_BIPN[i].mask) >>
++ WTBL_UMAC_DW5_BIPN[i].shift);
++ i++;
++ }
++ }
++}
++
++static void parse_fmac_uwtbl_sn(struct seq_file *s, u8 *uwtbl)
++{
++ u32 *addr = 0;
++ u32 u2SN = 0;
++
++ /* UMAC WTBL DW SN part */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "UWTBL SN\n");
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID0_SN_DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID0_SN_MASK) >> WF_UWTBL_TID0_SN_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "TID0_AC0_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID1_SN_DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID1_SN_MASK) >> WF_UWTBL_TID1_SN_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "TID1_AC1_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_7_0__DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID2_SN_7_0__MASK) >>
++ WF_UWTBL_TID2_SN_7_0__SHIFT;
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_11_8__DW*4]);
++ u2SN |= (((*addr) & WF_UWTBL_TID2_SN_11_8__MASK) >>
++ WF_UWTBL_TID2_SN_11_8__SHIFT) << 8;
++ seq_printf(s, "\t%s:%u\n", "TID2_AC2_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID3_SN_DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID3_SN_MASK) >> WF_UWTBL_TID3_SN_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "TID3_AC3_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID4_SN_DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID4_SN_MASK) >> WF_UWTBL_TID4_SN_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "TID4_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_3_0__DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID5_SN_3_0__MASK) >>
++ WF_UWTBL_TID5_SN_3_0__SHIFT;
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_11_4__DW*4]);
++ u2SN |= (((*addr) & WF_UWTBL_TID5_SN_11_4__MASK) >>
++ WF_UWTBL_TID5_SN_11_4__SHIFT) << 4;
++ seq_printf(s, "\t%s:%u\n", "TID5_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID6_SN_DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID6_SN_MASK) >> WF_UWTBL_TID6_SN_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "TID6_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_TID7_SN_DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_TID7_SN_MASK) >> WF_UWTBL_TID7_SN_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "TID7_SN", u2SN);
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_COM_SN_DW*4]);
++ u2SN = ((*addr) & WF_UWTBL_COM_SN_MASK) >> WF_UWTBL_COM_SN_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "COM_SN", u2SN);
++}
++
++static void dump_key_table(
++ struct seq_file *s,
++ uint16_t keyloc0,
++ uint16_t keyloc1,
++ uint16_t keyloc2
++)
++{
++#define ONE_KEY_ENTRY_LEN_IN_DW 8
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u8 keytbl[ONE_KEY_ENTRY_LEN_IN_DW*4] = {0};
++ uint16_t x;
++
++ seq_printf(s, "\t\n");
++ seq_printf(s, "\t%s:%d\n", "keyloc0", keyloc0);
++ if (keyloc0 != INVALID_KEY_ENTRY) {
++
++ /* Don't swap below two lines, halWtblReadRaw will
++ * write new value WF_WTBLON_TOP_WDUCR_ADDR
++ */
++ mt7996_wtbl_read_raw(dev, keyloc0,
++ WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
++ seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++ KEYTBL_IDX2BASE(keyloc0, 0));
++ for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
++ seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
++ x,
++ keytbl[x * 4 + 3],
++ keytbl[x * 4 + 2],
++ keytbl[x * 4 + 1],
++ keytbl[x * 4]);
++ }
++ }
++
++ seq_printf(s, "\t%s:%d\n", "keyloc1", keyloc1);
++ if (keyloc1 != INVALID_KEY_ENTRY) {
++ /* Don't swap below two lines, halWtblReadRaw will
++ * write new value WF_WTBLON_TOP_WDUCR_ADDR
++ */
++ mt7996_wtbl_read_raw(dev, keyloc1,
++ WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
++ seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++ KEYTBL_IDX2BASE(keyloc1, 0));
++ for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
++ seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
++ x,
++ keytbl[x * 4 + 3],
++ keytbl[x * 4 + 2],
++ keytbl[x * 4 + 1],
++ keytbl[x * 4]);
++ }
++ }
++
++ seq_printf(s, "\t%s:%d\n", "keyloc2", keyloc2);
++ if (keyloc2 != INVALID_KEY_ENTRY) {
++ /* Don't swap below two lines, halWtblReadRaw will
++ * write new value WF_WTBLON_TOP_WDUCR_ADDR
++ */
++ mt7996_wtbl_read_raw(dev, keyloc2,
++ WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
++ seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++ KEYTBL_IDX2BASE(keyloc2, 0));
++ for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
++ seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
++ x,
++ keytbl[x * 4 + 3],
++ keytbl[x * 4 + 2],
++ keytbl[x * 4 + 1],
++ keytbl[x * 4]);
++ }
++ }
++}
++
++static void parse_fmac_uwtbl_key_info(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ uint16_t keyloc0 = INVALID_KEY_ENTRY;
++ uint16_t keyloc1 = INVALID_KEY_ENTRY;
++ uint16_t keyloc2 = INVALID_KEY_ENTRY;
++
++ /* UMAC WTBL DW 7 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "UWTBL key info\n");
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC0_DW*4]);
++ dw_value = *addr;
++ keyloc0 = (dw_value & WF_UWTBL_KEY_LOC0_MASK) >> WF_UWTBL_KEY_LOC0_SHIFT;
++ keyloc1 = (dw_value & WF_UWTBL_KEY_LOC1_MASK) >> WF_UWTBL_KEY_LOC1_SHIFT;
++
++ seq_printf(s, "\t%s:%u/%u\n", "Key Loc 0/1", keyloc0, keyloc1);
++
++ /* UMAC WTBL DW 6 for BIGTK */
++ if (is_wtbl_bigtk_exist(lwtbl) == true) {
++ addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC2_DW*4]);
++ dw_value = *addr;
++ keyloc2 = (dw_value & WF_UWTBL_KEY_LOC2_MASK) >>
++ WF_UWTBL_KEY_LOC2_SHIFT;
++ seq_printf(s, "\t%s:%u\n", "Key Loc 2", keyloc2);
++ }
++
++ /* Parse KEY link */
++ dump_key_table(s, keyloc0, keyloc1, keyloc2);
++}
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW8[] = {
++ {"UWTBL_WMM_Q", WF_UWTBL_WMM_Q_MASK, WF_UWTBL_WMM_Q_SHIFT, false},
++ {"UWTBL_QOS", WF_UWTBL_QOS_MASK, NO_SHIFT_DEFINE, false},
++ {"UWTBL_HT_VHT_HE", WF_UWTBL_HT_MASK, NO_SHIFT_DEFINE, false},
++ {"UWTBL_HDRT_MODE", WF_UWTBL_HDRT_MODE_MASK, NO_SHIFT_DEFINE, true},
++ {NULL,}
++};
++
++static void parse_fmac_uwtbl_msdu_info(struct seq_file *s, u8 *uwtbl)
++{
++ u32 *addr = 0;
++ u32 dw_value = 0;
++ u32 amsdu_len = 0;
++ u16 i = 0;
++
++ /* UMAC WTBL DW 8 */
++ seq_printf(s, "\t\n");
++ seq_printf(s, "UWTBL DW8\n");
++
++ addr = (u32 *)&(uwtbl[WF_UWTBL_AMSDU_CFG_DW*4]);
++ dw_value = *addr;
++
++ while (WTBL_UMAC_DW8[i].name) {
++
++ if (WTBL_UMAC_DW8[i].shift == NO_SHIFT_DEFINE)
++ seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW8[i].name,
++ (dw_value & WTBL_UMAC_DW8[i].mask) ? 1 : 0);
++ else
++ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW8[i].name,
++ (dw_value & WTBL_UMAC_DW8[i].mask) >>
++ WTBL_UMAC_DW8[i].shift);
++ i++;
++ }
++
++ /* UMAC WTBL DW 8 - SEC_ADDR_MODE */
++ addr = (u32 *)&(uwtbl[WF_UWTBL_SEC_ADDR_MODE_DW*4]);
++ dw_value = *addr;
++ seq_printf(s, "\t%s:%lu\n", "SEC_ADDR_MODE",
++ (dw_value & WTBL_SEC_ADDR_MODE_MASK) >> WTBL_SEC_ADDR_MODE_OFFSET);
++
++ /* UMAC WTBL DW 8 - AMSDU_CFG */
++ seq_printf(s, "\t%s:%d\n", "HW AMSDU Enable",
++ (dw_value & WTBL_AMSDU_EN_MASK) ? 1 : 0);
++
++ amsdu_len = (dw_value & WTBL_AMSDU_LEN_MASK) >> WTBL_AMSDU_LEN_OFFSET;
++ if (amsdu_len == 0)
++ seq_printf(s, "\t%s:invalid (WTBL value=0x%x)\n", "HW AMSDU Len",
++ amsdu_len);
++ else if (amsdu_len == 1)
++ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++ 1,
++ 255,
++ amsdu_len);
++ else if (amsdu_len == 2)
++ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++ 256,
++ 511,
++ amsdu_len);
++ else if (amsdu_len == 3)
++ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++ 512,
++ 767,
++ amsdu_len);
++ else
++ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++ 256 * (amsdu_len - 1),
++ 256 * (amsdu_len - 1) + 255,
++ amsdu_len);
++
++ seq_printf(s, "\t%s:%lu (WTBL value=0x%lx)\n", "HW AMSDU Num",
++ ((dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET) + 1,
++ (dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET);
++}
++
++static int mt7996_wtbl_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u8 lwtbl[LWTBL_LEN_IN_DW * 4] = {0};
++ u8 uwtbl[UWTBL_LEN_IN_DW * 4] = {0};
++ int x;
++
++ mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_LMAC, 0,
++ LWTBL_LEN_IN_DW, lwtbl);
++ seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
++ seq_printf(s, "LMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++ MT_DBG_WTBLON_TOP_WDUCR_ADDR,
++ mt76_rr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR),
++ LWTBL_IDX2BASE(dev->wlan_idx, 0));
++ for (x = 0; x < LWTBL_LEN_IN_DW; x++) {
++ seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
++ x,
++ lwtbl[x * 4 + 3],
++ lwtbl[x * 4 + 2],
++ lwtbl[x * 4 + 1],
++ lwtbl[x * 4]);
++ }
++
++ /* Parse LWTBL */
++ parse_fmac_lwtbl_dw0_1(s, lwtbl);
++ parse_fmac_lwtbl_dw2(s, lwtbl);
++ parse_fmac_lwtbl_dw3(s, lwtbl);
++ parse_fmac_lwtbl_dw4(s, lwtbl);
++ parse_fmac_lwtbl_dw5(s, lwtbl);
++ parse_fmac_lwtbl_dw6(s, lwtbl);
++ parse_fmac_lwtbl_dw7(s, lwtbl);
++ parse_fmac_lwtbl_dw8(s, lwtbl);
++ parse_fmac_lwtbl_dw9(s, lwtbl);
++ parse_fmac_lwtbl_dw10(s, lwtbl);
++ parse_fmac_lwtbl_dw11(s, lwtbl);
++ parse_fmac_lwtbl_dw12(s, lwtbl);
++ parse_fmac_lwtbl_dw13(s, lwtbl);
++ parse_fmac_lwtbl_dw14(s, lwtbl);
++ parse_fmac_lwtbl_mlo_info(s, lwtbl);
++ parse_fmac_lwtbl_dw31(s, lwtbl);
++ parse_fmac_lwtbl_dw32(s, lwtbl);
++ parse_fmac_lwtbl_rx_stats(s, lwtbl);
++
++ mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_UMAC, 0,
++ UWTBL_LEN_IN_DW, uwtbl);
++ seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
++ seq_printf(s, "UMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++ UWTBL_IDX2BASE(dev->wlan_idx, 0));
++ for (x = 0; x < UWTBL_LEN_IN_DW; x++) {
++ seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
++ x,
++ uwtbl[x * 4 + 3],
++ uwtbl[x * 4 + 2],
++ uwtbl[x * 4 + 1],
++ uwtbl[x * 4]);
++ }
++
++ /* Parse UWTBL */
++ parse_fmac_uwtbl_mlo_info(s, uwtbl);
++ parse_fmac_uwtbl_pn(s, uwtbl, lwtbl);
++ parse_fmac_uwtbl_sn(s, uwtbl);
++ parse_fmac_uwtbl_key_info(s, uwtbl, lwtbl);
++ parse_fmac_uwtbl_msdu_info(s, uwtbl);
++
++ return 0;
++}
++
++static int mt7996_sta_info(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u8 lwtbl[LWTBL_LEN_IN_DW*4] = {0};
++ u16 i = 0;
++
++ for (i=0; i < mt7996_wtbl_size(dev); i++) {
++ mt7996_wtbl_read_raw(dev, i, WTBL_TYPE_LMAC, 0,
++ LWTBL_LEN_IN_DW, lwtbl);
++
++ if (lwtbl[4] || lwtbl[5] || lwtbl[6] || lwtbl[7] || lwtbl[0] || lwtbl[1]) {
++ u32 *addr, dw_value;
++
++ seq_printf(s, "wcid:%d\tAddr: %02x:%02x:%02x:%02x:%02x:%02x",
++ i, lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
++
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
++ dw_value = *addr;
++ seq_printf(s, "\t%s:%u", WTBL_LMAC_DW2[0].name,
++ (dw_value & WTBL_LMAC_DW2[0].mask) >> WTBL_LMAC_DW2[0].shift);
++
++ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
++ dw_value = *addr;
++ seq_printf(s, "\tPSM:%u\n", !!(dw_value & WF_LWTBL_PSM_MASK));
++ }
++ }
++
++ return 0;
++}
++
++static int mt7996_token_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ int msdu_id;
++ struct mt76_txwi_cache *txwi;
++
++ seq_printf(s, "Token from host:\n");
++ spin_lock_bh(&dev->mt76.token_lock);
++ idr_for_each_entry(&dev->mt76.token, txwi, msdu_id) {
++ seq_printf(s, "%4d (pending time %u ms)\n", msdu_id,
++ jiffies_to_msecs(jiffies - txwi->jiffies));
++ }
++ spin_unlock_bh(&dev->mt76.token_lock);
++ seq_printf(s, "\n");
++
++ return 0;
++}
++
++int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
++{
++ struct mt7996_dev *dev = phy->dev;
++ u32 device_id = (dev->mt76.rev) >> 16;
++ int i = 0;
++ static const struct mt7996_dbg_reg_desc dbg_reg_s[] = {
++ { 0x7990, mt7996_dbg_offs },
++ { 0x7992, mt7992_dbg_offs },
++ };
++
++ for (i = 0; i < ARRAY_SIZE(dbg_reg_s); i++) {
++ if (device_id == dbg_reg_s[i].id) {
++ dev->dbg_reg = &dbg_reg_s[i];
++ break;
++ }
++ }
++
++ if (is_mt7996(&dev->mt76)) {
++ WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7996;
++ WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7996;
++ WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7996;
++ } else {
++ WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7992;
++ WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7992;
++ WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7992;
++ }
++
++ /* agg */
++ debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info0", dir,
++ mt7996_agginfo_read_band0);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info1", dir,
++ mt7996_agginfo_read_band1);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info2", dir,
++ mt7996_agginfo_read_band2);
++ /* amsdu */
++ debugfs_create_devm_seqfile(dev->mt76.dev, "amsdu_info", dir,
++ mt7996_amsdu_result_read);
++
++ debugfs_create_file("fw_debug_module", 0600, dir, dev,
++ &fops_fw_debug_module);
++ debugfs_create_file("fw_debug_level", 0600, dir, dev,
++ &fops_fw_debug_level);
++ debugfs_create_file("fw_wa_query", 0600, dir, dev, &fops_wa_query);
++ debugfs_create_file("fw_wa_set", 0600, dir, dev, &fops_wa_set);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "fw_version", dir,
++ mt7996_dump_version);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wa_info", dir,
++ mt7996_fw_wa_info_read);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wm_info", dir,
++ mt7996_fw_wm_info_read);
++
++ debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info0", dir,
++ mt7996_mibinfo_band0);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info1", dir,
++ mt7996_mibinfo_band1);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info2", dir,
++ mt7996_mibinfo_band2);
++
++ debugfs_create_devm_seqfile(dev->mt76.dev, "sta_info", dir,
++ mt7996_sta_info);
++
++ debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
++ mt7996_trinfo_read);
++
++ debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
++ mt7996_wtbl_read);
++
++ debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
++
++ debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
++
++ return 0;
++}
++
++#endif
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+new file mode 100644
+index 000000000..c16b25ab5
+--- /dev/null
++++ b/mt7996/mtk_mcu.c
+@@ -0,0 +1,39 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2023 MediaTek Inc.
++ */
++
++#include <linux/firmware.h>
++#include <linux/fs.h>
++#include "mt7996.h"
++#include "mcu.h"
++#include "mac.h"
++#include "mtk_mcu.h"
++
++#ifdef CONFIG_MTK_DEBUG
++
++
++
++
++int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
++{
++ struct {
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++
++ __le16 item;
++ u8 __rsv2[2];
++ __le32 value;
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_CMD_MURU_DBG_INFO),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .item = cpu_to_le16(item),
++ .value = cpu_to_le32(val),
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
++ sizeof(req), true);
++}
++#endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+new file mode 100644
+index 000000000..7f4d4e029
+--- /dev/null
++++ b/mt7996/mtk_mcu.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: ISC */
++/*
++ * Copyright (C) 2023 MediaTek Inc.
++ */
++
++#ifndef __MT7996_MTK_MCU_H
++#define __MT7996_MTK_MCU_H
++
++#include "../mt76_connac_mcu.h"
++
++#ifdef CONFIG_MTK_DEBUG
++
++enum {
++ UNI_CMD_MURU_DBG_INFO = 0x18,
++};
++
++#endif
++
++#endif
+diff --git a/tools/fwlog.c b/tools/fwlog.c
+index e5d4a1051..3c6a61d71 100644
+--- a/tools/fwlog.c
++++ b/tools/fwlog.c
+@@ -26,7 +26,7 @@ static const char *debugfs_path(const char *phyname, const char *file)
+ return path;
+ }
+
+-static int mt76_set_fwlog_en(const char *phyname, bool en)
++static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ {
+ FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
+
+@@ -35,7 +35,13 @@ static int mt76_set_fwlog_en(const char *phyname, bool en)
+ return 1;
+ }
+
+- fprintf(f, "7");
++ if (en && val)
++ fprintf(f, "%s", val);
++ else if (en)
++ fprintf(f, "7");
++ else
++ fprintf(f, "0");
++
+ fclose(f);
+
+ return 0;
+@@ -76,6 +82,7 @@ static void handle_signal(int sig)
+
+ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ {
++#define BUF_SIZE 1504
+ struct sockaddr_in local = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = INADDR_ANY,
+@@ -84,9 +91,9 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ .sin_family = AF_INET,
+ .sin_port = htons(55688),
+ };
+- char buf[1504];
++ char *buf = calloc(BUF_SIZE, sizeof(char));
+ int ret = 0;
+- int yes = 1;
++ /* int yes = 1; */
+ int s, fd;
+
+ if (argc < 1) {
+@@ -105,13 +112,13 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ return 1;
+ }
+
+- setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
++ /* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
+ if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
+ perror("bind");
+ return 1;
+ }
+
+- if (mt76_set_fwlog_en(phyname, true))
++ if (mt76_set_fwlog_en(phyname, true, argv[1]))
+ return 1;
+
+ fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
+@@ -145,8 +152,8 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ if (!r)
+ continue;
+
+- if (len > sizeof(buf)) {
+- fprintf(stderr, "Length error: %d > %d\n", len, (int)sizeof(buf));
++ if (len > BUF_SIZE) {
++ fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
+ ret = 1;
+ break;
+ }
+@@ -171,7 +178,7 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ close(fd);
+
+ out:
+- mt76_set_fwlog_en(phyname, false);
++ mt76_set_fwlog_en(phyname, false, NULL);
+
+ return ret;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch
new file mode 100644
index 0000000..ccf5c1c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch
@@ -0,0 +1,66 @@
+From 8acf6c19fc99fc2183e62fd04b1c7b7c6ae82d3a Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Thu, 8 Jun 2023 17:32:33 +0800
+Subject: [PATCH 021/116] mtk: wifi: mt76: mt7996: add check for hostapd config
+ he_ldpc
+
+Add check for hostapd config he_ldpc.
+This capabilities is checked in mcu_beacon_check_caps in 7915.
+
+Add check for STA LDPC cap, if STA only have BCC we should not overwrite the phy_cap with config he_ldpc.
+
+CR-Id: WCNCR00259302
+Change-Id: I6d6f59df8897e3c00f2e0a1e3c6e5701e31c5e4b
+Change-Id: Ibe7e40ec1dbb40bd3f3d96741e9933ec00b50df0
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ mt7996/mcu.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 57cfa1494..bad370839 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1188,7 +1188,8 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ }
+
+ static void
+-mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
+ {
+ struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp mcs_map;
+@@ -1208,6 +1209,11 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ he->he_phy_cap[i] = elem->phy_cap_info[i];
+ }
+
++ if (vif->type == NL80211_IFTYPE_AP &&
++ (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
++ u8p_replace_bits(&he->he_phy_cap[1], vif->bss_conf.he_ldpc,
++ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
++
+ mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
+ switch (sta->deflink.bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+@@ -2113,7 +2119,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ * update sta_rec_he here.
+ */
+ if (changed)
+- mt7996_mcu_sta_he_tlv(skb, sta);
++ mt7996_mcu_sta_he_tlv(skb, vif, sta);
+
+ /* sta_rec_ra accommodates BW, NSS and only MCS range format
+ * i.e 0-{7,8,9} for VHT.
+@@ -2201,7 +2207,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ /* starec amsdu */
+ mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
+ /* starec he */
+- mt7996_mcu_sta_he_tlv(skb, sta);
++ mt7996_mcu_sta_he_tlv(skb, vif, sta);
+ /* starec he 6g*/
+ mt7996_mcu_sta_he_6g_tlv(skb, sta);
+ /* starec eht */
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch
new file mode 100644
index 0000000..167a546
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch
@@ -0,0 +1,2375 @@
+From 02ec22e1dac31435e5d5defea0b50778061da97e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 28 Dec 2022 22:24:25 +0800
+Subject: [PATCH 022/116] mtk: wifi: mt76: testmode: add basic testmode support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I0e09e7f5bc0fb9aa4e4ec906a0f5f169bcc261cb
+
+Add testmode eeprom buffer mode support
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Fix power & freq offset issue for iTest power cal & tx/rx verifcation
+1. Wait for fw to tx. Otherwise, iTest testing tool cannot get the
+accurate tx power.
+2. In crystal mode, freq offset is set in 6G band and forwarded to 5G
+and 2G band. Therefore, we should avoid reseting freq offset to 0 when
+6G interface is off.
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I849b11b4ccdecd2b7b525b29801c02b5207bbf91
+
+edcca return err in testmode; therefore, bypass it when we are in testmode idle state or testmode bf is on
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ eeprom.c | 6 +-
+ mac80211.c | 3 +-
+ mt76.h | 36 +++
+ mt76_connac_mcu.h | 2 +
+ mt7996/Makefile | 1 +
+ mt7996/eeprom.c | 37 ++-
+ mt7996/eeprom.h | 1 +
+ mt7996/init.c | 8 +
+ mt7996/mac.c | 3 +-
+ mt7996/main.c | 27 ++
+ mt7996/mcu.c | 59 +++-
+ mt7996/mcu.h | 33 +++
+ mt7996/mt7996.h | 28 +-
+ mt7996/testmode.c | 740 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/testmode.h | 299 +++++++++++++++++++
+ testmode.c | 126 ++++++--
+ testmode.h | 87 +++++-
+ tools/fields.c | 102 ++++++-
+ 18 files changed, 1548 insertions(+), 50 deletions(-)
+ create mode 100644 mt7996/testmode.c
+ create mode 100644 mt7996/testmode.h
+
+diff --git a/eeprom.c b/eeprom.c
+index 0bc66cc19..a0047d791 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -94,8 +94,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l
+ }
+
+ #ifdef CONFIG_NL80211_TESTMODE
+- dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
+- dev->test_mtd.offset = offset;
++ if (len == dev->eeprom.size) {
++ dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
++ dev->test_mtd.offset = offset;
++ }
+ #endif
+
+ out_put_node:
+diff --git a/mac80211.c b/mac80211.c
+index 993e155b9..b285407a8 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -845,7 +845,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
+ }
+
+ #ifdef CONFIG_NL80211_TESTMODE
+- if (phy->test.state == MT76_TM_STATE_RX_FRAMES) {
++ if (!(phy->test.flag & MT_TM_FW_RX_COUNT) &&
++ phy->test.state == MT76_TM_STATE_RX_FRAMES) {
+ phy->test.rx_stats.packets[q]++;
+ if (status->flag & RX_FLAG_FAILED_FCS_CRC)
+ phy->test.rx_stats.fcs_error[q]++;
+diff --git a/mt76.h b/mt76.h
+index 599787db2..58e8e726c 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -700,14 +700,21 @@ struct mt76_testmode_ops {
+ int (*set_params)(struct mt76_phy *phy, struct nlattr **tb,
+ enum mt76_testmode_state new_state);
+ int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
++ void (*reset_rx_stats)(struct mt76_phy *phy);
++ void (*tx_stop)(struct mt76_phy *phy);
++ int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
+ };
+
++#define MT_TM_FW_RX_COUNT BIT(0)
++
+ struct mt76_testmode_data {
+ enum mt76_testmode_state state;
+
+ u32 param_set[DIV_ROUND_UP(NUM_MT76_TM_ATTRS, 32)];
+ struct sk_buff *tx_skb;
+
++ u8 sku_en;
++
+ u32 tx_count;
+ u16 tx_mpdu_len;
+
+@@ -717,6 +724,7 @@ struct mt76_testmode_data {
+ u8 tx_rate_sgi;
+ u8 tx_rate_ldpc;
+ u8 tx_rate_stbc;
++ u16 tx_preamble_puncture;
+ u8 tx_ltf;
+
+ u8 tx_antenna_mask;
+@@ -726,6 +734,9 @@ struct mt76_testmode_data {
+ u32 tx_time;
+ u32 tx_ipg;
+
++ bool ibf;
++ bool ebf;
++
+ u32 freq_offset;
+
+ u8 tx_power[4];
+@@ -740,7 +751,16 @@ struct mt76_testmode_data {
+ struct {
+ u64 packets[__MT_RXQ_MAX];
+ u64 fcs_error[__MT_RXQ_MAX];
++ u64 len_mismatch;
+ } rx_stats;
++ u8 flag;
++
++ struct {
++ u8 type;
++ u8 enable;
++ } cfg;
++
++ u8 aid;
+ };
+
+ struct mt76_vif {
+@@ -1444,6 +1464,22 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
+ int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
+ int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
+
++static inline void
++mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
++{
++#ifdef CONFIG_NL80211_TESTMODE
++ td->param_set[idx / 32] |= BIT(idx % 32);
++#endif
++}
++
++static inline bool
++mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
++{
++#ifdef CONFIG_NL80211_TESTMODE
++ return td->param_set[idx / 32] & BIT(idx % 32);
++#endif
++}
++
+ static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
+ {
+ #ifdef CONFIG_NL80211_TESTMODE
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 70def0a3b..718552baf 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1267,12 +1267,14 @@ enum {
+ MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
+ MCU_UNI_CMD_RA = 0x2f,
+ MCU_UNI_CMD_MURU = 0x31,
++ MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32,
+ MCU_UNI_CMD_BF = 0x33,
+ MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
+ MCU_UNI_CMD_THERMAL = 0x35,
+ MCU_UNI_CMD_VOW = 0x37,
+ MCU_UNI_CMD_PP = 0x38,
+ MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
++ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ MCU_UNI_CMD_RRO = 0x57,
+ MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+ MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index a056b40e0..7bb17f440 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -8,5 +8,6 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+ debugfs.o mmio.o
+
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
++mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
+
+ mt7996e-y += mtk_debugfs.o mtk_mcu.o
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 121a3c958..f9b9ca25d 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -6,6 +6,11 @@
+ #include <linux/firmware.h>
+ #include "mt7996.h"
+ #include "eeprom.h"
++#include <linux/moduleparam.h>
++
++static bool testmode_enable;
++module_param(testmode_enable, bool, 0644);
++MODULE_PARM_DESC(testmode_enable, "Enable testmode");
+
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+@@ -43,6 +48,9 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+
+ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ {
++ if (dev->testmode_enable)
++ return MT7996_EEPROM_DEFAULT_TM;
++
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7990:
+ if (dev->chip_sku == MT7996_SKU_404)
+@@ -92,21 +100,36 @@ out:
+ return ret;
+ }
+
+-static int mt7996_eeprom_load(struct mt7996_dev *dev)
++int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
+ {
++ u8 *eeprom;
+ int ret;
+
++ /* load eeprom in flash or bin file mode to determine fw mode */
+ ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ dev->flash_mode = true;
+- } else {
+- u8 free_block_num;
+- u32 block_num, i;
+- u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
++ eeprom = dev->mt76.eeprom.data;
++ /* testmode enable priority: eeprom field > module parameter */
++ dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
++ testmode_enable;
++ }
++
++ return ret;
++}
++
++static int mt7996_eeprom_load(struct mt7996_dev *dev)
++{
++ int ret;
++ u8 free_block_num;
++ u32 block_num, i;
++ u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
+
++ /* flash or bin file mode eeprom is loaded before mcu init */
++ if (!dev->flash_mode) {
+ ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
+ if (ret < 0)
+ return ret;
+@@ -118,8 +141,8 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ /* read eeprom data from efuse */
+ block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+ for (i = 0; i < block_num; i++) {
+- ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size);
+- if (ret < 0)
++ ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
++ if (ret && ret != -EINVAL)
+ return ret;
+ }
+ }
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 72c38ad3b..de3ff4e27 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
+ MT_EE_MAC_ADDR = 0x004,
+ MT_EE_MAC_ADDR2 = 0x00a,
+ MT_EE_WIFI_CONF = 0x190,
++ MT_EE_TESTMODE_EN = 0x1af,
+ MT_EE_MAC_ADDR3 = 0x2c0,
+ MT_EE_RATE_DELTA_2G = 0x1400,
+ MT_EE_RATE_DELTA_5G = 0x147d,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index d58335a37..440e26d58 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -969,6 +969,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+
+ set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
+
++ ret = mt7996_eeprom_check_fw_mode(dev);
++ if (ret < 0)
++ return ret;
++
+ ret = mt7996_mcu_init(dev);
+ if (ret)
+ return ret;
+@@ -1419,6 +1423,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
+
+ mt7996_init_wiphy(hw, &dev->mt76.mmio.wed);
+
++#ifdef CONFIG_NL80211_TESTMODE
++ dev->mt76.test_ops = &mt7996_testmode_ops;
++#endif
++
+ ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ ARRAY_SIZE(mt76_rates));
+ if (ret)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 1f53d2303..603f6c0d7 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -685,7 +685,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ *info);
+ }
+
+- if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
++ if (rxv && mode >= MT_PHY_TYPE_HE_SU && mode < MT_PHY_TYPE_EHT_SU &&
++ !(status->flag & RX_FLAG_8023))
+ mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode);
+
+ if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 4fc1dd9a9..48cc87e07 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -23,6 +23,18 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
+ return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ }
+
++static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
++{
++ struct mt7996_phy *phy;
++ int i;
++
++ for (i = 0; i < __MT_MAX_BAND; i++) {
++ phy = __mt7996_phy(dev, i);
++ if (phy)
++ mt76_testmode_set_state(phy->mt76, MT76_TM_STATE_OFF);
++ }
++}
++
+ int mt7996_run(struct ieee80211_hw *hw)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+@@ -45,6 +57,8 @@ int mt7996_run(struct ieee80211_hw *hw)
+ }
+ }
+
++ mt7996_testmode_disable_all(dev);
++
+ mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
+
+ ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
+@@ -303,6 +317,11 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+
+ mt76_set_channel(phy->mt76);
+
++ if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
++ mt7996_tm_update_channel(phy);
++ goto out;
++ }
++
+ ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
+ if (ret)
+ goto out;
+@@ -411,6 +430,12 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ int ret;
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
++ if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
++ ret = mt7996_mcu_edcca_enable(phy, true);
++ if (ret)
++ return ret;
++ }
++
+ ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
+ phy->mt76->chandef.punctured);
+ if (ret)
+@@ -1523,6 +1548,8 @@ const struct ieee80211_ops mt7996_ops = {
+ .sta_set_decap_offload = mt7996_sta_set_decap_offload,
+ .add_twt_setup = mt7996_mac_add_twt_setup,
+ .twt_teardown_request = mt7996_twt_teardown_request,
++ CFG80211_TESTMODE_CMD(mt76_testmode_cmd)
++ CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ .sta_add_debugfs = mt7996_sta_add_debugfs,
+ #endif
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index bad370839..f09281430 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2865,8 +2865,12 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
+ {
+ int ret;
+
+- ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
+- MT7996_RAM_TYPE_WM);
++ if (dev->testmode_enable)
++ ret = __mt7996_load_ram(dev, "WM_TM", fw_name(dev, FIRMWARE_WM_TM),
++ MT7996_RAM_TYPE_WM_TM);
++ else
++ ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
++ MT7996_RAM_TYPE_WM);
+ if (ret)
+ return ret;
+
+@@ -3557,17 +3561,9 @@ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+ &req, sizeof(req), true);
+ }
+
+-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
++int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ {
+- struct {
+- u8 _rsv[4];
+-
+- __le16 tag;
+- __le16 len;
+- __le32 addr;
+- __le32 valid;
+- u8 data[16];
+- } __packed req = {
++ struct mt7996_mcu_eeprom_info req = {
+ .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .addr = cpu_to_le32(round_down(offset,
+@@ -3576,6 +3572,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
+ struct sk_buff *skb;
+ bool valid;
+ int ret;
++ u8 *buf = read_buf;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
+@@ -3586,7 +3583,9 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
+ valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
+ if (valid) {
+ u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
+- u8 *buf = (u8 *)dev->mt76.eeprom.data + addr;
++
++ if (!buf)
++ buf = (u8 *)dev->mt76.eeprom.data + addr;
+
+ skb_pull(skb, 48);
+ memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
+@@ -4609,3 +4608,37 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
+ &req, sizeof(req), false);
+ }
++
++int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct tx_power_ctrl req = {
++ .tag = cpu_to_le16(power_ctrl_id),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .power_ctrl_id = power_ctrl_id,
++ .band_idx = phy->mt76->band_idx,
++ };
++
++ switch (power_ctrl_id) {
++ case UNI_TXPOWER_SKU_POWER_LIMIT_CTRL:
++ req.sku_enable = !!data;
++ break;
++ case UNI_TXPOWER_PERCENTAGE_CTRL:
++ req.percentage_ctrl_enable = !!data;
++ break;
++ case UNI_TXPOWER_PERCENTAGE_DROP_CTRL:
++ req.power_drop_level = data;
++ break;
++ case UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL:
++ req.bf_backoff_enable = !!data;
++ break;
++ case UNI_TXPOWER_ATE_MODE_CTRL:
++ req.ate_mode_enable = !!data;
++ break;
++ default:
++ req.sku_enable = !!data;
++ }
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
++ &req, sizeof(req), false);
++}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 2052555fe..d5ac2b38e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -157,6 +157,16 @@ struct mt7996_mcu_eeprom {
+ __le16 buf_len;
+ } __packed;
+
++struct mt7996_mcu_eeprom_info {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++ __le32 addr;
++ __le32 valid;
++ u8 data[MT7996_EEPROM_BLOCK_SIZE];
++} __packed;
++
+ struct mt7996_mcu_phy_rx_info {
+ u8 category;
+ u8 rate;
+@@ -889,8 +899,31 @@ enum {
+ UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
+ };
+
++struct tx_power_ctrl {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 power_ctrl_id;
++ union {
++ bool sku_enable;
++ bool ate_mode_enable;
++ bool percentage_ctrl_enable;
++ bool bf_backoff_enable;
++ u8 power_drop_level;
++ };
++ u8 band_idx;
++ u8 rsv[1];
++} __packed;
++
+ enum {
++ UNI_TXPOWER_SKU_POWER_LIMIT_CTRL = 0,
++ UNI_TXPOWER_PERCENTAGE_CTRL = 1,
++ UNI_TXPOWER_PERCENTAGE_DROP_CTRL = 2,
++ UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
+ UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
++ UNI_TXPOWER_ATE_MODE_CTRL = 6,
+ };
+
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 696e16fa1..0c6a4b96b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -32,25 +32,30 @@
+ #define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin"
+ #define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin"
+ #define MT7996_FIRMWARE_DSP "mediatek/mt7996/mt7996_dsp.bin"
++#define MT7996_FIRMWARE_WM_TM "mediatek/mt7996/mt7996_wm_tm.bin"
+ #define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin"
+
+ #define MT7992_FIRMWARE_WA "mediatek/mt7996/mt7992_wa.bin"
+ #define MT7992_FIRMWARE_WM "mediatek/mt7996/mt7992_wm.bin"
+ #define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin"
++#define MT7992_FIRMWARE_WM_TM "mediatek/mt7996/mt7992_wm_tm.bin"
+ #define MT7992_ROM_PATCH "mediatek/mt7996/mt7992_rom_patch.bin"
+
+ #define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin"
+ #define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin"
+ #define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin"
++#define MT7992_FIRMWARE_WM_TM_24 "mediatek/mt7996/mt7992_wm_tm_24.bin"
+ #define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin"
+
+ #define MT7992_FIRMWARE_WA_23 "mediatek/mt7996/mt7992_wa_23.bin"
+ #define MT7992_FIRMWARE_WM_23 "mediatek/mt7996/mt7992_wm_23.bin"
+ #define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin"
++#define MT7992_FIRMWARE_WM_TM_23 "mediatek/mt7996/mt7992_wm_tm_23.bin"
+ #define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
+
+ #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
+ #define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
++#define MT7996_EEPROM_DEFAULT_TM "mediatek/mt7996/mt7996_eeprom_tm.bin"
+ #define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom_2i5i.bin"
+ #define MT7992_EEPROM_DEFAULT_EXT "mediatek/mt7996/mt7992_eeprom_2e5e.bin"
+ #define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin"
+@@ -127,6 +132,7 @@ enum mt7992_sku_type {
+
+ enum mt7996_ram_type {
+ MT7996_RAM_TYPE_WM,
++ MT7996_RAM_TYPE_WM_TM = MT7996_RAM_TYPE_WM,
+ MT7996_RAM_TYPE_WA,
+ MT7996_RAM_TYPE_DSP,
+ __MT7996_RAM_TYPE_MAX,
+@@ -277,6 +283,21 @@ struct mt7996_phy {
+
+ u8 pp_mode;
+ u16 punct_bitmap;
++
++#ifdef CONFIG_NL80211_TESTMODE
++ struct {
++ u32 *reg_backup;
++
++ s32 last_freq_offset;
++ u8 last_rcpi[4];
++ s8 last_rssi[4];
++ s8 last_ib_rssi[4];
++ s8 last_wb_rssi[4];
++ u8 last_snr;
++
++ u8 spe_idx;
++ } test;
++#endif
+ };
+
+ struct mt7996_dev {
+@@ -357,6 +378,8 @@ struct mt7996_dev {
+ spinlock_t lock;
+ } wed_rro;
+
++ bool testmode_enable;
++
+ bool ibf;
+ u8 fw_debug_wm;
+ u8 fw_debug_wa;
+@@ -472,6 +495,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
++extern const struct mt76_testmode_ops mt7996_testmode_ops;
+
+ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ void __iomem *mem_base, u32 device_id);
+@@ -481,6 +505,7 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+ int mt7996_register_device(struct mt7996_dev *dev);
+ void mt7996_unregister_device(struct mt7996_dev *dev);
+ int mt7996_eeprom_init(struct mt7996_dev *dev);
++int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ struct ieee80211_channel *chan);
+@@ -533,7 +558,7 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, void *data, u32 field);
+ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
+-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset);
++int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
+ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
+ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap);
+ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
+@@ -568,6 +593,7 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
++int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
+
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+new file mode 100644
+index 000000000..98eebceed
+--- /dev/null
++++ b/mt7996/testmode.c
+@@ -0,0 +1,740 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2022 MediaTek Inc.
++ */
++
++#include "mt7996.h"
++#include "mac.h"
++#include "mcu.h"
++#include "testmode.h"
++
++enum {
++ TM_CHANGED_TXPOWER,
++ TM_CHANGED_FREQ_OFFSET,
++ TM_CHANGED_SKU_EN,
++ TM_CHANGED_TX_LENGTH,
++ TM_CHANGED_TX_TIME,
++ TM_CHANGED_CFG,
++
++ /* must be last */
++ NUM_TM_CHANGED
++};
++
++static const u8 tm_change_map[] = {
++ [TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
++ [TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
++ [TM_CHANGED_SKU_EN] = MT76_TM_ATTR_SKU_EN,
++ [TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
++ [TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
++ [TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
++};
++
++static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
++{
++ static const u8 width_to_bw[][NUM_BW_MAP] = {
++ [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
++ [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
++ [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
++ [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
++ [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
++ [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
++ [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
++ [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
++ [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
++ };
++
++ if (width >= ARRAY_SIZE(width_to_bw))
++ return 0;
++
++ return width_to_bw[width][method];
++}
++
++static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
++{
++ static const u8 rate_to_phy[] = {
++ [MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
++ [MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
++ [MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
++ [MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
++ [MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
++ [MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
++ [MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
++ [MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
++ [MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
++ [MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
++ [MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
++ };
++
++ if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
++ return -EINVAL;
++
++ return rate_to_phy[tx_rate_mode];
++}
++
++static int
++mt7996_tm_check_antenna(struct mt7996_phy *phy)
++{
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_dev *dev = phy->dev;
++ u8 band_idx = phy->mt76->band_idx;
++ u32 chainmask = phy->mt76->chainmask;
++ u32 aux_rx_mask;
++
++ chainmask = chainmask >> dev->chainshift[band_idx];
++ aux_rx_mask = BIT(fls(chainmask)) * phy->has_aux_rx;
++ if (td->tx_antenna_mask & ~(chainmask | aux_rx_mask)) {
++ dev_err(dev->mt76.dev,
++ "tx antenna mask 0x%x exceeds hw limit (chainmask 0x%x, has aux rx: %s)\n",
++ td->tx_antenna_mask, chainmask, phy->has_aux_rx ? "yes" : "no");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int
++mt7996_tm_set(struct mt7996_dev *dev, u32 func_idx, u32 data)
++{
++ struct mt7996_tm_req req = {
++ .rf_test = {
++ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++ .len = cpu_to_le16(sizeof(req.rf_test)),
++ .action = RF_ACTION_SET,
++ .op.rf.func_idx = func_idx,
++ .op.rf.param.func_data = cpu_to_le32(data),
++ },
++ };
++ bool wait = (data == RF_CMD(START_TX)) ? true : false;
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
++ sizeof(req), wait);
++}
++
++static int
++mt7996_tm_get(struct mt7996_dev *dev, u32 func_idx, u32 data, u32 *result)
++{
++ struct mt7996_tm_req req = {
++ .rf_test = {
++ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++ .len = cpu_to_le16(sizeof(req.rf_test)),
++ .action = RF_ACTION_GET,
++ .op.rf.func_idx = func_idx,
++ .op.rf.param.func_data = cpu_to_le32(data),
++ },
++ };
++ struct mt7996_tm_event *event;
++ struct sk_buff *skb;
++ int ret;
++
++ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_CTRL),
++ &req, sizeof(req), true, &skb);
++ if (ret)
++ return ret;
++
++ event = (struct mt7996_tm_event *)skb->data;
++ *result = event->result.payload_length;
++
++ dev_kfree_skb(skb);
++
++ return ret;
++}
++
++static void
++mt7996_tm_set_antenna(struct mt7996_phy *phy, u32 func_idx)
++{
++#define SPE_INDEX_MASK BIT(31)
++#define TX_ANTENNA_MASK GENMASK(3, 0)
++#define RX_ANTENNA_MASK GENMASK(20, 16) /* RX antenna mask at most 5 bit */
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ u32 antenna_mask;
++
++ if (!mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA))
++ return;
++
++ if (func_idx == SET_ID(TX_PATH))
++ antenna_mask = td->tx_spe_idx ? (SPE_INDEX_MASK | td->tx_spe_idx) :
++ td->tx_antenna_mask & TX_ANTENNA_MASK;
++ else if (func_idx == SET_ID(RX_PATH))
++ antenna_mask = u32_encode_bits(td->tx_antenna_mask, RX_ANTENNA_MASK);
++ else
++ return;
++
++ mt7996_tm_set(dev, func_idx, antenna_mask);
++}
++
++static void
++mt7996_tm_set_mac_addr(struct mt7996_dev *dev, u8 *addr, u32 func_idx)
++{
++#define REMAIN_PART_TAG BIT(18)
++ u32 own_mac_first = 0, own_mac_remain = 0;
++ int len = sizeof(u32);
++
++ memcpy(&own_mac_first, addr, len);
++ mt7996_tm_set(dev, func_idx, own_mac_first);
++ /* Set the remain part of mac address */
++ memcpy(&own_mac_remain, addr + len, ETH_ALEN - len);
++ mt7996_tm_set(dev, func_idx | REMAIN_PART_TAG, own_mac_remain);
++}
++
++static int
++mt7996_tm_rf_switch_mode(struct mt7996_dev *dev, u32 op_mode)
++{
++ struct mt7996_tm_req req = {
++ .rf_test = {
++ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++ .len = cpu_to_le16(sizeof(req.rf_test)),
++ .action = RF_ACTION_SWITCH_TO_RF_TEST,
++ .op.op_mode = cpu_to_le32(op_mode),
++ },
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
++ sizeof(req), false);
++}
++
++static void
++mt7996_tm_init(struct mt7996_phy *phy, bool en)
++{
++ struct mt7996_dev *dev = phy->dev;
++ u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
++
++ if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
++ return;
++
++ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(ATE_MODE), en);
++ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), !en);
++ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), !en);
++
++ mt7996_tm_rf_switch_mode(dev, rf_test_mode);
++
++ mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
++ mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en, false);
++
++ mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
++
++ /* use firmware counter for RX stats */
++ phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++}
++
++static void
++mt7996_tm_update_channel(struct mt7996_phy *phy)
++{
++#define CHAN_FREQ_BW_80P80_TAG (SET_ID(CHAN_FREQ) | BIT(16))
++ struct mt7996_dev *dev = phy->dev;
++ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++ struct ieee80211_channel *chan = chandef->chan;
++ u8 width = chandef->width;
++ static const u8 ch_band[] = {
++ [NL80211_BAND_2GHZ] = 0,
++ [NL80211_BAND_5GHZ] = 1,
++ [NL80211_BAND_6GHZ] = 2,
++ };
++
++ if (!chan || !chandef) {
++ dev_info(dev->mt76.dev, "chandef not found, channel update failed!\n");
++ return;
++ }
++
++ /* system bw */
++ mt7996_tm_set(dev, SET_ID(CBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++
++ if (width == NL80211_CHAN_WIDTH_80P80) {
++ width = NL80211_CHAN_WIDTH_160;
++ mt7996_tm_set(dev, CHAN_FREQ_BW_80P80_TAG, chandef->center_freq2 * 1000);
++ }
++
++ /* TODO: define per-packet bw */
++ /* per-packet bw */
++ mt7996_tm_set(dev, SET_ID(DBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++
++ /* control channel selection index */
++ mt7996_tm_set(dev, SET_ID(PRIMARY_CH), 0);
++ mt7996_tm_set(dev, SET_ID(BAND), ch_band[chan->band]);
++
++ /* trigger switch channel calibration */
++ mt7996_tm_set(dev, SET_ID(CHAN_FREQ), chandef->center_freq1 * 1000);
++
++ // TODO: update power limit table
++}
++
++static void
++mt7996_tm_tx_stop(struct mt76_phy *mphy)
++{
++ struct mt76_testmode_data *td = &mphy->test;
++ struct mt7996_phy *phy = mphy->priv;
++ struct mt7996_dev *dev = phy->dev;
++
++ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
++ td->tx_pending = 0;
++}
++
++static void
++mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
++{
++#define FRAME_CONTROL 0x88
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_dev *dev = phy->dev;
++
++ //TODO: RU operation, replace mcs, nss, and ldpc
++ if (en) {
++ mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
++ mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
++ mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
++ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
++
++ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
++ mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
++
++ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME)) {
++ mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
++ mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
++ } else {
++ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
++ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
++ }
++
++ mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
++ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
++ mt7996_tm_set(dev, SET_ID(STBC), td->tx_rate_stbc);
++ mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
++ mt7996_tm_set(dev, SET_ID(IBF_ENABLE), td->ibf);
++ mt7996_tm_set(dev, SET_ID(EBF_ENABLE), td->ebf);
++ mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
++ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
++ mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
++ mt7996_tm_set(dev, SET_ID(AID_OFFSET), 0);
++ mt7996_tm_set(dev, SET_ID(PUNCTURE), td->tx_preamble_puncture);
++
++ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
++ mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
++ mt7996_tm_update_channel(phy);
++
++ /* trigger firmware to start TX */
++ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
++ } else {
++ mt7996_tm_tx_stop(phy->mt76);
++ }
++}
++
++static int
++mt7996_tm_rx_stats_user_ctrl(struct mt7996_phy *phy, u16 user_idx)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_rx_req req = {
++ .band = phy->mt76->band_idx,
++ .user_ctrl = {
++ .tag = cpu_to_le16(UNI_TM_RX_STAT_SET_USER_CTRL),
++ .len = cpu_to_le16(sizeof(req.user_ctrl)),
++ .band_idx = phy->mt76->band_idx,
++ .user_idx = cpu_to_le16(user_idx),
++ },
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_RX_STAT), &req,
++ sizeof(req), false);
++}
++
++static void
++mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
++{
++#define RX_MU_DISABLE 0xf800
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_dev *dev = phy->dev;
++ int ret;
++
++ if (en) {
++ ret = mt7996_tm_rx_stats_user_ctrl(phy, td->aid);
++ if (ret) {
++ dev_info(dev->mt76.dev, "Set RX stats user control failed!\n");
++ return;
++ }
++
++ mt7996_tm_update_channel(phy);
++
++ if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
++ if (td->aid)
++ ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), td->aid);
++ else
++ ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
++ }
++ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
++ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
++ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
++
++ mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
++
++ /* trigger firmware to start RX */
++ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_RX));
++ } else {
++ /* trigger firmware to stop RX */
++ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
++ }
++}
++
++static void
++mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
++{
++#define CONT_WAVE_MODE_OFDM 3
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_dev *dev = phy->dev;
++
++ if (en) {
++ mt7996_tm_update_channel(phy);
++ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
++ /* fix payload is OFDM */
++ mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
++ mt7996_tm_set(dev, SET_ID(ANT_MASK), td->tx_antenna_mask);
++
++ /* trigger firmware to start CONT TX */
++ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(CONT_WAVE));
++ } else {
++ /* trigger firmware to stop CONT TX */
++ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
++ }
++}
++
++static void
++mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
++{
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_dev *dev = phy->dev;
++
++ if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) {
++ mt7996_tm_set(dev, SET_ID(FREQ_OFFSET), td->freq_offset);
++ mt7996_tm_set(dev, SET_ID(FREQ_OFFSET_C2), td->freq_offset);
++ }
++ if (changed & BIT(TM_CHANGED_TXPOWER))
++ mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
++ if (changed & BIT(TM_CHANGED_SKU_EN)) {
++ mt7996_tm_update_channel(phy);
++ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
++ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
++ mt7996_mcu_set_txpower_sku(phy);
++ }
++ if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
++ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
++ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
++ }
++ if (changed & BIT(TM_CHANGED_TX_TIME)) {
++ mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
++ mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
++ }
++ if (changed & BIT(TM_CHANGED_CFG)) {
++ u32 func_idx = td->cfg.enable ? SET_ID(CFG_ON) : SET_ID(CFG_OFF);
++
++ mt7996_tm_set(dev, func_idx, td->cfg.type);
++ }
++}
++
++static int
++mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
++{
++ struct mt76_testmode_data *td = &mphy->test;
++ struct mt7996_phy *phy = mphy->priv;
++ enum mt76_testmode_state prev_state = td->state;
++
++ mphy->test.state = state;
++
++ if (prev_state != MT76_TM_STATE_OFF)
++ mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
++
++ if (prev_state == MT76_TM_STATE_TX_FRAMES ||
++ state == MT76_TM_STATE_TX_FRAMES)
++ mt7996_tm_set_tx_frames(phy, state == MT76_TM_STATE_TX_FRAMES);
++ else if (prev_state == MT76_TM_STATE_RX_FRAMES ||
++ state == MT76_TM_STATE_RX_FRAMES)
++ mt7996_tm_set_rx_frames(phy, state == MT76_TM_STATE_RX_FRAMES);
++ else if (prev_state == MT76_TM_STATE_TX_CONT ||
++ state == MT76_TM_STATE_TX_CONT)
++ mt7996_tm_set_tx_cont(phy, state == MT76_TM_STATE_TX_CONT);
++ else if (prev_state == MT76_TM_STATE_OFF ||
++ state == MT76_TM_STATE_OFF)
++ mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
++
++ if ((state == MT76_TM_STATE_IDLE &&
++ prev_state == MT76_TM_STATE_OFF) ||
++ (state == MT76_TM_STATE_OFF &&
++ prev_state == MT76_TM_STATE_IDLE)) {
++ u32 changed = 0;
++ int i, ret;
++
++ for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
++ u16 cur = tm_change_map[i];
++
++ if (mt76_testmode_param_present(td, cur))
++ changed |= BIT(i);
++ }
++
++ ret = mt7996_tm_check_antenna(phy);
++ if (ret)
++ return ret;
++
++ mt7996_tm_update_params(phy, changed);
++ }
++
++ return 0;
++}
++
++static int
++mt7996_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
++ enum mt76_testmode_state new_state)
++{
++ struct mt76_testmode_data *td = &mphy->test;
++ struct mt7996_phy *phy = mphy->priv;
++ struct mt7996_dev *dev = phy->dev;
++ u32 changed = 0;
++ int i, ret;
++
++ BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
++
++ if (new_state == MT76_TM_STATE_OFF ||
++ td->state == MT76_TM_STATE_OFF)
++ return 0;
++
++ ret = mt7996_tm_check_antenna(phy);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
++ if (tb[tm_change_map[i]])
++ changed |= BIT(i);
++ }
++
++ mt7996_tm_set(dev, SET_ID(BAND_IDX), mphy->band_idx);
++ mt7996_tm_update_params(phy, changed);
++
++ return 0;
++}
++
++static int
++mt7996_tm_get_rx_stats(struct mt7996_phy *phy)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_rx_req req = {
++ .band = phy->mt76->band_idx,
++ .rx_stat_all = {
++ .tag = cpu_to_le16(UNI_TM_RX_STAT_GET_ALL_V2),
++ .len = cpu_to_le16(sizeof(req.rx_stat_all)),
++ .band_idx = phy->mt76->band_idx,
++ },
++ };
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_tm_rx_event *rx_stats;
++ struct mt7996_tm_rx_event_stat_all *rx_stats_all;
++ struct sk_buff *skb;
++ enum mt76_rxq_id qid;
++ int i, ret = 0;
++ u32 mac_rx_mdrdy_cnt;
++ u16 mac_rx_len_mismatch, fcs_err_count;
++
++ if (td->state != MT76_TM_STATE_RX_FRAMES)
++ return 0;
++
++ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_RX_STAT),
++ &req, sizeof(req), true, &skb);
++
++ if (ret)
++ return ret;
++
++ rx_stats = (struct mt7996_tm_rx_event *)skb->data;
++ rx_stats_all = &rx_stats->rx_stat_all;
++
++ phy->test.last_freq_offset = le32_to_cpu(rx_stats_all->user_info[0].freq_offset);
++ phy->test.last_snr = le32_to_cpu(rx_stats_all->user_info[0].snr);
++ for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++) {
++ phy->test.last_rcpi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rcpi);
++ phy->test.last_rssi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rssi);
++ phy->test.last_ib_rssi[i] = rx_stats_all->fagc[i].ib_rssi;
++ phy->test.last_wb_rssi[i] = rx_stats_all->fagc[i].wb_rssi;
++ }
++
++ if (phy->mt76->band_idx == 2)
++ qid = MT_RXQ_BAND2;
++ else if (phy->mt76->band_idx == 1)
++ qid = MT_RXQ_BAND1;
++ else
++ qid = MT_RXQ_MAIN;
++
++ fcs_err_count = le16_to_cpu(rx_stats_all->band_info.mac_rx_fcs_err_cnt);
++ mac_rx_len_mismatch = le16_to_cpu(rx_stats_all->band_info.mac_rx_len_mismatch);
++ mac_rx_mdrdy_cnt = le32_to_cpu(rx_stats_all->band_info.mac_rx_mdrdy_cnt);
++ td->rx_stats.packets[qid] += mac_rx_mdrdy_cnt;
++ td->rx_stats.packets[qid] += fcs_err_count;
++ td->rx_stats.fcs_error[qid] += fcs_err_count;
++ td->rx_stats.len_mismatch += mac_rx_len_mismatch;
++
++ dev_kfree_skb(skb);
++
++ return ret;
++}
++
++static void
++mt7996_tm_reset_trx_stats(struct mt76_phy *mphy)
++{
++ struct mt7996_phy *phy = mphy->priv;
++ struct mt7996_dev *dev = phy->dev;
++
++ memset(&mphy->test.rx_stats, 0, sizeof(mphy->test.rx_stats));
++ mt7996_tm_set(dev, SET_ID(TRX_COUNTER_RESET), 0);
++}
++
++static int
++mt7996_tm_get_tx_stats(struct mt7996_phy *phy)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ int ret;
++
++ if (td->state != MT76_TM_STATE_TX_FRAMES)
++ return 0;
++
++ ret = mt7996_tm_get(dev, GET_ID(TXED_COUNT), 0, &td->tx_done);
++ if (ret)
++ return ret;
++
++ td->tx_pending = td->tx_count - td->tx_done;
++
++ return ret;
++}
++
++static int
++mt7996_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
++{
++ struct mt7996_phy *phy = mphy->priv;
++ void *rx, *rssi;
++ int i;
++
++ mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
++ mt7996_tm_get_rx_stats(phy);
++ mt7996_tm_get_tx_stats(phy);
++
++ rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
++ if (!rx)
++ return -ENOMEM;
++
++ if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset))
++ return -ENOMEM;
++
++ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
++ if (!rssi)
++ return -ENOMEM;
++
++ for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++)
++ if (nla_put_u8(msg, i, phy->test.last_rcpi[i]))
++ return -ENOMEM;
++
++ nla_nest_end(msg, rssi);
++
++ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RSSI);
++ if (!rssi)
++ return -ENOMEM;
++
++ for (i = 0; i < ARRAY_SIZE(phy->test.last_rssi); i++)
++ if (nla_put_s8(msg, i, phy->test.last_rssi[i]))
++ return -ENOMEM;
++
++ nla_nest_end(msg, rssi);
++
++ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
++ if (!rssi)
++ return -ENOMEM;
++
++ for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++)
++ if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i]))
++ return -ENOMEM;
++
++ nla_nest_end(msg, rssi);
++
++ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
++ if (!rssi)
++ return -ENOMEM;
++
++ for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++)
++ if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i]))
++ return -ENOMEM;
++
++ nla_nest_end(msg, rssi);
++
++ if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, phy->test.last_snr))
++ return -ENOMEM;
++
++ nla_nest_end(msg, rx);
++
++ return 0;
++}
++
++static int
++mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
++{
++ struct mt7996_mcu_eeprom_info req = {
++ .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ };
++ u8 read_buf[MT76_TM_EEPROM_BLOCK_SIZE], *eeprom = dev->mt76.eeprom.data;
++ int i, ret = -EINVAL;
++
++ /* prevent from damaging chip id in efuse */
++ if (mt76_chip(&dev->mt76) != get_unaligned_le16(eeprom))
++ goto out;
++
++ for (i = 0; i < MT7996_EEPROM_SIZE; i += MT76_TM_EEPROM_BLOCK_SIZE) {
++ req.addr = cpu_to_le32(i);
++ memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
++
++ ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
++ if (ret < 0)
++ return ret;
++
++ if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
++ continue;
++
++ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
++ &req, sizeof(req), true);
++ if (ret)
++ return ret;
++ }
++
++out:
++ return ret;
++}
++
++static int
++mt7996_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
++{
++ struct mt7996_phy *phy = mphy->priv;
++ struct mt7996_dev *dev = phy->dev;
++ u8 *eeprom = dev->mt76.eeprom.data;
++ int ret = 0;
++
++ if (offset >= MT7996_EEPROM_SIZE)
++ return -EINVAL;
++
++ switch (action) {
++ case MT76_TM_EEPROM_ACTION_UPDATE_DATA:
++ memcpy(eeprom + offset, val, MT76_TM_EEPROM_BLOCK_SIZE);
++ break;
++ case MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE:
++ ret = mt7996_mcu_set_eeprom(dev);
++ break;
++ case MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE:
++ ret = mt7996_tm_write_back_to_efuse(dev);
++ break;
++ default:
++ break;
++ }
++
++ return ret;
++}
++
++const struct mt76_testmode_ops mt7996_testmode_ops = {
++ .set_state = mt7996_tm_set_state,
++ .set_params = mt7996_tm_set_params,
++ .dump_stats = mt7996_tm_dump_stats,
++ .reset_rx_stats = mt7996_tm_reset_trx_stats,
++ .tx_stop = mt7996_tm_tx_stop,
++ .set_eeprom = mt7996_tm_set_eeprom,
++};
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+new file mode 100644
+index 000000000..319ef257a
+--- /dev/null
++++ b/mt7996/testmode.h
+@@ -0,0 +1,299 @@
++/* SPDX-License-Identifier: ISC */
++/* Copyright (C) 2020 MediaTek Inc. */
++
++#ifndef __MT7996_TESTMODE_H
++#define __MT7996_TESTMODE_H
++
++enum {
++ TM_CBW_20MHZ,
++ TM_CBW_40MHZ,
++ TM_CBW_80MHZ,
++ TM_CBW_10MHZ,
++ TM_CBW_5MHZ,
++ TM_CBW_160MHZ,
++ TM_CBW_8080MHZ,
++ TM_CBW_320MHZ = 12,
++};
++
++/* BW defined in FW hal_cal_flow_rom.h */
++enum {
++ FW_CDBW_20MHZ,
++ FW_CDBW_40MHZ,
++ FW_CDBW_80MHZ,
++ FW_CDBW_160MHZ,
++ FW_CDBW_320MHZ,
++ FW_CDBW_5MHZ,
++ FW_CDBW_10MHZ,
++ FW_CDBW_8080MHZ,
++};
++
++enum bw_mapping_method {
++ BW_MAP_NL_TO_FW,
++ BW_MAP_NL_TO_TM,
++
++ NUM_BW_MAP,
++};
++
++struct mt7996_tm_rf_test {
++ __le16 tag;
++ __le16 len;
++
++ u8 action;
++ u8 icap_len;
++ u8 _rsv[2];
++ union {
++ __le32 op_mode;
++ __le32 freq;
++
++ struct {
++ __le32 func_idx;
++ union {
++ __le32 func_data;
++ __le32 cal_dump;
++
++ u8 _pad[80];
++ } param;
++ } rf;
++ } op;
++} __packed;
++
++struct mt7996_tm_req {
++ u8 _rsv[4];
++
++ struct mt7996_tm_rf_test rf_test;
++} __packed;
++
++struct mt7996_tm_rf_test_result {
++ __le32 func_idx;
++ __le32 payload_length;
++ u8 event[0];
++};
++
++struct mt7996_tm_event {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++ struct mt7996_tm_rf_test_result result;
++} __packed;
++
++enum {
++ RF_ACTION_SWITCH_TO_RF_TEST,
++ RF_ACTION_IN_RF_TEST,
++ RF_ACTION_SET = 3,
++ RF_ACTION_GET,
++};
++
++enum {
++ RF_OPER_NORMAL,
++ RF_OPER_RF_TEST,
++ RF_OPER_ICAP,
++ RF_OPER_ICAP_OVERLAP,
++ RF_OPER_WIFI_SPECTRUM,
++};
++
++enum {
++ UNI_RF_TEST_CTRL,
++};
++
++#define RF_CMD(cmd) RF_TEST_CMD_##cmd
++
++enum {
++ RF_TEST_CMD_STOP_TEST = 0,
++ RF_TEST_CMD_START_TX = 1,
++ RF_TEST_CMD_START_RX = 2,
++ RF_TEST_CMD_CONT_WAVE = 10,
++ RF_TEST_CMD_TX_COMMIT = 18,
++ RF_TEST_CMD_RX_COMMIT = 19,
++};
++
++#define SET_ID(id) RF_TEST_ID_SET_##id
++#define GET_ID(id) RF_TEST_ID_GET_##id
++
++enum {
++ RF_TEST_ID_SET_COMMAND = 1,
++ RF_TEST_ID_SET_POWER = 2,
++ RF_TEST_ID_SET_TX_RATE = 3,
++ RF_TEST_ID_SET_TX_MODE = 4,
++ RF_TEST_ID_SET_TX_LEN = 6,
++ RF_TEST_ID_SET_TX_COUNT = 7,
++ RF_TEST_ID_SET_IPG = 8,
++ RF_TEST_ID_SET_GI = 16,
++ RF_TEST_ID_SET_STBC = 17,
++ RF_TEST_ID_SET_CHAN_FREQ = 18,
++ RF_TEST_ID_GET_TXED_COUNT = 32,
++ RF_TEST_ID_SET_CONT_WAVE_MODE = 65,
++ RF_TEST_ID_SET_DA = 68,
++ RF_TEST_ID_SET_SA = 69,
++ RF_TEST_ID_SET_CBW = 71,
++ RF_TEST_ID_SET_DBW = 72,
++ RF_TEST_ID_SET_PRIMARY_CH = 73,
++ RF_TEST_ID_SET_ENCODE_MODE = 74,
++ RF_TEST_ID_SET_BAND = 90,
++ RF_TEST_ID_SET_TRX_COUNTER_RESET = 91,
++ RF_TEST_ID_SET_MAC_HEADER = 101,
++ RF_TEST_ID_SET_SEQ_CTRL = 102,
++ RF_TEST_ID_SET_PAYLOAD = 103,
++ RF_TEST_ID_SET_BAND_IDX = 104,
++ RF_TEST_ID_SET_RX_PATH = 106,
++ RF_TEST_ID_SET_FREQ_OFFSET = 107,
++ RF_TEST_ID_GET_FREQ_OFFSET = 108,
++ RF_TEST_ID_SET_TX_PATH = 113,
++ RF_TEST_ID_SET_NSS = 114,
++ RF_TEST_ID_SET_ANT_MASK = 115,
++ RF_TEST_ID_SET_IBF_ENABLE = 126,
++ RF_TEST_ID_SET_EBF_ENABLE = 127,
++ RF_TEST_ID_GET_TX_POWER = 136,
++ RF_TEST_ID_SET_RX_MU_AID = 157,
++ RF_TEST_ID_SET_HW_TX_MODE = 167,
++ RF_TEST_ID_SET_PUNCTURE = 168,
++ RF_TEST_ID_SET_FREQ_OFFSET_C2 = 171,
++ RF_TEST_ID_GET_FREQ_OFFSET_C2 = 172,
++ RF_TEST_ID_SET_CFG_ON = 176,
++ RF_TEST_ID_SET_CFG_OFF = 177,
++ RF_TEST_ID_SET_BSSID = 189,
++ RF_TEST_ID_SET_TX_TIME = 190,
++ RF_TEST_ID_SET_MAX_PE = 191,
++ RF_TEST_ID_SET_AID_OFFSET = 204,
++};
++
++#define POWER_CTRL(type) UNI_TXPOWER_##type##_CTRL
++
++struct mt7996_tm_rx_stat_user_ctrl {
++ __le16 tag;
++ __le16 len;
++
++ u8 band_idx;
++ u8 rsv;
++ __le16 user_idx;
++} __packed;
++
++struct mt7996_tm_rx_stat_all {
++ __le16 tag;
++ __le16 len;
++
++ u8 band_idx;
++ u8 rsv[3];
++} __packed;
++
++struct mt7996_tm_rx_req {
++ u8 band;
++ u8 _rsv[3];
++
++ union {
++ struct mt7996_tm_rx_stat_user_ctrl user_ctrl;
++ struct mt7996_tm_rx_stat_all rx_stat_all;
++ };
++} __packed;
++
++enum {
++ UNI_TM_RX_STAT_SET_USER_CTRL = 7,
++ UNI_TM_RX_STAT_GET_ALL_V2 = 9,
++};
++
++struct rx_band_info {
++ /* mac part */
++ __le16 mac_rx_fcs_err_cnt;
++ __le16 mac_rx_len_mismatch;
++ __le16 mac_rx_fcs_ok_cnt;
++ u8 rsv1[2];
++ __le32 mac_rx_mdrdy_cnt;
++
++ /* phy part */
++ __le16 phy_rx_fcs_err_cnt_cck;
++ __le16 phy_rx_fcs_err_cnt_ofdm;
++ __le16 phy_rx_pd_cck;
++ __le16 phy_rx_pd_ofdm;
++ __le16 phy_rx_sig_err_cck;
++ __le16 phy_rx_sfd_err_cck;
++ __le16 phy_rx_sig_err_ofdm;
++ __le16 phy_rx_tag_err_ofdm;
++ __le16 phy_rx_mdrdy_cnt_cck;
++ __le16 phy_rx_mdrdy_cnt_ofdm;
++} __packed;
++
++struct rx_band_info_ext {
++ /* mac part */
++ __le32 mac_rx_mpdu_cnt;
++
++ /* phy part */
++ u8 rsv[4];
++} __packed;
++
++struct rx_common_info {
++ __le16 rx_fifo_full;
++ u8 rsv[2];
++ __le32 aci_hit_low;
++ __le32 aci_hit_high;
++} __packed;
++
++struct rx_common_info_ext {
++ __le32 driver_rx_count;
++ __le32 sinr;
++ __le32 mu_pkt_count;
++
++ /* mac part */
++ u8 _rsv[4];
++
++ /* phy part */
++ u8 sig_mcs;
++ u8 rsv[3];
++} __packed;
++
++struct rx_rxv_info {
++ __le16 rcpi;
++ s16 rssi;
++ s16 snr;
++ s16 adc_rssi;
++} __packed;
++
++struct rx_rssi_info {
++ s8 ib_rssi;
++ s8 wb_rssi;
++ u8 rsv[2];
++} __packed;
++
++struct rx_user_info {
++ s32 freq_offset;
++ s32 snr;
++ __le32 fcs_err_count;
++} __packed;
++
++struct rx_user_info_ext {
++ s8 ne_var_db_all_user;
++ u8 rsv[3];
++} __packed;
++
++#define MAX_ANTENNA_NUM 8
++#define MAX_USER_NUM 16
++
++struct mt7996_tm_rx_event_stat_all {
++ __le16 tag;
++ __le16 len;
++
++ struct rx_band_info band_info;
++ struct rx_band_info_ext band_info_ext;
++ struct rx_common_info common_info;
++ struct rx_common_info_ext common_info_ext;
++
++ /* RXV info */
++ struct rx_rxv_info rxv_info[MAX_ANTENNA_NUM];
++
++ /* RSSI info */
++ struct rx_rssi_info fagc[MAX_ANTENNA_NUM];
++ struct rx_rssi_info inst[MAX_ANTENNA_NUM];
++
++ /* User info */
++ struct rx_user_info user_info[MAX_USER_NUM];
++ struct rx_user_info_ext user_info_ext[MAX_USER_NUM];
++} __packed;
++
++struct mt7996_tm_rx_event {
++ u8 _rsv[4];
++
++ union {
++ struct mt7996_tm_rx_event_stat_all rx_stat_all;
++ };
++} __packed;
++
++#endif
+diff --git a/testmode.c b/testmode.c
+index ca4feccf3..44f3a5bfc 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -2,11 +2,13 @@
+ /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
+
+ #include <linux/random.h>
++#include "mt76_connac.h"
+ #include "mt76.h"
+
+ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ [MT76_TM_ATTR_RESET] = { .type = NLA_FLAG },
+ [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
+@@ -82,6 +84,11 @@ mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode)
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991)
+ return IEEE80211_MAX_MPDU_LEN_VHT_7991;
+ return IEEE80211_MAX_MPDU_LEN_VHT_11454;
++ case MT76_TM_TX_MODE_EHT_SU:
++ case MT76_TM_TX_MODE_EHT_TRIG:
++ case MT76_TM_TX_MODE_EHT_MU:
++ /* TODO: check the limit */
++ return UINT_MAX;
+ case MT76_TM_TX_MODE_CCK:
+ case MT76_TM_TX_MODE_OFDM:
+ default:
+@@ -183,6 +190,9 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
+ u8 max_nss = hweight8(phy->antenna_mask);
+ int ret;
+
++ if (is_mt7996(phy->dev))
++ return 0;
++
+ ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
+ if (ret)
+ return ret;
+@@ -275,7 +285,9 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
+ td->tx_queued = 0;
+ td->tx_done = 0;
+ td->tx_pending = td->tx_count;
+- mt76_worker_schedule(&dev->tx_worker);
++
++ if (!is_mt7996(dev))
++ mt76_worker_schedule(&dev->tx_worker);
+ }
+
+ static void
+@@ -284,6 +296,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
+ struct mt76_testmode_data *td = &phy->test;
+ struct mt76_dev *dev = phy->dev;
+
++ if (is_mt7996(dev) && dev->test_ops->tx_stop) {
++ dev->test_ops->tx_stop(phy);
++ return;
++ }
++
+ mt76_worker_disable(&dev->tx_worker);
+
+ td->tx_pending = 0;
+@@ -296,22 +313,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
+ mt76_testmode_free_skb(phy);
+ }
+
+-static inline void
+-mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
+-{
+- td->param_set[idx / 32] |= BIT(idx % 32);
+-}
+-
+-static inline bool
+-mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
+-{
+- return td->param_set[idx / 32] & BIT(idx % 32);
+-}
+-
+ static void
+ mt76_testmode_init_defaults(struct mt76_phy *phy)
+ {
+ struct mt76_testmode_data *td = &phy->test;
++ u8 addr[ETH_ALEN] = {phy->band_idx, 0x11, 0x22, 0xaa, 0xbb, 0xcc};
+
+ if (td->tx_mpdu_len > 0)
+ return;
+@@ -319,11 +325,18 @@ mt76_testmode_init_defaults(struct mt76_phy *phy)
+ td->tx_mpdu_len = 1024;
+ td->tx_count = 1;
+ td->tx_rate_mode = MT76_TM_TX_MODE_OFDM;
++ td->tx_rate_idx = 7;
+ td->tx_rate_nss = 1;
++ /* 0xffff for OFDMA no puncture */
++ td->tx_preamble_puncture = ~(td->tx_preamble_puncture & 0);
++ td->tx_ipg = 50;
+
+- memcpy(td->addr[0], phy->macaddr, ETH_ALEN);
+- memcpy(td->addr[1], phy->macaddr, ETH_ALEN);
+- memcpy(td->addr[2], phy->macaddr, ETH_ALEN);
++ /* rx stat user config */
++ td->aid = 1;
++
++ memcpy(td->addr[0], addr, ETH_ALEN);
++ memcpy(td->addr[1], addr, ETH_ALEN);
++ memcpy(td->addr[2], addr, ETH_ALEN);
+ }
+
+ static int
+@@ -353,7 +366,7 @@ __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state)
+ if (state == MT76_TM_STATE_TX_FRAMES)
+ mt76_testmode_tx_start(phy);
+ else if (state == MT76_TM_STATE_RX_FRAMES) {
+- memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats));
++ dev->test_ops->reset_rx_stats(phy);
+ }
+
+ phy->test.state = state;
+@@ -404,6 +417,44 @@ mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max)
+ return 0;
+ }
+
++static int
++mt76_testmode_set_eeprom(struct mt76_phy *phy, struct nlattr **tb)
++{
++ struct mt76_dev *dev = phy->dev;
++ u8 action, val[MT76_TM_EEPROM_BLOCK_SIZE];
++ u32 offset = 0;
++ int err = -EINVAL;
++
++ if (!dev->test_ops->set_eeprom)
++ return -EOPNOTSUPP;
++
++ if (mt76_tm_get_u8(tb[MT76_TM_ATTR_EEPROM_ACTION], &action,
++ 0, MT76_TM_EEPROM_ACTION_MAX))
++ goto out;
++
++ if (tb[MT76_TM_ATTR_EEPROM_OFFSET]) {
++ struct nlattr *cur;
++ int rem, idx = 0;
++
++ offset = nla_get_u32(tb[MT76_TM_ATTR_EEPROM_OFFSET]);
++ if (!!(offset % MT76_TM_EEPROM_BLOCK_SIZE) ||
++ !tb[MT76_TM_ATTR_EEPROM_VAL])
++ goto out;
++
++ nla_for_each_nested(cur, tb[MT76_TM_ATTR_EEPROM_VAL], rem) {
++ if (nla_len(cur) != 1 || idx >= ARRAY_SIZE(val))
++ goto out;
++
++ val[idx++] = nla_get_u8(cur);
++ }
++ }
++
++ err = dev->test_ops->set_eeprom(phy, offset, val, action);
++
++out:
++ return err;
++}
++
+ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len)
+ {
+@@ -427,6 +478,11 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mutex_lock(&dev->mutex);
+
++ if (tb[MT76_TM_ATTR_EEPROM_ACTION]) {
++ err = mt76_testmode_set_eeprom(phy, tb);
++ goto out;
++ }
++
+ if (tb[MT76_TM_ATTR_RESET]) {
+ mt76_testmode_set_state(phy, MT76_TM_STATE_OFF);
+ memset(td, 0, sizeof(*td));
+@@ -434,6 +490,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mt76_testmode_init_defaults(phy);
+
++ if (tb[MT76_TM_ATTR_SKU_EN])
++ td->sku_en = nla_get_u8(tb[MT76_TM_ATTR_SKU_EN]);
++
+ if (tb[MT76_TM_ATTR_TX_COUNT])
+ td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]);
+
+@@ -454,7 +513,8 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE],
+ &td->tx_duty_cycle, 0, 99) ||
+ mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
+- &td->tx_power_control, 0, 1))
++ &td->tx_power_control, 0, 1) ||
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
+ goto out;
+
+ if (tb[MT76_TM_ATTR_TX_LENGTH]) {
+@@ -494,7 +554,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ idx >= ARRAY_SIZE(td->tx_power))
+ goto out;
+
+- td->tx_power[idx++] = nla_get_u8(cur);
++ err = mt76_tm_get_u8(cur, &td->tx_power[idx++], 0, 63);
++ if (err)
++ return err;
+ }
+ }
+
+@@ -512,6 +574,22 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ }
+ }
+
++ if (tb[MT76_TM_ATTR_CFG]) {
++ struct nlattr *cur;
++ int rem, idx = 0;
++
++ nla_for_each_nested(cur, tb[MT76_TM_ATTR_CFG], rem) {
++ if (nla_len(cur) != 1 || idx >= 2)
++ goto out;
++
++ if (idx == 0)
++ td->cfg.type = nla_get_u8(cur);
++ else
++ td->cfg.enable = nla_get_u8(cur);
++ idx++;
++ }
++ }
++
+ if (dev->test_ops->set_params) {
+ err = dev->test_ops->set_params(phy, tb, state);
+ if (err)
+@@ -561,6 +639,9 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
+ nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets,
+ MT76_TM_STATS_ATTR_PAD) ||
+ nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error,
++ MT76_TM_STATS_ATTR_PAD) ||
++ nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
++ td->rx_stats.len_mismatch,
+ MT76_TM_STATS_ATTR_PAD))
+ return -EMSGSIZE;
+
+@@ -613,7 +694,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+
+ if (dev->test_mtd.name &&
+ (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
+- nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
++ nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset) ||
++ nla_put_u8(msg, MT76_TM_ATTR_BAND_IDX, phy->band_idx)))
+ goto out;
+
+ if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
+@@ -624,6 +706,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
++ nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
++ nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
+ (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
+ nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
+ (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
+@@ -639,7 +723,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
+ nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) ||
+ (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) &&
+- nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
++ nla_put_u32(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
+ goto out;
+
+ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
+diff --git a/testmode.h b/testmode.h
+index 5e2792d81..96872e8cd 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -5,7 +5,8 @@
+ #ifndef __MT76_TESTMODE_H
+ #define __MT76_TESTMODE_H
+
+-#define MT76_TM_TIMEOUT 10
++#define MT76_TM_TIMEOUT 10
++#define MT76_TM_EEPROM_BLOCK_SIZE 16
+
+ /**
+ * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
+@@ -17,7 +18,9 @@
+ *
+ * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
+ * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
++ * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
+ *
++ * @MT76_TM_ATTR_SKU_EN: config txpower sku is enabled or disabled in testmode (u8)
+ * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
+ * state to MT76_TM_STATE_TX_FRAMES (u32)
+ * @MT76_TM_ATTR_TX_PENDING: pending frames during MT76_TM_STATE_TX_FRAMES (u32)
+@@ -38,6 +41,11 @@
+ *
+ * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
+ *
++ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
++ * @MT76_TM_ATTR_PRECAL_INFO: group size, dpd size, dpd_info, transmit size,
++ * eeprom cal indicator (u32),
++ * dpd_info = [dpd_per_chan_size, chan_num_2g,
++ * chan_num_5g, chan_num_6g]
+ * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8)
+ *
+ * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8)
+@@ -47,6 +55,29 @@
+ * @MT76_TM_ATTR_DRV_DATA: driver specific netlink attrs (nested)
+ *
+ * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested)
++ *
++ * @MT76_TM_ATTR_EEPROM_ACTION: eeprom setting actions
++ * (u8, see &enum mt76_testmode_eeprom_action)
++ * @MT76_TM_ATTR_EEPROM_OFFSET: offset of eeprom data block for writing (u32)
++ * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
++ * (nested, u8 attrs)
++ *
++ * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
++ * @MT76_TM_ATTR_TXBF_ACT: txbf setting actions (u8)
++ * @MT76_TM_ATTR_TXBF_PARAM: txbf parameters (nested)
++ *
++ * @MT76_TM_ATTR_OFF_CH_SCAN_CH: config the channel of background chain (ZWDFS) (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH: config the center channel of background chain (ZWDFS) (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_BW: config the bandwidth of background chain (ZWDFS) (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_PATH: config the tx path of background chain (ZWDFS) (u8)
++ *
++ * @MT76_TM_ATTR_IPI_THRESHOLD: config the IPI index you want to read (u8)
++ * @MT76_TM_ATTR_IPI_PERIOD: config the time period for reading
++ * the histogram of specific IPI index (u8)
++ * @MT76_TM_ATTR_IPI_ANTENNA_INDEX: config the antenna index for reading
++ * the histogram of specific IPI index (u8)
++ * @MT76_TM_ATTR_IPI_RESET: Reset the IPI counter
++ *
+ */
+ enum mt76_testmode_attr {
+ MT76_TM_ATTR_UNSPEC,
+@@ -56,7 +87,9 @@ enum mt76_testmode_attr {
+
+ MT76_TM_ATTR_MTD_PART,
+ MT76_TM_ATTR_MTD_OFFSET,
++ MT76_TM_ATTR_BAND_IDX,
+
++ MT76_TM_ATTR_SKU_EN,
+ MT76_TM_ATTR_TX_COUNT,
+ MT76_TM_ATTR_TX_LENGTH,
+ MT76_TM_ATTR_TX_RATE_MODE,
+@@ -74,6 +107,8 @@ enum mt76_testmode_attr {
+ MT76_TM_ATTR_FREQ_OFFSET,
+
+ MT76_TM_ATTR_STATS,
++ MT76_TM_ATTR_PRECAL,
++ MT76_TM_ATTR_PRECAL_INFO,
+
+ MT76_TM_ATTR_TX_SPE_IDX,
+
+@@ -84,6 +119,27 @@ enum mt76_testmode_attr {
+ MT76_TM_ATTR_DRV_DATA,
+
+ MT76_TM_ATTR_MAC_ADDRS,
++ MT76_TM_ATTR_AID,
++ MT76_TM_ATTR_RU_ALLOC,
++ MT76_TM_ATTR_RU_IDX,
++
++ MT76_TM_ATTR_EEPROM_ACTION,
++ MT76_TM_ATTR_EEPROM_OFFSET,
++ MT76_TM_ATTR_EEPROM_VAL,
++
++ MT76_TM_ATTR_CFG,
++ MT76_TM_ATTR_TXBF_ACT,
++ MT76_TM_ATTR_TXBF_PARAM,
++
++ MT76_TM_ATTR_OFF_CH_SCAN_CH,
++ MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
++ MT76_TM_ATTR_OFF_CH_SCAN_BW,
++ MT76_TM_ATTR_OFF_CH_SCAN_PATH,
++
++ MT76_TM_ATTR_IPI_THRESHOLD,
++ MT76_TM_ATTR_IPI_PERIOD,
++ MT76_TM_ATTR_IPI_ANTENNA_INDEX,
++ MT76_TM_ATTR_IPI_RESET,
+
+ /* keep last */
+ NUM_MT76_TM_ATTRS,
+@@ -101,6 +157,8 @@ enum mt76_testmode_attr {
+ * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64)
+ * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet
+ * see &enum mt76_testmode_rx_attr
++ * @MT76_TM_STATS_ATTR_RX_LEN_MISMATCH: number of rx packets with length
++ * mismatch error (u64)
+ */
+ enum mt76_testmode_stats_attr {
+ MT76_TM_STATS_ATTR_UNSPEC,
+@@ -113,6 +171,7 @@ enum mt76_testmode_stats_attr {
+ MT76_TM_STATS_ATTR_RX_PACKETS,
+ MT76_TM_STATS_ATTR_RX_FCS_ERROR,
+ MT76_TM_STATS_ATTR_LAST_RX,
++ MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
+
+ /* keep last */
+ NUM_MT76_TM_STATS_ATTRS,
+@@ -125,6 +184,7 @@ enum mt76_testmode_stats_attr {
+ *
+ * @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32)
+ * @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
++ * @MT76_TM_RX_ATTR_RSSI: received signal strength indicator (array, s8)
+ * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
+ * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
+ * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8)
+@@ -134,6 +194,7 @@ enum mt76_testmode_rx_attr {
+
+ MT76_TM_RX_ATTR_FREQ_OFFSET,
+ MT76_TM_RX_ATTR_RCPI,
++ MT76_TM_RX_ATTR_RSSI,
+ MT76_TM_RX_ATTR_IB_RSSI,
+ MT76_TM_RX_ATTR_WB_RSSI,
+ MT76_TM_RX_ATTR_SNR,
+@@ -177,6 +238,9 @@ enum mt76_testmode_state {
+ * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU
+ * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based
+ * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO
++ * @MT76_TM_TX_MODE_EHT_SU: 802.11be single-user MIMO
++ * @MT76_TM_TX_MODE_EHT_TRIG: 802.11be trigger-based
++ * @MT76_TM_TX_MODE_EHT_MU: 802.11be multi-user MIMO
+ */
+ enum mt76_testmode_tx_mode {
+ MT76_TM_TX_MODE_CCK,
+@@ -187,12 +251,33 @@ enum mt76_testmode_tx_mode {
+ MT76_TM_TX_MODE_HE_EXT_SU,
+ MT76_TM_TX_MODE_HE_TB,
+ MT76_TM_TX_MODE_HE_MU,
++ MT76_TM_TX_MODE_EHT_SU,
++ MT76_TM_TX_MODE_EHT_TRIG,
++ MT76_TM_TX_MODE_EHT_MU,
+
+ /* keep last */
+ NUM_MT76_TM_TX_MODES,
+ MT76_TM_TX_MODE_MAX = NUM_MT76_TM_TX_MODES - 1,
+ };
+
++/**
++ * enum mt76_testmode_eeprom_action - eeprom setting actions
++ *
++ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
++ * eeprom data block
++ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
++ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
++ */
++enum mt76_testmode_eeprom_action {
++ MT76_TM_EEPROM_ACTION_UPDATE_DATA,
++ MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE,
++ MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE,
++
++ /* keep last */
++ NUM_MT76_TM_EEPROM_ACTION,
++ MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
++};
++
+ extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
+
+ #endif
+diff --git a/tools/fields.c b/tools/fields.c
+index e3f690896..055f90f3c 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
+ [MT76_TM_STATE_IDLE] = "idle",
+ [MT76_TM_STATE_TX_FRAMES] = "tx_frames",
+ [MT76_TM_STATE_RX_FRAMES] = "rx_frames",
++ [MT76_TM_STATE_TX_CONT] = "tx_cont",
+ };
+
+ static const char * const testmode_tx_mode[] = {
+@@ -21,6 +22,9 @@ static const char * const testmode_tx_mode[] = {
+ [MT76_TM_TX_MODE_HE_EXT_SU] = "he_ext_su",
+ [MT76_TM_TX_MODE_HE_TB] = "he_tb",
+ [MT76_TM_TX_MODE_HE_MU] = "he_mu",
++ [MT76_TM_TX_MODE_EHT_SU] = "eht_su",
++ [MT76_TM_TX_MODE_EHT_TRIG] = "eht_tb",
++ [MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
+ };
+
+ static void print_enum(const struct tm_field *field, struct nlattr *attr)
+@@ -65,7 +69,7 @@ static bool parse_u8(const struct tm_field *field, int idx,
+
+ static void print_u8(const struct tm_field *field, struct nlattr *attr)
+ {
+- printf("%d", nla_get_u8(attr));
++ printf("%u", nla_get_u8(attr));
+ }
+
+ static void print_s8(const struct tm_field *field, struct nlattr *attr)
+@@ -86,12 +90,12 @@ static void print_s32(const struct tm_field *field, struct nlattr *attr)
+
+ static void print_u32(const struct tm_field *field, struct nlattr *attr)
+ {
+- printf("%d", nla_get_u32(attr));
++ printf("%u", nla_get_u32(attr));
+ }
+
+ static void print_u64(const struct tm_field *field, struct nlattr *attr)
+ {
+- printf("%lld", (unsigned long long)nla_get_u64(attr));
++ printf("%llu", (unsigned long long)nla_get_u64(attr));
+ }
+
+ static bool parse_flag(const struct tm_field *field, int idx,
+@@ -201,6 +205,62 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
+ printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
+ }
+
++static bool parse_mac(const struct tm_field *field, int idx,
++ struct nl_msg *msg, const char *val)
++{
++#define ETH_ALEN 6
++ bool ret = true;
++ char *str, *cur, *ap;
++ void *a;
++
++ str = strdup(val);
++ ap = str;
++
++ a = nla_nest_start(msg, idx);
++
++ idx = 0;
++ while ((cur = strsep(&ap, ",")) != NULL) {
++ unsigned char addr[ETH_ALEN];
++ char *val, *tmp = cur;
++ int i = 0;
++
++ while ((val = strsep(&tmp, ":")) != NULL) {
++ if (i >= ETH_ALEN)
++ break;
++
++ addr[i++] = strtoul(val, NULL, 16);
++ }
++
++ nla_put(msg, idx, ETH_ALEN, addr);
++
++ idx++;
++ }
++
++ nla_nest_end(msg, a);
++
++ free(str);
++
++ return ret;
++}
++
++static void print_mac(const struct tm_field *field, struct nlattr *attr)
++{
++#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
++#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
++ unsigned char addr[3][6];
++ struct nlattr *cur;
++ int idx = 0;
++ int rem;
++
++ nla_for_each_nested(cur, attr, rem) {
++ if (nla_len(cur) != 6)
++ continue;
++ memcpy(addr[idx++], nla_data(cur), 6);
++ }
++
++ printf("" MACSTR "," MACSTR "," MACSTR "",
++ MAC2STR(addr[0]), MAC2STR(addr[1]), MAC2STR(addr[2]));
++}
+
+ #define FIELD_GENERIC(_field, _name, ...) \
+ [FIELD_NAME(_field)] = { \
+@@ -250,10 +310,18 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
+ ##__VA_ARGS__ \
+ )
+
++#define FIELD_MAC(_field, _name) \
++ [FIELD_NAME(_field)] = { \
++ .name = _name, \
++ .parse = parse_mac, \
++ .print = print_mac \
++ }
++
+ #define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
+ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
+ FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
+ FIELD_ARRAY_RO(u8, RCPI, "rcpi"),
++ FIELD_ARRAY_RO(s8, RSSI, "rssi"),
+ FIELD_ARRAY_RO(s8, IB_RSSI, "ib_rssi"),
+ FIELD_ARRAY_RO(s8, WB_RSSI, "wb_rssi"),
+ FIELD_RO(s8, SNR, "snr"),
+@@ -261,6 +329,7 @@ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
+ static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
+ [MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ [MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
++ [MT76_TM_RX_ATTR_RSSI] = { .type = NLA_NESTED },
+ [MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
+ [MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
+ [MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
+@@ -274,6 +343,7 @@ static const struct tm_field stats_fields[NUM_MT76_TM_STATS_ATTRS] = {
+ FIELD_RO(u32, TX_DONE, "tx_done"),
+ FIELD_RO(u64, RX_PACKETS, "rx_packets"),
+ FIELD_RO(u64, RX_FCS_ERROR, "rx_fcs_error"),
++ FIELD_RO(u64, RX_LEN_MISMATCH, "rx_len_mismatch"),
+ FIELD_NESTED_RO(LAST_RX, rx, "last_"),
+ };
+ static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
+@@ -282,6 +352,7 @@ static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
+ [MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
+ [MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
+ [MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
++ [MT76_TM_STATS_ATTR_RX_LEN_MISMATCH] = { .type = NLA_U64 },
+ };
+ #undef FIELD_NAME
+
+@@ -291,6 +362,7 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ FIELD_ENUM(STATE, "state", testmode_state),
+ FIELD_RO(string, MTD_PART, "mtd_part"),
+ FIELD_RO(u32, MTD_OFFSET, "mtd_offset"),
++ FIELD(u8, SKU_EN, "sku_en"),
+ FIELD(u32, TX_COUNT, "tx_count"),
+ FIELD(u32, TX_LENGTH, "tx_length"),
+ FIELD_ENUM(TX_RATE_MODE, "tx_rate_mode", testmode_tx_mode),
+@@ -300,12 +372,20 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
+ FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
+ FIELD(u8, TX_LTF, "tx_ltf"),
++ FIELD(u8, TX_DUTY_CYCLE, "tx_duty_cycle"),
++ FIELD(u32, TX_IPG, "tx_ipg"),
++ FIELD(u32, TX_TIME, "tx_time"),
+ FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
+ FIELD_ARRAY(u8, TX_POWER, "tx_power"),
+ FIELD(u8, TX_ANTENNA, "tx_antenna"),
+ FIELD(u32, FREQ_OFFSET, "freq_offset"),
++ FIELD(u8, AID, "aid"),
++ FIELD(u8, RU_ALLOC, "ru_alloc"),
++ FIELD(u8, RU_IDX, "ru_idx"),
++ FIELD_MAC(MAC_ADDRS, "mac_addrs"),
+ FIELD_NESTED_RO(STATS, stats, "",
+ .print_extra = print_extra_stats),
++
+ };
+ #undef FIELD_NAME
+
+@@ -313,6 +393,7 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
+ [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
+ [MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
++ [MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
+@@ -322,10 +403,25 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
+ [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 },
++ [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
++ [MT76_TM_ATTR_AID] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_RU_ALLOC] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_RU_IDX] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
++ [MT76_TM_ATTR_TXBF_ACT] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
++ [MT76_TM_ATTR_IPI_ANTENNA_INDEX] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
+ };
+
+ const struct tm_field msg_field = {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch
new file mode 100644
index 0000000..dc9298f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch
@@ -0,0 +1,900 @@
+From d0914a4cc7a181e2a2c8c1295a99f2812525e31c Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 31 Mar 2023 11:27:24 +0800
+Subject: [PATCH 023/116] mtk: wifi: mt76: testmode: add testmode
+ pre-calibration support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: If8a6cc02fa20e35f079c826e0571e8c04c3f9c7e
+---
+ mac80211.c | 21 ---
+ mt76.h | 22 +++
+ mt76_connac_mcu.h | 2 +
+ mt7996/eeprom.c | 66 +++++++
+ mt7996/eeprom.h | 47 +++++
+ mt7996/mcu.c | 5 +
+ mt7996/mt7996.h | 7 +
+ mt7996/testmode.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/testmode.h | 20 ++-
+ testmode.c | 12 ++
+ testmode.h | 8 +
+ tools/fields.c | 8 +
+ 12 files changed, 632 insertions(+), 23 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index b285407a8..1127ef7b5 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -6,27 +6,6 @@
+ #include <linux/of.h>
+ #include "mt76.h"
+
+-#define CHAN2G(_idx, _freq) { \
+- .band = NL80211_BAND_2GHZ, \
+- .center_freq = (_freq), \
+- .hw_value = (_idx), \
+- .max_power = 30, \
+-}
+-
+-#define CHAN5G(_idx, _freq) { \
+- .band = NL80211_BAND_5GHZ, \
+- .center_freq = (_freq), \
+- .hw_value = (_idx), \
+- .max_power = 30, \
+-}
+-
+-#define CHAN6G(_idx, _freq) { \
+- .band = NL80211_BAND_6GHZ, \
+- .center_freq = (_freq), \
+- .hw_value = (_idx), \
+- .max_power = 30, \
+-}
+-
+ static const struct ieee80211_channel mt76_channels_2ghz[] = {
+ CHAN2G(1, 2412),
+ CHAN2G(2, 2417),
+diff --git a/mt76.h b/mt76.h
+index 58e8e726c..97a691991 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -23,6 +23,27 @@
+ #include "util.h"
+ #include "testmode.h"
+
++#define CHAN2G(_idx, _freq) { \
++ .band = NL80211_BAND_2GHZ, \
++ .center_freq = (_freq), \
++ .hw_value = (_idx), \
++ .max_power = 30, \
++}
++
++#define CHAN5G(_idx, _freq) { \
++ .band = NL80211_BAND_5GHZ, \
++ .center_freq = (_freq), \
++ .hw_value = (_idx), \
++ .max_power = 30, \
++}
++
++#define CHAN6G(_idx, _freq) { \
++ .band = NL80211_BAND_6GHZ, \
++ .center_freq = (_freq), \
++ .hw_value = (_idx), \
++ .max_power = 30, \
++}
++
+ #define MT_MCU_RING_SIZE 32
+ #define MT_RX_BUF_SIZE 2048
+ #define MT_SKB_HEAD_LEN 256
+@@ -703,6 +724,7 @@ struct mt76_testmode_ops {
+ void (*reset_rx_stats)(struct mt76_phy *phy);
+ void (*tx_stop)(struct mt76_phy *phy);
+ int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
++ int (*dump_precal)(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type);
+ };
+
+ #define MT_TM_FW_RX_COUNT BIT(0)
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 718552baf..f5ea719e4 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1042,8 +1042,10 @@ enum {
+ MCU_UNI_EVENT_RDD_REPORT = 0x11,
+ MCU_UNI_EVENT_ROC = 0x27,
+ MCU_UNI_EVENT_TX_DONE = 0x2d,
++ MCU_UNI_EVENT_BF = 0x33,
+ MCU_UNI_EVENT_THERMAL = 0x35,
+ MCU_UNI_EVENT_NIC_CAPAB = 0x43,
++ MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
+ MCU_UNI_EVENT_WED_RRO = 0x57,
+ MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index f9b9ca25d..62c1ad489 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -12,6 +12,42 @@ static bool testmode_enable;
+ module_param(testmode_enable, bool, 0644);
+ MODULE_PARM_DESC(testmode_enable, "Enable testmode");
+
++const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
++ CHAN2G(3, 2422),
++ CHAN2G(7, 2442),
++ CHAN2G(11, 2462)
++};
++
++const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
++ CHAN5G(50, 5250),
++ CHAN5G(114, 5570),
++ CHAN5G(163, 5815)
++};
++
++const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
++ CHAN6G(15, 6025),
++ CHAN6G(47, 6185),
++ CHAN6G(79, 6345),
++ CHAN6G(111, 6505),
++ CHAN6G(143, 6665),
++ CHAN6G(175, 6825),
++ CHAN6G(207, 6985)
++};
++
++const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
++ CHAN6G(31, 6105),
++ CHAN6G(63, 6265),
++ CHAN6G(95, 6425),
++ CHAN6G(127, 6585),
++ CHAN6G(159, 6745),
++ CHAN6G(191, 6905)
++};
++
++const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
++const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
++const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
++const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
++
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+ #define FEM_INT 0
+@@ -74,6 +110,36 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ }
+ }
+
++int
++mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
++{
++ /* handle different sku */
++ static const u8 band_to_idx[] = {
++ [NL80211_BAND_2GHZ] = MT_BAND0,
++ [NL80211_BAND_5GHZ] = MT_BAND1,
++ [NL80211_BAND_6GHZ] = MT_BAND2,
++ };
++ struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
++ struct mt76_phy *mphy;
++ int dpd_size;
++
++ if (!phy)
++ return 0;
++
++ mphy = phy->mt76;
++
++ if (band == NL80211_BAND_2GHZ)
++ dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
++ else if (band == NL80211_BAND_5GHZ)
++ dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++ dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++ else
++ dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++ (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
++
++ return dpd_size;
++}
++
+ static int
+ mt7996_eeprom_load_default(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index de3ff4e27..849b8bcab 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
+ MT_EE_MAC_ADDR = 0x004,
+ MT_EE_MAC_ADDR2 = 0x00a,
+ MT_EE_WIFI_CONF = 0x190,
++ MT_EE_DO_PRE_CAL = 0x1a5,
+ MT_EE_TESTMODE_EN = 0x1af,
+ MT_EE_MAC_ADDR3 = 0x2c0,
+ MT_EE_RATE_DELTA_2G = 0x1400,
+@@ -32,6 +33,52 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0)
+ #define MT_EE_WIFI_PA_LNA_CONFIG GENMASK(1, 0)
+
++#define MT_EE_WIFI_CAL_GROUP_2G BIT(0)
++#define MT_EE_WIFI_CAL_GROUP_5G BIT(1)
++#define MT_EE_WIFI_CAL_GROUP_6G BIT(2)
++#define MT_EE_WIFI_CAL_GROUP GENMASK(2, 0)
++#define MT_EE_WIFI_CAL_DPD_2G BIT(3)
++#define MT_EE_WIFI_CAL_DPD_5G BIT(4)
++#define MT_EE_WIFI_CAL_DPD_6G BIT(5)
++#define MT_EE_WIFI_CAL_DPD GENMASK(5, 3)
++
++#define MT_EE_CAL_UNIT 1024
++#define MT_EE_CAL_GROUP_SIZE_2G (4 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_GROUP_SIZE_5G (45 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_GROUP_SIZE_6G (125 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_ADCDCOC_SIZE_2G (4 * 4)
++#define MT_EE_CAL_ADCDCOC_SIZE_5G (4 * 4)
++#define MT_EE_CAL_ADCDCOC_SIZE_6G (4 * 5)
++#define MT_EE_CAL_GROUP_SIZE (MT_EE_CAL_GROUP_SIZE_2G + \
++ MT_EE_CAL_GROUP_SIZE_5G + \
++ MT_EE_CAL_GROUP_SIZE_6G + \
++ MT_EE_CAL_ADCDCOC_SIZE_2G + \
++ MT_EE_CAL_ADCDCOC_SIZE_5G + \
++ MT_EE_CAL_ADCDCOC_SIZE_6G)
++
++#define DPD_PER_CH_LEGACY_SIZE (4 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_MEM_SIZE (13 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_OTFG0_SIZE (2 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_BW20_SIZE (DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
++#define DPD_PER_CH_GT_BW20_SIZE (DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
++#define MT_EE_CAL_DPD_SIZE (780 * MT_EE_CAL_UNIT)
++
++extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
++extern const u32 dpd_2g_bw20_ch_num;
++extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
++extern const u32 dpd_5g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
++extern const u32 dpd_6g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
++extern const u32 dpd_6g_bw320_ch_num;
++
++#define RF_DPD_FLAT_CAL BIT(28)
++#define RF_PRE_CAL BIT(29)
++#define RF_DPD_FLAT_5G_CAL GENMASK(29, 28)
++#define RF_DPD_FLAT_5G_MEM_CAL (BIT(30) | BIT(28))
++#define RF_DPD_FLAT_6G_CAL GENMASK(30, 28)
++#define RF_DPD_FLAT_6G_MEM_CAL (BIT(31) | BIT(28))
++
+ #define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND2 GENMASK(5, 3)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index f09281430..82528f7b2 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -715,6 +715,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ case MCU_UNI_EVENT_WED_RRO:
+ mt7996_mcu_wed_rro_event(dev, skb);
+ break;
++#ifdef CONFIG_NL80211_TESTMODE
++ case MCU_UNI_EVENT_TESTMODE_CTRL:
++ mt7996_tm_rf_test_event(dev, skb);
++ break;
++#endif
+ default:
+ break;
+ }
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 0c6a4b96b..121c4a1a7 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -390,6 +390,9 @@ struct mt7996_dev {
+ struct dentry *debugfs_dir;
+ struct rchan *relay_fwlog;
+
++ void *cal;
++ u32 cur_prek_offset;
++
+ struct {
+ u16 table_mask;
+ u8 n_agrt;
+@@ -510,6 +513,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ struct ieee80211_channel *chan);
+ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
++int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
+ int mt7996_dma_init(struct mt7996_dev *dev);
+ void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+@@ -594,6 +598,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
++#ifdef CONFIG_NL80211_TESTMODE
++void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
++#endif
+
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 98eebceed..a756ee10d 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -7,6 +7,8 @@
+ #include "mac.h"
+ #include "mcu.h"
+ #include "testmode.h"
++#include "eeprom.h"
++#include "mtk_mcu.h"
+
+ enum {
+ TM_CHANGED_TXPOWER,
+@@ -397,6 +399,436 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
+ }
+ }
+
++static int
++mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
++{
++ u8 *eeprom;
++ u32 i, group_size, dpd_size, size, offs, *pre_cal;
++ int ret = 0;
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_dev *mdev = &dev->mt76;
++ struct mt7996_tm_req req = {
++ .rf_test = {
++ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++ .len = cpu_to_le16(sizeof(req.rf_test)),
++ .action = RF_ACTION_IN_RF_TEST,
++ .icap_len = RF_TEST_ICAP_LEN,
++ .op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++ .op.rf.param.cal_param.func_data = cpu_to_le32(RF_PRE_CAL),
++ .op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
++ },
++ };
++
++ if (!dev->flash_mode) {
++ dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
++ return -EOPNOTSUPP;
++ }
++
++ eeprom = mdev->eeprom.data;
++ dev->cur_prek_offset = 0;
++ group_size = MT_EE_CAL_GROUP_SIZE;
++ dpd_size = MT_EE_CAL_DPD_SIZE;
++ size = group_size + dpd_size;
++ offs = MT_EE_DO_PRE_CAL;
++
++ switch (state) {
++ case MT76_TM_STATE_GROUP_PREK:
++ if (!dev->cal) {
++ dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++ if (!dev->cal)
++ return -ENOMEM;
++ }
++
++ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
++ sizeof(req), false);
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
++ 30 * HZ);
++
++ if (ret) {
++ dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
++ return ret;
++ }
++
++ if (!ret)
++ eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
++ break;
++ case MT76_TM_STATE_GROUP_PREK_DUMP:
++ pre_cal = (u32 *)dev->cal;
++ if (!pre_cal) {
++ dev_info(dev->mt76.dev, "Not group pre-cal yet!\n");
++ return ret;
++ }
++ dev_info(dev->mt76.dev, "Group Pre-Cal:\n");
++ for (i = 0; i < (group_size / sizeof(u32)); i += 4) {
++ dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++ i * sizeof(u32), pre_cal[i], pre_cal[i + 1],
++ pre_cal[i + 2], pre_cal[i + 3]);
++ }
++ break;
++ case MT76_TM_STATE_GROUP_PREK_CLEAN:
++ pre_cal = (u32 *)dev->cal;
++ if (!pre_cal)
++ return ret;
++ memset(pre_cal, 0, group_size);
++ eeprom[offs] &= ~MT_EE_WIFI_CAL_GROUP;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return ret;
++}
++
++static int
++mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
++ const struct ieee80211_channel *chan_list, u32 channel_size,
++ enum nl80211_chan_width width, u32 func_data)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_phy *mphy = phy->mt76;
++ struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
++ struct ieee80211_channel chan_backup;
++ int i, ret;
++
++ if (!chan_list)
++ return -EOPNOTSUPP;
++
++ req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
++
++ memcpy(&chan_backup, chandef->chan, sizeof(struct ieee80211_channel));
++ memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
++
++ for (i = 0; i < channel_size; i++) {
++ memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
++ chandef->width = width;
++
++ /* set channel switch reason */
++ mphy->hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
++ mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++
++ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), req,
++ sizeof(*req), false);
++ if (ret) {
++ dev_err(dev->mt76.dev, "DPD Pre-cal: mcu send msg failed!\n");
++ goto out;
++ }
++ }
++
++out:
++ mphy->hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
++ memcpy(chandef, &chandef_backup, sizeof(struct cfg80211_chan_def));
++ memcpy(chandef->chan, &chan_backup, sizeof(struct ieee80211_channel));
++ mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++
++ return ret;
++}
++
++static int
++mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_dev *mdev = &dev->mt76;
++ struct mt76_phy *mphy = phy->mt76;
++ struct mt7996_tm_req req = {
++ .rf_test = {
++ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++ .len = cpu_to_le16(sizeof(req.rf_test)),
++ .action = RF_ACTION_IN_RF_TEST,
++ .icap_len = RF_TEST_ICAP_LEN,
++ .op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++ .op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
++ },
++ };
++ u32 i, j, group_size, dpd_size, size, offs, *pre_cal;
++ u32 wait_on_prek_offset = 0;
++ u8 do_precal, *eeprom;
++ int ret = 0;
++
++ if (!dev->flash_mode) {
++ dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
++ return -EOPNOTSUPP;
++ }
++
++ eeprom = mdev->eeprom.data;
++ dev->cur_prek_offset = 0;
++ group_size = MT_EE_CAL_GROUP_SIZE;
++ dpd_size = MT_EE_CAL_DPD_SIZE;
++ size = group_size + dpd_size;
++ offs = MT_EE_DO_PRE_CAL;
++
++ if (!dev->cal && state < MT76_TM_STATE_DPD_DUMP) {
++ dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++ if (!dev->cal)
++ return -ENOMEM;
++ }
++
++ switch (state) {
++ case MT76_TM_STATE_DPD_2G:
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
++ dpd_2g_bw20_ch_num,
++ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
++ wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait,
++ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++ do_precal = MT_EE_WIFI_CAL_DPD_2G;
++ break;
++ case MT76_TM_STATE_DPD_5G:
++ /* 5g channel bw20 calibration */
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_5g.sband.channels,
++ mphy->sband_5g.sband.n_channels,
++ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
++ if (ret)
++ return ret;
++ wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait,
++ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++ /* 5g channel bw160 calibration */
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
++ dpd_5g_bw160_ch_num,
++ NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
++ wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait,
++ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++ do_precal = MT_EE_WIFI_CAL_DPD_5G;
++ break;
++ case MT76_TM_STATE_DPD_6G:
++ /* 6g channel bw20 calibration */
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_6g.sband.channels,
++ mphy->sband_6g.sband.n_channels,
++ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
++ if (ret)
++ return ret;
++ wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait,
++ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++ /* 6g channel bw160 calibration */
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
++ dpd_6g_bw160_ch_num,
++ NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
++ if (ret)
++ return ret;
++ wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait,
++ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++ /* 6g channel bw320 calibration */
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
++ dpd_6g_bw320_ch_num,
++ NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
++ wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait,
++ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++ do_precal = MT_EE_WIFI_CAL_DPD_6G;
++ break;
++ case MT76_TM_STATE_DPD_DUMP:
++ if (!dev->cal) {
++ dev_info(dev->mt76.dev, "Not DPD pre-cal yet!\n");
++ return ret;
++ }
++ pre_cal = (u32 *)dev->cal;
++ dev_info(dev->mt76.dev, "DPD Pre-Cal:\n");
++ for (i = 0; i < dpd_size / sizeof(u32); i += 4) {
++ j = i + (group_size / sizeof(u32));
++ dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++ j * sizeof(u32), pre_cal[j], pre_cal[j + 1],
++ pre_cal[j + 2], pre_cal[j + 3]);
++ }
++ return 0;
++ case MT76_TM_STATE_DPD_CLEAN:
++ pre_cal = (u32 *)dev->cal;
++ if (!pre_cal)
++ return ret;
++ memset(pre_cal + (group_size / sizeof(u32)), 0, dpd_size);
++ do_precal = MT_EE_WIFI_CAL_DPD;
++ eeprom[offs] &= ~do_precal;
++ return 0;
++ default:
++ return -EINVAL;
++ }
++
++ if (!ret)
++ eeprom[offs] |= do_precal;
++
++ return ret;
++}
++
++static int
++mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type)
++{
++#define DPD_PER_CHAN_SIZE_MASK GENMASK(31, 30)
++#define DPD_2G_RATIO_MASK GENMASK(29, 20)
++#define DPD_5G_RATIO_MASK GENMASK(19, 10)
++#define DPD_6G_RATIO_MASK GENMASK(9, 0)
++ struct mt7996_phy *phy = mphy->priv;
++ struct mt7996_dev *dev = phy->dev;
++ u32 i, group_size, dpd_size, total_size, size, dpd_info = 0;
++ u32 dpd_size_2g, dpd_size_5g, dpd_size_6g;
++ u32 base, offs, transmit_size = 1000;
++ u8 *pre_cal, *eeprom;
++ void *precal;
++ enum prek_ops {
++ PREK_GET_INFO,
++ PREK_SYNC_ALL,
++ PREK_SYNC_GROUP,
++ PREK_SYNC_DPD_2G,
++ PREK_SYNC_DPD_5G,
++ PREK_SYNC_DPD_6G,
++ PREK_CLEAN_GROUP,
++ PREK_CLEAN_DPD,
++ };
++
++ if (!dev->cal) {
++ dev_info(dev->mt76.dev, "Not pre-cal yet!\n");
++ return 0;
++ }
++
++ group_size = MT_EE_CAL_GROUP_SIZE;
++ dpd_size = MT_EE_CAL_DPD_SIZE;
++ total_size = group_size + dpd_size;
++ pre_cal = dev->cal;
++ eeprom = dev->mt76.eeprom.data;
++ offs = MT_EE_DO_PRE_CAL;
++
++ dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++ dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++ dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++
++ switch (type) {
++ case PREK_SYNC_ALL:
++ base = 0;
++ size = total_size;
++ break;
++ case PREK_SYNC_GROUP:
++ base = 0;
++ size = group_size;
++ break;
++ case PREK_SYNC_DPD_2G:
++ base = group_size;
++ size = dpd_size_2g;
++ break;
++ case PREK_SYNC_DPD_5G:
++ base = group_size + dpd_size_2g;
++ size = dpd_size_5g;
++ break;
++ case PREK_SYNC_DPD_6G:
++ base = group_size + dpd_size_2g + dpd_size_5g;
++ size = dpd_size_6g;
++ break;
++ case PREK_GET_INFO:
++ break;
++ default:
++ return 0;
++ }
++
++ if (!flag) {
++ if (eeprom[offs] & MT_EE_WIFI_CAL_DPD) {
++ dpd_info |= u32_encode_bits(1, DPD_PER_CHAN_SIZE_MASK) |
++ u32_encode_bits(dpd_size_2g / MT_EE_CAL_UNIT,
++ DPD_2G_RATIO_MASK) |
++ u32_encode_bits(dpd_size_5g / MT_EE_CAL_UNIT,
++ DPD_5G_RATIO_MASK) |
++ u32_encode_bits(dpd_size_6g / MT_EE_CAL_UNIT,
++ DPD_6G_RATIO_MASK);
++ }
++ dev->cur_prek_offset = 0;
++ precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL_INFO);
++ if (!precal)
++ return -ENOMEM;
++ nla_put_u32(msg, 0, group_size);
++ nla_put_u32(msg, 1, dpd_size);
++ nla_put_u32(msg, 2, dpd_info);
++ nla_put_u32(msg, 3, transmit_size);
++ nla_put_u32(msg, 4, eeprom[offs]);
++ nla_nest_end(msg, precal);
++ } else {
++ precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL);
++ if (!precal)
++ return -ENOMEM;
++
++ transmit_size = (dev->cur_prek_offset + transmit_size < size) ?
++ transmit_size : (size - dev->cur_prek_offset);
++ for (i = 0; i < transmit_size; i++) {
++ if (nla_put_u8(msg, i, pre_cal[base + dev->cur_prek_offset + i]))
++ return -ENOMEM;
++ }
++ dev->cur_prek_offset += transmit_size;
++
++ nla_nest_end(msg, precal);
++ }
++
++ return 0;
++}
++
++static void
++mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *result,
++ struct mt7996_tm_rf_test_data *data)
++{
++ u32 base, dpd_size_2g, dpd_size_5g, dpd_size_6g, cal_idx, cal_type, len = 0;
++ u8 *pre_cal;
++
++ pre_cal = dev->cal;
++ dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++ dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++ dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++
++ cal_idx = le32_to_cpu(data->cal_idx);
++ cal_type = le32_to_cpu(data->cal_type);
++ len = le32_to_cpu(result->payload_length);
++ len = len - sizeof(struct mt7996_tm_rf_test_data);
++
++ switch (cal_type) {
++ case RF_PRE_CAL:
++ base = 0;
++ break;
++ case RF_DPD_FLAT_CAL:
++ base = MT_EE_CAL_GROUP_SIZE;
++ break;
++ case RF_DPD_FLAT_5G_CAL:
++ case RF_DPD_FLAT_5G_MEM_CAL:
++ base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g;
++ break;
++ case RF_DPD_FLAT_6G_CAL:
++ case RF_DPD_FLAT_6G_MEM_CAL:
++ base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g + dpd_size_5g;
++ break;
++ default:
++ dev_info(dev->mt76.dev, "Unknown calibration type!\n");
++ return;
++ }
++ pre_cal += (base + dev->cur_prek_offset);
++
++ memcpy(pre_cal, data->cal_data, len);
++ dev->cur_prek_offset += len;
++}
++
++void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++ struct mt7996_tm_event *event;
++ struct mt7996_tm_rf_test_result *result;
++ struct mt7996_tm_rf_test_data *data;
++ static u32 event_type;
++
++ skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
++ event = (struct mt7996_tm_event *)skb->data;
++ result = (struct mt7996_tm_rf_test_result *)&event->result;
++ data = (struct mt7996_tm_rf_test_data *)result->data;
++
++ event_type = le32_to_cpu(result->func_idx);
++
++ switch (event_type) {
++ case RF_TEST_RE_CAL:
++ mt7996_tm_re_cal_event(dev, result, data);
++ break;
++ default:
++ break;
++ }
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -454,6 +886,10 @@ mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+ else if (prev_state == MT76_TM_STATE_OFF ||
+ state == MT76_TM_STATE_OFF)
+ mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
++ else if (state >= MT76_TM_STATE_GROUP_PREK && state <= MT76_TM_STATE_GROUP_PREK_CLEAN)
++ return mt7996_tm_group_prek(phy, state);
++ else if (state >= MT76_TM_STATE_DPD_2G && state <= MT76_TM_STATE_DPD_CLEAN)
++ return mt7996_tm_dpd_prek(phy, state);
+
+ if ((state == MT76_TM_STATE_IDLE &&
+ prev_state == MT76_TM_STATE_OFF) ||
+@@ -737,4 +1173,5 @@ const struct mt76_testmode_ops mt7996_testmode_ops = {
+ .reset_rx_stats = mt7996_tm_reset_trx_stats,
+ .tx_stop = mt7996_tm_tx_stop,
+ .set_eeprom = mt7996_tm_set_eeprom,
++ .dump_precal = mt7996_tm_dump_precal,
+ };
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index 319ef257a..9bfb86f28 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -34,6 +34,12 @@ enum bw_mapping_method {
+ NUM_BW_MAP,
+ };
+
++struct tm_cal_param {
++ __le32 func_data;
++ u8 band_idx;
++ u8 rsv[3];
++};
++
+ struct mt7996_tm_rf_test {
+ __le16 tag;
+ __le16 len;
+@@ -50,7 +56,7 @@ struct mt7996_tm_rf_test {
+ union {
+ __le32 func_data;
+ __le32 cal_dump;
+-
++ struct tm_cal_param cal_param;
+ u8 _pad[80];
+ } param;
+ } rf;
+@@ -63,10 +69,16 @@ struct mt7996_tm_req {
+ struct mt7996_tm_rf_test rf_test;
+ } __packed;
+
++struct mt7996_tm_rf_test_data {
++ __le32 cal_idx;
++ __le32 cal_type;
++ u8 cal_data[0];
++} __packed;
++
+ struct mt7996_tm_rf_test_result {
+ __le32 func_idx;
+ __le32 payload_length;
+- u8 event[0];
++ u8 data[0];
+ };
+
+ struct mt7996_tm_event {
+@@ -77,6 +89,8 @@ struct mt7996_tm_event {
+ struct mt7996_tm_rf_test_result result;
+ } __packed;
+
++#define RF_TEST_RE_CAL 0x01
++
+ enum {
+ RF_ACTION_SWITCH_TO_RF_TEST,
+ RF_ACTION_IN_RF_TEST,
+@@ -84,6 +98,8 @@ enum {
+ RF_ACTION_GET,
+ };
+
++#define RF_TEST_ICAP_LEN 120
++
+ enum {
+ RF_OPER_NORMAL,
+ RF_OPER_RF_TEST,
+diff --git a/testmode.c b/testmode.c
+index 44f3a5bfc..cd8cb6553 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -674,6 +674,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+
+ mutex_lock(&dev->mutex);
+
++ if (tb[MT76_TM_ATTR_PRECAL] || tb[MT76_TM_ATTR_PRECAL_INFO]) {
++ int flag, type;
++
++ err = -EINVAL;
++ flag = tb[MT76_TM_ATTR_PRECAL] ? 1 : 0;
++ type = flag ? nla_get_u8(tb[MT76_TM_ATTR_PRECAL_INFO]) : 0;
++ if (dev->test_ops->dump_precal)
++ err = dev->test_ops->dump_precal(phy, msg, flag, type);
++
++ goto out;
++ }
++
+ if (tb[MT76_TM_ATTR_STATS]) {
+ err = -EINVAL;
+
+diff --git a/testmode.h b/testmode.h
+index 96872e8cd..d6601cdcf 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -220,6 +220,14 @@ enum mt76_testmode_state {
+ MT76_TM_STATE_TX_FRAMES,
+ MT76_TM_STATE_RX_FRAMES,
+ MT76_TM_STATE_TX_CONT,
++ MT76_TM_STATE_GROUP_PREK,
++ MT76_TM_STATE_GROUP_PREK_DUMP,
++ MT76_TM_STATE_GROUP_PREK_CLEAN,
++ MT76_TM_STATE_DPD_2G,
++ MT76_TM_STATE_DPD_5G,
++ MT76_TM_STATE_DPD_6G,
++ MT76_TM_STATE_DPD_DUMP,
++ MT76_TM_STATE_DPD_CLEAN,
+ MT76_TM_STATE_ON,
+
+ /* keep last */
+diff --git a/tools/fields.c b/tools/fields.c
+index 055f90f3c..b01227638 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -11,6 +11,14 @@ static const char * const testmode_state[] = {
+ [MT76_TM_STATE_TX_FRAMES] = "tx_frames",
+ [MT76_TM_STATE_RX_FRAMES] = "rx_frames",
+ [MT76_TM_STATE_TX_CONT] = "tx_cont",
++ [MT76_TM_STATE_GROUP_PREK] = "group_prek",
++ [MT76_TM_STATE_GROUP_PREK_DUMP] = "group_prek_dump",
++ [MT76_TM_STATE_GROUP_PREK_CLEAN] = "group_prek_clean",
++ [MT76_TM_STATE_DPD_2G] = "dpd_2g",
++ [MT76_TM_STATE_DPD_5G] = "dpd_5g",
++ [MT76_TM_STATE_DPD_6G] = "dpd_6g",
++ [MT76_TM_STATE_DPD_DUMP] = "dpd_dump",
++ [MT76_TM_STATE_DPD_CLEAN] = "dpd_clean",
+ };
+
+ static const char * const testmode_tx_mode[] = {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch
new file mode 100644
index 0000000..740ee4a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch
@@ -0,0 +1,285 @@
+From 9b77eae5dca30ef64dea0b2a48c05b22a6a8bc25 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 1 Mar 2023 12:12:51 +0800
+Subject: [PATCH 024/116] mtk: wifi: mt76: mt7996: add normal mode
+ pre-calibration support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76_connac_mcu.h | 1 +
+ mt7996/eeprom.c | 4 ++
+ mt7996/eeprom.h | 2 +
+ mt7996/init.c | 6 ++
+ mt7996/main.c | 6 ++
+ mt7996/mcu.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mt7996.h | 3 +
+ 7 files changed, 188 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index f5ea719e4..c785edb80 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1277,6 +1277,7 @@ enum {
+ MCU_UNI_CMD_PP = 0x38,
+ MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
++ MCU_UNI_CMD_PRECAL_RESULT = 0x47,
+ MCU_UNI_CMD_RRO = 0x57,
+ MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+ MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 62c1ad489..4afa2a295 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -356,6 +356,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ return ret;
+ }
+
++ ret = mt7996_eeprom_load_precal(dev);
++ if (ret)
++ return ret;
++
+ ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
+ if (ret < 0)
+ return ret;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 849b8bcab..58179c0c5 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -25,6 +25,8 @@ enum mt7996_eeprom_field {
+ MT_EE_TX0_POWER_6G = 0x1310,
+
+ __MT_EE_MAX = 0x1dff,
++ /* 0x1e10 ~ 0x2d644 used to save group cal data */
++ MT_EE_PRECAL = 0x1e10,
+ };
+
+ #define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 440e26d58..201daf110 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -985,6 +985,12 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ if (ret < 0)
+ return ret;
+
++ if (dev->flash_mode) {
++ ret = mt7996_mcu_apply_group_cal(dev);
++ if (ret)
++ return ret;
++ }
++
+ /* Beacon and mgmt frames should occupy wcid 0 */
+ idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+ if (idx)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 48cc87e07..479940095 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -317,6 +317,12 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+
+ mt76_set_channel(phy->mt76);
+
++ if (dev->flash_mode) {
++ ret = mt7996_mcu_apply_tx_dpd(phy);
++ if (ret)
++ goto out;
++ }
++
+ if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
+ mt7996_tm_update_channel(phy);
+ goto out;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 82528f7b2..a006b0e34 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3631,6 +3631,172 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+ return 0;
+ }
+
++static int mt7996_mcu_set_pre_cal(struct mt7996_dev *dev, u32 idx,
++ u8 *cal, u32 len, u32 cal_id)
++{
++#define PRECAL_CMD_PRE_CAL_RESULT 0x0
++ struct {
++ /* fixed field */
++ u8 action;
++ u8 dest;
++ u8 attribute;
++ u8 tag_num;
++
++ __le16 tag;
++ __le16 len;
++
++ __le32 cal_id;
++ s8 precal;
++ u8 band;
++ u8 rsv[2];
++ __le32 idx;
++ __le32 cal_len;
++ } req = {
++ .tag = cpu_to_le16(PRECAL_CMD_PRE_CAL_RESULT),
++ .len = cpu_to_le16(sizeof(req) - 4 + len),
++ .cal_id = cpu_to_le32(cal_id),
++ .idx = cpu_to_le32(idx),
++ .cal_len = cpu_to_le32(len),
++ };
++ struct sk_buff *skb;
++
++ if (!len)
++ return 0;
++
++ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len);
++ if (!skb)
++ return -ENOMEM;
++
++ skb_put_data(skb, &req, sizeof(req));
++ skb_put_data(skb, cal, len);
++
++ return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(PRECAL_RESULT), false);
++}
++
++int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev)
++{
++ u8 *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
++ u32 idx = 0, total_idx = MT_EE_CAL_GROUP_SIZE / MT_EE_CAL_UNIT;
++ u32 offs = MT_EE_DO_PRE_CAL;
++ int ret = 0;
++
++ if (!(eeprom[offs] & MT_EE_WIFI_CAL_GROUP))
++ return 0;
++
++ for (idx = 0; idx < total_idx; idx++, cal += MT_EE_CAL_UNIT) {
++ ret = mt7996_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, RF_PRE_CAL);
++ if (ret)
++ goto out;
++ }
++
++ ret = mt7996_mcu_set_pre_cal(dev, total_idx, cal,
++ MT_EE_CAL_GROUP_SIZE % MT_EE_CAL_UNIT, RF_PRE_CAL);
++
++out:
++ return ret;
++}
++
++int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_phy *mphy = phy->mt76;
++ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++ enum nl80211_band band = chandef->chan->band;
++ enum nl80211_chan_width bw = chandef->width;
++ const struct ieee80211_channel *chan_list;
++ u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
++ u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
++ u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
++ u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
++ int idx, i, ret;
++
++ dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++ dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++
++ switch (band) {
++ case NL80211_BAND_2GHZ:
++ dpd_mask = MT_EE_WIFI_CAL_DPD_2G;
++ /* channel 14 don't need DPD cal */
++ if (channel >= 1 && channel <= 4)
++ channel = 3;
++ else if (channel >= 5 && channel <= 9)
++ channel = 7;
++ else if (channel >= 10 && channel <= 13)
++ channel = 11;
++ else
++ return 0;
++ cal_id = RF_DPD_FLAT_CAL;
++ chan_list = dpd_2g_ch_list_bw20;
++ chan_list_size = dpd_2g_bw20_ch_num;
++ break;
++ case NL80211_BAND_5GHZ:
++ dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
++ cal_id = RF_DPD_FLAT_5G_CAL;
++ chan_list = mphy->sband_5g.sband.channels;
++ chan_list_size = mphy->sband_5g.sband.n_channels;
++ base_offset += dpd_size_2g;
++ if (bw == NL80211_CHAN_WIDTH_160) {
++ base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++ cal_id = RF_DPD_FLAT_5G_MEM_CAL;
++ chan_list = dpd_5g_ch_list_bw160;
++ chan_list_size = dpd_5g_bw160_ch_num;
++ } else if (bw > NL80211_CHAN_WIDTH_20) {
++ /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
++ channel -= 2;
++ }
++ break;
++ case NL80211_BAND_6GHZ:
++ dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
++ cal_id = RF_DPD_FLAT_6G_CAL;
++ chan_list = mphy->sband_6g.sband.channels;
++ chan_list_size = mphy->sband_6g.sband.n_channels;
++ base_offset += dpd_size_2g + dpd_size_5g;
++ if (bw == NL80211_CHAN_WIDTH_160) {
++ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++ chan_list = dpd_6g_ch_list_bw160;
++ chan_list_size = dpd_6g_bw160_ch_num;
++ } else if (bw == NL80211_CHAN_WIDTH_320) {
++ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++ dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++ chan_list = dpd_6g_ch_list_bw320;
++ chan_list_size = dpd_6g_bw320_ch_num;
++ } else if (bw > NL80211_CHAN_WIDTH_20) {
++ /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
++ channel -= 2;
++ }
++ break;
++ default:
++ dpd_mask = 0;
++ break;
++ }
++
++ if (!(eeprom[offs] & dpd_mask))
++ return 0;
++
++ for (idx = 0; idx < chan_list_size; idx++)
++ if (channel == chan_list[idx].hw_value)
++ break;
++ if (idx == chan_list_size)
++ return -EINVAL;
++
++ cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
++
++ for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
++ ret = mt7996_mcu_set_pre_cal(dev, i, cal, MT_EE_CAL_UNIT, cal_id);
++ if (ret)
++ return ret;
++
++ cal += MT_EE_CAL_UNIT;
++ }
++
++ return ret;
++}
++
+ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
+ {
+ #define NIC_CAP 3
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 121c4a1a7..5e2d8bef0 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -598,6 +598,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
++int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
++int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev);
++int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch
new file mode 100644
index 0000000..8e37799
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch
@@ -0,0 +1,304 @@
+From d5f1a204501372080b078364e41762992e81eec6 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 8 May 2023 09:03:50 +0800
+Subject: [PATCH 025/116] mtk: wifi: mt76: mt7996: enable SCS feature for
+ mt7996 driver
+
+Enable Smart Carrier Sense algorithn by default to improve performance
+in a noisy environment.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt76_connac_mcu.h | 1 +
+ mt7996/init.c | 1 +
+ mt7996/mac.c | 2 +
+ mt7996/main.c | 7 +++
+ mt7996/mcu.c | 105 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h | 6 +++
+ mt7996/mt7996.h | 15 +++++++
+ mt7996/mtk_debugfs.c | 11 +++++
+ 8 files changed, 148 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index c785edb80..d8830dc25 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1262,6 +1262,7 @@ enum {
+ MCU_UNI_CMD_GET_STAT_INFO = 0x23,
+ MCU_UNI_CMD_SNIFFER = 0x24,
+ MCU_UNI_CMD_SR = 0x25,
++ MCU_UNI_CMD_SCS = 0x26,
+ MCU_UNI_CMD_ROC = 0x27,
+ MCU_UNI_CMD_SET_DBDC_PARMS = 0x28,
+ MCU_UNI_CMD_TXPOWER = 0x2b,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 201daf110..6a394c364 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1415,6 +1415,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ dev->mt76.phy.priv = &dev->phy;
+ INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
+ INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
++ INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
+ INIT_LIST_HEAD(&dev->sta_rc_list);
+ INIT_LIST_HEAD(&dev->twt_list);
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 603f6c0d7..c9f45abef 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1795,6 +1795,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
+ cancel_delayed_work_sync(&phy2->mt76->mac_work);
+ if (phy3)
+ cancel_delayed_work_sync(&phy3->mt76->mac_work);
++ cancel_delayed_work_sync(&dev->scs_work);
+
+ mutex_lock(&dev->mt76.mutex);
+ for (i = 0; i < 10; i++) {
+@@ -1830,6 +1831,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
+ ieee80211_queue_delayed_work(phy3->mt76->hw,
+ &phy3->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
++ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
+ }
+
+ void mt7996_mac_reset_work(struct work_struct *work)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 479940095..57865f1de 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -81,11 +81,17 @@ int mt7996_run(struct ieee80211_hw *hw)
+ if (ret)
+ goto out;
+
++ ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
++ if (ret)
++ goto out;
++
+ set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+ ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
+
++ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++
+ if (!running)
+ mt7996_mac_reset_counters(phy);
+
+@@ -113,6 +119,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+
+ cancel_delayed_work_sync(&phy->mt76->mac_work);
++ cancel_delayed_work_sync(&dev->scs_work);
+
+ mutex_lock(&dev->mt76.mutex);
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a006b0e34..2650dc6ff 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4813,3 +4813,108 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
+ &req, sizeof(req), false);
+ }
++
++int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
++{
++ struct mt7996_scs_ctrl ctrl = phy->scs_ctrl;
++ struct {
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 _rsv2[6];
++ s8 min_rssi;
++ u8 _rsv3;
++ } __packed req = {
++ .band_idx = phy->mt76->band_idx,
++ .tag = cpu_to_le16(UNI_CMD_SCS_SEND_DATA),
++ .len = cpu_to_le16(sizeof(req) - 4),
++
++ .min_rssi = ctrl.sta_min_rssi,
++ };
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
++ &req, sizeof(req), false);
++}
++
++void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
++{
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_phy *poll_phy = (struct mt7996_phy *) data;
++
++ if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
++ poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
++}
++
++void mt7996_mcu_scs_sta_poll(struct work_struct *work)
++{
++ struct mt7996_dev *dev = container_of(work, struct mt7996_dev,
++ scs_work.work);
++ bool scs_enable_flag = false;
++ u8 i;
++
++ for (i = 0; i < __MT_MAX_BAND; i++) {
++ struct mt7996_phy *phy;
++
++ switch (i) {
++ case MT_BAND0:
++ phy = dev->mphy.priv;
++ break;
++ case MT_BAND1:
++ phy = mt7996_phy2(dev);
++ break;
++ case MT_BAND2:
++ phy = mt7996_phy3(dev);
++ break;
++ default:
++ phy = NULL;
++ break;
++ }
++
++ if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state) ||
++ !phy->scs_ctrl.scs_enable)
++ continue;
++
++ ieee80211_iterate_stations_atomic(phy->mt76->hw,
++ mt7996_sta_rssi_work, phy);
++
++ scs_enable_flag = true;
++ if (mt7996_mcu_set_scs_stats(phy))
++ dev_err(dev->mt76.dev, "Failed to send scs mcu cmd\n");
++ phy->scs_ctrl.sta_min_rssi = 0;
++ }
++
++ if (scs_enable_flag)
++ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++}
++
++
++int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct {
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 scs_enable;
++ u8 _rsv2[3];
++ } __packed req = {
++ .band_idx = phy->mt76->band_idx,
++ .tag = cpu_to_le16(UNI_CMD_SCS_ENABLE),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .scs_enable = enable,
++ };
++
++ phy->scs_ctrl.scs_enable = enable;
++
++ if (enable == SCS_ENABLE)
++ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
++ &req, sizeof(req), false);
++}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index d5ac2b38e..5953b25e8 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -966,6 +966,12 @@ enum pp_mode {
+ PP_USR_MODE,
+ };
+
++enum {
++ UNI_CMD_SCS_SEND_DATA,
++ UNI_CMD_SCS_SET_PD_THR_RANGE = 2,
++ UNI_CMD_SCS_ENABLE,
++};
++
+ #define MT7996_PATCH_SEC GENMASK(31, 24)
+ #define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8)
+ #define MT7996_PATCH_AES_KEY GENMASK(7, 0)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 5e2d8bef0..186af3c1d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -234,6 +234,16 @@ struct mt7996_hif {
+ int irq;
+ };
+
++struct mt7996_scs_ctrl {
++ u8 scs_enable;
++ s8 sta_min_rssi;
++};
++
++enum {
++ SCS_DISABLE = 0,
++ SCS_ENABLE,
++};
++
+ struct mt7996_wed_rro_addr {
+ u32 head_low;
+ u32 head_high : 4;
+@@ -284,6 +294,8 @@ struct mt7996_phy {
+ u8 pp_mode;
+ u16 punct_bitmap;
+
++ struct mt7996_scs_ctrl scs_ctrl;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ struct {
+ u32 *reg_backup;
+@@ -330,6 +342,7 @@ struct mt7996_dev {
+ struct work_struct rc_work;
+ struct work_struct dump_work;
+ struct work_struct reset_work;
++ struct delayed_work scs_work;
+ wait_queue_head_t reset_wait;
+ struct {
+ u32 state;
+@@ -604,6 +617,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
++int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
++void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 8baf27c76..9b4d5f68c 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2430,6 +2430,16 @@ static int mt7996_token_read(struct seq_file *s, void *data)
+ return 0;
+ }
+
++static int
++mt7996_scs_enable_set(void *data, u64 val)
++{
++ struct mt7996_phy *phy = data;
++
++ return mt7996_mcu_set_scs(phy, (u8) val);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
++ mt7996_scs_enable_set, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -2500,6 +2510,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
+
+ debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
++ debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+
+ return 0;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-wifi-mt76-mt7996-add-txpower-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-wifi-mt76-mt7996-add-txpower-support.patch
new file mode 100644
index 0000000..b056189
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-wifi-mt76-mt7996-add-txpower-support.patch
@@ -0,0 +1,1047 @@
+From c3e9680c5a1387067019b361390df5a150eb232b Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Fri, 24 Mar 2023 23:35:30 +0800
+Subject: [PATCH 026/116] mtk: wifi: mt76: mt7996: add txpower support
+
+Add single sku and default enable sku.
+
+mtk: wifi: mt76: mt7996: Porting wifi6 txpower fix to eagle
+
+Refactor txpower flow.
+1. Fix wrong bbp CR address
+2. Ignore RegDB power limit when we have single sku table. And dump more informaiton in debugfs.
+3. Refactor get_txpower ops flow, we only consider CCK and OFDM power value as maximum.
+4. Remove sku_disable due to SQC is over and default enable both sku tables.
+
+Fix wrong power value when user set limit close to path table limit.
+
+CR-Id: WCNCR00259302
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ eeprom.c | 54 ++++++-
+ mt76.h | 9 ++
+ mt76_connac_mcu.c | 2 +-
+ mt7996/eeprom.c | 34 ++++
+ mt7996/eeprom.h | 42 +++++
+ mt7996/init.c | 16 +-
+ mt7996/main.c | 15 ++
+ mt7996/mcu.c | 69 ++++++++-
+ mt7996/mcu.h | 2 +
+ mt7996/mt7996.h | 4 +
+ mt7996/mtk_debugfs.c | 362 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c | 23 +++
+ mt7996/mtk_mcu.h | 92 +++++++++++
+ mt7996/regs.h | 27 ++--
+ 14 files changed, 724 insertions(+), 27 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index a0047d791..11efe2937 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -305,9 +305,10 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
+ static void
+ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
+ const __be32 *data, size_t len, s8 target_power,
+- s8 nss_delta, s8 *max_power)
++ s8 nss_delta)
+ {
+ int i, cur;
++ s8 max_power = -128;
+
+ if (!data)
+ return;
+@@ -319,7 +320,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
+ break;
+
+ mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
+- target_power, nss_delta, max_power);
++ target_power, nss_delta, &max_power);
+ if (--cur > 0)
+ continue;
+
+@@ -335,6 +336,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
+ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ struct ieee80211_channel *chan,
+ struct mt76_power_limits *dest,
++ struct mt76_power_path_limits *dest_path,
+ s8 target_power)
+ {
+ struct mt76_dev *dev = phy->dev;
+@@ -342,16 +344,20 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ const __be32 *val;
+ char name[16];
+ u32 mcs_rates = dev->drv->mcs_rates;
+- u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
+ char band;
+ size_t len;
+- s8 max_power = 0;
++ s8 max_power = -127;
++ s8 max_power_backoff = -127;
+ s8 txs_delta;
++ int n_chains = hweight16(phy->chainmask);
++ s8 target_power_combine = target_power + mt76_tx_power_nss_delta(n_chains);
+
+ if (!mcs_rates)
+- mcs_rates = 10;
++ mcs_rates = 12;
+
+ memset(dest, target_power, sizeof(*dest));
++ if (dest_path != NULL)
++ memset(dest_path, 0, sizeof(*dest_path));
+
+ if (!IS_ENABLED(CONFIG_OF))
+ return target_power;
+@@ -397,12 +403,44 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
+ mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
+ ARRAY_SIZE(dest->mcs), val, len,
+- target_power, txs_delta, &max_power);
++ target_power, txs_delta);
+
+- val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
++ val = mt76_get_of_array(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
+ mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
+ ARRAY_SIZE(dest->ru), val, len,
+- target_power, txs_delta, &max_power);
++ target_power, txs_delta);
++
++ val = mt76_get_of_array(np, "rates-eht", &len, ARRAY_SIZE(dest->eht[0]) + 1);
++ mt76_apply_multi_array_limit(dest->eht[0], ARRAY_SIZE(dest->eht[0]),
++ ARRAY_SIZE(dest->eht), val, len,
++ target_power, txs_delta);
++
++ if (dest_path == NULL)
++ return max_power;
++
++ max_power_backoff = max_power;
++
++ val = mt76_get_of_array(np, "paths-cck", &len, ARRAY_SIZE(dest_path->cck));
++ mt76_apply_array_limit(dest_path->cck, ARRAY_SIZE(dest_path->cck), val,
++ target_power_combine, txs_delta, &max_power_backoff);
++
++ val = mt76_get_of_array(np, "paths-ofdm", &len, ARRAY_SIZE(dest_path->ofdm));
++ mt76_apply_array_limit(dest_path->ofdm, ARRAY_SIZE(dest_path->ofdm), val,
++ target_power_combine, txs_delta, &max_power_backoff);
++
++ val = mt76_get_of_array(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest_path->ofdm_bf));
++ mt76_apply_array_limit(dest_path->ofdm_bf, ARRAY_SIZE(dest_path->ofdm_bf), val,
++ target_power_combine, txs_delta, &max_power_backoff);
++
++ val = mt76_get_of_array(np, "paths-ru", &len, ARRAY_SIZE(dest_path->ru[0]) + 1);
++ mt76_apply_multi_array_limit(dest_path->ru[0], ARRAY_SIZE(dest_path->ru[0]),
++ ARRAY_SIZE(dest_path->ru), val, len,
++ target_power_combine, txs_delta);
++
++ val = mt76_get_of_array(np, "paths-ru-bf", &len, ARRAY_SIZE(dest_path->ru_bf[0]) + 1);
++ mt76_apply_multi_array_limit(dest_path->ru_bf[0], ARRAY_SIZE(dest_path->ru_bf[0]),
++ ARRAY_SIZE(dest_path->ru_bf), val, len,
++ target_power_combine, txs_delta);
+
+ return max_power;
+ }
+diff --git a/mt76.h b/mt76.h
+index 97a691991..20fdd050a 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -1059,6 +1059,14 @@ struct mt76_power_limits {
+ s8 eht[16][16];
+ };
+
++struct mt76_power_path_limits {
++ s8 cck[5];
++ s8 ofdm[5];
++ s8 ofdm_bf[4];
++ s8 ru[16][15];
++ s8 ru_bf[16][15];
++};
++
+ struct mt76_ethtool_worker_info {
+ u64 *data;
+ int idx;
+@@ -1669,6 +1677,7 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
+ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ struct ieee80211_channel *chan,
+ struct mt76_power_limits *dest,
++ struct mt76_power_path_limits *dest_path,
+ s8 target_power);
+
+ static inline bool mt76_queue_is_wed_tx_free(struct mt76_queue *q)
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 1e34e0ae3..0c7b69352 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -2150,7 +2150,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
+ sar_power = mt76_get_sar_power(phy, &chan, reg_power);
+
+ mt76_get_rate_power_limits(phy, &chan, limits,
+- sar_power);
++ NULL, sar_power);
+
+ tx_power_tlv.last_msg = ch_list[idx] == last_ch;
+ sku_tlbv.channel = ch_list[idx];
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 4afa2a295..6ac992a81 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -408,3 +408,37 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
+
+ return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
+ }
++
++const u8 mt7996_sku_group_len[] = {
++ [SKU_CCK] = 4,
++ [SKU_OFDM] = 8,
++ [SKU_HT20] = 8,
++ [SKU_HT40] = 9,
++ [SKU_VHT20] = 12,
++ [SKU_VHT40] = 12,
++ [SKU_VHT80] = 12,
++ [SKU_VHT160] = 12,
++ [SKU_HE26] = 12,
++ [SKU_HE52] = 12,
++ [SKU_HE106] = 12,
++ [SKU_HE242] = 12,
++ [SKU_HE484] = 12,
++ [SKU_HE996] = 12,
++ [SKU_HE2x996] = 12,
++ [SKU_EHT26] = 16,
++ [SKU_EHT52] = 16,
++ [SKU_EHT106] = 16,
++ [SKU_EHT242] = 16,
++ [SKU_EHT484] = 16,
++ [SKU_EHT996] = 16,
++ [SKU_EHT2x996] = 16,
++ [SKU_EHT4x996] = 16,
++ [SKU_EHT26_52] = 16,
++ [SKU_EHT26_106] = 16,
++ [SKU_EHT484_242] = 16,
++ [SKU_EHT996_484] = 16,
++ [SKU_EHT996_484_242] = 16,
++ [SKU_EHT2x996_484] = 16,
++ [SKU_EHT3x996] = 16,
++ [SKU_EHT3x996_484] = 16,
++};
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 58179c0c5..b19ff068e 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -125,4 +125,46 @@ mt7996_get_channel_group_6g(int channel)
+ return DIV_ROUND_UP(channel - 29, 32);
+ }
+
++enum mt7996_sku_rate_group {
++ SKU_CCK,
++ SKU_OFDM,
++
++ SKU_HT20,
++ SKU_HT40,
++
++ SKU_VHT20,
++ SKU_VHT40,
++ SKU_VHT80,
++ SKU_VHT160,
++
++ SKU_HE26,
++ SKU_HE52,
++ SKU_HE106,
++ SKU_HE242,
++ SKU_HE484,
++ SKU_HE996,
++ SKU_HE2x996,
++
++ SKU_EHT26,
++ SKU_EHT52,
++ SKU_EHT106,
++ SKU_EHT242,
++ SKU_EHT484,
++ SKU_EHT996,
++ SKU_EHT2x996,
++ SKU_EHT4x996,
++ SKU_EHT26_52,
++ SKU_EHT26_106,
++ SKU_EHT484_242,
++ SKU_EHT996_484,
++ SKU_EHT996_484_242,
++ SKU_EHT2x996_484,
++ SKU_EHT3x996,
++ SKU_EHT3x996_484,
++
++ MAX_SKU_RATE_GROUP_NUM,
++};
++
++extern const u8 mt7996_sku_group_len[MAX_SKU_RATE_GROUP_NUM];
++
+ #endif
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 6a394c364..f1d681787 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -295,7 +295,12 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
+ int nss_delta = mt76_tx_power_nss_delta(nss);
+ int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
+ struct mt76_power_limits limits;
++ struct mt76_power_path_limits limits_path;
++ struct device_node *np;
+
++ phy->sku_limit_en = true;
++ phy->sku_path_en = true;
++ np = mt76_find_power_limits_node(&dev->mt76);
+ for (i = 0; i < sband->n_channels; i++) {
+ struct ieee80211_channel *chan = &sband->channels[i];
+ int target_power = mt7996_eeprom_get_target_power(dev, chan);
+@@ -303,11 +308,18 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
+ target_power += pwr_delta;
+ target_power = mt76_get_rate_power_limits(phy->mt76, chan,
+ &limits,
++ &limits_path,
+ target_power);
++ if (!limits_path.ofdm[0])
++ phy->sku_path_en = false;
++
+ target_power += nss_delta;
+ target_power = DIV_ROUND_UP(target_power, 2);
+- chan->max_power = min_t(int, chan->max_reg_power,
+- target_power);
++ if (!np)
++ chan->max_power = min_t(int, chan->max_reg_power,
++ target_power);
++ else
++ chan->max_power = target_power;
+ chan->orig_mpwr = target_power;
+ }
+ }
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 57865f1de..52870fa72 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -85,6 +85,21 @@ int mt7996_run(struct ieee80211_hw *hw)
+ if (ret)
+ goto out;
+
++#ifdef CONFIG_MTK_DEBUG
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++ dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
++
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++ dev->dbg.sku_disable ? 0 : phy->sku_path_en);
++#else
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++ phy->sku_limit_en);
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++ phy->sku_path_en);
++#endif
++ if (ret)
++ goto out;
++
+ set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+ ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 2650dc6ff..1330ff397 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4665,9 +4665,31 @@ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
+ sizeof(req), true);
+ }
+
++static void
++mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
++{
++ struct mt76_phy *mphy = phy->mt76;
++ struct ieee80211_channel *chan = mphy->main_chan;
++ int e2p_power_limit = 0;
++
++ if (chan == NULL) {
++ mphy->txpower_cur = tx_power;
++ return;
++ }
++
++ e2p_power_limit = mt7996_eeprom_get_target_power(phy->dev, chan);
++ e2p_power_limit += mt7996_eeprom_get_power_delta(phy->dev, chan->band);
++
++ if (phy->sku_limit_en)
++ mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power);
++ else
++ mphy->txpower_cur = e2p_power_limit;
++}
++
+ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+ {
+ #define TX_POWER_LIMIT_TABLE_RATE 0
++#define TX_POWER_LIMIT_TABLE_PATH 1
+ struct mt7996_dev *dev = phy->dev;
+ struct mt76_phy *mphy = phy->mt76;
+ struct ieee80211_hw *hw = mphy->hw;
+@@ -4687,13 +4709,22 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+ .band_idx = phy->mt76->band_idx,
+ };
+ struct mt76_power_limits la = {};
++ struct mt76_power_path_limits la_path = {};
+ struct sk_buff *skb;
+- int i, tx_power;
++ int i, ret, txpower_limit;
++
++ if (hw->conf.power_level == INT_MIN)
++ hw->conf.power_level = 127;
++ txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
+
+- tx_power = mt7996_get_power_bound(phy, hw->conf.power_level);
+- tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+- &la, tx_power);
+- mphy->txpower_cur = tx_power;
++ if (phy->sku_limit_en) {
++ txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
++ &la, &la_path, txpower_limit);
++ mt7996_update_max_txpower_cur(phy, txpower_limit);
++ } else {
++ mt7996_update_max_txpower_cur(phy, txpower_limit);
++ return 0;
++ }
+
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
+ sizeof(req) + MT7996_SKU_PATH_NUM);
+@@ -4723,6 +4754,34 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+ /* padding */
+ skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM);
+
++ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
++ MCU_WM_UNI_CMD(TXPOWER), true);
++ if (ret)
++ return ret;
++
++ /* only set per-path power table when it's configured */
++ if (!phy->sku_path_en)
++ return 0;
++
++ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
++ sizeof(req) + MT7996_SKU_PATH_NUM);
++ if (!skb)
++ return -ENOMEM;
++ req.power_limit_type = TX_POWER_LIMIT_TABLE_PATH;
++
++ skb_put_data(skb, &req, sizeof(req));
++ skb_put_data(skb, &la_path.cck, sizeof(la_path.cck));
++ skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
++ skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
++
++ for (i = 0; i < 32; i++) {
++ bool bf = i % 2;
++ u8 idx = i / 2;
++ s8 *buf = bf ? la_path.ru_bf[idx] : la_path.ru[idx];
++
++ skb_put_data(skb, buf, sizeof(la_path.ru[0]));
++ }
++
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(TXPOWER), true);
+ }
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 5953b25e8..5a60ccc55 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -911,6 +911,7 @@ struct tx_power_ctrl {
+ bool ate_mode_enable;
+ bool percentage_ctrl_enable;
+ bool bf_backoff_enable;
++ u8 show_info_category;
+ u8 power_drop_level;
+ };
+ u8 band_idx;
+@@ -924,6 +925,7 @@ enum {
+ UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
+ UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
+ UNI_TXPOWER_ATE_MODE_CTRL = 6,
++ UNI_TXPOWER_SHOW_INFO = 7,
+ };
+
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 186af3c1d..6ab45cc7e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -296,6 +296,9 @@ struct mt7996_phy {
+
+ struct mt7996_scs_ctrl scs_ctrl;
+
++ bool sku_limit_en;
++ bool sku_path_en;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ struct {
+ u32 *reg_backup;
+@@ -617,6 +620,7 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
++int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 9b4d5f68c..48209668c 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2440,6 +2440,364 @@ mt7996_scs_enable_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
+ mt7996_scs_enable_set, "%lld\n");
+
++static int
++mt7996_txpower_level_set(void *data, u64 val)
++{
++ struct mt7996_phy *phy = data;
++ int ret;
++
++ if (val > 100)
++ return -EINVAL;
++
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_CTRL, !!val);
++ if (ret)
++ return ret;
++
++ return mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_DROP_CTRL, val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_txpower_level, NULL,
++ mt7996_txpower_level_set, "%lld\n");
++
++static ssize_t
++mt7996_get_txpower_info(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct mt7996_phy *phy = file->private_data;
++ struct mt7996_mcu_txpower_event *event;
++ struct txpower_basic_info *basic_info;
++ struct device_node *np;
++ static const size_t size = 2048;
++ int len = 0;
++ ssize_t ret;
++ char *buf;
++
++ buf = kzalloc(size, GFP_KERNEL);
++ event = kzalloc(sizeof(*event), GFP_KERNEL);
++ if (!buf || !event) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ ret = mt7996_mcu_get_tx_power_info(phy, BASIC_INFO, event);
++ if (ret ||
++ le32_to_cpu(event->basic_info.category) != UNI_TXPOWER_BASIC_INFO)
++ goto out;
++
++ basic_info = &event->basic_info;
++
++ len += scnprintf(buf + len, size - len,
++ "======================== BASIC INFO ========================\n");
++ len += scnprintf(buf + len, size - len, " Band Index: %d, Channel Band: %d\n",
++ basic_info->band_idx, basic_info->band);
++ len += scnprintf(buf + len, size - len, " PA Type: %s\n",
++ basic_info->is_epa ? "ePA" : "iPA");
++ len += scnprintf(buf + len, size - len, " LNA Type: %s\n",
++ basic_info->is_elna ? "eLNA" : "iLNA");
++
++ len += scnprintf(buf + len, size - len,
++ "------------------------------------------------------------\n");
++ len += scnprintf(buf + len, size - len, " SKU: %s\n",
++ basic_info->sku_enable ? "enable" : "disable");
++ len += scnprintf(buf + len, size - len, " Percentage Control: %s\n",
++ basic_info->percentage_ctrl_enable ? "enable" : "disable");
++ len += scnprintf(buf + len, size - len, " Power Drop: %d [dBm]\n",
++ basic_info->power_drop_level >> 1);
++ len += scnprintf(buf + len, size - len, " Backoff: %s\n",
++ basic_info->bf_backoff_enable ? "enable" : "disable");
++ len += scnprintf(buf + len, size - len, " TX Front-end Loss: %d, %d, %d, %d\n",
++ basic_info->front_end_loss_tx[0], basic_info->front_end_loss_tx[1],
++ basic_info->front_end_loss_tx[2], basic_info->front_end_loss_tx[3]);
++ len += scnprintf(buf + len, size - len, " RX Front-end Loss: %d, %d, %d, %d\n",
++ basic_info->front_end_loss_rx[0], basic_info->front_end_loss_rx[1],
++ basic_info->front_end_loss_rx[2], basic_info->front_end_loss_rx[3]);
++ len += scnprintf(buf + len, size - len,
++ " MU TX Power Mode: %s\n",
++ basic_info->mu_tx_power_manual_enable ? "manual" : "auto");
++ len += scnprintf(buf + len, size - len,
++ " MU TX Power (Auto / Manual): %d / %d [0.5 dBm]\n",
++ basic_info->mu_tx_power_auto, basic_info->mu_tx_power_manual);
++ len += scnprintf(buf + len, size - len,
++ " Thermal Compensation: %s\n",
++ basic_info->thermal_compensate_enable ? "enable" : "disable");
++ len += scnprintf(buf + len, size - len,
++ " Theraml Compensation Value: %d\n",
++ basic_info->thermal_compensate_value);
++ np = mt76_find_power_limits_node(phy->mt76->dev);
++ len += scnprintf(buf + len, size - len,
++ " RegDB: %s\n",
++ !np ? "enable" : "disable");
++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++out:
++ kfree(buf);
++ kfree(event);
++ return ret;
++}
++
++static const struct file_operations mt7996_txpower_info_fops = {
++ .read = mt7996_get_txpower_info,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++#define mt7996_txpower_puts(rate, _len) \
++({ \
++ len += scnprintf(buf + len, size - len, "%-*s:", _len, #rate " (TMAC)"); \
++ for (i = 0; i < mt7996_sku_group_len[SKU_##rate]; i++, offs++) \
++ len += scnprintf(buf + len, size - len, " %6d", \
++ event->phy_rate_info.frame_power[offs][band_idx]); \
++ len += scnprintf(buf + len, size - len, "\n"); \
++})
++
++static ssize_t
++mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct mt7996_phy *phy = file->private_data;
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_mcu_txpower_event *event;
++ struct ieee80211_channel *chan = phy->mt76->chandef.chan;
++ struct ieee80211_supported_band sband;
++ u8 band_idx = phy->mt76->band_idx;
++ static const size_t size = 5120;
++ int i, offs = 0, len = 0;
++ u32 target_power = 0;
++ int n_chains = hweight16(phy->mt76->chainmask);
++ int nss_delta = mt76_tx_power_nss_delta(n_chains);
++ int pwr_delta;
++ ssize_t ret;
++ char *buf;
++ u32 reg;
++
++ buf = kzalloc(size, GFP_KERNEL);
++ event = kzalloc(sizeof(*event), GFP_KERNEL);
++ if (!buf || !event) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ ret = mt7996_mcu_get_tx_power_info(phy, PHY_RATE_INFO, event);
++ if (ret ||
++ le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_PHY_RATE_INFO)
++ goto out;
++
++ len += scnprintf(buf + len, size - len,
++ "\nPhy %d TX Power Table (Channel %d)\n",
++ band_idx, phy->mt76->chandef.chan->hw_value);
++ len += scnprintf(buf + len, size - len, "%-21s %6s %6s %6s %6s\n",
++ " ", "1m", "2m", "5m", "11m");
++ mt7996_txpower_puts(CCK, 21);
++
++ len += scnprintf(buf + len, size - len,
++ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
++ " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m",
++ "54m");
++ mt7996_txpower_puts(OFDM, 21);
++
++ len += scnprintf(buf + len, size - len,
++ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
++ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4",
++ "mcs5", "mcs6", "mcs7");
++ mt7996_txpower_puts(HT20, 21);
++
++ len += scnprintf(buf + len, size - len,
++ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
++ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
++ "mcs6", "mcs7", "mcs32");
++ mt7996_txpower_puts(HT40, 21);
++
++ len += scnprintf(buf + len, size - len,
++ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
++ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
++ "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
++ mt7996_txpower_puts(VHT20, 21);
++ mt7996_txpower_puts(VHT40, 21);
++ mt7996_txpower_puts(VHT80, 21);
++ mt7996_txpower_puts(VHT160, 21);
++ mt7996_txpower_puts(HE26, 21);
++ mt7996_txpower_puts(HE52, 21);
++ mt7996_txpower_puts(HE106, 21);
++ len += scnprintf(buf + len, size - len, "BW20/");
++ mt7996_txpower_puts(HE242, 16);
++ len += scnprintf(buf + len, size - len, "BW40/");
++ mt7996_txpower_puts(HE484, 16);
++ len += scnprintf(buf + len, size - len, "BW80/");
++ mt7996_txpower_puts(HE996, 16);
++ len += scnprintf(buf + len, size - len, "BW160/");
++ mt7996_txpower_puts(HE2x996, 15);
++
++ len += scnprintf(buf + len, size - len,
++ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s ",
++ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", "mcs6", "mcs7");
++ len += scnprintf(buf + len, size - len,
++ "%6s %6s %6s %6s %6s %6s %6s %6s\n",
++ "mcs8", "mcs9", "mcs10", "mcs11", "mcs12", "mcs13", "mcs14", "mcs15");
++ mt7996_txpower_puts(EHT26, 21);
++ mt7996_txpower_puts(EHT52, 21);
++ mt7996_txpower_puts(EHT106, 21);
++ len += scnprintf(buf + len, size - len, "BW20/");
++ mt7996_txpower_puts(EHT242, 16);
++ len += scnprintf(buf + len, size - len, "BW40/");
++ mt7996_txpower_puts(EHT484, 16);
++ len += scnprintf(buf + len, size - len, "BW80/");
++ mt7996_txpower_puts(EHT996, 16);
++ len += scnprintf(buf + len, size - len, "BW160/");
++ mt7996_txpower_puts(EHT2x996, 15);
++ len += scnprintf(buf + len, size - len, "BW320/");
++ mt7996_txpower_puts(EHT4x996, 15);
++ mt7996_txpower_puts(EHT26_52, 21);
++ mt7996_txpower_puts(EHT26_106, 21);
++ mt7996_txpower_puts(EHT484_242, 21);
++ mt7996_txpower_puts(EHT996_484, 21);
++ mt7996_txpower_puts(EHT996_484_242, 21);
++ mt7996_txpower_puts(EHT2x996_484, 21);
++ mt7996_txpower_puts(EHT3x996, 21);
++ mt7996_txpower_puts(EHT3x996_484, 21);
++
++ len += scnprintf(buf + len, size - len, "\nePA Gain: %d\n",
++ event->phy_rate_info.epa_gain);
++ len += scnprintf(buf + len, size - len, "Max Power Bound: %d\n",
++ event->phy_rate_info.max_power_bound);
++ len += scnprintf(buf + len, size - len, "Min Power Bound: %d\n",
++ event->phy_rate_info.min_power_bound);
++
++ reg = MT_WF_PHYDFE_TSSI_TXCTRL01(band_idx);
++ len += scnprintf(buf + len, size - len,
++ "\nBBP TX Power (target power from TMAC) : %6ld [0.5 dBm]\n",
++ mt76_get_field(dev, reg, MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC));
++ len += scnprintf(buf + len, size - len,
++ "RegDB maximum power:\t%d [dBm]\n",
++ chan->max_reg_power);
++
++ if (chan->band == NL80211_BAND_2GHZ)
++ sband = phy->mt76->sband_2g.sband;
++ else if (chan->band == NL80211_BAND_5GHZ)
++ sband = phy->mt76->sband_5g.sband;
++ else if (chan->band == NL80211_BAND_6GHZ)
++ sband = phy->mt76->sband_6g.sband;
++
++ pwr_delta = mt7996_eeprom_get_power_delta(dev, sband.band);
++
++ target_power = max_t(u32, target_power, mt7996_eeprom_get_target_power(dev, chan));
++ target_power += pwr_delta + nss_delta;
++ target_power = DIV_ROUND_UP(target_power, 2);
++ len += scnprintf(buf + len, size - len,
++ "eeprom maximum power:\t%d [dBm]\n",
++ target_power);
++
++ len += scnprintf(buf + len, size - len,
++ "nss_delta:\t%d [0.5 dBm]\n",
++ nss_delta);
++
++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++out:
++ kfree(buf);
++ kfree(event);
++ return ret;
++}
++
++static const struct file_operations mt7996_txpower_sku_fops = {
++ .read = mt7996_get_txpower_sku,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++#define mt7996_txpower_path_puts(rate, arr_length) \
++({ \
++ len += scnprintf(buf + len, size - len, "%23s:", #rate " (TMAC)"); \
++ for (i = 0; i < arr_length; i++, offs++) \
++ len += scnprintf(buf + len, size - len, " %4d", \
++ event->backoff_table_info.frame_power[offs]); \
++ len += scnprintf(buf + len, size - len, "\n"); \
++})
++
++static ssize_t
++mt7996_get_txpower_path(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct mt7996_phy *phy = file->private_data;
++ struct mt7996_mcu_txpower_event *event;
++ static const size_t size = 5120;
++ int i, offs = 0, len = 0;
++ ssize_t ret;
++ char *buf;
++
++ buf = kzalloc(size, GFP_KERNEL);
++ event = kzalloc(sizeof(*event), GFP_KERNEL);
++ if (!buf || !event) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ ret = mt7996_mcu_get_tx_power_info(phy, BACKOFF_TABLE_INFO, event);
++ if (ret ||
++ le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO)
++ goto out;
++
++ len += scnprintf(buf + len, size - len, "\n%*c", 25, ' ');
++ len += scnprintf(buf + len, size - len, "1T1S/2T1S/3T1S/4T1S/5T1S/2T2S/3T2S/4T2S/5T2S/"
++ "3T3S/4T3S/5T3S/4T4S/5T4S/5T5S\n");
++
++ mt7996_txpower_path_puts(CCK, 5);
++ mt7996_txpower_path_puts(OFDM, 5);
++ mt7996_txpower_path_puts(BF-OFDM, 4);
++
++ mt7996_txpower_path_puts(RU26, 15);
++ mt7996_txpower_path_puts(BF-RU26, 15);
++ mt7996_txpower_path_puts(RU52, 15);
++ mt7996_txpower_path_puts(BF-RU52, 15);
++ mt7996_txpower_path_puts(RU26_52, 15);
++ mt7996_txpower_path_puts(BF-RU26_52, 15);
++ mt7996_txpower_path_puts(RU106, 15);
++ mt7996_txpower_path_puts(BF-RU106, 15);
++ mt7996_txpower_path_puts(RU106_52, 15);
++ mt7996_txpower_path_puts(BF-RU106_52, 15);
++
++ mt7996_txpower_path_puts(BW20/RU242, 15);
++ mt7996_txpower_path_puts(BF-BW20/RU242, 15);
++ mt7996_txpower_path_puts(BW40/RU484, 15);
++ mt7996_txpower_path_puts(BF-BW40/RU484, 15);
++ mt7996_txpower_path_puts(RU242_484, 15);
++ mt7996_txpower_path_puts(BF-RU242_484, 15);
++ mt7996_txpower_path_puts(BW80/RU996, 15);
++ mt7996_txpower_path_puts(BF-BW80/RU996, 15);
++ mt7996_txpower_path_puts(RU484_996, 15);
++ mt7996_txpower_path_puts(BF-RU484_996, 15);
++ mt7996_txpower_path_puts(RU242_484_996, 15);
++ mt7996_txpower_path_puts(BF-RU242_484_996, 15);
++ mt7996_txpower_path_puts(BW160/RU996x2, 15);
++ mt7996_txpower_path_puts(BF-BW160/RU996x2, 15);
++ mt7996_txpower_path_puts(RU484_996x2, 15);
++ mt7996_txpower_path_puts(BF-RU484_996x2, 15);
++ mt7996_txpower_path_puts(RU996x3, 15);
++ mt7996_txpower_path_puts(BF-RU996x3, 15);
++ mt7996_txpower_path_puts(RU484_996x3, 15);
++ mt7996_txpower_path_puts(BF-RU484_996x3, 15);
++ mt7996_txpower_path_puts(BW320/RU996x4, 15);
++ mt7996_txpower_path_puts(BF-BW320/RU996x4, 15);
++
++ len += scnprintf(buf + len, size - len, "\nBackoff table: %s\n",
++ event->backoff_table_info.backoff_en ? "enable" : "disable");
++
++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++out:
++ kfree(buf);
++ kfree(event);
++ return ret;
++}
++
++static const struct file_operations mt7996_txpower_path_fops = {
++ .read = mt7996_get_txpower_path,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -2503,6 +2861,10 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+
+ debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
+ mt7996_trinfo_read);
++ debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
++ debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
++ debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
++ debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
+
+ debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
+ mt7996_wtbl_read);
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index c16b25ab5..e56ddd8ff 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -12,8 +12,31 @@
+
+ #ifdef CONFIG_MTK_DEBUG
+
++int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct tx_power_ctrl req = {
++ .tag = cpu_to_le16(UNI_TXPOWER_SHOW_INFO),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .power_ctrl_id = UNI_TXPOWER_SHOW_INFO,
++ .show_info_category = category,
++ .band_idx = phy->mt76->band_idx,
++ };
++ struct sk_buff *skb;
++ int ret;
+
++ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
++ MCU_WM_UNI_CMD_QUERY(TXPOWER),
++ &req, sizeof(req), true, &skb);
++ if (ret)
++ return ret;
+
++ memcpy(event, skb->data, sizeof(struct mt7996_mcu_txpower_event));
++
++ dev_kfree_skb(skb);
++
++ return 0;
++}
+
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
+ {
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 7f4d4e029..c30418cae 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -14,6 +14,98 @@ enum {
+ UNI_CMD_MURU_DBG_INFO = 0x18,
+ };
+
++struct txpower_basic_info {
++ u8 category;
++ u8 rsv1;
++
++ /* basic info */
++ u8 band_idx;
++ u8 band;
++
++ /* board type info */
++ bool is_epa;
++ bool is_elna;
++
++ /* power percentage info */
++ bool percentage_ctrl_enable;
++ s8 power_drop_level;
++
++ /* frond-end loss TX info */
++ s8 front_end_loss_tx[4];
++
++ /* frond-end loss RX info */
++ s8 front_end_loss_rx[4];
++
++ /* thermal info */
++ bool thermal_compensate_enable;
++ s8 thermal_compensate_value;
++ u8 rsv2;
++
++ /* TX power max/min limit info */
++ s8 max_power_bound;
++ s8 min_power_bound;
++
++ /* power limit info */
++ bool sku_enable;
++ bool bf_backoff_enable;
++
++ /* MU TX power info */
++ bool mu_tx_power_manual_enable;
++ s8 mu_tx_power_auto;
++ s8 mu_tx_power_manual;
++ u8 rsv3;
++};
++
++struct txpower_phy_rate_info {
++ u8 category;
++ u8 band_idx;
++ u8 band;
++ u8 epa_gain;
++
++ /* rate power info [dBm] */
++ s8 frame_power[MT7996_SKU_RATE_NUM][__MT_MAX_BAND];
++
++ /* TX power max/min limit info */
++ s8 max_power_bound;
++ s8 min_power_bound;
++ u8 rsv1;
++};
++
++struct txpower_backoff_table_info {
++ u8 category;
++ u8 band_idx;
++ u8 band;
++ u8 backoff_en;
++
++ s8 frame_power[MT7996_SKU_PATH_NUM];
++ u8 rsv[3];
++};
++
++struct mt7996_mcu_txpower_event {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ union {
++ struct txpower_basic_info basic_info;
++ struct txpower_phy_rate_info phy_rate_info;
++ struct txpower_backoff_table_info backoff_table_info;
++ };
++};
++
++enum txpower_category {
++ BASIC_INFO,
++ BACKOFF_TABLE_INFO,
++ PHY_RATE_INFO,
++};
++
++enum txpower_event {
++ UNI_TXPOWER_BASIC_INFO = 0,
++ UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO = 3,
++ UNI_TXPOWER_PHY_RATE_INFO = 5,
++};
++
+ #endif
+
+ #endif
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 4c20a67d7..8ec1dc1c6 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -693,24 +693,29 @@ enum offs_rev {
+ ((_wf) << 16) + (ofs))
+ #define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
+
+-/* PHYRX CTRL */
+-#define MT_WF_PHYRX_BAND_BASE 0x83080000
+-#define MT_WF_PHYRX_BAND(_band, ofs) (MT_WF_PHYRX_BAND_BASE + \
++/* PHYDFE CTRL */
++#define MT_WF_PHYDFE_TSSI_TXCTRL01(_band) MT_WF_PHYRX_CSD(_band, 0, 0xc718)
++#define MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC GENMASK(31, 24)
++
++/* PHY CTRL */
++#define MT_WF_PHY_BAND_BASE 0x83080000
++#define MT_WF_PHY_BAND(_band, ofs) (MT_WF_PHY_BAND_BASE + \
+ ((_band) << 20) + (ofs))
+
+-#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band) MT_WF_PHYRX_BAND(_band, 0x1054)
+-#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band) MT_WF_PHYRX_BAND(_band, 0x1058)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band) MT_WF_PHYRX_BAND(_band, 0x105c)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band) MT_WF_PHYRX_BAND(_band, 0x1060)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band) MT_WF_PHYRX_BAND(_band, 0x1064)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band) MT_WF_PHYRX_BAND(_band, 0x1068)
++#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band) MT_WF_PHY_BAND(_band, 0x1054)
++#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band) MT_WF_PHY_BAND(_band, 0x1058)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band) MT_WF_PHY_BAND(_band, 0x105c)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band) MT_WF_PHY_BAND(_band, 0x1060)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band) MT_WF_PHY_BAND(_band, 0x1064)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band) MT_WF_PHY_BAND(_band, 0x1068)
+
+-#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHYRX_BAND(_band, 0x2004)
++/* PHYRX CTRL */
++#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHY_BAND(_band, 0x2004)
+ #define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0)
+ #define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9)
+
+ /* PHYRX CSD BAND */
+-#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHYRX_BAND(_band, 0x8230)
++#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHY_BAND(_band, 0x8230)
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18)
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29)
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch
new file mode 100644
index 0000000..5ae752a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch
@@ -0,0 +1,369 @@
+From 83c7e6d692657d514891430ad530df1337b90799 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 31 Mar 2023 11:36:34 +0800
+Subject: [PATCH 027/116] mtk: wifi: mt76: mt7996: add binfile mode support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I06369a46f75c0707d9ababd8ade70d79a89b1a1c
+
+Fix binfile cannot sync precal data to atenl
+Binfile is viewed as efuse mode in atenl, so atenl does not allocate
+precal memory for its eeprom file
+Use mtd offset == 0xFFFFFFFF to determine whether it is binfile or flash mode
+Add support for loading precal in binfile mode
+
+Align upstream
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I4c8fd2b0a9956d2ee961b70cd28973e2e9410aca
+---
+ eeprom.c | 25 +++++++++++
+ mt76.h | 3 ++
+ mt7996/eeprom.c | 103 ++++++++++++++++++++++++++++++++++++++++---
+ mt7996/eeprom.h | 7 +++
+ mt7996/mt7996.h | 4 ++
+ mt7996/mtk_debugfs.c | 41 +++++++++++++++++
+ testmode.h | 2 +-
+ 7 files changed, 179 insertions(+), 6 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index 11efe2937..3da94926e 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -161,6 +161,31 @@ static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len)
+ return mt76_get_of_data_from_nvmem(dev, eep, "eeprom", len);
+ }
+
++bool mt76_check_bin_file_mode(struct mt76_dev *dev)
++{
++ struct device_node *np = dev->dev->of_node;
++ const char *bin_file_name = NULL;
++
++ if (!np)
++ return false;
++
++ of_property_read_string(np, "bin_file_name", &bin_file_name);
++
++ dev->bin_file_name = bin_file_name;
++ if (dev->bin_file_name) {
++ dev_info(dev->dev, "Using bin file %s\n", dev->bin_file_name);
++#ifdef CONFIG_NL80211_TESTMODE
++ dev->test_mtd.name = devm_kstrdup(dev->dev, bin_file_name, GFP_KERNEL);
++ dev->test_mtd.offset = -1;
++#endif
++ }
++
++ of_node_put(np);
++
++ return dev->bin_file_name ? true : false;
++}
++EXPORT_SYMBOL_GPL(mt76_check_bin_file_mode);
++
+ void
+ mt76_eeprom_override(struct mt76_phy *phy)
+ {
+diff --git a/mt76.h b/mt76.h
+index 20fdd050a..0b6d8b84e 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -954,6 +954,8 @@ struct mt76_dev {
+ struct mt76_usb usb;
+ struct mt76_sdio sdio;
+ };
++
++ const char *bin_file_name;
+ };
+
+ /* per-phy stats. */
+@@ -1225,6 +1227,7 @@ void mt76_eeprom_override(struct mt76_phy *phy);
+ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len);
+ int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep,
+ const char *cell_name, int len);
++bool mt76_check_bin_file_mode(struct mt76_dev *dev);
+
+ struct mt76_queue *
+ mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 6ac992a81..fe8b25352 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -82,10 +82,17 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ }
+ }
+
+-static char *mt7996_eeprom_name(struct mt7996_dev *dev)
++const char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ {
+- if (dev->testmode_enable)
+- return MT7996_EEPROM_DEFAULT_TM;
++ if (dev->bin_file_mode)
++ return dev->mt76.bin_file_name;
++
++ if (dev->testmode_enable) {
++ if (is_mt7992(&dev->mt76))
++ return MT7992_EEPROM_DEFAULT_TM;
++ else
++ return MT7996_EEPROM_DEFAULT_TM;
++ }
+
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7990:
+@@ -152,7 +159,10 @@ mt7996_eeprom_load_default(struct mt7996_dev *dev)
+ return ret;
+
+ if (!fw || !fw->data) {
+- dev_err(dev->mt76.dev, "Invalid default bin\n");
++ if (dev->bin_file_mode)
++ dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
++ else
++ dev_err(dev->mt76.dev, "Invalid default bin\n");
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -166,18 +176,45 @@ out:
+ return ret;
+ }
+
++static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
++{
++ int ret = 1;
++
++ /* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
++ dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
++ if (dev->bin_file_mode) {
++ dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
++ dev->mt76.eeprom.data = devm_kzalloc(dev->mt76.dev, dev->mt76.eeprom.size,
++ GFP_KERNEL);
++ if (!dev->mt76.eeprom.data)
++ return -ENOMEM;
++
++ if (mt7996_eeprom_load_default(dev))
++ return 0;
++
++ if (mt7996_check_eeprom(dev))
++ return 0;
++ } else {
++ ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
++ }
++
++ return ret;
++}
++
+ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
+ {
+ u8 *eeprom;
+ int ret;
+
+ /* load eeprom in flash or bin file mode to determine fw mode */
+- ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
++ ret = mt7996_eeprom_load_flash(dev);
++
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ dev->flash_mode = true;
++ dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
+ eeprom = dev->mt76.eeprom.data;
+ /* testmode enable priority: eeprom field > module parameter */
+ dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
+@@ -211,6 +248,7 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ if (ret && ret != -EINVAL)
+ return ret;
+ }
++ dev->eeprom_mode = EFUSE_MODE;
+ }
+
+ return mt7996_check_eeprom(dev);
+@@ -337,6 +375,59 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ return mt7996_eeprom_parse_band_config(phy);
+ }
+
++static int
++mt7996_eeprom_load_precal_binfile(struct mt7996_dev *dev, u32 offs, u32 size)
++{
++ const struct firmware *fw = NULL;
++ int ret;
++
++ ret = request_firmware(&fw, dev->mt76.bin_file_name, dev->mt76.dev);
++ if (ret)
++ return ret;
++
++ if (!fw || !fw->data) {
++ dev_err(dev->mt76.dev, "Invalid bin (bin file mode), load precal fail\n");
++ ret = -EINVAL;
++ goto out;
++ }
++
++ memcpy(dev->cal, fw->data + offs, size);
++
++out:
++ release_firmware(fw);
++
++ return ret;
++}
++
++static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
++{
++ struct mt76_dev *mdev = &dev->mt76;
++ u8 *eeprom = mdev->eeprom.data;
++ u32 offs = MT_EE_DO_PRE_CAL;
++ u32 size, val = eeprom[offs];
++ int ret;
++
++ mt7996_eeprom_init_precal(dev);
++
++ if (!dev->flash_mode || !val)
++ return 0;
++
++ size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
++
++ dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++ if (!dev->cal)
++ return -ENOMEM;
++
++ if (dev->bin_file_mode)
++ return mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
++
++ ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
++ if (!ret)
++ return ret;
++
++ return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
++}
++
+ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ int ret;
+@@ -351,6 +442,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ return ret;
+
+ dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
++ dev->bin_file_mode = false;
++ dev->eeprom_mode = DEFAULT_BIN_MODE;
+ ret = mt7996_eeprom_load_default(dev);
+ if (ret)
+ return ret;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index b19ff068e..8f0f87b6b 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -102,6 +102,13 @@ enum mt7996_eeprom_band {
+ MT_EE_BAND_SEL_6GHZ,
+ };
+
++enum mt7915_eeprom_mode {
++ DEFAULT_BIN_MODE,
++ EFUSE_MODE,
++ FLASH_MODE,
++ BIN_FILE_MODE,
++};
++
+ static inline int
+ mt7996_get_channel_group_5g(int channel)
+ {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 6ab45cc7e..9d6e85c1d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -62,6 +62,7 @@
+ #define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
+ #define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
+ #define MT7992_EEPROM_DEFAULT_23_EXT "mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
++#define MT7992_EEPROM_DEFAULT_TM "mediatek/mt7996/mt7992_eeprom_tm.bin"
+ #define MT7996_EEPROM_SIZE 7680
+ #define MT7996_EEPROM_BLOCK_SIZE 16
+ #define MT7996_TOKEN_SIZE 16384
+@@ -395,6 +396,8 @@ struct mt7996_dev {
+ } wed_rro;
+
+ bool testmode_enable;
++ bool bin_file_mode;
++ u8 eeprom_mode;
+
+ bool ibf;
+ u8 fw_debug_wm;
+@@ -523,6 +526,7 @@ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
+ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+ int mt7996_register_device(struct mt7996_dev *dev);
+ void mt7996_unregister_device(struct mt7996_dev *dev);
++const char *mt7996_eeprom_name(struct mt7996_dev *dev);
+ int mt7996_eeprom_init(struct mt7996_dev *dev);
+ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 48209668c..3ecce1329 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2798,6 +2798,44 @@ static const struct file_operations mt7996_txpower_path_fops = {
+ .llseek = default_llseek,
+ };
+
++static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ struct mt76_dev *mdev = &dev->mt76;
++#ifdef CONFIG_NL80211_TESTMODE
++ const char *mtd_name = mdev->test_mtd.name;
++ u32 mtd_offset = mdev->test_mtd.offset;
++#else
++ const char *mtd_name = NULL;
++ u32 mtd_offset;
++#endif
++
++ seq_printf(s, "Current eeprom mode:\n");
++
++ switch (dev->eeprom_mode) {
++ case DEFAULT_BIN_MODE:
++ seq_printf(s, " default bin mode\n filename = %s\n", mt7996_eeprom_name(dev));
++ break;
++ case EFUSE_MODE:
++ seq_printf(s, " efuse mode\n");
++ break;
++ case FLASH_MODE:
++ if (mtd_name)
++ seq_printf(s, " flash mode\n mtd name = %s\n flash offset = 0x%x\n",
++ mtd_name, mtd_offset);
++ else
++ seq_printf(s, " flash mode\n");
++ break;
++ case BIN_FILE_MODE:
++ seq_printf(s, " bin file mode\n filename = %s\n", mt7996_eeprom_name(dev));
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -2866,6 +2904,9 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
+ debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
+
++ debugfs_create_devm_seqfile(dev->mt76.dev, "eeprom_mode", dir,
++ mt7996_show_eeprom_mode);
++
+ debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
+ mt7996_wtbl_read);
+
+diff --git a/testmode.h b/testmode.h
+index d6601cdcf..5d677f8c1 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -16,7 +16,7 @@
+ * @MT76_TM_ATTR_RESET: reset parameters to default (flag)
+ * @MT76_TM_ATTR_STATE: test state (u32), see &enum mt76_testmode_state
+ *
+- * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
++ * @MT76_TM_ATTR_MTD_PART: mtd partition or binfile used for eeprom data (string)
+ * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
+ * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
+ *
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch
new file mode 100644
index 0000000..c01db67
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch
@@ -0,0 +1,490 @@
+From 6e64a8af10129018ea1ff7ccd4803d5b0c7ff7ad Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 22 Mar 2023 11:19:52 +0800
+Subject: [PATCH 028/116] mtk: wifi: mt76: testmode: add testmode ZWDFS
+ verification support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76.h | 8 ++
+ mt7996/mt7996.h | 1 +
+ mt7996/testmode.c | 248 ++++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/testmode.h | 44 ++++++++
+ testmode.c | 22 +++-
+ tools/fields.c | 15 +++
+ 6 files changed, 326 insertions(+), 12 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 0b6d8b84e..4a3e6bf40 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -783,6 +783,14 @@ struct mt76_testmode_data {
+ } cfg;
+
+ u8 aid;
++
++ u8 offchan_ch;
++ u8 offchan_center_ch;
++ u8 offchan_bw;
++
++ u8 ipi_threshold;
++ u32 ipi_period;
++ u8 ipi_reset;
+ };
+
+ struct mt76_vif {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 9d6e85c1d..7d6ec8ba4 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -289,6 +289,7 @@ struct mt7996_phy {
+
+ struct mt76_mib_stats mib;
+ struct mt76_channel_state state_ts;
++ struct delayed_work ipi_work;
+
+ bool has_aux_rx;
+
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index a756ee10d..836211f98 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -17,6 +17,12 @@ enum {
+ TM_CHANGED_TX_LENGTH,
+ TM_CHANGED_TX_TIME,
+ TM_CHANGED_CFG,
++ TM_CHANGED_OFF_CHAN_CH,
++ TM_CHANGED_OFF_CHAN_CENTER_CH,
++ TM_CHANGED_OFF_CHAN_BW,
++ TM_CHANGED_IPI_THRESHOLD,
++ TM_CHANGED_IPI_PERIOD,
++ TM_CHANGED_IPI_RESET,
+
+ /* must be last */
+ NUM_TM_CHANGED
+@@ -29,20 +35,31 @@ static const u8 tm_change_map[] = {
+ [TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
+ [TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
+ [TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
++ [TM_CHANGED_OFF_CHAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
++ [TM_CHANGED_OFF_CHAN_CENTER_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
++ [TM_CHANGED_OFF_CHAN_BW] = MT76_TM_ATTR_OFF_CH_SCAN_BW,
++ [TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
++ [TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
++ [TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
+ };
+
+-static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
++static void mt7996_tm_ipi_work(struct work_struct *work);
++
++static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
+ {
+- static const u8 width_to_bw[][NUM_BW_MAP] = {
+- [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
+- [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
+- [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
+- [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
+- [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
+- [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
+- [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
+- [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
+- [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
++ static const u32 width_to_bw[][NUM_BW_MAP] = {
++ [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
++ FIRST_CONTROL_CHAN_BITMAP_BW40},
++ [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
++ FIRST_CONTROL_CHAN_BITMAP_BW80},
++ [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
++ [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
++ FIRST_CONTROL_CHAN_BITMAP_BW160},
++ [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
++ [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
++ [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
++ [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
++ [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
+ };
+
+ if (width >= ARRAY_SIZE(width_to_bw))
+@@ -217,6 +234,9 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+
+ /* use firmware counter for RX stats */
+ phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++
++ if (en)
++ INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
+ }
+
+ static void
+@@ -829,6 +849,204 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ }
+ }
+
++static u8
++mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
++{
++ struct mt76_phy *mphy = phy->mt76;
++ const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
++ u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
++ u16 first_control = 0, control_chan = chandef->chan->hw_value;
++
++ bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
++ if (!bitmap)
++ return control_chan;
++
++ width_mhz = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_MHZ);
++ offset = width_mhz / 10 - 2;
++
++ for (i = 0; i < size; i++) {
++ if (!((1 << i) & bitmap))
++ continue;
++
++ if (control_chan >= chan[i].hw_value)
++ first_control = chan[i].hw_value;
++ else
++ break;
++ }
++
++ if (i == size || first_control == 0)
++ return control_chan;
++
++ return first_control + offset;
++}
++
++static int
++mt7996_tm_set_offchan(struct mt7996_phy *phy, bool no_center)
++{
++ struct mt76_phy *mphy = phy->mt76;
++ struct mt7996_dev *dev = phy->dev;
++ struct ieee80211_hw *hw = mphy->hw;
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct cfg80211_chan_def chandef = {};
++ struct ieee80211_channel *chan;
++ int ret, freq = ieee80211_channel_to_frequency(td->offchan_ch, NL80211_BAND_5GHZ);
++
++ if (!mphy->cap.has_5ghz || !freq) {
++ ret = -EINVAL;
++ dev_info(dev->mt76.dev, "Failed to set offchan (invalid band or channel)!\n");
++ goto out;
++ }
++
++ chandef.width = td->offchan_bw;
++ chan = ieee80211_get_channel(hw->wiphy, freq);
++ chandef.chan = chan;
++ if (no_center)
++ td->offchan_center_ch = mt7996_tm_get_center_chan(phy, &chandef);
++ chandef.center_freq1 = ieee80211_channel_to_frequency(td->offchan_center_ch,
++ NL80211_BAND_5GHZ);
++ if (!cfg80211_chandef_valid(&chandef)) {
++ ret = -EINVAL;
++ dev_info(dev->mt76.dev, "Failed to set offchan, chandef is invalid!\n");
++ goto out;
++ }
++
++ memset(&dev->rdd2_chandef, 0, sizeof(struct cfg80211_chan_def));
++
++ ret = mt7996_mcu_rdd_background_enable(phy, &chandef);
++
++ if (ret)
++ goto out;
++
++ dev->rdd2_phy = phy;
++ dev->rdd2_chandef = chandef;
++
++ return 0;
++
++out:
++ td->offchan_ch = 0;
++ td->offchan_center_ch = 0;
++ td->offchan_bw = 0;
++
++ return ret;
++}
++
++static void
++mt7996_tm_ipi_hist_ctrl(struct mt7996_phy *phy, struct mt7996_tm_rdd_ipi_ctrl *data, u8 cmd)
++{
++#define MT_IPI_RESET 0x830a5dfc
++#define MT_IPI_RESET_MASK BIT(28)
++#define MT_IPI_COUNTER_BASE 0x83041000
++#define MT_IPI_COUNTER(idx) (MT_IPI_COUNTER_BASE + ((idx) * 4))
++ struct mt7996_dev *dev = phy->dev;
++ bool val;
++ int i;
++
++ if (cmd == RDD_SET_IPI_HIST_RESET) {
++ val = mt76_rr(dev, MT_IPI_RESET) & MT_IPI_RESET_MASK;
++ mt76_rmw_field(dev, MT_IPI_RESET, MT_IPI_RESET_MASK, !val);
++ return;
++ }
++
++ for (i = 0; i < POWER_INDICATE_HIST_MAX; i++)
++ data->ipi_hist_val[i] = mt76_rr(dev, MT_IPI_COUNTER(i));
++}
++
++static void
++mt7996_tm_ipi_work(struct work_struct *work)
++{
++#define PRECISION 100
++ struct mt7996_phy *phy = container_of(work, struct mt7996_phy, ipi_work.work);
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_tm_rdd_ipi_ctrl data;
++ u32 ipi_idx, ipi_free_count, ipi_percentage;
++ u32 ipi_hist_count_th = 0, ipi_hist_total_count = 0;
++ u32 self_idle_ratio, ipi_idle_ratio, channel_load;
++ u32 *ipi_hist_data;
++ const char *power_lower_bound, *power_upper_bound;
++ static const char * const ipi_idx_to_power_bound[] = {
++ [RDD_IPI_HIST_0] = "-92",
++ [RDD_IPI_HIST_1] = "-89",
++ [RDD_IPI_HIST_2] = "-86",
++ [RDD_IPI_HIST_3] = "-83",
++ [RDD_IPI_HIST_4] = "-80",
++ [RDD_IPI_HIST_5] = "-75",
++ [RDD_IPI_HIST_6] = "-70",
++ [RDD_IPI_HIST_7] = "-65",
++ [RDD_IPI_HIST_8] = "-60",
++ [RDD_IPI_HIST_9] = "-55",
++ [RDD_IPI_HIST_10] = "inf",
++ };
++
++ memset(&data, 0, sizeof(data));
++ mt7996_tm_ipi_hist_ctrl(phy, &data, RDD_IPI_HIST_ALL_CNT);
++
++ ipi_hist_data = data.ipi_hist_val;
++ for (ipi_idx = 0; ipi_idx < POWER_INDICATE_HIST_MAX; ipi_idx++) {
++ power_lower_bound = ipi_idx ? ipi_idx_to_power_bound[ipi_idx - 1] : "-inf";
++ power_upper_bound = ipi_idx_to_power_bound[ipi_idx];
++
++ dev_info(dev->mt76.dev, "IPI %d (power range: (%s, %s] dBm): ipi count = %d\n",
++ ipi_idx, power_lower_bound, power_upper_bound, ipi_hist_data[ipi_idx]);
++
++ if (td->ipi_threshold <= ipi_idx && ipi_idx <= RDD_IPI_HIST_10)
++ ipi_hist_count_th += ipi_hist_data[ipi_idx];
++
++ ipi_hist_total_count += ipi_hist_data[ipi_idx];
++ }
++
++ ipi_free_count = ipi_hist_data[RDD_IPI_FREE_RUN_CNT];
++
++ dev_info(dev->mt76.dev, "IPI threshold %d: ipi_hist_count_th = %d, ipi_free_count = %d\n",
++ td->ipi_threshold, ipi_hist_count_th, ipi_free_count);
++ dev_info(dev->mt76.dev, "TX assert time = %d [ms]\n", data.tx_assert_time / 1000);
++
++ /* calculate channel load = (self idle ratio - idle ratio) / self idle ratio */
++ if (ipi_hist_count_th >= UINT_MAX / (100 * PRECISION))
++ ipi_percentage = 100 * PRECISION *
++ (ipi_hist_count_th / (100 * PRECISION)) /
++ (ipi_free_count / (100 * PRECISION));
++ else
++ ipi_percentage = PRECISION * 100 * ipi_hist_count_th / ipi_free_count;
++
++ ipi_idle_ratio = ((100 * PRECISION) - ipi_percentage) / PRECISION;
++
++ self_idle_ratio = PRECISION * 100 *
++ (td->ipi_period - (data.tx_assert_time / 1000)) /
++ td->ipi_period / PRECISION;
++
++ if (self_idle_ratio < ipi_idle_ratio)
++ channel_load = 0;
++ else
++ channel_load = self_idle_ratio - ipi_idle_ratio;
++
++ if (self_idle_ratio <= td->ipi_threshold) {
++ dev_info(dev->mt76.dev, "band[%d]: self idle ratio = %d%%, idle ratio = %d%%\n",
++ phy->mt76->band_idx, self_idle_ratio, ipi_idle_ratio);
++ return;
++ }
++
++ channel_load = (100 * channel_load) / self_idle_ratio;
++ dev_info(dev->mt76.dev,
++ "band[%d]: chan load = %d%%, self idle ratio = %d%%, idle ratio = %d%%\n",
++ phy->mt76->band_idx, channel_load, self_idle_ratio, ipi_idle_ratio);
++}
++
++static int
++mt7996_tm_set_ipi(struct mt7996_phy *phy)
++{
++ struct mt76_testmode_data *td = &phy->mt76->test;
++
++ /* reset IPI CR */
++ mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
++
++ cancel_delayed_work(&phy->ipi_work);
++ ieee80211_queue_delayed_work(phy->mt76->hw, &phy->ipi_work,
++ msecs_to_jiffies(td->ipi_period));
++
++ return 0;
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -860,6 +1078,14 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+
+ mt7996_tm_set(dev, func_idx, td->cfg.type);
+ }
++ if ((changed & BIT(TM_CHANGED_OFF_CHAN_CH)) &&
++ (changed & BIT(TM_CHANGED_OFF_CHAN_BW)))
++ mt7996_tm_set_offchan(phy, !(changed & BIT(TM_CHANGED_OFF_CHAN_CENTER_CH)));
++ if ((changed & BIT(TM_CHANGED_IPI_THRESHOLD)) &&
++ (changed & BIT(TM_CHANGED_IPI_PERIOD)))
++ mt7996_tm_set_ipi(phy);
++ if (changed & BIT(TM_CHANGED_IPI_RESET))
++ mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
+ }
+
+ static int
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index 9bfb86f28..78662b2ed 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -27,9 +27,15 @@ enum {
+ FW_CDBW_8080MHZ,
+ };
+
++#define FIRST_CONTROL_CHAN_BITMAP_BW40 0x5555555
++#define FIRST_CONTROL_CHAN_BITMAP_BW80 0x111111
++#define FIRST_CONTROL_CHAN_BITMAP_BW160 0x100101
++
+ enum bw_mapping_method {
+ BW_MAP_NL_TO_FW,
+ BW_MAP_NL_TO_TM,
++ BW_MAP_NL_TO_MHZ,
++ BW_MAP_NL_TO_CONTROL_BITMAP_5G,
+
+ NUM_BW_MAP,
+ };
+@@ -312,4 +318,42 @@ struct mt7996_tm_rx_event {
+ };
+ } __packed;
+
++enum {
++ RDD_SET_IPI_CR_INIT, /* CR initialization */
++ RDD_SET_IPI_HIST_RESET, /* Reset IPI histogram counter */
++ RDD_SET_IDLE_POWER, /* Idle power info */
++ RDD_SET_IPI_HIST_NUM
++};
++
++enum {
++ RDD_IPI_HIST_0, /* IPI count for power <= -92 (dBm) */
++ RDD_IPI_HIST_1, /* IPI count for -92 < power <= -89 (dBm) */
++ RDD_IPI_HIST_2, /* IPI count for -89 < power <= -86 (dBm) */
++ RDD_IPI_HIST_3, /* IPI count for -86 < power <= -83 (dBm) */
++ RDD_IPI_HIST_4, /* IPI count for -83 < power <= -80 (dBm) */
++ RDD_IPI_HIST_5, /* IPI count for -80 < power <= -75 (dBm) */
++ RDD_IPI_HIST_6, /* IPI count for -75 < power <= -70 (dBm) */
++ RDD_IPI_HIST_7, /* IPI count for -70 < power <= -65 (dBm) */
++ RDD_IPI_HIST_8, /* IPI count for -65 < power <= -60 (dBm) */
++ RDD_IPI_HIST_9, /* IPI count for -60 < power <= -55 (dBm) */
++ RDD_IPI_HIST_10, /* IPI count for -55 < power (dBm) */
++ RDD_IPI_FREE_RUN_CNT, /* IPI count for counter++ per 8 us */
++ RDD_IPI_HIST_ALL_CNT, /* Get all IPI */
++ RDD_IPI_HIST_0_TO_10_CNT, /* Get IPI histogram 0 to 10 */
++ RDD_IPI_HIST_2_TO_10_CNT, /* Get IPI histogram 2 to 10 */
++ RDD_TX_ASSERT_TIME, /* Get band 1 TX assert time */
++ RDD_IPI_HIST_NUM
++};
++
++#define POWER_INDICATE_HIST_MAX RDD_IPI_FREE_RUN_CNT
++#define IPI_HIST_TYPE_NUM (POWER_INDICATE_HIST_MAX + 1)
++
++struct mt7996_tm_rdd_ipi_ctrl {
++ u8 ipi_hist_idx;
++ u8 band_idx;
++ u8 rsv[2];
++ __le32 ipi_hist_val[IPI_HIST_TYPE_NUM];
++ __le32 tx_assert_time; /* unit: us */
++} __packed;
++
+ #endif
+diff --git a/testmode.c b/testmode.c
+index cd8cb6553..69147f866 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -27,6 +27,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED },
++ [MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_OFF_CH_SCAN_PATH] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
++ [MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
++ [MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
+ };
+ EXPORT_SYMBOL_GPL(mt76_tm_policy);
+
+@@ -499,6 +506,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ if (tb[MT76_TM_ATTR_TX_RATE_IDX])
+ td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]);
+
++ if (tb[MT76_TM_ATTR_IPI_PERIOD])
++ td->ipi_period = nla_get_u32(tb[MT76_TM_ATTR_IPI_PERIOD]);
++
+ if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode,
+ 0, MT76_TM_TX_MODE_MAX) ||
+ mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss,
+@@ -514,7 +524,14 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ &td->tx_duty_cycle, 0, 99) ||
+ mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
+ &td->tx_power_control, 0, 1) ||
+- mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH], &td->offchan_ch, 36, 196) ||
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH], &td->offchan_center_ch,
++ 36, 196) ||
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
++ &td->offchan_bw, NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_THRESHOLD], &td->ipi_threshold, 0, 10) ||
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_RESET], &td->ipi_reset, 0, 1))
+ goto out;
+
+ if (tb[MT76_TM_ATTR_TX_LENGTH]) {
+@@ -720,6 +737,9 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
+ nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
+ nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
++ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CH, td->offchan_ch) ||
++ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH, td->offchan_center_ch) ||
++ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_BW, td->offchan_bw) ||
+ (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
+ nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
+ (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
+diff --git a/tools/fields.c b/tools/fields.c
+index b01227638..77696ce7b 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -35,6 +35,15 @@ static const char * const testmode_tx_mode[] = {
+ [MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
+ };
+
++static const char * const testmode_offchan_bw[] = {
++ [NL80211_CHAN_WIDTH_20_NOHT] = "NOHT",
++ [NL80211_CHAN_WIDTH_20] = "20",
++ [NL80211_CHAN_WIDTH_40] = "40",
++ [NL80211_CHAN_WIDTH_80] = "80",
++ [NL80211_CHAN_WIDTH_80P80] = "80p80",
++ [NL80211_CHAN_WIDTH_160] = "160",
++};
++
+ static void print_enum(const struct tm_field *field, struct nlattr *attr)
+ {
+ unsigned int i = nla_get_u8(attr);
+@@ -390,6 +399,12 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ FIELD(u8, AID, "aid"),
+ FIELD(u8, RU_ALLOC, "ru_alloc"),
+ FIELD(u8, RU_IDX, "ru_idx"),
++ FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
++ FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
++ FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
++ FIELD(u8, IPI_THRESHOLD, "ipi_threshold"),
++ FIELD(u32, IPI_PERIOD, "ipi_period"),
++ FIELD(u8, IPI_RESET, "ipi_reset"),
+ FIELD_MAC(MAC_ADDRS, "mac_addrs"),
+ FIELD_NESTED_RO(STATS, stats, "",
+ .print_extra = print_extra_stats),
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch
new file mode 100644
index 0000000..e7908ef
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch
@@ -0,0 +1,155 @@
+From 72f979ee7ce94f3f28b6fc761a1aadb37ab2d63a Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:00:17 +0800
+Subject: [PATCH 029/116] mtk: wifi: mt76: mt7996: support eagle ZWDFS on iFEM
+
+Fix the case that control channel is not first chan during first
+interface setup.
+Refactor ifem adie logic (if/else to switch, use sku_type & fem_type)
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/main.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++---
+ mt7996/mcu.c | 6 +++--
+ mt7996/mt7996.h | 1 +
+ 3 files changed, 67 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 52870fa72..1a1f0242c 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1444,6 +1444,61 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
++static void
++mt7996_background_radar_handle_7975_ifem(struct ieee80211_hw *hw,
++ struct cfg80211_chan_def *user_chandef,
++ struct cfg80211_chan_def *fw_chandef)
++{
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct cfg80211_chan_def *c = user_chandef;
++ struct ieee80211_channel *first_chan;
++ bool is_ifem_adie, expand = false;
++
++ switch (mt76_chip(&dev->mt76)) {
++ case 0x7990:
++ is_ifem_adie = dev->fem_type == MT7996_FEM_INT &&
++ dev->chip_sku != MT7996_SKU_233;
++ break;
++ case 0x7992:
++ is_ifem_adie = dev->chip_sku == MT7992_SKU_44 &&
++ dev->fem_type != MT7996_FEM_EXT;
++ break;
++ default:
++ return;
++ }
++
++ if (!user_chandef || !is_ifem_adie)
++ goto out;
++
++ if (user_chandef->width == NL80211_CHAN_WIDTH_160) {
++ first_chan = ieee80211_get_channel(hw->wiphy, user_chandef->center_freq1 - 70);
++ if (dev->bg_nxt_freq)
++ goto out;
++
++ if (first_chan->flags & IEEE80211_CHAN_RADAR)
++ dev->bg_nxt_freq = first_chan->center_freq;
++ else
++ c = fw_chandef;
++
++ c->chan = ieee80211_get_channel(hw->wiphy, first_chan->center_freq + 80);
++ } else {
++ if (!dev->bg_nxt_freq)
++ goto out;
++
++ c->chan = ieee80211_get_channel(hw->wiphy, dev->bg_nxt_freq);
++ dev->bg_nxt_freq = 0;
++ expand = true;
++ }
++ c->width = NL80211_CHAN_WIDTH_80;
++ c->center_freq1 = c->chan->center_freq + 30;
++
++ if (c == user_chandef)
++ cfg80211_background_radar_update_channel(hw->wiphy, c, expand);
++ return;
++out:
++ dev->bg_nxt_freq = 0;
++}
++
+ static int
+ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ struct cfg80211_chan_def *chandef)
+@@ -1452,6 +1507,7 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ struct mt7996_dev *dev = phy->dev;
+ int ret = -EINVAL;
+ bool running;
++ struct cfg80211_chan_def ifem_chandef = {};
+
+ mutex_lock(&dev->mt76.mutex);
+
+@@ -1464,13 +1520,14 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ goto out;
+ }
+
++ mt7996_background_radar_handle_7975_ifem(hw, chandef, &ifem_chandef);
++
+ /* rdd2 already configured on a radar channel */
+ running = dev->rdd2_phy &&
+ cfg80211_chandef_valid(&dev->rdd2_chandef) &&
+ !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR);
+
+- if (!chandef || running ||
+- !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) {
++ if (!chandef || running) {
+ ret = mt7996_mcu_rdd_background_enable(phy, NULL);
+ if (ret)
+ goto out;
+@@ -1479,7 +1536,9 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ goto update_phy;
+ }
+
+- ret = mt7996_mcu_rdd_background_enable(phy, chandef);
++ ret = mt7996_mcu_rdd_background_enable(phy,
++ ifem_chandef.chan ?
++ &ifem_chandef : chandef);
+ if (ret)
+ goto out;
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1330ff397..f375a5aba 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -372,12 +372,14 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
+ if (!mphy)
+ return;
+
+- if (r->band_idx == MT_RX_SEL2)
++ if (r->band_idx == MT_RX_SEL2) {
++ dev->bg_nxt_freq = 0;
+ cfg80211_background_radar_event(mphy->hw->wiphy,
+ &dev->rdd2_chandef,
+ GFP_ATOMIC);
+- else
++ } else {
+ ieee80211_radar_detected(mphy->hw);
++ }
+ dev->hw_pattern++;
+ }
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 7d6ec8ba4..fd93db2fc 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -399,6 +399,7 @@ struct mt7996_dev {
+ bool testmode_enable;
+ bool bin_file_mode;
+ u8 eeprom_mode;
++ u32 bg_nxt_freq;
+
+ bool ibf;
+ u8 fw_debug_wm;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch
new file mode 100644
index 0000000..30750f5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch
@@ -0,0 +1,485 @@
+From fcdb091e77f60c86abd005508edc7beb6dd33154 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 11 Mar 2024 10:43:03 +0800
+Subject: [PATCH 030/116] mtk: wifi: mt76: mt7996: refactor eeprom loading flow
+ for sku checking
+
+Add eeprom sku checking mechanism to avoid using the
+wrong eeprom in flash/binfile mode
+The fields listed below will be checked by comparing the loaded eeprom to the default bin
+1. FEM type
+2. MAC address (warning for using default MAC address)
+3. RF path & streams
+ (to distinguish cases such as BE7200 4i5i, BE6500 3i5i, and BE5040 2i3i)
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: If1905086f2a876a593d07f23a5facad35067f94a
+
+1. Reset eeprom content before loading efuse
+ eeprom array might contain incomplete data read from flash or
+ binfile, which is not overwritten since this block is invalid
+ in efuse.
+2. Remove testmode default bin since it is unnecessary
+ Not used in logan. Directly load normal mode default bin is fine.
+ Also, this way is better since we don't need to put testmode default
+ eeprom for each sku (especially kite's sku) in our SDK.
+3. Set testmode_en field for default bin mode for consistency sake
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+1.
+Fix efuse mode txpower = 0 issue
+This fix might be changed if fw supports efuse merge for buffer mode = EE_MODE_EFUSE
+2.
+Add Eagle BE19000 ifem default bin, add Eagle default bin bootstrip
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 207 ++++++++++++++++++++++++-------------------
+ mt7996/eeprom.h | 32 +++++++
+ mt7996/mcu.c | 18 +---
+ mt7996/mt7996.h | 3 +-
+ mt7996/mtk_debugfs.c | 2 +-
+ 5 files changed, 150 insertions(+), 112 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index fe8b25352..f97d76cc4 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -50,54 +50,84 @@ const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+-#define FEM_INT 0
+-#define FEM_EXT 3
+ u8 *eeprom = dev->mt76.eeprom.data;
+- u8 i, fem[__MT_MAX_BAND], fem_type;
+ u16 val = get_unaligned_le16(eeprom);
+
+- for (i = 0; i < __MT_MAX_BAND; i++)
+- fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
+-
+ switch (val) {
+ case 0x7990:
+ return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
+ case 0x7992:
+- if (dev->fem_type == MT7996_FEM_UNSET)
+- return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
+-
+- if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
+- fem_type = MT7996_FEM_EXT;
+- else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
+- fem_type = MT7996_FEM_INT;
+- else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
+- fem_type = MT7996_FEM_MIX;
+- else
+- return -EINVAL;
+-
+- return (is_mt7992(&dev->mt76) ? 0 : -EINVAL) |
+- (dev->fem_type == fem_type ? 0 : -EINVAL);
++ return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
+ default:
+ return -EINVAL;
+ }
+ }
+
+-const char *mt7996_eeprom_name(struct mt7996_dev *dev)
++static int mt7996_check_eeprom_sku(struct mt7996_dev *dev, const u8 *dflt)
+ {
+- if (dev->bin_file_mode)
+- return dev->mt76.bin_file_name;
++#define FEM_INT 0
++#define FEM_EXT 3
++ u8 *eeprom = dev->mt76.eeprom.data;
++ u8 i, fem[__MT_MAX_BAND], fem_type;
++ u16 mac_addr[__MT_MAX_BAND] = {MT_EE_MAC_ADDR, MT_EE_MAC_ADDR2, MT_EE_MAC_ADDR3};
++ int max_band = is_mt7996(&dev->mt76) ? __MT_MAX_BAND : 2;
++
++ if (dev->fem_type == MT7996_FEM_UNSET)
++ return -EINVAL;
++
++ /* FEM type */
++ for (i = 0; i < max_band; i++)
++ fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
++
++ if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
++ fem_type = MT7996_FEM_EXT;
++ else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
++ fem_type = MT7996_FEM_INT;
++ else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
++ fem_type = MT7996_FEM_MIX;
++ else
++ return -EINVAL;
+
+- if (dev->testmode_enable) {
+- if (is_mt7992(&dev->mt76))
+- return MT7992_EEPROM_DEFAULT_TM;
+- else
+- return MT7996_EEPROM_DEFAULT_TM;
++ if (dev->fem_type != fem_type)
++ return -EINVAL;
++
++ /* RF path & stream */
++ for (i = 0; i < max_band; i++) {
++ u8 path, rx_path, nss;
++ u8 dflt_path, dflt_rx_path, dflt_nss;
++
++ /* MAC address */
++ if (ether_addr_equal(eeprom + mac_addr[i], dflt + mac_addr[i]))
++ dev_warn(dev->mt76.dev,
++ "Currently using default MAC address for band %d\n", i);
++
++ mt7996_parse_eeprom_stream(eeprom, i, &path, &rx_path, &nss);
++ mt7996_parse_eeprom_stream(dflt, i, &dflt_path, &dflt_rx_path, &dflt_nss);
++ if (path > dflt_path || rx_path > dflt_rx_path || nss > dflt_nss) {
++ dev_err(dev->mt76.dev,
++ "Invalid path/stream configuration for band %d\n", i);
++ return -EINVAL;
++ } else if (path < dflt_path || rx_path < dflt_rx_path || nss < dflt_nss) {
++ dev_warn(dev->mt76.dev,
++ "Restricted path/stream configuration for band %d\n", i);
++ dev_warn(dev->mt76.dev,
++ "path: %u/%u, rx_path: %u/%u, nss: %u/%u\n",
++ path, dflt_path, rx_path, dflt_rx_path, nss, dflt_nss);
++ }
+ }
+
++ return 0;
++}
++
++const char *mt7996_eeprom_name(struct mt7996_dev *dev)
++{
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7990:
+ if (dev->chip_sku == MT7996_SKU_404)
+ return MT7996_EEPROM_DEFAULT_404;
++
++ if (dev->fem_type == MT7996_FEM_INT)
++ return MT7996_EEPROM_DEFAULT_INT;
+ return MT7996_EEPROM_DEFAULT;
+ case 0x7992:
+ if (dev->chip_sku == MT7992_SKU_23) {
+@@ -148,21 +178,18 @@ mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
+ }
+
+ static int
+-mt7996_eeprom_load_default(struct mt7996_dev *dev)
++mt7996_eeprom_load_bin(struct mt7996_dev *dev)
+ {
+ u8 *eeprom = dev->mt76.eeprom.data;
+ const struct firmware *fw = NULL;
+ int ret;
+
+- ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev);
++ ret = request_firmware(&fw, dev->mt76.bin_file_name, dev->mt76.dev);
+ if (ret)
+ return ret;
+
+ if (!fw || !fw->data) {
+- if (dev->bin_file_mode)
+- dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
+- else
+- dev_err(dev->mt76.dev, "Invalid default bin\n");
++ dev_err(dev->mt76.dev, "Invalid bin %s\n", dev->mt76.bin_file_name);
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -180,7 +207,7 @@ static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
+ {
+ int ret = 1;
+
+- /* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
++ /* return > 0 for load success, return 0 for load failed, return < 0 for no memory */
+ dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
+ if (dev->bin_file_mode) {
+ dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
+@@ -189,15 +216,15 @@ static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
+ if (!dev->mt76.eeprom.data)
+ return -ENOMEM;
+
+- if (mt7996_eeprom_load_default(dev))
+- return 0;
+-
+- if (mt7996_check_eeprom(dev))
++ if (mt7996_eeprom_load_bin(dev))
+ return 0;
+ } else {
+ ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
+ }
+
++ if (mt7996_check_eeprom(dev))
++ return 0;
++
+ return ret;
+ }
+
+@@ -206,30 +233,30 @@ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
+ u8 *eeprom;
+ int ret;
+
++ dev->testmode_enable = testmode_enable;
++
+ /* load eeprom in flash or bin file mode to determine fw mode */
+ ret = mt7996_eeprom_load_flash(dev);
++ if (ret <= 0)
++ goto out;
+
+- if (ret < 0)
+- return ret;
+-
+- if (ret) {
+- dev->flash_mode = true;
+- dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
+- eeprom = dev->mt76.eeprom.data;
+- /* testmode enable priority: eeprom field > module parameter */
+- dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
+- testmode_enable;
+- }
++ dev->flash_mode = true;
++ dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
++ eeprom = dev->mt76.eeprom.data;
++ /* testmode enable priority: eeprom field > module parameter */
++ dev->testmode_enable = eeprom[MT_EE_TESTMODE_EN];
+
++out:
+ return ret;
+ }
+
+ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ {
++ const struct firmware *fw = NULL;
+ int ret;
+- u8 free_block_num;
+ u32 block_num, i;
+ u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
++ u8 free_block_num, *eeprom = dev->mt76.eeprom.data;
+
+ /* flash or bin file mode eeprom is loaded before mcu init */
+ if (!dev->flash_mode) {
+@@ -239,19 +266,49 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+
+ /* efuse info isn't enough */
+ if (free_block_num >= 59)
+- return -EINVAL;
++ goto dflt;
++
++ memset(eeprom, 0, MT7996_EEPROM_SIZE);
++ /* check if efuse contains valid eeprom data */
++ if (mt7996_mcu_get_eeprom(dev, 0, NULL) ||
++ mt7996_check_eeprom(dev))
++ goto dflt;
+
+ /* read eeprom data from efuse */
+ block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+- for (i = 0; i < block_num; i++) {
++ for (i = 1; i < block_num; i++) {
+ ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
+ if (ret && ret != -EINVAL)
+- return ret;
++ goto dflt;
+ }
+ dev->eeprom_mode = EFUSE_MODE;
+ }
+
+- return mt7996_check_eeprom(dev);
++dflt:
++ ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev);
++ if (ret)
++ return ret;
++
++ if (!fw || !fw->data) {
++ dev_err(dev->mt76.dev, "Invalid default bin\n");
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (dev->eeprom_mode && !mt7996_check_eeprom_sku(dev, fw->data)) {
++ ret = 0;
++ goto out;
++ }
++
++ memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE);
++ dev->bin_file_mode = false;
++ dev->flash_mode = true;
++ dev->eeprom_mode = DEFAULT_BIN_MODE;
++ eeprom[MT_EE_TESTMODE_EN] = dev->testmode_enable;
++ dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
++out:
++ release_firmware(fw);
++ return ret;
+ }
+
+ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
+@@ -323,32 +380,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ int max_path = 5, max_nss = 4;
+ int ret;
+
+- switch (band_idx) {
+- case MT_BAND1:
+- path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
+- eeprom[MT_EE_WIFI_CONF + 2]);
+- rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
+- eeprom[MT_EE_WIFI_CONF + 3]);
+- nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
+- eeprom[MT_EE_WIFI_CONF + 5]);
+- break;
+- case MT_BAND2:
+- path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
+- eeprom[MT_EE_WIFI_CONF + 2]);
+- rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
+- eeprom[MT_EE_WIFI_CONF + 4]);
+- nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
+- eeprom[MT_EE_WIFI_CONF + 5]);
+- break;
+- default:
+- path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
+- eeprom[MT_EE_WIFI_CONF + 1]);
+- rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
+- eeprom[MT_EE_WIFI_CONF + 3]);
+- nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
+- eeprom[MT_EE_WIFI_CONF + 4]);
+- break;
+- }
++ mt7996_parse_eeprom_stream(eeprom, band_idx, &path, &rx_path, &nss);
+
+ if (!path || path > max_path)
+ path = max_path;
+@@ -437,17 +469,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ return ret;
+
+ ret = mt7996_eeprom_load(dev);
+- if (ret < 0) {
+- if (ret != -EINVAL)
+- return ret;
+-
+- dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
+- dev->bin_file_mode = false;
+- dev->eeprom_mode = DEFAULT_BIN_MODE;
+- ret = mt7996_eeprom_load_default(dev);
+- if (ret)
+- return ret;
+- }
++ if (ret)
++ return ret;
+
+ ret = mt7996_eeprom_load_precal(dev);
+ if (ret)
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 8f0f87b6b..03a4fd07d 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -132,6 +132,38 @@ mt7996_get_channel_group_6g(int channel)
+ return DIV_ROUND_UP(channel - 29, 32);
+ }
+
++static inline void
++mt7996_parse_eeprom_stream(const u8 *eep, int band_idx,
++ u8 *path, u8 *rx_path, u8 *nss)
++{
++ switch (band_idx) {
++ case MT_BAND1:
++ *path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
++ eep[MT_EE_WIFI_CONF + 2]);
++ *rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
++ eep[MT_EE_WIFI_CONF + 3]);
++ *nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
++ eep[MT_EE_WIFI_CONF + 5]);
++ break;
++ case MT_BAND2:
++ *path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
++ eep[MT_EE_WIFI_CONF + 2]);
++ *rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
++ eep[MT_EE_WIFI_CONF + 4]);
++ *nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
++ eep[MT_EE_WIFI_CONF + 5]);
++ break;
++ default:
++ *path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
++ eep[MT_EE_WIFI_CONF + 1]);
++ *rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
++ eep[MT_EE_WIFI_CONF + 3]);
++ *nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
++ eep[MT_EE_WIFI_CONF + 4]);
++ break;
++ }
++}
++
+ enum mt7996_sku_rate_group {
+ SKU_CCK,
+ SKU_OFDM,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index f375a5aba..15751a54b 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3507,7 +3507,7 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ &req, sizeof(req), true);
+ }
+
+-static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
++int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+ {
+ #define MAX_PAGE_IDX_MASK GENMASK(7, 5)
+ #define PAGE_IDX_MASK GENMASK(4, 2)
+@@ -3552,22 +3552,6 @@ static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
+ return 0;
+ }
+
+-int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+-{
+- struct mt7996_mcu_eeprom req = {
+- .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
+- .len = cpu_to_le16(sizeof(req) - 4),
+- .buffer_mode = EE_MODE_EFUSE,
+- .format = EE_FORMAT_WHOLE
+- };
+-
+- if (dev->flash_mode)
+- return mt7996_mcu_set_eeprom_flash(dev);
+-
+- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
+- &req, sizeof(req), true);
+-}
+-
+ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ {
+ struct mt7996_mcu_eeprom_info req = {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index fd93db2fc..b2a938162 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -54,15 +54,14 @@
+ #define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
+
+ #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
++#define MT7996_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7996_eeprom_2i5i6i.bin"
+ #define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
+-#define MT7996_EEPROM_DEFAULT_TM "mediatek/mt7996/mt7996_eeprom_tm.bin"
+ #define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom_2i5i.bin"
+ #define MT7992_EEPROM_DEFAULT_EXT "mediatek/mt7996/mt7992_eeprom_2e5e.bin"
+ #define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin"
+ #define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
+ #define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
+ #define MT7992_EEPROM_DEFAULT_23_EXT "mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
+-#define MT7992_EEPROM_DEFAULT_TM "mediatek/mt7996/mt7992_eeprom_tm.bin"
+ #define MT7996_EEPROM_SIZE 7680
+ #define MT7996_EEPROM_BLOCK_SIZE 16
+ #define MT7996_TOKEN_SIZE 16384
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 3ecce1329..50b2df1ec 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2827,7 +2827,7 @@ static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
+ seq_printf(s, " flash mode\n");
+ break;
+ case BIN_FILE_MODE:
+- seq_printf(s, " bin file mode\n filename = %s\n", mt7996_eeprom_name(dev));
++ seq_printf(s, " bin file mode\n filename = %s\n", dev->mt76.bin_file_name);
+ break;
+ default:
+ break;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch
new file mode 100644
index 0000000..ade9a1e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch
@@ -0,0 +1,1433 @@
+From 0e813e3d82da805835d53d1af182bd2d700aaafa Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 13 Dec 2022 15:17:43 +0800
+Subject: [PATCH 031/116] mtk: wifi: mt76: mt7996: add vendor commands support
+
+mtk: wifi: mt76: fix muru_onoff as all enabled by default
+
+Fix muru_onoff default value as 0xF, which means all MU & RU are
+enabled. The purpose of this commit is to align muru_onoff value with
+hostapd and mt76 driver
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+mtk: wifi: mt76: mt7996: add vendor cmd to get available color bitmap
+
+Add a vendor cmd to notify user space available color bitmap.
+The OBSS BSS color bitmap is maintained in mac80211, so mt76 will make use of that.
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+
+mtk: wifi: mt76: mt7996: add vendor subcmd EDCCA ctrl enable
+
+mtk: wifi: mt76: mt7996: add ibf control vendor cmd
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+
+mtk: wifi: mt76: mt7996: add three wire pta support
+
+three wire enable bit 0 & 1 for EXT0 & EXT1, respectively
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: mt7996: Add static pp vendor support
+
+Add static pp vendor support for setting pp mode.
+User can enable/disable preamble puncture feature through hostapd
+configuration and hostapd_cli. Driver can receive the nl80211 vendor
+message and convert it to mcu commands.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt76_connac_mcu.h | 2 +
+ mt7996/Makefile | 3 +-
+ mt7996/init.c | 9 +
+ mt7996/mac.c | 4 +
+ mt7996/main.c | 4 +
+ mt7996/mcu.c | 37 ++-
+ mt7996/mcu.h | 14 +
+ mt7996/mt7996.h | 53 +++
+ mt7996/mtk_mcu.c | 87 +++++
+ mt7996/mtk_mcu.h | 15 +
+ mt7996/vendor.c | 805 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h | 153 +++++++++
+ 12 files changed, 1180 insertions(+), 6 deletions(-)
+ create mode 100644 mt7996/vendor.c
+ create mode 100644 mt7996/vendor.h
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index d8830dc25..2a6091939 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1252,6 +1252,7 @@ enum {
+ MCU_UNI_CMD_REG_ACCESS = 0x0d,
+ MCU_UNI_CMD_CHIP_CONFIG = 0x0e,
+ MCU_UNI_CMD_POWER_CTRL = 0x0f,
++ MCU_UNI_CMD_CFG_SMESH = 0x10,
+ MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
+ MCU_UNI_CMD_SER = 0x13,
+ MCU_UNI_CMD_TWT = 0x14,
+@@ -1284,6 +1285,7 @@ enum {
+ MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+ MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
+ MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
++ MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
+ };
+
+ enum {
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index 7bb17f440..6643c7a38 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -1,11 +1,12 @@
+ # SPDX-License-Identifier: ISC
+ EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
+ EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
++EXTRA_CFLAGS += -DCONFIG_MTK_VENDOR
+
+ obj-$(CONFIG_MT7996E) += mt7996e.o
+
+ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+- debugfs.o mmio.o
++ debugfs.o mmio.o vendor.o
+
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
+ mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f1d681787..5cc2e6fbf 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -378,6 +378,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+
+ phy->slottime = 9;
+ phy->beacon_rate = -1;
++ phy->muru_onoff = OFDMA_UL | OFDMA_DL | MUMIMO_DL | MUMIMO_UL;
+
+ hw->sta_data_size = sizeof(struct mt7996_sta);
+ hw->vif_data_size = sizeof(struct mt7996_vif);
+@@ -629,6 +630,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ if (ret)
+ goto error;
+
++#ifdef CONFIG_MTK_VENDOR
++ mt7996_vendor_register(phy);
++#endif
++
+ ret = mt76_register_phy(mphy, true, mt76_rates,
+ ARRAY_SIZE(mt76_rates));
+ if (ret)
+@@ -1446,6 +1451,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ dev->mt76.test_ops = &mt7996_testmode_ops;
+ #endif
+
++#ifdef CONFIG_MTK_VENDOR
++ mt7996_vendor_register(&dev->phy);
++#endif
++
+ ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ ARRAY_SIZE(mt76_rates));
+ if (ret)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index c9f45abef..d55e5a761 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -679,6 +679,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
+ *qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ }
++#ifdef CONFIG_MTK_VENDOR
++ if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
++ mt7996_vendor_amnt_fill_rx(phy, skb);
++#endif
+ } else {
+ status->flag |= RX_FLAG_8023;
+ mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 1a1f0242c..6d1f61cd0 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -760,6 +760,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ mt7996_mac_wtbl_update(dev, idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
++#ifdef CONFIG_MTK_VENDOR
++ mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
++#endif
++
+ ret = mt7996_mcu_add_sta(dev, vif, sta, true, true);
+ if (ret)
+ return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 15751a54b..91c2dab1a 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1378,6 +1378,8 @@ static void
+ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ {
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_phy *phy = mvif->phy;
+ struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ struct sta_rec_muru *muru;
+ struct tlv *tlv;
+@@ -1389,11 +1391,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
+
+ muru = (struct sta_rec_muru *)tlv;
+- muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
+- vif->bss_conf.he_mu_beamformer ||
+- vif->bss_conf.vht_mu_beamformer ||
+- vif->bss_conf.vht_mu_beamformee;
+- muru->cfg.ofdma_dl_en = true;
++ muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
++ vif->bss_conf.he_mu_beamformer ||
++ vif->bss_conf.vht_mu_beamformer ||
++ vif->bss_conf.vht_mu_beamformee) &&
++ !!(phy->muru_onoff & MUMIMO_DL);
++ muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
++ muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
++ muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
+
+ if (sta->deflink.vht_cap.vht_supported)
+ muru->mimo_dl.vht_mu_bfee =
+@@ -4963,3 +4968,25 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
+ &req, sizeof(req), false);
+ }
++
++#ifdef CONFIG_MTK_VENDOR
++void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
++{
++ u8 mode, val;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_phy *phy = mvif->phy;
++
++ mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
++ val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
++
++ switch (mode) {
++ case RATE_PARAM_AUTO_MU:
++ if (val < 0 || val > 15) {
++ printk("Wrong value! The value is between 0-15.\n");
++ break;
++ }
++ phy->muru_onoff = val;
++ break;
++ }
++}
++#endif
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 5a60ccc55..2e845e920 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -754,8 +754,20 @@ enum {
+ RATE_PARAM_FIXED_MCS,
+ RATE_PARAM_FIXED_GI = 11,
+ RATE_PARAM_AUTO = 20,
++#ifdef CONFIG_MTK_VENDOR
++ RATE_PARAM_AUTO_MU = 32,
++#endif
+ };
+
++#define RATE_CFG_MODE GENMASK(15, 8)
++#define RATE_CFG_VAL GENMASK(7, 0)
++
++/* MURU */
++#define OFDMA_DL BIT(0)
++#define OFDMA_UL BIT(1)
++#define MUMIMO_DL BIT(2)
++#define MUMIMO_UL BIT(3)
++
+ enum {
+ BF_SOUNDING_ON = 1,
+ BF_HW_EN_UPDATE = 17,
+@@ -833,6 +845,8 @@ mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower)
+
+ enum {
+ UNI_BAND_CONFIG_RADIO_ENABLE,
++ UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
++ UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
+ UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+ };
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index b2a938162..402327d92 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -258,6 +258,34 @@ struct mt7996_wed_rro_session_id {
+ u16 id;
+ };
+
++#ifdef CONFIG_MTK_VENDOR
++#define MT7996_AIR_MONITOR_MAX_ENTRY 16
++#define MT7996_AIR_MONITOR_MAX_GROUP (MT7996_AIR_MONITOR_MAX_ENTRY >> 1)
++
++struct mt7996_air_monitor_group {
++ bool enable;
++ bool used[2];
++};
++
++struct mt7996_air_monitor_entry {
++ bool enable;
++
++ u8 group_idx;
++ u8 group_used_idx;
++ u8 muar_idx;
++ u8 addr[ETH_ALEN];
++ u32 last_seen;
++ s8 rssi[4];
++ struct ieee80211_sta *sta;
++};
++
++struct mt7996_air_monitor_ctrl {
++ u8 enable;
++ struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
++ struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
++};
++#endif
++
+ struct mt7996_phy {
+ struct mt76_phy *mt76;
+ struct mt7996_dev *dev;
+@@ -300,6 +328,8 @@ struct mt7996_phy {
+ bool sku_limit_en;
+ bool sku_path_en;
+
++ u8 muru_onoff;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ struct {
+ u32 *reg_backup;
+@@ -314,6 +344,10 @@ struct mt7996_phy {
+ u8 spe_idx;
+ } test;
+ #endif
++#ifdef CONFIG_MTK_VENDOR
++ spinlock_t amnt_lock;
++ struct mt7996_air_monitor_ctrl amnt_ctrl;
++#endif
+ };
+
+ struct mt7996_dev {
+@@ -740,6 +774,25 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ bool hif2, int *irq);
+ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+
++#ifdef CONFIG_MTK_VENDOR
++void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
++void mt7996_vendor_register(struct mt7996_phy *phy);
++void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
++int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
++ struct ieee80211_sta *sta);
++#endif
++
++int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
++int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set);
++
++enum edcca_bw_id {
++ EDCCA_BW_20 = 0,
++ EDCCA_BW_40,
++ EDCCA_BW_80,
++ EDCCA_BW_160,
++ EDCCA_MAX_BW_NUM,
++};
++
+ #ifdef CONFIG_MTK_DEBUG
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index e56ddd8ff..5c54d02c4 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -59,4 +59,91 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
+ sizeof(req), true);
+ }
++
++int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++ enum nl80211_band band = chandef->chan->band;
++ struct {
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++ u8 enable;
++ u8 std;
++ u8 _rsv2[2];
++ } __packed req = {
++ .band_idx = phy->mt76->band_idx,
++ .tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_ENABLE),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .enable = enable,
++ .std = EDCCA_DEFAULT,
++ };
++
++ switch (dev->mt76.region) {
++ case NL80211_DFS_JP:
++ req.std = EDCCA_JAPAN;
++ break;
++ case NL80211_DFS_FCC:
++ if (band == NL80211_BAND_6GHZ)
++ req.std = EDCCA_FCC;
++ break;
++ case NL80211_DFS_ETSI:
++ if (band == NL80211_BAND_6GHZ)
++ req.std = EDCCA_ETSI;
++ break;
++ default:
++ break;
++ }
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
++ &req, sizeof(req), true);
++}
++
++int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
++{
++ struct {
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++ u8 threshold[4];
++ bool init;
++ } __packed *res, req = {
++ .band_idx = phy->mt76->band_idx,
++ .tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_THRESHOLD),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .init = false,
++ };
++ struct sk_buff *skb;
++ int ret;
++ int i;
++
++ for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
++ req.threshold[i] = value[i];
++
++ if (set)
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
++ &req, sizeof(req), true);
++
++ ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76,
++ MCU_WM_UNI_CMD_QUERY(BAND_CONFIG),
++ &req, sizeof(req), true, &skb);
++
++ if (ret)
++ return ret;
++
++ res = (void *)skb->data;
++
++ for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
++ value[i] = res->threshold[i];
++
++ dev_kfree_skb(skb);
++
++ return 0;
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index c30418cae..36a58ad63 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -106,6 +106,21 @@ enum txpower_event {
+ UNI_TXPOWER_PHY_RATE_INFO = 5,
+ };
+
++enum {
++ EDCCA_CTRL_SET_EN = 0,
++ EDCCA_CTRL_SET_THRES,
++ EDCCA_CTRL_GET_EN,
++ EDCCA_CTRL_GET_THRES,
++ EDCCA_CTRL_NUM,
++};
++
++enum {
++ EDCCA_DEFAULT = 0,
++ EDCCA_FCC = 1,
++ EDCCA_ETSI = 2,
++ EDCCA_JAPAN = 3
++};
++
+ #endif
+
+ #endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+new file mode 100644
+index 000000000..0d6fa7792
+--- /dev/null
++++ b/mt7996/vendor.c
+@@ -0,0 +1,805 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2020, MediaTek Inc. All rights reserved.
++ */
++
++#include <net/netlink.h>
++
++#include "mt7996.h"
++#include "mcu.h"
++#include "vendor.h"
++#include "mtk_mcu.h"
++
++static const struct nla_policy
++mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
++ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++};
++
++static const struct nla_policy
++amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
++ [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
++};
++
++static const struct nla_policy
++amnt_set_policy[NUM_MTK_VENDOR_ATTRS_AMNT_SET] = {
++ [MTK_VENDOR_ATTR_AMNT_SET_INDEX] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_AMNT_SET_MACADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
++};
++
++static const struct nla_policy
++amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
++ [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy
++bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
++ [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
++};
++
++static const struct nla_policy
++edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_S8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
++ [MTK_VENDOR_ATTR_EDCCA_DUMP_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
++static const struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
++ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
++};
++
++struct mt7996_amnt_data {
++ u8 idx;
++ u8 addr[ETH_ALEN];
++ s8 rssi[4];
++ u32 last_seen;
++};
++
++static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
++ struct wireless_dev *wdev,
++ const void *data,
++ int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
++ int err;
++ u8 val8;
++ u32 val32 = 0;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
++ mu_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
++ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
++ FIELD_PREP(RATE_CFG_VAL, val8);
++ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7996_set_wireless_vif, &val32);
++ }
++
++ return 0;
++}
++
++static int
++mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ struct sk_buff *skb, const void *data, int data_len,
++ unsigned long *storage)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ int len = 0;
++
++ if (*storage == 1)
++ return -ENOENT;
++ *storage = 1;
++
++ if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
++ return -ENOMEM;
++ len += 1;
++
++ return len;
++}
++
++void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
++{
++ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
++ struct mt7996_air_monitor_ctrl *ctrl = &phy->amnt_ctrl;
++ struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
++ __le16 fc = hdr->frame_control;
++ u8 addr[ETH_ALEN];
++ int i;
++
++ if (!ieee80211_has_fromds(fc))
++ ether_addr_copy(addr, hdr->addr2);
++ else if (ieee80211_has_tods(fc))
++ ether_addr_copy(addr, hdr->addr4);
++ else
++ ether_addr_copy(addr, hdr->addr3);
++
++ spin_lock_bh(&phy->amnt_lock);
++ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) {
++ struct mt7996_air_monitor_entry *entry;
++
++ if (ether_addr_equal(addr, ctrl->entry[i].addr)) {
++ entry = &ctrl->entry[i];
++ entry->rssi[0] = status->chain_signal[0];
++ entry->rssi[1] = status->chain_signal[1];
++ entry->rssi[2] = status->chain_signal[2];
++ entry->rssi[3] = status->chain_signal[3];
++ entry->last_seen = jiffies;
++ break;
++ }
++ }
++ spin_unlock_bh(&phy->amnt_lock);
++}
++
++static int
++mt7996_vendor_smesh_ctrl(struct mt7996_phy *phy, u8 write,
++ u8 enable, u8 *value)
++{
++#define UNI_CMD_SMESH_PARAM 0
++ struct mt7996_dev *dev = phy->dev;
++ struct smesh_param {
++ __le16 tag;
++ __le16 length;
++
++ u8 enable;
++ bool a2;
++ bool a1;
++ bool data;
++ bool mgnt;
++ bool ctrl;
++ u8 padding[2];
++ } req = {
++ .tag = cpu_to_le16(UNI_CMD_SMESH_PARAM),
++ .length = cpu_to_le16(sizeof(req) - 4),
++
++ .enable = enable,
++ .a2 = true,
++ .a1 = true,
++ .data = true,
++ .mgnt = false,
++ .ctrl = false,
++ };
++ struct smesh_param *res;
++ struct sk_buff *skb;
++ int ret = 0;
++
++ if (!value)
++ return -EINVAL;
++
++ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD(CFG_SMESH),
++ &req, sizeof(req), !write, &skb);
++
++ if (ret || write)
++ return ret;
++
++ res = (struct smesh_param *) skb->data;
++
++ *value = res->enable;
++
++ dev_kfree_skb(skb);
++
++ return 0;
++}
++
++static int
++mt7996_vendor_amnt_muar(struct mt7996_phy *phy, u8 muar_idx, u8 *addr)
++{
++#define UNI_CMD_MUAR_ENTRY 2
++ struct mt7996_dev *dev = phy->dev;
++ struct muar_entry {
++ __le16 tag;
++ __le16 length;
++
++ bool smesh;
++ u8 hw_bss_index;
++ u8 muar_idx;
++ u8 entry_add;
++ u8 mac_addr[6];
++ u8 padding[2];
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_CMD_MUAR_ENTRY),
++ .length = cpu_to_le16(sizeof(req) - 4),
++
++ .smesh = true,
++ .hw_bss_index = phy != &dev->phy,
++ .muar_idx = muar_idx,
++ .entry_add = 1,
++ };
++
++ ether_addr_copy(req.mac_addr, addr);
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req,
++ sizeof(req), true);
++}
++
++static int
++mt7996_vendor_amnt_set_en(struct mt7996_phy *phy, u8 enable)
++{
++ u8 status;
++ int ret;
++
++ ret = mt7996_vendor_smesh_ctrl(phy, 0, enable, &status);
++ if (ret)
++ return ret;
++
++ if (status == enable)
++ return 0;
++
++ ret = mt7996_vendor_smesh_ctrl(phy, 1, enable, &status);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int
++mt7996_vendor_amnt_set_addr(struct mt7996_phy *phy, u8 index, u8 *addr)
++{
++ struct mt7996_air_monitor_ctrl *amnt_ctrl = &phy->amnt_ctrl;
++ struct mt7996_air_monitor_group *group;
++ struct mt7996_air_monitor_entry *entry;
++ int ret, i, j;
++
++ if (index >= MT7996_AIR_MONITOR_MAX_ENTRY)
++ return -1;
++
++ spin_lock_bh(&phy->amnt_lock);
++ entry = &amnt_ctrl->entry[index];
++ if (!is_zero_ether_addr(addr)) {
++ if (entry->enable == false) {
++ for (i = 0; i < MT7996_AIR_MONITOR_MAX_GROUP; i++) {
++ group = &(amnt_ctrl->group[i]);
++ if (group->used[0] == false)
++ j = 0;
++ else if (group->used[1] == false)
++ j = 1;
++ else
++ continue;
++
++ group->enable = true;
++ group->used[j] = true;
++ entry->enable = true;
++ entry->group_idx = i;
++ entry->group_used_idx = j;
++ entry->muar_idx = 32 + 4 * i + 2 * j;
++ break;
++ }
++ }
++ } else {
++ group = &(amnt_ctrl->group[entry->group_idx]);
++
++ group->used[entry->group_used_idx] = false;
++ if (group->used[0] == false && group->used[1] == false)
++ group->enable = false;
++
++ entry->enable = false;
++ }
++ ether_addr_copy(entry->addr, addr);
++ amnt_ctrl->enable &= ~(1 << entry->group_idx);
++ amnt_ctrl->enable |= entry->enable << entry->group_idx;
++ spin_unlock_bh(&phy->amnt_lock);
++
++ ret = mt7996_vendor_amnt_muar(phy, entry->muar_idx, addr);
++ if (ret)
++ return ret;
++
++ return mt7996_vendor_amnt_set_en(phy, amnt_ctrl->enable);
++}
++
++static int
++mt7966_vendor_amnt_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++ const void *data, int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_SET];
++ u8 index = 0;
++ u8 mac_addr[ETH_ALEN];
++ int err;
++
++ err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
++ amnt_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET])
++ return -EINVAL;
++
++ err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_SET_MAX,
++ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET], amnt_set_policy, NULL);
++
++ if (!tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX] ||
++ !tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR])
++ return -EINVAL;
++
++ index = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX]);
++ memcpy(mac_addr, nla_data(tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]), ETH_ALEN);
++
++ return mt7996_vendor_amnt_set_addr(phy, index, mac_addr);
++}
++
++int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
++ struct ieee80211_sta *sta)
++{
++ u8 zero[ETH_ALEN] = {};
++ int i;
++
++ if (!phy->amnt_ctrl.enable)
++ return 0;
++
++ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
++ if (ether_addr_equal(sta->addr, phy->amnt_ctrl.entry[i].addr))
++ return mt7996_vendor_amnt_set_addr(phy, i, zero);
++ return 0;
++}
++
++static int
++mt7996_amnt_dump(struct mt7996_phy *phy, struct sk_buff *skb,
++ u8 amnt_idx, int *attrtype)
++{
++ struct mt7996_air_monitor_entry *entry;
++ struct mt7996_amnt_data data;
++ u32 last_seen = 0;
++
++ spin_lock_bh(&phy->amnt_lock);
++ entry = &phy->amnt_ctrl.entry[amnt_idx];
++ if (entry->enable == 0) {
++ spin_unlock_bh(&phy->amnt_lock);
++ return 0;
++ }
++
++ last_seen = jiffies_to_msecs(jiffies - entry->last_seen);
++ ether_addr_copy(data.addr, entry->addr);
++ data.rssi[0] = entry->rssi[0];
++ data.rssi[1] = entry->rssi[1];
++ data.rssi[2] = entry->rssi[2];
++ data.rssi[3] = entry->rssi[3];
++ spin_unlock_bh(&phy->amnt_lock);
++
++ data.idx = amnt_idx;
++ data.last_seen = last_seen;
++
++ nla_put(skb, (*attrtype)++, sizeof(struct mt7996_amnt_data), &data);
++
++ return 1;
++}
++
++static int
++mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ struct sk_buff *skb, const void *data, int data_len,
++ unsigned long *storage)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
++ void *a, *b;
++ int err = 0, attrtype = 0, i, len = 0;
++ u8 amnt_idx;
++
++ if (*storage == 1)
++ return -ENOENT;
++ *storage = 1;
++
++ err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
++ amnt_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
++ return -EINVAL;
++
++ err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_DUMP_MAX,
++ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP],
++ amnt_dump_policy, NULL);
++ if (err)
++ return err;
++
++ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX])
++ return -EINVAL;
++
++ amnt_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]);
++
++ a = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP);
++ b = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_DUMP_RESULT);
++
++ if (amnt_idx != 0xff) {
++ len += mt7996_amnt_dump(phy, skb, amnt_idx, &attrtype);
++ } else {
++ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
++ len += mt7996_amnt_dump(phy, skb, i, &attrtype);
++ }
++
++ nla_nest_end(skb, b);
++
++ nla_put_u8(skb, MTK_VENDOR_ATTR_AMNT_DUMP_LEN, len);
++
++ nla_nest_end(skb, a);
++
++ return len + 1;
++}
++
++static int
++mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ struct sk_buff *skb, const void *data, int data_len,
++ unsigned long *storage)
++{
++ struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
++ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
++ int len = 0;
++
++ if (*storage == 1)
++ return -ENOENT;
++ *storage = 1;
++
++ if (nla_put_u64_64bit(skb, MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++ ~bss_conf->used_color_bitmap, NL80211_ATTR_PAD))
++ return -ENOMEM;
++ len += 1;
++
++ return len;
++}
++
++static int mt7996_vendor_edcca_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++ const void *data, int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
++ int err;
++ u8 edcca_mode;
++ u8 edcca_value[EDCCA_MAX_BW_NUM];
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
++ edcca_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
++ return -EINVAL;
++
++ edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
++ if (edcca_mode == EDCCA_CTRL_SET_EN) {
++ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL])
++ return -EINVAL;
++
++ edcca_value[0] =
++ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
++
++ err = mt7996_mcu_edcca_enable(phy, !!edcca_value[0]);
++ if (err)
++ return err;
++ } else if (edcca_mode == EDCCA_CTRL_SET_THRES) {
++ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] ||
++ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] ||
++ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] ||
++ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]) {
++ return -EINVAL;
++ }
++ edcca_value[EDCCA_BW_20] =
++ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
++ edcca_value[EDCCA_BW_40] =
++ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL]);
++ edcca_value[EDCCA_BW_80] =
++ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL]);
++ edcca_value[EDCCA_BW_160] =
++ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]);
++
++ err = mt7996_mcu_edcca_threshold_ctrl(phy, edcca_value, true);
++
++ if (err)
++ return err;
++ } else {
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++
++static int
++mt7996_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ struct sk_buff *skb, const void *data, int data_len,
++ unsigned long *storage)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
++ int err;
++ u8 edcca_mode;
++ u8 value[EDCCA_MAX_BW_NUM];
++
++ if (*storage == 1)
++ return -ENOENT;
++ *storage = 1;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
++ edcca_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
++ return -EINVAL;
++
++ edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
++
++ if (edcca_mode != EDCCA_CTRL_GET_THRES)
++ return -EINVAL;
++
++ err = mt7996_mcu_edcca_threshold_ctrl(phy, value, false);
++
++ if (err)
++ return err;
++
++ if (nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL, value[EDCCA_BW_20]) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL, value[EDCCA_BW_40]) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL, value[EDCCA_BW_80]) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL, value[EDCCA_BW_160]))
++ return -ENOMEM;
++
++ return EDCCA_MAX_BW_NUM;
++}
++
++static int mt7996_vendor_3wire_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++ const void *data, int data_len)
++{
++#define UNI_3WIRE_EXT_EN 0
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL];
++ struct {
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++ u8 three_wire_mode;
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_3WIRE_EXT_EN),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ };
++ int err;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_3WIRE_CTRL_MAX, data, data_len,
++ three_wire_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (!tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE])
++ return -EINVAL;
++
++ req.three_wire_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE]);
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PTA_3WIRE_CTRL), &req,
++ sizeof(req), false);
++}
++
++static int mt7996_vendor_ibf_ctrl(struct wiphy *wiphy,
++ struct wireless_dev *wdev,
++ const void *data,
++ int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_dev *dev = phy->dev;
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_IBF_CTRL];
++ int err;
++ u8 val;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_IBF_CTRL_MAX, data, data_len,
++ ibf_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]) {
++ val = nla_get_u8(tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]);
++
++ dev->ibf = !!val;
++
++ err = mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
++ if (err)
++ return err;
++ }
++ return 0;
++}
++
++static int
++mt7996_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ struct sk_buff *skb, const void *data, int data_len,
++ unsigned long *storage)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_dev *dev = phy->dev;
++
++ if (*storage == 1)
++ return -ENOENT;
++ *storage = 1;
++
++ if (nla_put_u8(skb, MTK_VENDOR_ATTR_IBF_DUMP_ENABLE, dev->ibf))
++ return -ENOMEM;
++
++ return 1;
++}
++
++static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++ const void *data, int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_PP_CTRL];
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_phy *phy;
++ struct mt76_phy *mphy;
++ struct cfg80211_chan_def *chandef;
++ int err;
++ u8 val8, band_idx = 0;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX, data, data_len,
++ pp_ctrl_policy, NULL);
++
++ if (tb[MTK_VENDOR_ATTR_PP_BAND_IDX]) {
++ band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_BAND_IDX]);
++ }
++
++ if (!mt7996_band_valid(dev, band_idx))
++ goto error;
++
++ mphy = dev->mt76.phys[band_idx];
++ if (!mphy)
++ goto error;
++
++ phy = (struct mt7996_phy *)mphy->priv;
++ if (!phy)
++ goto error;
++
++ chandef = &phy->chanctx->chandef;
++ if (!chandef)
++ goto error;
++
++ if (chandef->chan->band == NL80211_BAND_2GHZ)
++ return 0;
++
++ if (tb[MTK_VENDOR_ATTR_PP_MODE]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_MODE]);
++ switch (val8) {
++ case PP_DISABLE:
++ case PP_FW_MODE:
++ err = mt7996_mcu_set_pp_en(phy, val8, 0);
++ break;
++ case PP_USR_MODE:
++ /* handled by add_chanctx */
++ err = 0;
++ break;
++ default:
++ err = -EINVAL;
++ }
++ }
++
++ return err;
++error:
++ dev_err(dev->mt76.dev, "Invalid band idx: %d\n", band_idx);
++ return -EINVAL;
++}
++
++static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_MU_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_mu_ctrl,
++ .dumpit = mt7996_vendor_mu_ctrl_dump,
++ .policy = mu_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
++ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7966_vendor_amnt_ctrl,
++ .dumpit = mt7966_vendor_amnt_ctrl_dump,
++ .policy = amnt_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .dumpit = mt7996_vendor_bss_color_ctrl_dump,
++ .policy = bss_color_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
++ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_edcca_ctrl,
++ .dumpit = mt7996_vendor_edcca_ctrl_dump,
++ .policy = edcca_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
++ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_3wire_ctrl,
++ .policy = three_wire_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_3WIRE_CTRL_MAX,
++ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_ibf_ctrl,
++ .dumpit = mt7996_vendor_ibf_ctrl_dump,
++ .policy = ibf_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_IBF_CTRL_MAX,
++ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_PP_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_pp_ctrl,
++ .policy = pp_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
++ },
++};
++
++void mt7996_vendor_register(struct mt7996_phy *phy)
++{
++ phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
++ phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
++
++ spin_lock_init(&phy->amnt_lock);
++}
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+new file mode 100644
+index 000000000..8aaa18eec
+--- /dev/null
++++ b/mt7996/vendor.h
+@@ -0,0 +1,153 @@
++#ifndef __MT7996_VENDOR_H
++#define __MT7996_VENDOR_H
++
++#define MTK_NL80211_VENDOR_ID 0x0ce7
++
++enum mtk_nl80211_vendor_subcmds {
++ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
++ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
++ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++ MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++ MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_dump {
++ MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
++
++ MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
++};
++
++enum mtk_vendor_attr_3wire_ctrl {
++ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
++ MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
++};
++
++enum mtk_vendor_attr_mu_ctrl {
++ MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
++ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
++ MTK_VENDOR_ATTR_MU_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_ctrl {
++ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_CTRL_SET,
++ MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
++ MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_set {
++ MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_SET_INDEX,
++ MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_SET,
++ MTK_VENDOR_ATTR_AMNT_SET_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
++};
++
++enum mtk_vendor_attr_mnt_dump {
++ MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
++ MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
++ MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++ MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
++};
++
++enum mtk_vendor_attr_bss_color_ctrl {
++ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
++ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_ctrl {
++ MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
++ MTK_VENDOR_ATTR_IBF_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_dump {
++ MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
++ MTK_VENDOR_ATTR_IBF_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
++};
++
++enum mtk_vendor_attr_pp_ctrl {
++ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_PP_MODE,
++ MTK_VENDOR_ATTR_PP_BAND_IDX,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
++ MTK_VENDOR_ATTR_PP_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
++};
++
++#endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
new file mode 100644
index 0000000..87e3e08
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
@@ -0,0 +1,171 @@
+From 0b8743892c4e39e7ba7f2c248ee8929f6c599e76 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 19 May 2023 14:56:07 +0800
+Subject: [PATCH 032/116] mtk: wifi: mt76: mt7996: add debugfs for fw coredump.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mt7996/debugfs.c | 19 +++++++++++++++++--
+ mt7996/mac.c | 28 +++++++++++++++++++++++++---
+ mt7996/mcu.h | 4 ++++
+ mt7996/mt7996.h | 10 ++++++++++
+ 4 files changed, 56 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 344c759c0..3074202bb 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -84,6 +84,8 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
+ * 7: trigger & enable system error L4 mdp recovery.
+ * 8: trigger & enable system error full recovery.
+ * 9: trigger firmware crash.
++ * 10: trigger grab wa firmware coredump.
++ * 11: trigger grab wm firmware coredump.
+ */
+ case UNI_CMD_SER_QUERY:
+ ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band);
+@@ -105,15 +107,25 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
+ /* enable full chip reset */
+ case UNI_CMD_SER_SET_RECOVER_FULL:
+ mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
+- dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
++ dev->recovery.state |= MT_MCU_CMD_WM_WDT;
+ mt7996_reset(dev);
+ break;
+
+ /* WARNING: trigger firmware crash */
+ case UNI_CMD_SER_SET_SYSTEM_ASSERT:
++ // trigger wm assert exception
+ ret = mt7996_mcu_trigger_assert(dev);
+ if (ret)
+ return ret;
++ // trigger wa assert exception
++ mt76_wr(dev, 0x89098108, 0x20);
++ mt76_wr(dev, 0x89098118, 0x20);
++ break;
++ case UNI_CMD_SER_FW_COREDUMP_WA:
++ mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WA);
++ break;
++ case UNI_CMD_SER_FW_COREDUMP_WM:
++ mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WM);
+ break;
+ default:
+ break;
+@@ -160,7 +172,10 @@ mt7996_sys_recovery_get(struct file *file, char __user *user_buf,
+ "8: trigger system error full recovery\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "9: trigger firmware crash\n");
+-
++ desc += scnprintf(buff + desc, bufsz - desc,
++ "10: trigger grab wa firmware coredump\n");
++ desc += scnprintf(buff + desc, bufsz - desc,
++ "11: trigger grab wm firmware coredump\n");
+ /* SER statistics */
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "\nlet's dump firmware SER statistics...\n");
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index d55e5a761..1c1b3eb51 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2080,15 +2080,36 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ struct mt7996_dev *dev;
+
+ dev = container_of(work, struct mt7996_dev, dump_work);
+- if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
++ if (dev->dump_state == MT7996_COREDUMP_MANUAL_WA ||
++ READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
+ mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
+
+- if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
++ if (dev->dump_state == MT7996_COREDUMP_MANUAL_WM ||
++ READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
+ mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
+
+- queue_work(dev->mt76.wq, &dev->reset_work);
++ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK)
++ queue_work(dev->mt76.wq, &dev->reset_work);
++
++ dev->dump_state = MT7996_COREDUMP_IDLE;
+ }
+
++void mt7996_coredump(struct mt7996_dev *dev, u8 state)
++{
++ if (state == MT7996_COREDUMP_IDLE ||
++ state >= __MT7996_COREDUMP_TYPE_MAX)
++ return;
++
++ if (dev->dump_state != MT7996_COREDUMP_IDLE)
++ return;
++
++ dev->dump_state = state;
++ dev_info(dev->mt76.dev, "%s attempting grab coredump\n",
++ wiphy_name(dev->mt76.hw->wiphy));
++
++ queue_work(dev->mt76.wq, &dev->dump_work);
++ }
++
+ void mt7996_reset(struct mt7996_dev *dev)
+ {
+ if (!dev->recovery.hw_init_done)
+@@ -2106,6 +2127,7 @@ void mt7996_reset(struct mt7996_dev *dev)
+
+ mt7996_irq_disable(dev, MT_INT_MCU_CMD);
+ queue_work(dev->mt76.wq, &dev->dump_work);
++ mt7996_coredump(dev, MT7996_COREDUMP_AUTO);
+ return;
+ }
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 2e845e920..cb7260fb3 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -958,7 +958,11 @@ enum {
+ UNI_CMD_SER_SET_RECOVER_L3_BF,
+ UNI_CMD_SER_SET_RECOVER_L4_MDP,
+ UNI_CMD_SER_SET_RECOVER_FULL,
++ /* fw assert */
+ UNI_CMD_SER_SET_SYSTEM_ASSERT,
++ /* coredump */
++ UNI_CMD_SER_FW_COREDUMP_WA,
++ UNI_CMD_SER_FW_COREDUMP_WM,
+ /* action */
+ UNI_CMD_SER_ENABLE = 1,
+ UNI_CMD_SER_SET,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 402327d92..623b782c9 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -138,6 +138,14 @@ enum mt7996_ram_type {
+ __MT7996_RAM_TYPE_MAX,
+ };
+
++enum mt7996_coredump_state {
++ MT7996_COREDUMP_IDLE = 0,
++ MT7996_COREDUMP_MANUAL_WA,
++ MT7996_COREDUMP_MANUAL_WM,
++ MT7996_COREDUMP_AUTO,
++ __MT7996_COREDUMP_TYPE_MAX,
++};
++
+ enum mt7996_txq_id {
+ MT7996_TXQ_FWDL = 16,
+ MT7996_TXQ_MCU_WM,
+@@ -393,6 +401,7 @@ struct mt7996_dev {
+
+ /* protects coredump data */
+ struct mutex dump_mutex;
++ u8 dump_state;
+ #ifdef CONFIG_DEV_COREDUMP
+ struct {
+ struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
+@@ -580,6 +589,7 @@ void mt7996_init_txpower(struct mt7996_phy *phy);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
+ int mt7996_get_chip_sku(struct mt7996_dev *dev);
+ void mt7996_reset(struct mt7996_dev *dev);
++void mt7996_coredump(struct mt7996_dev *dev, u8 state);
+ int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
+ int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch
new file mode 100644
index 0000000..d0aa9bb
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch
@@ -0,0 +1,164 @@
+From 7582fc5bf2165b744eb41631f3892969ca6b510c Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Mon, 25 Dec 2023 15:17:49 +0800
+Subject: [PATCH 033/116] mtk: wifi: mt76: mt7996: Add mt7992 coredump support
+
+1. Add mt7992 coredump support
+2. fixed if new ic have not support coredump, it may cause crash when remove module
+
+CR-Id: WCNCR00259516
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+Change-Id: I2ae5425aac6be8ff69a2c411e796be308b558b6b
+---
+ mt7996/coredump.c | 80 ++++++++++++++++++++++++++++++++++++++---------
+ mt7996/mt7996.h | 1 +
+ 2 files changed, 67 insertions(+), 14 deletions(-)
+
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index a7f91b56d..d09bcd4bd 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -67,6 +67,44 @@ static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
+ },
+ };
+
++static const struct mt7996_mem_region mt7992_wm_mem_regions[] = {
++ {
++ .start = 0x00800000,
++ .len = 0x0004bfff,
++ .name = "ULM0",
++ },
++ {
++ .start = 0x00900000,
++ .len = 0x00035fff,
++ .name = "ULM1",
++ },
++ {
++ .start = 0x02200000,
++ .len = 0x0003ffff,
++ .name = "ULM2",
++ },
++ {
++ .start = 0x00400000,
++ .len = 0x00027fff,
++ .name = "SRAM",
++ },
++ {
++ .start = 0xe0000000,
++ .len = 0x0015ffff,
++ .name = "CRAM0",
++ },
++ {
++ .start = 0xe0160000,
++ .len = 0x00c7fff,
++ .name = "CRAM1",
++ },
++ {
++ .start = 0x7c050000,
++ .len = 0x00007fff,
++ .name = "CONN_INFRA",
++ },
++};
++
+ const struct mt7996_mem_region*
+ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+@@ -80,6 +118,14 @@ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+
+ *num = ARRAY_SIZE(mt7996_wm_mem_regions);
+ return &mt7996_wm_mem_regions[0];
++ case 0x7992:
++ if (type == MT7996_RAM_TYPE_WA) {
++ /* mt7992 wa memory regions is the same as mt7996 */
++ *num = ARRAY_SIZE(mt7996_wa_mem_regions);
++ return &mt7996_wa_mem_regions[0];
++ }
++ *num = ARRAY_SIZE(mt7992_wm_mem_regions);
++ return &mt7992_wm_mem_regions[0];
+ default:
+ return NULL;
+ }
+@@ -115,7 +161,7 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+
+ lockdep_assert_held(&dev->dump_mutex);
+
+- if (!coredump_memdump)
++ if (!coredump_memdump || !crash_data->supported)
+ return NULL;
+
+ guid_gen(&crash_data->guid);
+@@ -289,40 +335,46 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
+ for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
+ crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
+ if (!crash_data)
+- return -ENOMEM;
++ goto nomem;
+
+ dev->coredump.crash_data[i] = crash_data;
++ crash_data->supported = false;
+
+ if (coredump_memdump) {
+ crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
+ if (!crash_data->memdump_buf_len)
+ /* no memory content */
+- return 0;
++ continue;
+
+ crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
+- if (!crash_data->memdump_buf) {
+- vfree(crash_data);
+- return -ENOMEM;
+- }
++ if (!crash_data->memdump_buf)
++ goto nomem;
++
++ crash_data->supported = true;
+ }
+ }
+
+ return 0;
++nomem:
++ mt7996_coredump_unregister(dev);
++ return -ENOMEM;
+ }
+
+ void mt7996_coredump_unregister(struct mt7996_dev *dev)
+ {
+ int i;
++ struct mt7996_crash_data *crash_data;
+
+ for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
+- if (dev->coredump.crash_data[i]->memdump_buf) {
+- vfree(dev->coredump.crash_data[i]->memdump_buf);
+- dev->coredump.crash_data[i]->memdump_buf = NULL;
+- dev->coredump.crash_data[i]->memdump_buf_len = 0;
+- }
++ crash_data = dev->coredump.crash_data[i];
++
++ if (!crash_data)
++ continue;
++
++ if (crash_data->memdump_buf)
++ vfree(crash_data->memdump_buf);
+
+- vfree(dev->coredump.crash_data[i]);
+- dev->coredump.crash_data[i] = NULL;
++ vfree(crash_data);
+ }
+ }
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 623b782c9..97425a025 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -229,6 +229,7 @@ struct mt7996_vif {
+ struct mt7996_crash_data {
+ guid_t guid;
+ struct timespec64 timestamp;
++ bool supported;
+
+ u8 *memdump_buf;
+ size_t memdump_buf_len;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch
new file mode 100644
index 0000000..8688630
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch
@@ -0,0 +1,44 @@
+From 4982c35a7957ef3a6b9ec0985544a4245b5d5f14 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 6 Jun 2023 16:57:10 +0800
+Subject: [PATCH 034/116] mtk: wifi: mt76: mt7996: add support for runtime set
+ in-band discovery
+
+with this patch, AP can runtime set inband discovery via hostapd_cli
+
+Usage:
+Enable FILS: hostapd_cli -i [interface] inband_discovery 2 20
+Enable UBPR: hostapd_cli -i [interface] inband_discovery 1 20
+Disable inband discovery: hostapd_cli -i [interface] inband_discovery 0 0
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ mt7996/mcu.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 91c2dab1a..94c5da5e8 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2609,8 +2609,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ if (IS_ERR(rskb))
+ return PTR_ERR(rskb);
+
+- if (changed & BSS_CHANGED_FILS_DISCOVERY &&
+- vif->bss_conf.fils_discovery.max_interval) {
++ if (changed & BSS_CHANGED_FILS_DISCOVERY) {
+ interval = vif->bss_conf.fils_discovery.max_interval;
+ skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
+ } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
+@@ -2645,7 +2644,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
+ discov->tx_interval = interval;
+ discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
+- discov->enable = true;
++ discov->enable = !!(interval);
+ discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
+
+ buf = (u8 *)tlv + sizeof(*discov);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch
new file mode 100644
index 0000000..4b4c28f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch
@@ -0,0 +1,398 @@
+From e4af7a0b0c39e05e0bded3de7b63831cd9b5dd97 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 10 Jul 2023 11:47:29 +0800
+Subject: [PATCH 035/116] mtk: wifi: mt76: mt7996: add support spatial reuse
+ debug commands
+
+This commit adds the following debug commands in debugfs:
+1. sr_enable: enable/disable spatial reuse feature. Default is on.
+2. sr_enhanced_enable: enable/disable enhanced spatial reuse feature.
+Default is on. This feature is mtk proprietary feature.
+3. sr_stats: Check the Spatial reuse tx statistics.
+4. sr_scene_cond: Check the result of mtk scene detection algorithm. Mtk
+scene detection algorithm in firmware may decide whether current
+environment can SR Tx or not.
+
+To learn more details of these commands, please check:
+https://wiki.mediatek.inc/display/APKB/mt76+Phy+feature+debug+Cheetsheet#mt76PhyfeaturedebugCheetsheet-SpatialReuse
+
+Change-Id: I5bf83013ea2788dfc93caaaef08486a4f97a3649
+---
+ mt76_connac_mcu.h | 1 +
+ mt7996/main.c | 6 +++
+ mt7996/mcu.c | 8 ++++
+ mt7996/mt7996.h | 6 +++
+ mt7996/mtk_debugfs.c | 82 ++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c | 111 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h | 56 ++++++++++++++++++++++
+ 7 files changed, 270 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 2a6091939..864a802d7 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1040,6 +1040,7 @@ enum {
+ MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c,
+ MCU_UNI_EVENT_SCAN_DONE = 0x0e,
+ MCU_UNI_EVENT_RDD_REPORT = 0x11,
++ MCU_UNI_EVENT_SR = 0x25,
+ MCU_UNI_EVENT_ROC = 0x27,
+ MCU_UNI_EVENT_TX_DONE = 0x2d,
+ MCU_UNI_EVENT_BF = 0x33,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 6d1f61cd0..f8ac51707 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -6,6 +6,9 @@
+ #include "mt7996.h"
+ #include "mcu.h"
+ #include "mac.h"
++#ifdef CONFIG_MTK_DEBUG
++#include "mtk_mcu.h"
++#endif
+
+ static bool mt7996_dev_running(struct mt7996_dev *dev)
+ {
+@@ -86,6 +89,9 @@ int mt7996_run(struct ieee80211_hw *hw)
+ goto out;
+
+ #ifdef CONFIG_MTK_DEBUG
++ phy->sr_enable = true;
++ phy->enhanced_sr_enable = true;
++
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 94c5da5e8..0be5a880f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -717,6 +717,14 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ case MCU_UNI_EVENT_WED_RRO:
+ mt7996_mcu_wed_rro_event(dev, skb);
+ break;
++#ifdef CONFIG_MTK_DEBUG
++ case MCU_UNI_EVENT_SR:
++ mt7996_mcu_rx_sr_event(dev, skb);
++ break;
++#endif
++ case MCU_UNI_EVENT_THERMAL:
++ mt7996_mcu_rx_thermal_notify(dev, skb);
++ break;
+ #ifdef CONFIG_NL80211_TESTMODE
+ case MCU_UNI_EVENT_TESTMODE_CTRL:
+ mt7996_tm_rf_test_event(dev, skb);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 97425a025..c06aae960 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -357,6 +357,10 @@ struct mt7996_phy {
+ spinlock_t amnt_lock;
+ struct mt7996_air_monitor_ctrl amnt_ctrl;
+ #endif
++#ifdef CONFIG_MTK_DEBUG
++ bool sr_enable:1;
++ bool enhanced_sr_enable:1;
++#endif
+ };
+
+ struct mt7996_dev {
+@@ -807,6 +811,8 @@ enum edcca_bw_id {
+ #ifdef CONFIG_MTK_DEBUG
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
++int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
++void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 50b2df1ec..9dc6ea778 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2836,6 +2836,83 @@ static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
+ return 0;
+ }
+
++static int
++mt7996_sr_enable_get(void *data, u64 *val)
++{
++ struct mt7996_phy *phy = data;
++
++ *val = phy->sr_enable;
++
++ return 0;
++}
++
++static int
++mt7996_sr_enable_set(void *data, u64 val)
++{
++ struct mt7996_phy *phy = data;
++ int ret;
++
++ if (!!val == phy->sr_enable)
++ return 0;
++
++ ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, val, true);
++ if (ret)
++ return ret;
++
++ return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, 0, false);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enable, mt7996_sr_enable_get,
++ mt7996_sr_enable_set, "%lld\n");
++static int
++mt7996_sr_enhanced_enable_get(void *data, u64 *val)
++{
++ struct mt7996_phy *phy = data;
++
++ *val = phy->enhanced_sr_enable;
++
++ return 0;
++}
++
++static int
++mt7996_sr_enhanced_enable_set(void *data, u64 val)
++{
++ struct mt7996_phy *phy = data;
++ int ret;
++
++ if (!!val == phy->enhanced_sr_enable)
++ return 0;
++
++ ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, val, true);
++ if (ret)
++ return ret;
++
++ return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, 0, false);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enhanced_enable, mt7996_sr_enhanced_enable_get,
++ mt7996_sr_enhanced_enable_set, "%lld\n");
++
++static int
++mt7996_sr_stats_show(struct seq_file *file, void *data)
++{
++ struct mt7996_phy *phy = file->private;
++
++ mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_IND, 0, false);
++
++ return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_sr_stats);
++
++static int
++mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
++{
++ struct mt7996_phy *phy = file->private;
++
++ mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_SW_SD, 0, false);
++
++ return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -2915,6 +2992,11 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
+ debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+
++ debugfs_create_file("sr_enable", 0600, dir, phy, &fops_sr_enable);
++ debugfs_create_file("sr_enhanced_enable", 0600, dir, phy, &fops_sr_enhanced_enable);
++ debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
++ debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
++
+ return 0;
+ }
+
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 5c54d02c4..dbdf8d809 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -146,4 +146,115 @@ int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
+ return 0;
+ }
+
++int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set)
++{
++ struct {
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++
++ __le32 val;
++
++ } __packed req = {
++ .band_idx = phy->mt76->band_idx,
++
++ .tag = cpu_to_le16(action),
++ .len = cpu_to_le16(sizeof(req) - 4),
++
++ .val = cpu_to_le32((u32) val),
++ };
++
++ if (set)
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SR), &req,
++ sizeof(req), false);
++ else
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD_QUERY(SR), &req,
++ sizeof(req), false);
++}
++
++void mt7996_mcu_rx_sr_swsd(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++#define SR_SCENE_DETECTION_TIMER_PERIOD_MS 500
++ struct mt7996_mcu_sr_swsd_event *event;
++ static const char * const rules[] = {"1 - NO CONNECTED", "2 - NO CONGESTION",
++ "3 - NO INTERFERENCE", "4 - SR ON"};
++ u8 idx;
++
++ event = (struct mt7996_mcu_sr_swsd_event *)skb->data;
++ idx = event->basic.band_idx;
++
++ dev_info(dev->mt76.dev, "Band index = %u\n", le16_to_cpu(event->basic.band_idx));
++ dev_info(dev->mt76.dev, "Hit Rule = %s\n", rules[event->tlv[idx].rule]);
++ dev_info(dev->mt76.dev, "Timer Period = %d(us)\n"
++ "Congestion Ratio = %d.%1d%%\n",
++ SR_SCENE_DETECTION_TIMER_PERIOD_MS * 1000,
++ le32_to_cpu(event->tlv[idx].total_airtime_ratio) / 10,
++ le32_to_cpu(event->tlv[idx].total_airtime_ratio) % 10);
++ dev_info(dev->mt76.dev,
++ "Total Airtime = %d(us)\n"
++ "ChBusy = %d\n"
++ "SrTx = %d\n"
++ "OBSS = %d\n"
++ "MyTx = %d\n"
++ "MyRx = %d\n"
++ "Interference Ratio = %d.%1d%%\n",
++ le32_to_cpu(event->tlv[idx].total_airtime),
++ le32_to_cpu(event->tlv[idx].channel_busy_time),
++ le32_to_cpu(event->tlv[idx].sr_tx_airtime),
++ le32_to_cpu(event->tlv[idx].obss_airtime),
++ le32_to_cpu(event->tlv[idx].my_tx_airtime),
++ le32_to_cpu(event->tlv[idx].my_rx_airtime),
++ le32_to_cpu(event->tlv[idx].obss_airtime_ratio) / 10,
++ le32_to_cpu(event->tlv[idx].obss_airtime_ratio) % 10);
++}
++
++void mt7996_mcu_rx_sr_hw_indicator(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++ struct mt7996_mcu_sr_hw_ind_event *event;
++
++ event = (struct mt7996_mcu_sr_hw_ind_event *)skb->data;
++
++ dev_info(dev->mt76.dev, "Inter PPDU Count = %u\n",
++ le16_to_cpu(event->inter_bss_ppdu_cnt));
++ dev_info(dev->mt76.dev, "SR Valid Count = %u\n",
++ le16_to_cpu(event->non_srg_valid_cnt));
++ dev_info(dev->mt76.dev, "SR Tx Count = %u\n",
++ le32_to_cpu(event->sr_ampdu_mpdu_cnt));
++ dev_info(dev->mt76.dev, "SR Tx Acked Count = %u\n",
++ le32_to_cpu(event->sr_ampdu_mpdu_acked_cnt));
++}
++
++void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++ struct mt76_phy *mphy = &dev->mt76.phy;
++ struct mt7996_phy *phy;
++ struct mt7996_mcu_sr_common_event *event;
++
++ event = (struct mt7996_mcu_sr_common_event *)skb->data;
++ mphy = dev->mt76.phys[event->basic.band_idx];
++ if (!mphy)
++ return;
++
++ phy = (struct mt7996_phy *)mphy->priv;
++
++ switch (le16_to_cpu(event->basic.tag)) {
++ case UNI_EVENT_SR_CFG_SR_ENABLE:
++ phy->sr_enable = le32_to_cpu(event->value) ? true : false;
++ break;
++ case UNI_EVENT_SR_HW_ESR_ENABLE:
++ phy->enhanced_sr_enable = le32_to_cpu(event->value) ? true : false;
++ break;
++ case UNI_EVENT_SR_SW_SD:
++ mt7996_mcu_rx_sr_swsd(dev, skb);
++ break;
++ case UNI_EVENT_SR_HW_IND:
++ mt7996_mcu_rx_sr_hw_indicator(dev, skb);
++ break;
++ default:
++ dev_info(dev->mt76.dev, "Unknown SR event tag %d\n",
++ le16_to_cpu(event->basic.tag));
++ }
++}
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 36a58ad63..098e63aef 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -121,6 +121,62 @@ enum {
+ EDCCA_JAPAN = 3
+ };
+
++enum {
++ UNI_EVENT_SR_CFG_SR_ENABLE = 0x1,
++ UNI_EVENT_SR_SW_SD = 0x83,
++ UNI_EVENT_SR_HW_IND = 0xC9,
++ UNI_EVENT_SR_HW_ESR_ENABLE = 0xD8,
++};
++enum {
++ UNI_CMD_SR_CFG_SR_ENABLE = 0x1,
++ UNI_CMD_SR_SW_SD = 0x84,
++ UNI_CMD_SR_HW_IND = 0xCB,
++ UNI_CMD_SR_HW_ENHANCE_SR_ENABLE = 0xDA,
++};
++
++struct mt7996_mcu_sr_basic_event {
++ struct mt7996_mcu_rxd rxd;
++
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++};
++
++struct sr_sd_tlv {
++ u8 _rsv[16];
++ __le32 sr_tx_airtime;
++ __le32 obss_airtime;
++ __le32 my_tx_airtime;
++ __le32 my_rx_airtime;
++ __le32 channel_busy_time;
++ __le32 total_airtime;
++ __le32 total_airtime_ratio;
++ __le32 obss_airtime_ratio;
++ u8 rule;
++ u8 _rsv2[59];
++} __packed;
++
++struct mt7996_mcu_sr_swsd_event {
++ struct mt7996_mcu_sr_basic_event basic;
++ struct sr_sd_tlv tlv[3];
++} __packed;
++
++struct mt7996_mcu_sr_common_event {
++ struct mt7996_mcu_sr_basic_event basic;
++ __le32 value;
++};
++
++struct mt7996_mcu_sr_hw_ind_event {
++ struct mt7996_mcu_sr_basic_event basic;
++ __le16 non_srg_valid_cnt;
++ u8 _rsv[4];
++ __le16 inter_bss_ppdu_cnt;
++ u8 _rsv2[4];
++ __le32 sr_ampdu_mpdu_cnt;
++ __le32 sr_ampdu_mpdu_acked_cnt;
++};
+ #endif
+
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch
new file mode 100644
index 0000000..5ea0ac5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch
@@ -0,0 +1,25 @@
+From 6888c8da2d1620dcd3a85834b07faef4a90c64b2 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 1 Aug 2023 16:02:28 +0800
+Subject: [PATCH 036/116] mtk: wifi: mt76: mt7996: Establish BA in VO queue
+
+---
+ mt7996/mac.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 1c1b3eb51..4e52aa1bf 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1032,8 +1032,6 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ return;
+
+ tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+- if (tid >= 6) /* skip VO queue */
+- return;
+
+ if (is_8023) {
+ fc = IEEE80211_FTYPE_DATA |
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch
new file mode 100644
index 0000000..e762d07
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch
@@ -0,0 +1,47 @@
+From 7bf50fae60cf0682279a316814c17c58425322c5 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Sat, 12 Aug 2023 04:17:22 +0800
+Subject: [PATCH 037/116] mtk: wifi: mt76: mt7996: report tx and rx byte to
+ tpt_led
+
+---
+ mt7996/mcu.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0be5a880f..d951c733e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -525,6 +525,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ u8 ac;
+ u16 wlan_idx;
+ struct mt76_wcid *wcid;
++ struct mt76_phy *mphy;
++ u32 tx_bytes, rx_bytes;
+
+ switch (le16_to_cpu(res->tag)) {
+ case UNI_ALL_STA_TXRX_RATE:
+@@ -544,11 +546,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ if (!wcid)
+ break;
+
++ mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+- wcid->stats.tx_bytes +=
+- le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
+- wcid->stats.rx_bytes +=
+- le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
++ tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
++ rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
++
++ wcid->stats.tx_bytes += tx_bytes;
++ wcid->stats.rx_bytes += rx_bytes;
++
++ ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
++ ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
+ }
+ break;
+ case UNI_ALL_STA_TXRX_MSDU_COUNT:
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch
new file mode 100644
index 0000000..2d8b5d1
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch
@@ -0,0 +1,72 @@
+From 389b458b00f2369eeb43eb8a237836b7404b6657 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 21 Sep 2023 00:52:46 +0800
+Subject: [PATCH 038/116] mtk: wifi: mt76: mt7996: support dup wtbl
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I14ba41ace8341c23c1cfb6e9c4fbb2d5e93a5714
+---
+ mt7996/init.c | 1 +
+ mt7996/mt7996.h | 1 +
+ mt7996/mtk_mcu.c | 23 +++++++++++++++++++++++
+ 3 files changed, 25 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 5cc2e6fbf..d4b0a72eb 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -686,6 +686,7 @@ static void mt7996_init_work(struct work_struct *work)
+ mt7996_mcu_set_eeprom(dev);
+ mt7996_mac_init(dev);
+ mt7996_txbf_init(dev);
++ mt7996_mcu_set_dup_wtbl(dev);
+ }
+
+ void mt7996_wfsys_reset(struct mt7996_dev *dev)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c06aae960..dbc4aa634 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -813,6 +813,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
+ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
++int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index dbdf8d809..ea4e5bf28 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -257,4 +257,27 @@ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ le16_to_cpu(event->basic.tag));
+ }
+ }
++
++int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
++{
++#define CHIP_CONFIG_DUP_WTBL 4
++#define DUP_WTBL_NUM 80
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++ __le16 base;
++ __le16 num;
++ u8 _rsv2[4];
++ } __packed req = {
++ .tag = cpu_to_le16(CHIP_CONFIG_DUP_WTBL),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .base = cpu_to_le16(MT7996_WTBL_STA - DUP_WTBL_NUM + 1),
++ .num = cpu_to_le16(DUP_WTBL_NUM),
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
++ sizeof(req), true);
++}
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch
new file mode 100644
index 0000000..918badb
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch
@@ -0,0 +1,214 @@
+From 39e2b93d2071ec41a6e85523df19f5269e9550a3 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Nov 2023 11:10:10 +0800
+Subject: [PATCH 039/116] mtk: wifi: mt76: try more times when send message
+ timeout.
+
+CR-Id: WCNCR00334773
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Change-Id: Ib7c01e6c9f74f68d8404a3d8bada9e5a10c4e232
+---
+ dma.c | 7 ++++--
+ mcu.c | 65 ++++++++++++++++++++++++++++++++++++----------------
+ mt7996/mac.c | 37 ++++++++++--------------------
+ 3 files changed, 62 insertions(+), 47 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 560446395..66c000ef0 100644
+--- a/dma.c
++++ b/dma.c
+@@ -504,9 +504,12 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
+ {
+ struct mt76_queue_buf buf = {};
+ dma_addr_t addr;
++ int ret = -ENOMEM;
+
+- if (test_bit(MT76_MCU_RESET, &dev->phy.state))
++ if (test_bit(MT76_MCU_RESET, &dev->phy.state)) {
++ ret = -EAGAIN;
+ goto error;
++ }
+
+ if (q->queued + 1 >= q->ndesc - 1)
+ goto error;
+@@ -528,7 +531,7 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
+
+ error:
+ dev_kfree_skb(skb);
+- return -ENOMEM;
++ return ret;
+ }
+
+ static int
+diff --git a/mcu.c b/mcu.c
+index fa4b05441..2926f7150 100644
+--- a/mcu.c
++++ b/mcu.c
+@@ -4,6 +4,7 @@
+ */
+
+ #include "mt76.h"
++#include "mt76_connac.h"
+ #include <linux/moduleparam.h>
+
+ struct sk_buff *
+@@ -74,35 +75,59 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
+ int cmd, bool wait_resp,
+ struct sk_buff **ret_skb)
+ {
++#define MT76_MSG_MAX_RETRY_CNT 3
+ unsigned long expires;
+- int ret, seq;
++ int ret, seq, retry_cnt;
++ struct sk_buff *skb_tmp;
++ bool retry = wait_resp && is_mt7996(dev);
+
+ if (ret_skb)
+ *ret_skb = NULL;
+
+ mutex_lock(&dev->mcu.mutex);
+-
+- ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq);
+- if (ret < 0)
+- goto out;
+-
+- if (!wait_resp) {
+- ret = 0;
+- goto out;
++ retry_cnt = retry ? MT76_MSG_MAX_RETRY_CNT : 1;
++ while (retry_cnt) {
++ skb_tmp = mt76_mcu_msg_alloc(dev, skb->data, skb->len);
++ if (!skb_tmp)
++ goto out;
++
++ if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
++ if (test_bit(MT76_MCU_RESET, &dev->phy.state))
++ usleep_range(200000, 500000);
++ dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
++ }
++
++ ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
++ if (ret < 0 && ret != -EAGAIN)
++ goto out;
++
++ if (!wait_resp) {
++ ret = 0;
++ goto out;
++ }
++
++ expires = jiffies + dev->mcu.timeout;
++
++ do {
++ skb_tmp = mt76_mcu_get_response(dev, expires);
++ ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb_tmp, seq);
++ if (ret == -ETIMEDOUT)
++ break;
++
++ if (!ret && ret_skb)
++ *ret_skb = skb_tmp;
++ else
++ dev_kfree_skb(skb_tmp);
++
++ if (ret != -EAGAIN)
++ goto out;
++ } while (ret == -EAGAIN);
++
++ retry_cnt--;
+ }
+
+- expires = jiffies + dev->mcu.timeout;
+-
+- do {
+- skb = mt76_mcu_get_response(dev, expires);
+- ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb, seq);
+- if (!ret && ret_skb)
+- *ret_skb = skb;
+- else
+- dev_kfree_skb(skb);
+- } while (ret == -EAGAIN);
+-
+ out:
++ dev_kfree_skb(skb);
+ mutex_unlock(&dev->mcu.mutex);
+
+ return ret;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 4e52aa1bf..56827c9b3 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1784,13 +1784,24 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
+ phy3 = mt7996_phy3(dev);
+ dev->recovery.hw_full_reset = true;
+
+- wake_up(&dev->mt76.mcu.wait);
+ ieee80211_stop_queues(mt76_hw(dev));
+ if (phy2)
+ ieee80211_stop_queues(phy2->mt76->hw);
+ if (phy3)
+ ieee80211_stop_queues(phy3->mt76->hw);
+
++ set_bit(MT76_RESET, &dev->mphy.state);
++ set_bit(MT76_MCU_RESET, &dev->mphy.state);
++ wake_up(&dev->mt76.mcu.wait);
++ if (phy2) {
++ set_bit(MT76_RESET, &phy2->mt76->state);
++ set_bit(MT76_MCU_RESET, &phy2->mt76->state);
++ }
++ if (phy3) {
++ set_bit(MT76_RESET, &phy3->mt76->state);
++ set_bit(MT76_MCU_RESET, &phy3->mt76->state);
++ }
++
+ cancel_work_sync(&dev->wed_rro.work);
+ cancel_delayed_work_sync(&dev->mphy.mac_work);
+ if (phy2)
+@@ -1893,16 +1904,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+
+- cancel_work_sync(&dev->wed_rro.work);
+- cancel_delayed_work_sync(&dev->mphy.mac_work);
+- if (phy2) {
+- set_bit(MT76_RESET, &phy2->mt76->state);
+- cancel_delayed_work_sync(&phy2->mt76->mac_work);
+- }
+- if (phy3) {
+- set_bit(MT76_RESET, &phy3->mt76->state);
+- cancel_delayed_work_sync(&phy3->mt76->mac_work);
+- }
+ mt76_worker_disable(&dev->mt76.tx_worker);
+ mt76_for_each_q_rx(&dev->mt76, i) {
+ if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+@@ -1913,8 +1914,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ }
+ napi_disable(&dev->mt76.tx_napi);
+
+- mutex_lock(&dev->mt76.mutex);
+-
+ mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
+
+ if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+@@ -1987,20 +1986,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ if (phy3)
+ ieee80211_wake_queues(phy3->mt76->hw);
+
+- mutex_unlock(&dev->mt76.mutex);
+-
+ mt7996_update_beacons(dev);
+
+- ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
+- MT7996_WATCHDOG_TIME);
+- if (phy2)
+- ieee80211_queue_delayed_work(phy2->mt76->hw,
+- &phy2->mt76->mac_work,
+- MT7996_WATCHDOG_TIME);
+- if (phy3)
+- ieee80211_queue_delayed_work(phy3->mt76->hw,
+- &phy3->mt76->mac_work,
+- MT7996_WATCHDOG_TIME);
+ dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.",
+ wiphy_name(dev->mt76.hw->wiphy));
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch
new file mode 100644
index 0000000..1a2df19
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch
@@ -0,0 +1,106 @@
+From 6c5c10622f2dcba5a72b60df9954060ebdde7fe4 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Tue, 21 Nov 2023 09:55:46 +0800
+Subject: [PATCH 040/116] mtk: wifi: mt76: mt7996: add SER overlap handle
+
+CR-ID: WCNCR00355921
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mcu.c | 3 ++-
+ mt7996/mac.c | 11 +++++++++++
+ mt7996/mcu.c | 8 ++++++++
+ mt7996/mt7996.h | 2 ++
+ 4 files changed, 23 insertions(+), 1 deletion(-)
+
+diff --git a/mcu.c b/mcu.c
+index 2926f7150..a7afa2d7c 100644
+--- a/mcu.c
++++ b/mcu.c
+@@ -94,7 +94,8 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
+ if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
+ if (test_bit(MT76_MCU_RESET, &dev->phy.state))
+ usleep_range(200000, 500000);
+- dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
++ dev_err(dev->dev, "send message %08x timeout, try again(%d).\n",
++ cmd, (MT76_MSG_MAX_RETRY_CNT - retry_cnt));
+ }
+
+ ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 56827c9b3..9b5b9951e 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1885,6 +1885,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
+ return;
+
++ dev->recovery.l1_reset_last = dev->recovery.l1_reset;
+ dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
+ wiphy_name(dev->mt76.hw->wiphy));
+
+@@ -1902,6 +1903,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
+
+ set_bit(MT76_RESET, &dev->mphy.state);
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
++ if (phy2)
++ set_bit(MT76_RESET, &phy2->mt76->state);
++ if (phy3)
++ set_bit(MT76_RESET, &phy3->mt76->state);
+ wake_up(&dev->mt76.mcu.wait);
+
+ mt76_worker_disable(&dev->mt76.tx_worker);
+@@ -2097,6 +2102,9 @@ void mt7996_coredump(struct mt7996_dev *dev, u8 state)
+
+ void mt7996_reset(struct mt7996_dev *dev)
+ {
++ dev_info(dev->mt76.dev, "%s SER recovery state: 0x%08x\n",
++ wiphy_name(dev->mt76.hw->wiphy), READ_ONCE(dev->recovery.state));
++
+ if (!dev->recovery.hw_init_done)
+ return;
+
+@@ -2116,6 +2124,9 @@ void mt7996_reset(struct mt7996_dev *dev)
+ return;
+ }
+
++ if ((READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
++ dev->recovery.l1_reset++;
++
+ queue_work(dev->mt76.wq, &dev->reset_work);
+ wake_up(&dev->reset_wait);
+ }
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index d951c733e..fcc2512f3 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -246,6 +246,14 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ u32 val;
+ u8 seq;
+
++ if (dev->recovery.l1_reset_last != dev->recovery.l1_reset) {
++ dev_info(dev->mt76.dev,"\n%s L1 SER recovery overlap, drop message %08x.",
++ wiphy_name(dev->mt76.hw->wiphy), cmd);
++
++ dev_kfree_skb(skb);
++ return -EPERM;
++ }
++
+ mdev->mcu.timeout = 20 * HZ;
+
+ seq = ++dev->mt76.mcu.msg_seq & 0xf;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index dbc4aa634..d40f8bf43 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -397,6 +397,8 @@ struct mt7996_dev {
+ wait_queue_head_t reset_wait;
+ struct {
+ u32 state;
++ u32 l1_reset;
++ u32 l1_reset_last;
+ u32 wa_reset_count;
+ u32 wm_reset_count;
+ bool hw_full_reset:1;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch
new file mode 100644
index 0000000..8e1d41b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch
@@ -0,0 +1,56 @@
+From f2660cb8f6840bd545b093b9dee22c8f06ccb499 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 13 Jul 2023 16:36:36 +0800
+Subject: [PATCH 041/116] mtk: wifi: mt76: mt7996: kite default 1-pcie setting
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/pci.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 040561813..05830c01c 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -11,6 +11,9 @@
+ #include "mac.h"
+ #include "../trace.h"
+
++static bool hif2_enable = false;
++module_param(hif2_enable, bool, 0644);
++
+ static LIST_HEAD(hif_list);
+ static DEFINE_SPINLOCK(hif_lock);
+ static u32 hif_idx;
+@@ -63,6 +66,9 @@ static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
+ {
+ hif_idx++;
+
++ if (!hif2_enable)
++ return NULL;
++
+ if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL) &&
+ !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x799a, NULL))
+ return NULL;
+@@ -77,6 +83,9 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
+ {
+ struct mt7996_hif *hif;
+
++ if (!hif2_enable)
++ return 0;
++
+ hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL);
+ if (!hif)
+ return -ENOMEM;
+@@ -101,6 +110,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ int irq, hif2_irq, ret;
+ struct mt76_dev *mdev;
+
++ hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
++
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch
new file mode 100644
index 0000000..59b7901
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch
@@ -0,0 +1,291 @@
+From c8059abc389412d9031292c2795b2db52c78ddc5 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 28 Apr 2023 10:39:58 +0800
+Subject: [PATCH 042/116] mtk: wifi: mt76: mt7996: add debugfs knob for
+ rx_counters
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ agg-rx.c | 8 ++++++++
+ mac80211.c | 16 ++++++++++++++--
+ mt76.h | 15 +++++++++++++++
+ mt7996/mac.c | 18 +++++++++++++++---
+ mt7996/mtk_debugfs.c | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 94 insertions(+), 5 deletions(-)
+
+diff --git a/agg-rx.c b/agg-rx.c
+index 07c386c7b..37588ac20 100644
+--- a/agg-rx.c
++++ b/agg-rx.c
+@@ -33,10 +33,13 @@ mt76_rx_aggr_release_frames(struct mt76_rx_tid *tid,
+ struct sk_buff_head *frames,
+ u16 head)
+ {
++ struct mt76_phy *phy = mt76_dev_phy(tid->dev, tid->band_idx);
+ int idx;
+
+ while (ieee80211_sn_less(tid->head, head)) {
+ idx = tid->head % tid->size;
++ if (!tid->reorder_buf[idx])
++ phy->rx_stats.rx_agg_miss++;
+ mt76_aggr_release(tid, frames, idx);
+ }
+ }
+@@ -151,6 +154,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+ struct mt76_wcid *wcid = status->wcid;
+ struct ieee80211_sta *sta;
+ struct mt76_rx_tid *tid;
++ struct mt76_phy *phy;
+ bool sn_less;
+ u16 seqno, head, size, idx;
+ u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
+@@ -186,6 +190,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+ head = tid->head;
+ seqno = status->seqno;
+ size = tid->size;
++ phy = mt76_dev_phy(tid->dev, tid->band_idx);
+ sn_less = ieee80211_sn_less(seqno, head);
+
+ if (!tid->started) {
+@@ -197,6 +202,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+
+ if (sn_less) {
+ __skb_unlink(skb, frames);
++ phy->rx_stats.rx_dup_drop++;
+ dev_kfree_skb(skb);
+ goto out;
+ }
+@@ -223,6 +229,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+
+ /* Discard if the current slot is already in use */
+ if (tid->reorder_buf[idx]) {
++ phy->rx_stats.rx_dup_drop++;
+ dev_kfree_skb(skb);
+ goto out;
+ }
+@@ -254,6 +261,7 @@ int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno,
+ tid->head = ssn;
+ tid->size = size;
+ tid->num = tidno;
++ tid->band_idx = wcid->phy_idx;
+ INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work);
+ spin_lock_init(&tid->lock);
+
+diff --git a/mac80211.c b/mac80211.c
+index 1127ef7b5..0590aaf2e 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -783,6 +783,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
+ }
+
+ if (ether_addr_equal(skb->data + offset, rfc1042_header)) {
++ phy->rx_stats.rx_drop++;
+ dev_kfree_skb(skb);
+ return;
+ }
+@@ -1099,10 +1100,16 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+
+ *sta = wcid_to_sta(mstat.wcid);
+ *hw = mt76_phy_hw(dev, mstat.phy_idx);
++
++ if ((mstat.flag & RX_FLAG_8023) || ieee80211_is_data_qos(hdr->frame_control)) {
++ struct mt76_phy *phy = mt76_dev_phy(dev, mstat.phy_idx);
++
++ phy->rx_stats.rx_mac80211++;
++ }
+ }
+
+ static void
+-mt76_check_ccmp_pn(struct sk_buff *skb)
++mt76_check_ccmp_pn(struct mt76_dev *dev, struct sk_buff *skb)
+ {
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct mt76_wcid *wcid = status->wcid;
+@@ -1149,7 +1156,11 @@ skip_hdr_check:
+ ret = memcmp(status->iv, wcid->rx_key_pn[security_idx],
+ sizeof(status->iv));
+ if (ret <= 0) {
++ struct mt76_phy *phy = mt76_dev_phy(dev, status->phy_idx);
++
++ phy->rx_stats.rx_pn_iv_error++;
+ status->flag |= RX_FLAG_ONLY_MONITOR;
++
+ return;
+ }
+
+@@ -1330,7 +1341,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+ while ((skb = __skb_dequeue(frames)) != NULL) {
+ struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
+
+- mt76_check_ccmp_pn(skb);
++ mt76_check_ccmp_pn(dev, skb);
+ skb_shinfo(skb)->frag_list = NULL;
+ mt76_rx_convert(dev, skb, &hw, &sta);
+ ieee80211_rx_list(hw, sta, skb, &list);
+@@ -1353,6 +1364,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+ }
+
+ list_for_each_entry_safe(skb, tmp, &list, list) {
++ dev->rx_kernel++;
+ skb_list_del_init(skb);
+ napi_gro_receive(napi, skb);
+ }
+diff --git a/mt76.h b/mt76.h
+index 4a3e6bf40..631f82fd5 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -428,6 +428,7 @@ struct mt76_rx_tid {
+ struct rcu_head rcu_head;
+
+ struct mt76_dev *dev;
++ u8 band_idx;
+
+ spinlock_t lock;
+ struct delayed_work reorder_work;
+@@ -859,6 +860,19 @@ struct mt76_phy {
+ bool al;
+ u8 pin;
+ } leds;
++
++ struct {
++ u32 rx_mac80211;
++
++ u32 rx_drop;
++ u32 rx_rxd_drop;
++ u32 rx_dup_drop;
++ u32 rx_agg_miss;
++ u32 rx_icv_error;
++ u32 rx_fcs_error;
++ u32 rx_tkip_mic_error;
++ u32 rx_pn_iv_error;
++ } rx_stats;
+ };
+
+ struct mt76_dev {
+@@ -964,6 +978,7 @@ struct mt76_dev {
+ };
+
+ const char *bin_file_name;
++ u32 rx_kernel;
+ };
+
+ /* per-phy stats. */
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 9b5b9951e..18616fd29 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -469,8 +469,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ return -EINVAL;
+
+ /* ICV error or CCMP/BIP/WPI MIC error */
+- if (rxd1 & MT_RXD1_NORMAL_ICV_ERR)
++ if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) {
++ mphy->rx_stats.rx_icv_error++;
+ status->flag |= RX_FLAG_ONLY_MONITOR;
++ }
+
+ unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
+ idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
+@@ -501,11 +503,15 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ !(csum_status & (BIT(0) | BIT(2) | BIT(3))))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+- if (rxd1 & MT_RXD3_NORMAL_FCS_ERR)
++ if (rxd1 & MT_RXD3_NORMAL_FCS_ERR) {
++ mphy->rx_stats.rx_fcs_error++;
+ status->flag |= RX_FLAG_FAILED_FCS_CRC;
++ }
+
+- if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR)
++ if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) {
++ mphy->rx_stats.rx_tkip_mic_error++;
+ status->flag |= RX_FLAG_MMIC_ERROR;
++ }
+
+ if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 &&
+ !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) {
+@@ -1415,8 +1421,10 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ struct sk_buff *skb, u32 *info)
+ {
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++ struct mt76_phy *phy;
+ __le32 *rxd = (__le32 *)skb->data;
+ __le32 *end = (__le32 *)&skb->data[skb->len];
++ u8 band_idx;
+ enum rx_pkt_type type;
+
+ type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
+@@ -1458,6 +1466,10 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ }
+ fallthrough;
+ default:
++ band_idx = le32_get_bits(rxd[1], MT_RXD1_NORMAL_BAND_IDX);
++ phy = mt76_dev_phy(mdev, band_idx);
++ if (likely(phy))
++ phy->rx_stats.rx_rxd_drop++;
+ dev_kfree_skb(skb);
+ break;
+ }
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 9dc6ea778..321e43f83 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2913,6 +2913,46 @@ mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
+ }
+ DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
+
++static int mt7996_rx_counters(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u32 rx_mac80211 = 0;
++ int i = 0;
++
++ for (i = 0; i < __MT_MAX_BAND; i++) {
++ struct mt76_phy *phy = mt76_dev_phy(&dev->mt76, i);
++
++ if (!phy)
++ continue;
++
++ seq_printf(s, "\n==========PHY%d==========\n", i);
++
++#define SEQ_PRINT(_str, _rx_param) do { \
++ seq_printf(s, _str"\n", phy->rx_stats._rx_param); \
++ } while (0)
++
++ SEQ_PRINT("Rx to mac80211: %u", rx_mac80211);
++ SEQ_PRINT("Rx drop: %u", rx_drop);
++ SEQ_PRINT("Rx drop due to RXD type error: %u", rx_rxd_drop);
++ SEQ_PRINT("Rx duplicated drop: %u", rx_dup_drop);
++ SEQ_PRINT("Rx agg miss: %u", rx_agg_miss);
++ SEQ_PRINT("Rx ICV error: %u", rx_icv_error);
++ SEQ_PRINT("Rx FCS error: %u", rx_fcs_error);
++ SEQ_PRINT("Rx TKIP MIC error: %u", rx_tkip_mic_error);
++ SEQ_PRINT("Rx PN/IV error: %u", rx_pn_iv_error);
++#undef SEQ_PRINT
++
++ rx_mac80211 += phy->rx_stats.rx_mac80211;
++ }
++
++ seq_printf(s, "\n==========SUM==========\n");
++ seq_printf(s, "Rx to kernel: %u\n", dev->mt76.rx_kernel);
++ seq_printf(s, "Rx to mac80211: %u\n", rx_mac80211);
++
++
++ return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -2976,6 +3016,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+
+ debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
+ mt7996_trinfo_read);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "rx_counters", dir,
++ mt7996_rx_counters);
+ debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
+ debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
+ debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch
new file mode 100644
index 0000000..59dd3bf
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch
@@ -0,0 +1,1214 @@
+From a824fd4250a195e3010d24daa58e01cff4f45c46 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 3 Jan 2023 09:42:07 +0800
+Subject: [PATCH 043/116] mtk: wifi: mt76: mt7996: support BF/MIMO debug
+ commands
+
+This commit includes the following commands:
+1. starec_bf_read
+2. txbf_snd_info: start/stop sounding and set sounding period
+3. fbkRptInfo
+4. fix muru rate
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Id6cbaf16e4e6f63238a495ac9f45744e1dd38e9b
+
+fix the wrong wlan_idx for user3
+
+CR-Id: WCNCR00261410
+Change-Id: I7ece7399370f2bd22d2564029025baeda27057a5
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+Align the format of mcu event "mt7996_mcu_bf_starec_read" with
+firmware definition.
+
+Fw gerrit change:
+https://gerrit.mediatek.inc/c/neptune/firmware/bora/wifi/core/+/8218143
+
+CR-Id: WCNCR00240772
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mcu.c | 5 +
+ mt7996/mcu.h | 4 +
+ mt7996/mt7996.h | 5 +
+ mt7996/mtk_debugfs.c | 120 +++++++++
+ mt7996/mtk_mcu.c | 624 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h | 342 ++++++++++++++++++++++++
+ 6 files changed, 1100 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index fcc2512f3..4be5ca4bf 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -744,6 +744,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ case MCU_UNI_EVENT_TESTMODE_CTRL:
+ mt7996_tm_rf_test_event(dev, skb);
+ break;
++#endif
++#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
++ case MCU_UNI_EVENT_BF:
++ mt7996_mcu_rx_bf_event(dev, skb);
++ break;
+ #endif
+ default:
+ break;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index cb7260fb3..29bd7a55b 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -770,8 +770,12 @@ enum {
+
+ enum {
+ BF_SOUNDING_ON = 1,
++ BF_PFMU_TAG_READ = 5,
++ BF_STA_REC_READ = 11,
+ BF_HW_EN_UPDATE = 17,
+ BF_MOD_EN_CTRL = 20,
++ BF_FBRPT_DBG_INFO_READ = 23,
++ BF_TXSND_INFO = 24,
+ };
+
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d40f8bf43..4602b4e7a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -816,6 +816,11 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
+ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
++void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
++int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
++int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
++int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 321e43f83..36db55479 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2953,6 +2953,117 @@ static int mt7996_rx_counters(struct seq_file *s, void *data)
+ return 0;
+ }
+
++static int
++mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
++{
++ struct mt7996_phy *phy = data;
++
++ return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
++ mt7996_starec_bf_read_set, "%lld\n");
++
++static ssize_t
++mt7996_bf_txsnd_info_set(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct mt7996_phy *phy = file->private_data;
++ char buf[40];
++ int ret;
++
++ if (count >= sizeof(buf))
++ return -EINVAL;
++
++ if (copy_from_user(buf, user_buf, count))
++ return -EFAULT;
++
++ if (count && buf[count - 1] == '\n')
++ buf[count - 1] = '\0';
++ else
++ buf[count] = '\0';
++
++ ret = mt7996_mcu_set_txbf_snd_info(phy, buf);
++
++ if (ret) return -EFAULT;
++
++ return count;
++}
++
++static const struct file_operations fops_bf_txsnd_info = {
++ .write = mt7996_bf_txsnd_info_set,
++ .read = NULL,
++ .open = simple_open,
++ .llseek = default_llseek,
++};
++
++static int
++mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
++{
++ struct mt7996_phy *phy = data;
++
++ return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
++ mt7996_bf_fbk_rpt_set, "%lld\n");
++
++static int
++mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
++{
++ struct mt7996_phy *phy = data;
++
++ return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
++ mt7996_bf_pfmu_tag_read_set, "%lld\n");
++
++static int
++mt7996_muru_fixed_rate_set(void *data, u64 val)
++{
++ struct mt7996_dev *dev = data;
++
++ return mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL,
++ val);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_fixed_rate_enable, NULL,
++ mt7996_muru_fixed_rate_set, "%lld\n");
++
++static ssize_t
++mt7996_muru_fixed_rate_parameter_set(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct mt7996_dev *dev = file->private_data;
++ char buf[40];
++ int ret;
++
++ if (count >= sizeof(buf))
++ return -EINVAL;
++
++ if (copy_from_user(buf, user_buf, count))
++ return -EFAULT;
++
++ if (count && buf[count - 1] == '\n')
++ buf[count - 1] = '\0';
++ else
++ buf[count] = '\0';
++
++
++ ret = mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++ buf);
++
++ if (ret) return -EFAULT;
++
++ return count;
++}
++
++static const struct file_operations fops_muru_fixed_group_rate = {
++ .write = mt7996_muru_fixed_rate_parameter_set,
++ .read = NULL,
++ .open = simple_open,
++ .llseek = default_llseek,
++};
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -3039,6 +3150,15 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
+ debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
+
++ debugfs_create_file("muru_fixed_rate_enable", 0600, dir, dev,
++ &fops_muru_fixed_rate_enable);
++ debugfs_create_file("muru_fixed_group_rate", 0600, dir, dev,
++ &fops_muru_fixed_group_rate);
++ debugfs_create_file("bf_txsnd_info", 0600, dir, phy, &fops_bf_txsnd_info);
++ debugfs_create_file("bf_starec_read", 0600, dir, phy, &fops_starec_bf_read);
++ debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
++ debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
++
+ return 0;
+ }
+
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index ea4e5bf28..6b2cdad6f 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -280,4 +280,628 @@ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
+ sizeof(req), true);
+ }
++
++static struct tlv *
++__mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
++{
++ struct tlv *ptlv, tlv = {
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(len),
++ };
++
++ ptlv = skb_put(skb, len);
++ memcpy(ptlv, &tlv, sizeof(tlv));
++
++ return ptlv;
++}
++
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
++{
++ struct mt7996_dev *dev = phy->dev;
++#define MT7996_MTK_BF_MAX_SIZE sizeof(struct bf_starec_read)
++ struct uni_header hdr;
++ struct sk_buff *skb;
++ struct tlv *tlv;
++ int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
++
++ memset(&hdr, 0, sizeof(hdr));
++
++ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
++ if (!skb)
++ return -ENOMEM;
++
++ skb_put_data(skb, &hdr, sizeof(hdr));
++
++ switch (action) {
++ case BF_PFMU_TAG_READ: {
++ struct bf_pfmu_tag *req;
++
++ tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
++ req = (struct bf_pfmu_tag *)tlv;
++#define BFER 1
++ req->pfmu_id = idx;
++ req->bfer = BFER;
++ req->band_idx = phy->mt76->band_idx;
++ break;
++ }
++ case BF_STA_REC_READ: {
++ struct bf_starec_read *req;
++
++ tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
++ req = (struct bf_starec_read *)tlv;
++ req->wlan_idx = idx;
++ break;
++ }
++ case BF_FBRPT_DBG_INFO_READ: {
++ struct bf_fbk_rpt_info *req;
++
++ if (idx != 0) {
++ dev_info(dev->mt76.dev, "Invalid input");
++ return 0;
++ }
++
++ tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
++ req = (struct bf_fbk_rpt_info *)tlv;
++ req->action = idx;
++ req->band_idx = phy->mt76->band_idx;
++ break;
++ }
++ default:
++ return -EINVAL;
++ }
++
++ return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
++}
++
++int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
++{
++ char *buf = (char *)para;
++ __le16 input[5] = {0};
++ u8 recv_arg = 0;
++ struct bf_txsnd_info *req;
++ struct uni_header hdr;
++ struct sk_buff *skb;
++ struct tlv *tlv;
++ int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
++
++ memset(&hdr, 0, sizeof(hdr));
++
++ skb = mt76_mcu_msg_alloc(&phy->dev->mt76, NULL, len);
++ if (!skb)
++ return -ENOMEM;
++
++ skb_put_data(skb, &hdr, sizeof(hdr));
++
++ recv_arg = sscanf(buf, "%hx:%hx:%hx:%hx:%hx", &input[0], &input[1], &input[2],
++ &input[3], &input[4]);
++
++ if (!recv_arg)
++ return -EINVAL;
++
++ tlv = __mt7996_mcu_add_uni_tlv(skb, BF_TXSND_INFO, sizeof(*req));
++ req = (struct bf_txsnd_info *)tlv;
++ req->action = input[0];
++
++ switch (req->action) {
++ case BF_SND_READ_INFO: {
++ req->read_clr = input[1];
++ break;
++ }
++ case BF_SND_CFG_OPT: {
++ req->vht_opt = input[1];
++ req->he_opt = input[2];
++ req->glo_opt = input[3];
++ break;
++ }
++ case BF_SND_CFG_INTV: {
++ req->wlan_idx = input[1];
++ req->snd_intv = input[2];
++ break;
++ }
++ case BF_SND_STA_STOP: {
++ req->wlan_idx = input[1];
++ req->snd_stop = input[2];
++ break;
++ }
++ case BF_SND_CFG_MAX_STA: {
++ req->max_snd_stas = input[1];
++ break;
++ }
++ case BF_SND_CFG_BFRP: {
++ req->man = input[1];
++ req->tx_time = input[2];
++ req->mcs = input[3];
++ req->ldpc = input[4];
++ break;
++ }
++ case BF_SND_CFG_INF: {
++ req->inf = input[1];
++ break;
++ }
++ case BF_SND_CFG_TXOP_SND: {
++ req->man = input[1];
++ req->ac_queue = input[2];
++ req->sxn_protect = input[3];
++ req->direct_fbk = input[4];
++ break;
++ }
++ default:
++ return -EINVAL;
++ }
++
++ return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
++}
++
++void
++mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++#define HE_MODE 3
++ struct mt7996_mcu_bf_basic_event *event;
++
++ event = (struct mt7996_mcu_bf_basic_event *)skb->data;
++
++ dev_info(dev->mt76.dev, " bf_event tag = %d\n", event->tag);
++
++ switch (event->tag) {
++ case UNI_EVENT_BF_PFMU_TAG: {
++
++ struct mt7996_pfmu_tag_event *tag;
++ u32 *raw_t1, *raw_t2;
++
++ tag = (struct mt7996_pfmu_tag_event *) skb->data;
++
++ raw_t1 = (u32 *)&tag->t1;
++ raw_t2 = (u32 *)&tag->t2;
++
++ dev_info(dev->mt76.dev, "=================== TXBf Profile Tag1 Info ==================\n");
++ dev_info(dev->mt76.dev,
++ "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
++ raw_t1[0], raw_t1[1], raw_t1[2]);
++ dev_info(dev->mt76.dev,
++ "DW4 = 0x%08x, DW5 = 0x%08x, DW6 = 0x%08x\n\n",
++ raw_t1[3], raw_t1[4], raw_t1[5]);
++ dev_info(dev->mt76.dev, "PFMU ID = %d Invalid status = %d\n",
++ tag->t1.pfmu_idx, tag->t1.invalid_prof);
++ dev_info(dev->mt76.dev, "iBf/eBf = %d\n\n", tag->t1.ebf);
++ dev_info(dev->mt76.dev, "DBW = %d\n", tag->t1.data_bw);
++ dev_info(dev->mt76.dev, "SU/MU = %d\n", tag->t1.is_mu);
++ dev_info(dev->mt76.dev,
++ "nrow = %d, ncol = %d, ng = %d, LM = %d, CodeBook = %d MobCalEn = %d\n",
++ tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
++ tag->t1.mob_cal_en);
++
++ if (tag->t1.lm <= HE_MODE) {
++ dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
++ tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
++ } else {
++ dev_info(dev->mt76.dev, "PartialBW = %d\n",
++ tag->t1.bw_info.partial_bw_info);
++ }
++
++ dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
++ tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
++ dev_info(dev->mt76.dev, "Mem Col3 = %d, Mem Row3 = %d, Mem Col4 = %d, Mem Row4 = %d\n\n",
++ tag->t1.col_id3, tag->t1.row_id3, tag->t1.col_id4, tag->t1.row_id4);
++ dev_info(dev->mt76.dev,
++ "STS0_SNR = 0x%02x, STS1_SNR = 0x%02x, STS2_SNR = 0x%02x, STS3_SNR = 0x%02x\n",
++ tag->t1.snr_sts0, tag->t1.snr_sts1, tag->t1.snr_sts2, tag->t1.snr_sts3);
++ dev_info(dev->mt76.dev,
++ "STS4_SNR = 0x%02x, STS5_SNR = 0x%02x, STS6_SNR = 0x%02x, STS7_SNR = 0x%02x\n",
++ tag->t1.snr_sts4, tag->t1.snr_sts5, tag->t1.snr_sts6, tag->t1.snr_sts7);
++ dev_info(dev->mt76.dev, "=============================================================\n");
++
++ dev_info(dev->mt76.dev, "=================== TXBf Profile Tag2 Info ==================\n");
++ dev_info(dev->mt76.dev,
++ "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
++ raw_t2[0], raw_t2[1], raw_t2[2]);
++ dev_info(dev->mt76.dev,
++ "DW3 = 0x%08x, DW4 = 0x%08x, DW5 = 0x%08x\n\n",
++ raw_t2[3], raw_t2[4], raw_t2[5]);
++ dev_info(dev->mt76.dev, "Smart antenna ID = 0x%x, SE index = %d\n",
++ tag->t2.smart_ant, tag->t2.se_idx);
++ dev_info(dev->mt76.dev, "Timeout = 0x%x\n", tag->t2.ibf_timeout);
++ dev_info(dev->mt76.dev, "Desired BW = %d, Desired Ncol = %d, Desired Nrow = %d\n",
++ tag->t2.ibf_data_bw, tag->t2.ibf_nc, tag->t2.ibf_nr);
++ dev_info(dev->mt76.dev, "Desired RU Allocation = %d\n", tag->t2.ibf_ru);
++ dev_info(dev->mt76.dev, "Mobility DeltaT = %d, Mobility LQ = %d\n",
++ tag->t2.mob_delta_t, tag->t2.mob_lq_result);
++ dev_info(dev->mt76.dev, "=============================================================\n");
++ break;
++ }
++ case UNI_EVENT_BF_STAREC: {
++
++ struct mt7996_mcu_bf_starec_read *r;
++
++ r = (struct mt7996_mcu_bf_starec_read *)skb->data;
++ dev_info(dev->mt76.dev, "=================== BF StaRec ===================\n"
++ "rStaRecBf.u2PfmuId = %d\n"
++ "rStaRecBf.fgSU_MU = %d\n"
++ "rStaRecBf.u1TxBfCap = %d\n"
++ "rStaRecBf.ucSoundingPhy = %d\n"
++ "rStaRecBf.ucNdpaRate = %d\n"
++ "rStaRecBf.ucNdpRate = %d\n"
++ "rStaRecBf.ucReptPollRate= %d\n"
++ "rStaRecBf.ucTxMode = %d\n"
++ "rStaRecBf.ucNc = %d\n"
++ "rStaRecBf.ucNr = %d\n"
++ "rStaRecBf.ucCBW = %d\n"
++ "rStaRecBf.ucMemRequire20M = %d\n"
++ "rStaRecBf.ucMemRow0 = %d\n"
++ "rStaRecBf.ucMemCol0 = %d\n"
++ "rStaRecBf.ucMemRow1 = %d\n"
++ "rStaRecBf.ucMemCol1 = %d\n"
++ "rStaRecBf.ucMemRow2 = %d\n"
++ "rStaRecBf.ucMemCol2 = %d\n"
++ "rStaRecBf.ucMemRow3 = %d\n"
++ "rStaRecBf.ucMemCol3 = %d\n",
++ r->pfmu_id,
++ r->is_su_mu,
++ r->txbf_cap,
++ r->sounding_phy,
++ r->ndpa_rate,
++ r->ndp_rate,
++ r->rpt_poll_rate,
++ r->tx_mode,
++ r->nc,
++ r->nr,
++ r->bw,
++ r->mem_require_20m,
++ r->mem_row0,
++ r->mem_col0,
++ r->mem_row1,
++ r->mem_col1,
++ r->mem_row2,
++ r->mem_col2,
++ r->mem_row3,
++ r->mem_col3);
++
++ dev_info(dev->mt76.dev, "rStaRecBf.u2SmartAnt = 0x%x\n"
++ "rStaRecBf.ucSEIdx = %d\n"
++ "rStaRecBf.uciBfTimeOut = 0x%x\n"
++ "rStaRecBf.uciBfDBW = %d\n"
++ "rStaRecBf.uciBfNcol = %d\n"
++ "rStaRecBf.uciBfNrow = %d\n"
++ "rStaRecBf.nr_bw160 = %d\n"
++ "rStaRecBf.nc_bw160 = %d\n"
++ "rStaRecBf.ru_start_idx = %d\n"
++ "rStaRecBf.ru_end_idx = %d\n"
++ "rStaRecBf.trigger_su = %d\n"
++ "rStaRecBf.trigger_mu = %d\n"
++ "rStaRecBf.ng16_su = %d\n"
++ "rStaRecBf.ng16_mu = %d\n"
++ "rStaRecBf.codebook42_su = %d\n"
++ "rStaRecBf.codebook75_mu = %d\n"
++ "rStaRecBf.he_ltf = %d\n"
++ "======================================\n",
++ r->smart_ant,
++ r->se_idx,
++ r->bf_timeout,
++ r->bf_dbw,
++ r->bf_ncol,
++ r->bf_nrow,
++ r->nr_lt_bw80,
++ r->nc_lt_bw80,
++ r->ru_start_idx,
++ r->ru_end_idx,
++ r->trigger_su,
++ r->trigger_mu,
++ r->ng16_su,
++ r->ng16_mu,
++ r->codebook42_su,
++ r->codebook75_mu,
++ r->he_ltf);
++ break;
++ }
++ case UNI_EVENT_BF_FBK_INFO: {
++ struct mt7996_mcu_txbf_fbk_info *info;
++ __le32 total, i;
++
++ info = (struct mt7996_mcu_txbf_fbk_info *)skb->data;
++
++ total = info->u4PFMUWRDoneCnt + info->u4PFMUWRFailCnt;
++ total += info->u4PFMUWRTimeoutFreeCnt + info->u4FbRptPktDropCnt;
++
++ dev_info(dev->mt76.dev, "\n");
++ dev_info(dev->mt76.dev, "\x1b[32m =================================\x1b[m\n");
++ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRDoneCnt = %u\x1b[m\n",
++ info->u4PFMUWRDoneCnt);
++ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRFailCnt = %u\x1b[m\n",
++ info->u4PFMUWRFailCnt);
++ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeOutCnt = %u\x1b[m\n",
++ info->u4PFMUWRTimeOutCnt);
++ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeoutFreeCnt = %u\x1b[m\n",
++ info->u4PFMUWRTimeoutFreeCnt);
++ dev_info(dev->mt76.dev, "\x1b[32m FbRptPktDropCnt = %u\x1b[m\n",
++ info->u4FbRptPktDropCnt);
++ dev_info(dev->mt76.dev, "\x1b[32m TotalFbRptPkt = %u\x1b[m\n", total);
++ dev_info(dev->mt76.dev, "\x1b[32m PollPFMUIntrStatTimeOut = %u(micro-sec)\x1b[m\n",
++ info->u4PollPFMUIntrStatTimeOut);
++ dev_info(dev->mt76.dev, "\x1b[32m FbRptDeQInterval = %u(milli-sec)\x1b[m\n",
++ info->u4DeQInterval);
++ dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptTimeOutQ = %u\x1b[m\n",
++ info->u4RptPktTimeOutListNum);
++ dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptQ = %u\x1b[m\n",
++ info->u4RptPktListNum);
++
++ // [ToDo] Check if it is valid entry
++ for (i = 0; ((i < 5) && (i < CFG_BF_STA_REC_NUM)); i++) {
++
++ // [ToDo] AID needs to be refined
++ dev_info(dev->mt76.dev,"\x1b[32m AID%u RxFbRptCnt = %u\x1b[m\n"
++ , i, info->au4RxPerStaFbRptCnt[i]);
++ }
++
++ break;
++ }
++ case UNI_EVENT_BF_TXSND_INFO: {
++ struct mt7996_mcu_tx_snd_info *info;
++ struct uni_event_bf_txsnd_sta_info *snd_sta_info;
++ int Idx;
++ int max_wtbl_size = mt7996_wtbl_size(dev);
++
++ info = (struct mt7996_mcu_tx_snd_info *)skb->data;
++ dev_info(dev->mt76.dev, "=================== Global Setting ===================\n");
++
++ dev_info(dev->mt76.dev, "VhtOpt = 0x%02X, HeOpt = 0x%02X, GloOpt = 0x%02X\n",
++ info->vht_opt, info->he_opt, info->glo_opt);
++
++ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++ dev_info(dev->mt76.dev, "SuSta[%d] = 0x%08X,", Idx,
++ info->snd_rec_su_sta[Idx]);
++ if ((Idx & 0x03) == 0x03)
++ dev_info(dev->mt76.dev, "\n");
++ }
++
++ if ((Idx & 0x03) != 0x03)
++ dev_info(dev->mt76.dev, "\n");
++
++
++ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++ dev_info(dev->mt76.dev, "VhtMuSta[%d] = 0x%08X,", Idx, info->snd_rec_vht_mu_sta[Idx]);
++ if ((Idx & 0x03) == 0x03)
++ dev_info(dev->mt76.dev, "\n");
++ }
++
++ if ((Idx & 0x03) != 0x03)
++ dev_info(dev->mt76.dev, "\n");
++
++ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++ dev_info(dev->mt76.dev, "HeTBSta[%d] = 0x%08X,", Idx, info->snd_rec_he_tb_sta[Idx]);
++ if ((Idx & 0x03) == 0x03)
++ dev_info(dev->mt76.dev, "\n");
++ }
++
++ if ((Idx & 0x03) != 0x03)
++ dev_info(dev->mt76.dev, "\n");
++
++ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++ dev_info(dev->mt76.dev, "EhtTBSta[%d] = 0x%08X,", Idx, info->snd_rec_eht_tb_sta[Idx]);
++ if ((Idx & 0x03) == 0x03)
++ dev_info(dev->mt76.dev, "\n");
++ }
++
++ if ((Idx & 0x03) != 0x03)
++ dev_info(dev->mt76.dev, "\n");
++
++ for (Idx = 0; Idx < CFG_WIFI_RAM_BAND_NUM; Idx++) {
++ dev_info(dev->mt76.dev, "Band%u:\n", Idx);
++ dev_info(dev->mt76.dev, " Wlan Idx For VHT MC Sounding = %u\n", info->wlan_idx_for_mc_snd[Idx]);
++ dev_info(dev->mt76.dev, " Wlan Idx For HE TB Sounding = %u\n", info->wlan_idx_for_he_tb_snd[Idx]);
++ dev_info(dev->mt76.dev, " Wlan Idx For EHT TB Sounding = %u\n", info->wlan_idx_for_eht_tb_snd[Idx]);
++ }
++
++ dev_info(dev->mt76.dev, "ULLen = %d, ULMcs = %d, ULLDCP = %d\n",
++ info->ul_length, info->mcs, info->ldpc);
++
++ dev_info(dev->mt76.dev, "=================== STA Info ===================\n");
++
++ for (Idx = 1; (Idx < 5 && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
++ snd_sta_info = &info->snd_sta_info[Idx];
++ dev_info(dev->mt76.dev, "Idx%2u Interval = %d, interval counter = %d, TxCnt = %d, StopReason = 0x%02X\n",
++ Idx,
++ snd_sta_info->snd_intv,
++ snd_sta_info->snd_intv_cnt,
++ snd_sta_info->snd_tx_cnt,
++ snd_sta_info->snd_stop_reason);
++ }
++
++ dev_info(dev->mt76.dev, "=================== STA Info Connected ===================\n");
++ // [ToDo] How to iterate and get AID info of station
++ // Check UniEventBFCtrlTxSndHandle() on Logan
++
++ //hardcode max_wtbl_size as 5
++ max_wtbl_size = 5;
++ for (Idx = 1; ((Idx < max_wtbl_size) && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
++
++ // [ToDo] We do not show AID info here
++ snd_sta_info = &info->snd_sta_info[Idx];
++ dev_info(dev->mt76.dev, " Interval = %d (%u ms), interval counter = %d (%u ms), TxCnt = %d, StopReason = 0x%02X\n",
++ snd_sta_info->snd_intv,
++ snd_sta_info->snd_intv * 10,
++ snd_sta_info->snd_intv_cnt,
++ snd_sta_info->snd_intv_cnt * 10,
++ snd_sta_info->snd_tx_cnt,
++ snd_sta_info->snd_stop_reason);
++ }
++
++ dev_info(dev->mt76.dev, "======================================\n");
++
++ break;
++ }
++ default:
++ dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
++ __func__, event->tag);
++ }
++
++}
++
++
++int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val)
++{
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ __le16 value;
++ __le16 rsv;
++ } __packed data = {
++ .tag = cpu_to_le16(action),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ .value = cpu_to_le16(!!val),
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
++ false);
++}
++
++int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para)
++{
++ char *buf = (char *)para;
++ u8 num_user = 0, recv_arg = 0, max_mcs = 0, usr_mcs[4] = {0};
++ __le16 bw;
++ int i;
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 cmd_version;
++ u8 cmd_revision;
++ __le16 rsv;
++
++ struct uni_muru_mum_set_group_tbl_entry entry;
++ } __packed data = {
++ .tag = cpu_to_le16(action),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ };
++
++#define __RUALLOC_TYPE_CHECK_HE(BW) ((BW == RUALLOC_BW20) || (BW == RUALLOC_BW40) || (BW == RUALLOC_BW80) || (BW == RUALLOC_BW160))
++#define __RUALLOC_TYPE_CHECK_EHT(BW) (__RUALLOC_TYPE_CHECK_HE(BW) || (BW == RUALLOC_BW320))
++ /* [Num of user] - 1~4
++ * [RUAlloc] - BW320: 395, BW160: 137, BW80: 134, BW40: 130, BW20: 122
++ * [LTF/GI] - For VHT, short GI: 0, Long GI: 1; *
++ * For HE/EHT, 4xLTF+3.2us: 0, 4xLTF+0.8us: 1, 2xLTF+0.8us:2
++ * [Phy/FullBW] - VHT: 0 / HEFullBw: 1 / HEPartialBw: 2 / EHTFullBW: 3, EHTPartialBW: 4
++ * [DL/UL] DL: 0, UL: 1, DL_UL: 2
++ * [Wcid User0] - WCID 0
++ * [MCS of WCID0] - For HE/VHT, 0-11: 1ss MCS0-MCS11, 12-23: 2SS MCS0-MCS11
++ * For EHT, 0-13: 1ss MCS0-MCS13, 14-27: 2SS MCS0-MCS13
++ * [WCID 1]
++ * [MCS of WCID1]
++ * [WCID 2]
++ * [MCS of WCID2]
++ * [WCID 3]
++ * [MCS of WCID3]
++ */
++
++ recv_arg = sscanf(buf, "%hhu %hu %hhu %hhu %hhu %hu %hhu %hu %hhu %hu %hhu %hu %hhu",
++ &num_user, &bw, &data.entry.gi, &data.entry.capa, &data.entry.dl_ul,
++ &data.entry.wlan_idx0, &usr_mcs[0],
++ &data.entry.wlan_idx1, &usr_mcs[1],
++ &data.entry.wlan_idx2, &usr_mcs[2],
++ &data.entry.wlan_idx3, &usr_mcs[3]);
++
++ if (recv_arg != (5 + (2 * num_user))) {
++ dev_err(dev->mt76.dev, "The number of argument is invalid\n");
++ goto error;
++ }
++
++ if (num_user > 0 && num_user < 5)
++ data.entry.num_user = num_user - 1;
++ else {
++ dev_err(dev->mt76.dev, "The number of user count is invalid\n");
++ goto error;
++ }
++
++ /**
++ * Older chip shall be set as HE. Refer to getHWSupportByChip() in Logan
++ * driver to know the value for differnt chips
++ */
++ data.cmd_version = UNI_CMD_MURU_VER_EHT;
++
++ if (data.cmd_version == UNI_CMD_MURU_VER_EHT)
++ max_mcs = UNI_MAX_MCS_SUPPORT_EHT;
++ else
++ max_mcs = UNI_MAX_MCS_SUPPORT_HE;
++
++
++ // Parameter Check
++ if (data.cmd_version != UNI_CMD_MURU_VER_EHT) {
++ if ((data.entry.capa > MAX_MODBF_HE) || (bw == RUALLOC_BW320))
++ goto error;
++ } else {
++ if ((data.entry.capa <= MAX_MODBF_HE) && (bw == RUALLOC_BW320))
++ goto error;
++ }
++
++ if (data.entry.capa <= MAX_MODBF_HE)
++ max_mcs = UNI_MAX_MCS_SUPPORT_HE;
++
++ if (__RUALLOC_TYPE_CHECK_EHT(bw)) {
++ data.entry.ru_alloc = (u8)(bw & 0xFF);
++ if (bw == RUALLOC_BW320)
++ data.entry.ru_alloc_ext = (u8)(bw >> 8);
++ } else {
++ dev_err(dev->mt76.dev, "RU_ALLOC argument is invalid\n");
++ goto error;
++ }
++
++ if ((data.entry.gi > 2) ||
++ ((data.entry.gi > 1) && (data.entry.capa == MAX_MODBF_VHT))) {
++ dev_err(dev->mt76.dev, "GI argument is invalid\n");
++ goto error;
++ }
++
++ if (data.entry.dl_ul > 2) {
++ dev_err(dev->mt76.dev, "DL_UL argument is invalid\n");
++ goto error;
++ }
++
++#define __mcs_handler(_n) \
++ do { \
++ if (usr_mcs[_n] > max_mcs) { \
++ usr_mcs[_n] -= (max_mcs + 1); \
++ data.entry.nss##_n = 1; \
++ if (usr_mcs[_n] > max_mcs) \
++ usr_mcs[_n] = max_mcs; \
++ } \
++ if ((data.entry.dl_ul & 0x1) == 0) \
++ data.entry.dl_mcs_user##_n = usr_mcs[_n]; \
++ if ((data.entry.dl_ul & 0x3) > 0) \
++ data.entry.ul_mcs_user##_n = usr_mcs[_n]; \
++ } \
++ while (0)
++
++ for (i=0; i<= data.entry.num_user; i++) {
++ switch (i) {
++ case 0:
++ __mcs_handler(0);
++ break;
++ case 1:
++ __mcs_handler(1);
++ break;
++ case 2:
++ __mcs_handler(2);
++ break;
++ case 3:
++ __mcs_handler(3);
++ break;
++ default:
++ break;
++ }
++ }
++#undef __mcs_handler
++
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data,
++ sizeof(data), false);
++
++error:
++ dev_err(dev->mt76.dev, "Command failed!\n");
++ return -EINVAL;
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 098e63aef..27d6a05b8 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -119,6 +119,348 @@ enum {
+ EDCCA_FCC = 1,
+ EDCCA_ETSI = 2,
+ EDCCA_JAPAN = 3
++
++struct bf_pfmu_tag {
++ __le16 tag;
++ __le16 len;
++
++ u8 pfmu_id;
++ bool bfer;
++ u8 band_idx;
++ u8 __rsv[5];
++ u8 buf[56];
++} __packed;
++
++struct bf_starec_read {
++ __le16 tag;
++ __le16 len;
++
++ __le16 wlan_idx;
++ u8 __rsv[2];
++} __packed;
++
++struct bf_fbk_rpt_info {
++ __le16 tag;
++ __le16 len;
++
++ __le16 wlan_idx; // Only need for dynamic_pfmu_update 0x4
++ u8 action;
++ u8 band_idx;
++ u8 __rsv[4];
++
++} __packed;
++
++struct bf_txsnd_info {
++ __le16 tag;
++ __le16 len;
++
++ u8 action;
++ u8 read_clr;
++ u8 vht_opt;
++ u8 he_opt;
++ __le16 wlan_idx;
++ u8 glo_opt;
++ u8 snd_intv;
++ u8 snd_stop;
++ u8 max_snd_stas;
++ u8 tx_time;
++ u8 mcs;
++ u8 ldpc;
++ u8 inf;
++ u8 man;
++ u8 ac_queue;
++ u8 sxn_protect;
++ u8 direct_fbk;
++ u8 __rsv[2];
++} __packed;
++
++struct mt7996_mcu_bf_basic_event {
++ struct mt7996_mcu_rxd rxd;
++
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++};
++
++struct mt7996_mcu_bf_starec_read {
++
++ struct mt7996_mcu_bf_basic_event event;
++
++ __le16 pfmu_id;
++ bool is_su_mu;
++ u8 txbf_cap;
++ u8 sounding_phy;
++ u8 ndpa_rate;
++ u8 ndp_rate;
++ u8 rpt_poll_rate;
++ u8 tx_mode;
++ u8 nc;
++ u8 nr;
++ u8 bw;
++ u8 total_mem_require;
++ u8 mem_require_20m;
++ u8 mem_row0;
++ u8 mem_col0:6;
++ u8 mem_row0_msb:2;
++ u8 mem_row1;
++ u8 mem_col1:6;
++ u8 mem_row1_msb:2;
++ u8 mem_row2;
++ u8 mem_col2:6;
++ u8 mem_row2_msb:2;
++ u8 mem_row3;
++ u8 mem_col3:6;
++ u8 mem_row3_msb:2;
++
++ __le16 smart_ant;
++ u8 se_idx;
++ u8 auto_sounding_ctrl;
++
++ u8 bf_timeout;
++ u8 bf_dbw;
++ u8 bf_ncol;
++ u8 bf_nrow;
++
++ u8 nr_lt_bw80;
++ u8 nc_lt_bw80;
++ u8 ru_start_idx;
++ u8 ru_end_idx;
++
++ bool trigger_su;
++ bool trigger_mu;
++
++ bool ng16_su;
++ bool ng16_mu;
++
++ bool codebook42_su;
++ bool codebook75_mu;
++
++ u8 he_ltf;
++ u8 rsv[3];
++};
++
++#define TXBF_PFMU_ID_NUM_MAX 48
++
++#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 TXBF_PFMU_ID_NUM_MAX
++#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 TXBF_PFMU_ID_NUM_MAX
++#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2 TXBF_PFMU_ID_NUM_MAX
++
++/* CFG_BF_STA_REC shall be varied based on BAND Num */
++#define CFG_BF_STA_REC_NUM (TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2)
++
++#define BF_SND_CTRL_STA_DWORD_CNT ((CFG_BF_STA_REC_NUM + 0x1F) >> 5)
++
++#ifndef ALIGN_4
++ #define ALIGN_4(_value) (((_value) + 3) & ~3u)
++#endif /* ALIGN_4 */
++
++#define CFG_WIFI_RAM_BAND_NUM 3
++
++struct uni_event_bf_txsnd_sta_info {
++ u8 snd_intv; /* Sounding interval upper bound, unit:15ms */
++ u8 snd_intv_cnt; /* Sounding interval counter */
++ u8 snd_tx_cnt; /* Tx sounding count for debug */
++ u8 snd_stop_reason; /* Bitwise reason to put in Stop Queue */
++};
++
++struct mt7996_mcu_tx_snd_info {
++
++ struct mt7996_mcu_bf_basic_event event;
++
++ u8 vht_opt;
++ u8 he_opt;
++ u8 glo_opt;
++ u8 __rsv;
++ __le32 snd_rec_su_sta[BF_SND_CTRL_STA_DWORD_CNT];
++ __le32 snd_rec_vht_mu_sta[BF_SND_CTRL_STA_DWORD_CNT];
++ __le32 snd_rec_he_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
++ __le32 snd_rec_eht_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
++ __le16 wlan_idx_for_mc_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
++ __le16 wlan_idx_for_he_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
++ __le16 wlan_idx_for_eht_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
++ __le16 ul_length;
++ u8 mcs;
++ u8 ldpc;
++ struct uni_event_bf_txsnd_sta_info snd_sta_info[CFG_BF_STA_REC_NUM];
++};
++
++struct mt7996_mcu_txbf_fbk_info {
++
++ struct mt7996_mcu_bf_basic_event event;
++
++ __le32 u4DeQInterval; /* By ms */
++ __le32 u4PollPFMUIntrStatTimeOut; /* micro-sec */
++ __le32 u4RptPktTimeOutListNum;
++ __le32 u4RptPktListNum;
++ __le32 u4PFMUWRTimeOutCnt;
++ __le32 u4PFMUWRFailCnt;
++ __le32 u4PFMUWRDoneCnt;
++ __le32 u4PFMUWRTimeoutFreeCnt;
++ __le32 u4FbRptPktDropCnt;
++ __le32 au4RxPerStaFbRptCnt[CFG_BF_STA_REC_NUM];
++};
++
++struct pfmu_ru_field {
++ __le32 ru_start_id:7;
++ __le32 _rsv1:1;
++ __le32 ru_end_id:7;
++ __le32 _rsv2:1;
++} __packed;
++
++struct pfmu_partial_bw_info {
++ __le32 partial_bw_info:9;
++ __le32 _rsv1:7;
++} __packed;
++
++struct mt7996_pfmu_tag1 {
++ __le32 pfmu_idx:10;
++ __le32 ebf:1;
++ __le32 data_bw:3;
++ __le32 lm:3;
++ __le32 is_mu:1;
++ __le32 nr:3;
++ __le32 nc:3;
++ __le32 codebook:2;
++ __le32 ngroup:2;
++ __le32 invalid_prof:1;
++ __le32 _rsv:3;
++
++ __le32 col_id1:7, row_id1:9;
++ __le32 col_id2:7, row_id2:9;
++ __le32 col_id3:7, row_id3:9;
++ __le32 col_id4:7, row_id4:9;
++
++ union {
++ struct pfmu_ru_field field;
++ struct pfmu_partial_bw_info bw_info;
++ };
++ __le32 mob_cal_en:1;
++ __le32 _rsv2:3;
++ __le32 mob_ru_alloc:9; /* EHT profile uses full 9 bit */
++ __le32 _rsv3:3;
++
++ __le32 snr_sts0:8, snr_sts1:8, snr_sts2:8, snr_sts3:8;
++ __le32 snr_sts4:8, snr_sts5:8, snr_sts6:8, snr_sts7:8;
++
++ __le32 _rsv4;
++} __packed;
++
++struct mt7996_pfmu_tag2 {
++ __le32 smart_ant:24;
++ __le32 se_idx:5;
++ __le32 _rsv:3;
++
++ __le32 _rsv1:16;
++ __le32 ibf_timeout:8;
++ __le32 _rsv2:8;
++
++ __le32 ibf_data_bw:3;
++ __le32 ibf_nc:3;
++ __le32 ibf_nr:3;
++ __le32 ibf_ru:9;
++ __le32 _rsv3:14;
++
++ __le32 mob_delta_t:8;
++ __le32 mob_lq_result:7;
++ __le32 _rsv5:1;
++ __le32 _rsv6:16;
++
++ __le32 _rsv7;
++} __packed;
++
++struct mt7996_pfmu_tag_event {
++ struct mt7996_mcu_bf_basic_event event;
++
++ u8 bfer;
++ u8 __rsv[3];
++
++ struct mt7996_pfmu_tag1 t1;
++ struct mt7996_pfmu_tag2 t2;
++};
++
++enum {
++ UNI_EVENT_BF_PFMU_TAG = 0x5,
++ UNI_EVENT_BF_PFMU_DATA = 0x7,
++ UNI_EVENT_BF_STAREC = 0xB,
++ UNI_EVENT_BF_CAL_PHASE = 0xC,
++ UNI_EVENT_BF_FBK_INFO = 0x17,
++ UNI_EVENT_BF_TXSND_INFO = 0x18,
++ UNI_EVENT_BF_PLY_INFO = 0x19,
++ UNI_EVENT_BF_METRIC_INFO = 0x1A,
++ UNI_EVENT_BF_TXCMD_CFG_INFO = 0x1B,
++ UNI_EVENT_BF_SND_CNT_INFO = 0x1D,
++ UNI_EVENT_BF_MAX_NUM
++};
++
++enum {
++ UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
++ UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++};
++
++struct uni_muru_mum_set_group_tbl_entry {
++ __le16 wlan_idx0;
++ __le16 wlan_idx1;
++ __le16 wlan_idx2;
++ __le16 wlan_idx3;
++
++ u8 dl_mcs_user0:4;
++ u8 dl_mcs_user1:4;
++ u8 dl_mcs_user2:4;
++ u8 dl_mcs_user3:4;
++ u8 ul_mcs_user0:4;
++ u8 ul_mcs_user1:4;
++ u8 ul_mcs_user2:4;
++ u8 ul_mcs_user3:4;
++
++ u8 num_user:2;
++ u8 rsv:6;
++ u8 nss0:2;
++ u8 nss1:2;
++ u8 nss2:2;
++ u8 nss3:2;
++ u8 ru_alloc;
++ u8 ru_alloc_ext;
++
++ u8 capa;
++ u8 gi;
++ u8 dl_ul;
++ u8 _rsv2;
++};
++
++enum UNI_CMD_MURU_VER_T {
++ UNI_CMD_MURU_VER_LEG = 0,
++ UNI_CMD_MURU_VER_HE,
++ UNI_CMD_MURU_VER_EHT,
++ UNI_CMD_MURU_VER_MAX
++};
++
++#define UNI_MAX_MCS_SUPPORT_HE 11
++#define UNI_MAX_MCS_SUPPORT_EHT 13
++
++enum {
++ RUALLOC_BW20 = 122,
++ RUALLOC_BW40 = 130,
++ RUALLOC_BW80 = 134,
++ RUALLOC_BW160 = 137,
++ RUALLOC_BW320 = 395,
++};
++
++enum {
++ MAX_MODBF_VHT = 0,
++ MAX_MODBF_HE = 2,
++ MAX_MODBF_EHT = 4,
++};
++
++enum {
++ BF_SND_READ_INFO = 0,
++ BF_SND_CFG_OPT,
++ BF_SND_CFG_INTV,
++ BF_SND_STA_STOP,
++ BF_SND_CFG_MAX_STA,
++ BF_SND_CFG_BFRP,
++ BF_SND_CFG_INF,
++ BF_SND_CFG_TXOP_SND
+ };
+
+ enum {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch
new file mode 100644
index 0000000..a68e7f7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch
@@ -0,0 +1,190 @@
+From 8531498a7a6b120e4861772b19f4f9a9a63a2484 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 13 Jun 2023 14:49:02 +0800
+Subject: [PATCH 044/116] mtk: wifi: mt76: mt7996: add build the following MURU
+ mcu command tlvs
+
+It includes the following tlvs:
+1. MURU tlv id 0x10, 0x33, 0xC8, 0xC9, 0xCA, 0xCC, 0xCD
+2. BF tlv id 0x1c
+
+Change-Id: I0ae5cbed5b4370d39a6cfca50721873845659006
+---
+ mt7996/mcu.h | 1 +
+ mt7996/mt7996.h | 3 ++
+ mt7996/mtk_debugfs.c | 12 +++++++
+ mt7996/mtk_mcu.c | 78 ++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h | 14 ++++++++
+ 5 files changed, 108 insertions(+)
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 29bd7a55b..848c85dae 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -776,6 +776,7 @@ enum {
+ BF_MOD_EN_CTRL = 20,
+ BF_FBRPT_DBG_INFO_READ = 23,
+ BF_TXSND_INFO = 24,
++ BF_CFG_PHY = 28,
+ };
+
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 4602b4e7a..205a3c7b7 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -821,6 +821,9 @@ void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
+ int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
+ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
++int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
++int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
++int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 36db55479..e337ae4f7 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3064,6 +3064,16 @@ static const struct file_operations fops_muru_fixed_group_rate = {
+ .llseek = default_llseek,
+ };
+
++static int mt7996_muru_prot_thr_set(void *data, u64 val)
++{
++ struct mt7996_phy *phy = data;
++
++ return mt7996_mcu_muru_set_prot_frame_thr(phy->dev, (u32)val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
++ mt7996_muru_prot_thr_set, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -3159,6 +3169,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
+ debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
+
++ debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
++
+ return 0;
+ }
+
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 6b2cdad6f..686506234 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -904,4 +904,82 @@ error:
+ return -EINVAL;
+ }
+
++/**
++ * This function can be used to build the following commands
++ * MURU_SUTX_CTRL (0x10)
++ * SET_FORCE_MU (0x33)
++ * SET_MUDL_ACK_POLICY (0xC8)
++ * SET_TRIG_TYPE (0xC9)
++ * SET_20M_DYN_ALGO (0xCA)
++ * SET_CERT_MU_EDCA_OVERRIDE (0xCD)
++ */
++int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val)
++{
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 config;
++ u8 rsv[3];
++ } __packed data = {
++ .tag = cpu_to_le16(action),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ .config = (u8) val,
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
++ false);
++}
++
++int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val)
++{
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ __le32 prot_frame_thr;
++ } __packed data = {
++ .tag = cpu_to_le16(UNI_CMD_MURU_PROT_FRAME_THR),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ .prot_frame_thr = cpu_to_le32(val),
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
++ false);
++}
++
++int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
++{
++#define BF_PHY_SMTH_INT_BYPASS 0
++#define BYPASS_VAL 1
++ struct mt7996_dev *dev = phy->dev;
++ struct {
++ u8 _rsv[4];
++
++ u16 tag;
++ u16 len;
++
++ u8 action;
++ u8 band_idx;
++ u8 smthintbypass;
++ u8 __rsv2[5];
++ } __packed data = {
++ .tag = cpu_to_le16(BF_CFG_PHY),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ .action = BF_PHY_SMTH_INT_BYPASS,
++ .band_idx = phy->mt76->band_idx,
++ .smthintbypass = val,
++ };
++
++ if (val != BYPASS_VAL)
++ return -EINVAL;
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &data, sizeof(data),
++ true);
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 27d6a05b8..d9686ebb5 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -119,6 +119,20 @@ enum {
+ EDCCA_FCC = 1,
+ EDCCA_ETSI = 2,
+ EDCCA_JAPAN = 3
++};
++
++enum {
++ UNI_CMD_MURU_SUTX_CTRL = 0x10,
++ UNI_CMD_MURU_FIXED_RATE_CTRL,
++ UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++ UNI_CMD_MURU_SET_FORCE_MU = 0x33,
++ UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
++ UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
++ UNI_CMD_MURU_SET_TRIG_TYPE,
++ UNI_CMD_MURU_SET_20M_DYN_ALGO,
++ UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
++ UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
++};
+
+ struct bf_pfmu_tag {
+ __le16 tag;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-wifi-mt76-mt7996-add-cert-patch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-wifi-mt76-mt7996-add-cert-patch.patch
new file mode 100644
index 0000000..6113da2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-wifi-mt76-mt7996-add-cert-patch.patch
@@ -0,0 +1,1095 @@
+From 5316ac1eaa6a54cf11d0f7b66d074b6e101b5837 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Mon, 14 Aug 2023 13:36:58 +0800
+Subject: [PATCH 045/116] mtk: wifi: mt76: mt7996: add cert patch
+
+This patch includes TGac and TGax
+
+Commit histroy:
+
+Add vendor cmd set ap wireless rts_sigta support
+
+CR-ID: WCNCR00348946
+Signed-off-by: ye he <ye.he@mediatek.com>
+---
+ mt7996/mac.c | 9 ++
+ mt7996/main.c | 31 ++++++-
+ mt7996/mcu.c | 40 +++++++++
+ mt7996/mcu.h | 6 ++
+ mt7996/mt7996.h | 13 +++
+ mt7996/mtk_mcu.c | 205 ++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h | 184 +++++++++++++++++++++++++++++++++++--
+ mt7996/vendor.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/vendor.h | 67 ++++++++++++++
+ 9 files changed, 778 insertions(+), 7 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 18616fd29..70f0c56cc 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -10,6 +10,7 @@
+ #include "../dma.h"
+ #include "mac.h"
+ #include "mcu.h"
++#include "vendor.h"
+
+ #define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
+
+@@ -2284,6 +2285,14 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
+ }
+ }
+
++void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en)
++{
++ if (en)
++ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
++ else
++ ieee80211_hw_clear(hw, SUPPORTS_AMSDU_IN_AMPDU);
++}
++
+ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ {
+ struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f8ac51707..2f1dd0fb7 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -613,6 +613,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ bool beacon, bool mcast)
+ {
+ struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt76_phy *mphy = hw->priv;
+ u16 rate;
+ u8 i, idx;
+@@ -622,6 +623,9 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ if (beacon) {
+ struct mt7996_phy *phy = mphy->priv;
+
++ if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
++ rate = 0x0200;
++
+ /* odd index for driver, even index for firmware */
+ idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
+ if (phy->beacon_rate != rate)
+@@ -749,6 +753,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ u8 band_idx = mvif->phy->mt76->band_idx;
+ int ret, idx;
+
++#ifdef CONFIG_MTK_VENDOR
++ struct mt7996_phy *phy = &dev->phy;
++#endif
++
+ idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+ if (idx < 0)
+ return -ENOSPC;
+@@ -774,7 +782,28 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ if (ret)
+ return ret;
+
+- return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++ ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++ if (ret)
++ return ret;
++
++#ifdef CONFIG_MTK_VENDOR
++ switch (band_idx) {
++ case MT_BAND1:
++ phy = mt7996_phy2(dev);
++ break;
++ case MT_BAND2:
++ phy = mt7996_phy3(dev);
++ break;
++ case MT_BAND0:
++ default:
++ break;
++ }
++
++ if (phy && phy->muru_onoff & MUMIMO_DL_CERT)
++ mt7996_mcu_set_mimo(phy);
++#endif
++
++ return 0;
+ }
+
+ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 4be5ca4bf..b1296ce25 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1352,6 +1352,10 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ {
+ struct sta_rec_vht *vht;
+ struct tlv *tlv;
++#ifdef CONFIG_MTK_VENDOR
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->phy;
++#endif
+
+ /* For 6G band, this tlv is necessary to let hw work normally */
+ if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
+@@ -1363,6 +1367,9 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
+ vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
+ vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
++#ifdef CONFIG_MTK_VENDOR
++ vht->rts_bw_sig = phy->rts_bw_sig;
++#endif
+ }
+
+ static void
+@@ -4440,6 +4447,27 @@ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val)
+ &req, sizeof(req), true);
+ }
+
++int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable)
++{
++ struct {
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++ bool enable;
++ u8 _rsv2[3];
++ } __packed req = {
++ .band_idx = phy->mt76->band_idx,
++ .tag = cpu_to_le16(option),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .enable = enable,
++ };
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
++ &req, sizeof(req), true);
++}
++
+ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
+ {
+ struct {
+@@ -5007,6 +5035,18 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
+
+ switch (mode) {
++ case RATE_PARAM_FIXED_OFDMA:
++ if (val == 3)
++ phy->muru_onoff = OFDMA_DL;
++ else
++ phy->muru_onoff = val;
++ break;
++ case RATE_PARAM_FIXED_MIMO:
++ if (val == 0)
++ phy->muru_onoff = MUMIMO_DL_CERT | MUMIMO_DL;
++ else
++ phy->muru_onoff = MUMIMO_UL;
++ break;
+ case RATE_PARAM_AUTO_MU:
+ if (val < 0 || val > 15) {
+ printk("Wrong value! The value is between 0-15.\n");
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 848c85dae..806163806 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -755,6 +755,8 @@ enum {
+ RATE_PARAM_FIXED_GI = 11,
+ RATE_PARAM_AUTO = 20,
+ #ifdef CONFIG_MTK_VENDOR
++ RATE_PARAM_FIXED_MIMO = 30,
++ RATE_PARAM_FIXED_OFDMA = 31,
+ RATE_PARAM_AUTO_MU = 32,
+ #endif
+ };
+@@ -767,6 +769,7 @@ enum {
+ #define OFDMA_UL BIT(1)
+ #define MUMIMO_DL BIT(2)
+ #define MUMIMO_UL BIT(3)
++#define MUMIMO_DL_CERT BIT(4)
+
+ enum {
+ BF_SOUNDING_ON = 1,
+@@ -853,11 +856,14 @@ enum {
+ UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
+ UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
+ UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
++ UNI_BAND_CONFIG_RTS_SIGTA_EN = 0x09,
++ UNI_BAND_CONFIG_DIS_SECCH_CCA_DET = 0x0a,
+ };
+
+ enum {
+ UNI_WSYS_CONFIG_FW_LOG_CTRL,
+ UNI_WSYS_CONFIG_FW_DBG_CTRL,
++ UNI_CMD_CERT_CFG = 6,
+ };
+
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 205a3c7b7..30ceb0018 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -354,6 +354,7 @@ struct mt7996_phy {
+ } test;
+ #endif
+ #ifdef CONFIG_MTK_VENDOR
++ u8 rts_bw_sig;
+ spinlock_t amnt_lock;
+ struct mt7996_air_monitor_ctrl amnt_ctrl;
+ #endif
+@@ -482,6 +483,9 @@ struct mt7996_dev {
+ } dbg;
+ const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
++#ifdef CONFIG_MTK_VENDOR
++ bool cert_mode;
++#endif
+ };
+
+ enum {
+@@ -679,6 +683,7 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
++int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+@@ -797,6 +802,10 @@ void mt7996_vendor_register(struct mt7996_phy *phy);
+ void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
+ int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
+ struct ieee80211_sta *sta);
++void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
++void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
++int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
++int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
+ #endif
+
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+@@ -824,6 +833,10 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
+ int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
+ int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
+ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
++int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type);
++void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
++void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
++void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 686506234..b67d366d4 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -982,4 +982,209 @@ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
+ true);
+ }
+
++int mt7996_mcu_set_bsrp_ctrl(struct mt7996_phy *phy, u16 interval,
++ u16 ru_alloc, u32 trig_type, u8 trig_flow, u8 ext_cmd)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ __le16 interval;
++ __le16 ru_alloc;
++ __le32 trigger_type;
++ u8 trigger_flow;
++ u8 ext_cmd_bsrp;
++ u8 band_bitmap;
++ u8 _rsv2;
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_CMD_MURU_BSRP_CTRL),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .interval = cpu_to_le16(interval),
++ .ru_alloc = cpu_to_le16(ru_alloc),
++ .trigger_type = cpu_to_le32(trig_type),
++ .trigger_flow = trig_flow,
++ .ext_cmd_bsrp = ext_cmd,
++ .band_bitmap = BIT(phy->mt76->band_idx),
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
++ sizeof(req), false);
++}
++
++int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type)
++{
++ struct mt7996_dev *dev = phy->dev;
++ int ret = 0;
++ char buf[] = "01:00:00:1B";
++
++ if (enable) {
++ ret = mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_TRIG_TYPE, trig_type);
++ if (ret)
++ return ret;
++ }
++
++ switch (trig_type) {
++ case CAPI_BASIC:
++ return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 0, 0, enable);
++ case CAPI_BRP:
++ return mt7996_mcu_set_txbf_snd_info(phy, buf);
++ case CAPI_MU_BAR:
++ return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
++ MU_DL_ACK_POLICY_MU_BAR);
++ case CAPI_BSRP:
++ return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 4, 0, enable);
++ default:
++ return 0;
++ }
++}
++
++int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_muru *muru;
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 version;
++ u8 revision;
++ u8 _rsv2[2];
++
++ struct mt7996_muru muru;
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_CMD_MURU_MUNUAL_CONFIG),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .version = UNI_CMD_MURU_VER_EHT,
++ };
++
++ muru = (struct mt7996_muru *) data;
++ memcpy(&req.muru, muru, sizeof(struct mt7996_muru));
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
++ sizeof(req), false);
++}
++
++int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
++{
++ struct mt7996_muru *muru;
++ struct mt7996_muru_dl *dl;
++ struct mt7996_muru_ul *ul;
++ struct mt7996_muru_comm *comm;
++ int ret = 0;
++
++ muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
++ dl = &muru->dl;
++ ul = &muru->ul;
++ comm = &muru->comm;
++
++ switch (action) {
++ case MU_CTRL_DL_USER_CNT:
++ dl->user_num = val;
++ comm->ppdu_format = MURU_PPDU_HE_MU;
++ comm->sch_type = MURU_OFDMA_SCH_TYPE_DL;
++ muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
++ muru->cfg_dl = cpu_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
++ ret = mt7996_mcu_set_muru_cfg(phy, muru);
++ break;
++ case MU_CTRL_UL_USER_CNT:
++ ul->user_num = val;
++ comm->ppdu_format = MURU_PPDU_HE_TRIG;
++ comm->sch_type = MURU_OFDMA_SCH_TYPE_UL;
++ muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
++ muru->cfg_ul = cpu_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
++ ret = mt7996_mcu_set_muru_cfg(phy, muru);
++ break;
++ default:
++ break;
++ }
++
++ kfree(muru);
++ return ret;
++}
++
++void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type)
++{
++ struct mt7996_dev *dev = phy->dev;
++ int enable_su;
++
++ switch (ppdu_type) {
++ case CAPI_SU:
++ enable_su = 1;
++ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
++ mt7996_set_muru_cfg(phy, MU_CTRL_DL_USER_CNT, 0);
++ break;
++ case CAPI_MU:
++ enable_su = 0;
++ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
++ break;
++ default:
++ break;
++ }
++}
++
++void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 user_cnt)
++{
++ struct mt7996_dev *dev = phy->dev;
++ int enable_su = 0;
++
++ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
++ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
++ mt7996_mcu_muru_set_prot_frame_thr(dev, 9999);
++
++ mt7996_set_muru_cfg(phy, type, user_cnt);
++}
++
++void mt7996_mcu_set_mimo(struct mt7996_phy *phy)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++ int disable_ra = 1;
++ char buf[] = "2 134 0 1 0 1 2 2 2";
++ int force_mu = 1;
++
++ switch (chandef->width) {
++ case NL80211_CHAN_WIDTH_20_NOHT:
++ case NL80211_CHAN_WIDTH_20:
++ strscpy(buf, "2 122 0 1 0 1 2 2 2", sizeof(buf));
++ break;
++ case NL80211_CHAN_WIDTH_80:
++ break;
++ case NL80211_CHAN_WIDTH_160:
++ strscpy(buf, "2 137 0 1 0 1 2 2 2", sizeof(buf));
++ break;
++ default:
++ break;
++ }
++
++ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
++ mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL, disable_ra);
++ mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL, buf);
++ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_FORCE_MU, force_mu);
++}
++
++void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++ u8 action;
++ u8 _rsv2[3];
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_CMD_CERT_CFG),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .action = type, /* 1: CAPI Enable */
++ };
++
++ mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &req,
++ sizeof(req), false);
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index d9686ebb5..7a4140b57 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -122,14 +122,15 @@ enum {
+ };
+
+ enum {
++ UNI_CMD_MURU_BSRP_CTRL = 0x01,
+ UNI_CMD_MURU_SUTX_CTRL = 0x10,
+- UNI_CMD_MURU_FIXED_RATE_CTRL,
+- UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++ UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
++ UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL = 0x12,
+ UNI_CMD_MURU_SET_FORCE_MU = 0x33,
+ UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
+- UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
+- UNI_CMD_MURU_SET_TRIG_TYPE,
+- UNI_CMD_MURU_SET_20M_DYN_ALGO,
++ UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC8,
++ UNI_CMD_MURU_SET_TRIG_TYPE = 0xC9,
++ UNI_CMD_MURU_SET_20M_DYN_ALGO = 0xCA,
+ UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
+ UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
+ };
+@@ -533,6 +534,179 @@ struct mt7996_mcu_sr_hw_ind_event {
+ __le32 sr_ampdu_mpdu_cnt;
+ __le32 sr_ampdu_mpdu_acked_cnt;
+ };
++
++struct mt7996_muru_comm {
++ u8 pda_pol;
++ u8 band;
++ u8 spe_idx;
++ u8 proc_type;
++
++ __le16 mlo_ctrl;
++ u8 sch_type;
++ u8 ppdu_format;
++ u8 ac;
++ u8 _rsv[3];
++};
++
++struct mt7996_muru_dl {
++ u8 user_num;
++ u8 tx_mode;
++ u8 bw;
++ u8 gi;
++
++ u8 ltf;
++ u8 mcs;
++ u8 dcm;
++ u8 cmprs;
++
++ __le16 ru[16];
++
++ u8 c26[2];
++ u8 ack_policy;
++ u8 tx_power;
++
++ __le16 mu_ppdu_duration;
++ u8 agc_disp_order;
++ u8 _rsv1;
++
++ u8 agc_disp_pol;
++ u8 agc_disp_ratio;
++ __le16 agc_disp_linkMFG;
++
++ __le16 prmbl_punc_bmp;
++ u8 _rsv2[2];
++
++ struct {
++ __le16 wlan_idx;
++ u8 ru_alloc_seg;
++ u8 ru_idx;
++ u8 ldpc;
++ u8 nss;
++ u8 mcs;
++ u8 mu_group_idx;
++ u8 vht_groud_id;
++ u8 vht_up;
++ u8 he_start_stream;
++ u8 he_mu_spatial;
++ __le16 tx_power_alpha;
++ u8 ack_policy;
++ u8 ru_allo_ps160;
++ } usr[16];
++};
++
++struct mt7996_muru_ul {
++ u8 user_num;
++ u8 tx_mode;
++
++ u8 ba_type;
++ u8 _rsv;
++
++ u8 bw;
++ u8 gi_ltf;
++ __le16 ul_len;
++
++ __le16 trig_cnt;
++ u8 pad;
++ u8 trig_type;
++
++ __le16 trig_intv;
++ u8 trig_ta[ETH_ALEN];
++ __le16 ul_ru[16];
++
++ u8 c26[2];
++ __le16 agc_disp_linkMFG;
++
++ u8 agc_disp_mu_len;
++ u8 agc_disp_pol;
++ u8 agc_disp_ratio;
++ u8 agc_disp_pu_idx;
++
++ struct {
++ __le16 wlan_idx;
++ u8 ru_alloc_seg;
++ u8 ru_idx;
++ u8 ldpc;
++ u8 nss;
++ u8 mcs;
++ u8 target_rssi;
++ __le32 trig_pkt_size;
++ u8 ru_allo_ps160;
++ u8 _rsv2[3];
++ } usr[16];
++};
++
++struct mt7996_muru_dbg {
++ /* HE TB RX Debug */
++ __le32 rx_hetb_nonsf_en_bitmap;
++ __le32 rx_hetb_cfg[2];
++};
++
++struct mt7996_muru {
++ __le32 cfg_comm;
++ __le32 cfg_dl;
++ __le32 cfg_ul;
++ __le32 cfg_dbg;
++
++ struct mt7996_muru_comm comm;
++ struct mt7996_muru_dl dl;
++ struct mt7996_muru_ul ul;
++ struct mt7996_muru_dbg dbg;
++};
++
++
++#define MURU_PPDU_HE_TRIG BIT(2)
++#define MURU_PPDU_HE_MU BIT(3)
++
++#define MURU_OFDMA_SCH_TYPE_DL BIT(0)
++#define MURU_OFDMA_SCH_TYPE_UL BIT(1)
++
++/* Common Config */
++#define MURU_COMM_PPDU_FMT BIT(0)
++#define MURU_COMM_SCH_TYPE BIT(1)
++#define MURU_COMM_BAND BIT(2)
++#define MURU_COMM_WMM BIT(3)
++#define MURU_COMM_SPE_IDX BIT(4)
++#define MURU_COMM_PROC_TYPE BIT(5)
++#define MURU_COMM_SET (MURU_COMM_PPDU_FMT | MURU_COMM_SCH_TYPE)
++#define MURU_COMM_SET_TM (MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
++ MURU_COMM_WMM | MURU_COMM_SPE_IDX)
++
++/* DL Common config */
++#define MURU_FIXED_DL_TOTAL_USER_CNT BIT(4)
++
++/* UL Common Config */
++#define MURU_FIXED_UL_TOTAL_USER_CNT BIT(4)
++
++enum {
++ CAPI_SU,
++ CAPI_MU,
++ CAPI_ER_SU,
++ CAPI_TB,
++ CAPI_LEGACY
++};
++
++enum {
++ CAPI_BASIC,
++ CAPI_BRP,
++ CAPI_MU_BAR,
++ CAPI_MU_RTS,
++ CAPI_BSRP,
++ CAPI_GCR_MU_BAR,
++ CAPI_BQRP,
++ CAPI_NDP_FRP,
++};
++
++enum {
++ MU_DL_ACK_POLICY_MU_BAR = 3,
++ MU_DL_ACK_POLICY_TF_FOR_ACK = 4,
++ MU_DL_ACK_POLICY_SU_BAR = 5,
++};
++
++enum muru_vendor_ctrl {
++ MU_CTRL_UPDATE,
++ MU_CTRL_DL_USER_CNT,
++ MU_CTRL_UL_USER_CNT,
++};
+ #endif
+
+ #endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 0d6fa7792..7ab64471f 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -10,10 +10,31 @@
+ #include "vendor.h"
+ #include "mtk_mcu.h"
+
++#ifdef CONFIG_MTK_VENDOR
+ static const struct nla_policy
+ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
+ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
+ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_MU_CTRL_STRUCT] = {.type = NLA_BINARY },
++};
++
++static const struct nla_policy
++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
+ };
+
+ static const struct nla_policy
+@@ -76,6 +97,17 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ [MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
+ };
+
++static const struct nla_policy
++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++};
++
+ struct mt7996_amnt_data {
+ u8 idx;
+ u8 addr[ETH_ALEN];
+@@ -90,6 +122,8 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ {
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_muru *muru;
+ int err;
+ u8 val8;
+ u32 val32 = 0;
+@@ -105,9 +139,17 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ FIELD_PREP(RATE_CFG_VAL, val8);
+ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_set_wireless_vif, &val32);
++ } else if (tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT]) {
++ muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
++
++ nla_memcpy(muru, tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT],
++ sizeof(struct mt7996_muru));
++
++ err = mt7996_mcu_set_muru_cfg(phy, muru);
++ kfree(muru);
+ }
+
+- return 0;
++ return err;
+ }
+
+ static int
+@@ -130,6 +172,48 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ return len;
+ }
+
++void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++
++ switch (value) {
++ case BW_SIGNALING_STATIC:
++ case BW_SIGNALING_DYNAMIC:
++ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, true);
++ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, false);
++ break;
++ default:
++ value = BW_SIGNALING_DISABLE;
++ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, false);
++ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, true);
++ break;
++ }
++
++ phy->rts_bw_sig = value;
++
++ /* Set RTS Threshold to a lower Value */
++ mt7996_mcu_set_rts_thresh(phy, 500);
++}
++
++static int
++mt7996_vendor_wireless_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ struct sk_buff *skb, const void *data, int data_len,
++ unsigned long *storage)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ int len = 0;
++
++ if (*storage == 1)
++ return -ENOENT;
++ *storage = 1;
++
++ if (nla_put_u8(skb, MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++ ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU)))
++ return -ENOMEM;
++ len += 1;
++
++ return len;
++ }
++
+ void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
+ {
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+@@ -712,6 +796,126 @@ error:
+ return -EINVAL;
+ }
+
++static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
++ struct wireless_dev *wdev,
++ const void *data,
++ int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_dev *dev = phy->dev;
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
++ int err;
++ u32 val;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
++ rfeature_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ val = CAPI_RFEATURE_CHANGED;
++
++ if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
++ u8 enable, trig_type;
++ int rem;
++ struct nlattr *cur;
++
++ nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG], rem) {
++ switch (nla_type(cur)) {
++ case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN:
++ enable = nla_get_u8(cur);
++ break;
++ case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE:
++ trig_type = nla_get_u8(cur);
++ break;
++ default:
++ return -EINVAL;
++ };
++ }
++
++ err = mt7996_mcu_set_rfeature_trig_type(phy, enable, trig_type);
++ if (err)
++ return err;
++ } else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]) {
++ u8 ack_policy;
++
++ ack_policy = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]);
++ switch (ack_policy) {
++ case MU_DL_ACK_POLICY_TF_FOR_ACK:
++ return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
++ ack_policy);
++ default:
++ return 0;
++ }
++ }
++
++ return 0;
++}
++
++static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
++ struct wireless_dev *wdev,
++ const void *data,
++ int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_dev *dev = phy->dev;
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
++ int err;
++ u8 val8;
++ u16 val16;
++ u32 val32;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX, data, data_len,
++ wireless_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ val32 = CAPI_WIRELESS_CHANGED;
++
++ if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]);
++ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
++ FIELD_PREP(RATE_CFG_VAL, val8);
++ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7996_set_wireless_vif, &val32);
++ if (val8 == 3) /* DL20and80 */
++ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_20M_DYN_ALGO, 1);
++ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]) {
++ val16 = nla_get_u16(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]);
++ hw->max_tx_aggregation_subframes = val16;
++ hw->max_rx_aggregation_subframes = val16;
++ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]);
++ mt7996_mcu_set_ppdu_tx_type(phy, val8);
++ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]);
++ if (phy->muru_onoff & OFDMA_UL)
++ mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_UL_USER_CNT, val8);
++ else
++ mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_DL_USER_CNT, val8);
++ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]);
++ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
++ FIELD_PREP(RATE_CFG_VAL, val8);
++ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7996_set_wireless_vif, &val32);
++ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
++ dev->cert_mode = val8;
++ mt7996_mcu_set_cert(phy, val8);
++ mt7996_mcu_set_bypass_smthint(phy, val8);
++ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
++ mt7996_set_wireless_amsdu(hw, val8);
++ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]);
++ mt7996_set_wireless_rts_sigta(hw, val8);
++ }
++
++ return 0;
++}
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ {
+ .info = {
+@@ -725,6 +929,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ .policy = mu_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
+ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_wireless_ctrl,
++ .dumpit = mt7996_vendor_wireless_ctrl_dump,
++ .policy = wireless_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX,
++ },
+ {
+ .info = {
+ .vendor_id = MTK_NL80211_VENDOR_ID,
+@@ -794,6 +1010,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ .policy = pp_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
+ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_rfeature_ctrl,
++ .policy = rfeature_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
++ },
+ };
+
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+@@ -803,3 +1030,4 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
+
+ spin_lock_init(&phy->amnt_lock);
+ }
++#endif
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 8aaa18eec..2ee1339a5 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -3,8 +3,12 @@
+
+ #define MTK_NL80211_VENDOR_ID 0x0ce7
+
++#ifdef CONFIG_MTK_VENDOR
++
+ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+@@ -61,6 +65,7 @@ enum mtk_vendor_attr_mu_ctrl {
+
+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -68,6 +73,66 @@ enum mtk_vendor_attr_mu_ctrl {
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+
++enum mtk_capi_control_changed {
++ CAPI_RFEATURE_CHANGED = BIT(16),
++ CAPI_WIRELESS_CHANGED = BIT(17),
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA, /* reserve */
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_wireless_dump {
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
++};
++
++enum bw_sig {
++ BW_SIGNALING_DISABLE,
++ BW_SIGNALING_STATIC,
++ BW_SIGNALING_DYNAMIC
++};
++
+ enum mtk_vendor_attr_mnt_ctrl {
+ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
+
+@@ -151,3 +216,5 @@ enum mtk_vendor_attr_pp_ctrl {
+ };
+
+ #endif
++
++#endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch
new file mode 100644
index 0000000..120ee1e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch
@@ -0,0 +1,2067 @@
+From a0d5c65378876c6fbbc83b602e297d791640a7c3 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 6 Apr 2023 16:40:28 +0800
+Subject: [PATCH 046/116] mtk: wifi: mt76: testmode: add testmode bf support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Add iTest additional bf command
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+fw return Gx (5g band) ibf cal struct for both 2G and 5G ibf.
+Therefore, memcpy cannot be used for 2G ibf cal.
+https://gerrit.mediatek.inc/c/neptune/wlan_driver/logan/+/8206056
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: testmode: add testmode ibf ver2 support
+
+Add ibf ver2 support for chips after Kite
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: testmode: add testmode ibf 5T5R support
+
+Add testmode ibf 5T5R support for Kite BE7200 2i5i
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76.h | 5 +
+ mt76_connac_mcu.h | 4 +-
+ mt7996/mcu.c | 8 +-
+ mt7996/mcu.h | 46 ++-
+ mt7996/mt7996.h | 12 +-
+ mt7996/mtk_debugfs.c | 6 +-
+ mt7996/mtk_mcu.c | 143 +++++++-
+ mt7996/mtk_mcu.h | 441 +++++++++++++++++++++++-
+ mt7996/regs.h | 3 +
+ mt7996/testmode.c | 796 +++++++++++++++++++++++++++++++++++++++++--
+ mt7996/testmode.h | 19 ++
+ testmode.c | 62 ++++
+ testmode.h | 53 +++
+ tools/fields.c | 37 ++
+ 14 files changed, 1576 insertions(+), 59 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 631f82fd5..11cbb2d28 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -757,6 +757,11 @@ struct mt76_testmode_data {
+ u32 tx_time;
+ u32 tx_ipg;
+
++ u8 txbf_act;
++ u16 txbf_param[8];
++ bool is_txbf_dut;
++ bool bf_en;
++ bool bf_ever_en;
+ bool ibf;
+ bool ebf;
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 864a802d7..885e5883e 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -488,7 +488,8 @@ struct sta_rec_bf {
+ bool codebook75_mu;
+
+ u8 he_ltf;
+- u8 rsv[3];
++ u8 pp_fd_val;
++ u8 rsv[2];
+ } __packed;
+
+ struct sta_rec_bfee {
+@@ -1279,6 +1280,7 @@ enum {
+ MCU_UNI_CMD_VOW = 0x37,
+ MCU_UNI_CMD_PP = 0x38,
+ MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
++ MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ MCU_UNI_CMD_PRECAL_RESULT = 0x47,
+ MCU_UNI_CMD_RRO = 0x57,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index b1296ce25..8f3e5ebfd 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1073,7 +1073,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ bss->hw_bss_idx = idx;
+
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+- memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
++ struct mt76_testmode_data *td = &phy->test;
++
++ if (!td->bf_en)
++ memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
++ else
++ memcpy(bss->bssid, td->addr[2], ETH_ALEN);
+ return 0;
+ }
+
+@@ -4097,7 +4102,6 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
+ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
+ {
+ #define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv)
+-#define BF_PROCESSING 4
+ struct uni_header hdr;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 806163806..663128176 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -685,6 +685,22 @@ struct bf_sounding_on {
+ __le32 snd_period;
+ } __packed;
+
++enum sounding_mode {
++ SU_SOUNDING,
++ MU_SOUNDING,
++ SU_PERIODIC_SOUNDING,
++ MU_PERIODIC_SOUNDING,
++ BF_PROCESSING,
++ TXCMD_NONTB_SU_SOUNDING,
++ TXCMD_VHT_MU_SOUNDING,
++ TXCMD_TB_PER_BRP_SOUNDING,
++ TXCMD_TB_SOUNDING,
++
++ /* keep last */
++ NUM_SOUNDING_MODE,
++ SOUNDING_MODE_MAX = NUM_SOUNDING_MODE - 1,
++};
++
+ struct bf_hw_en_status_update {
+ __le16 tag;
+ __le16 len;
+@@ -710,6 +726,25 @@ union bf_tag_tlv {
+ struct bf_mod_en_ctrl bf_mod_en;
+ };
+
++enum {
++ BF_SOUNDING_OFF = 0,
++ BF_SOUNDING_ON = 1,
++ BF_DATA_PACKET_APPLY = 2,
++ BF_PFMU_TAG_READ = 5,
++ BF_PFMU_TAG_WRITE = 6,
++ BF_STA_REC_READ = 11,
++ BF_PHASE_CALIBRATION = 12,
++ BF_IBF_PHASE_COMP = 13,
++ BF_PROFILE_WRITE_20M_ALL = 15,
++ BF_HW_EN_UPDATE = 17,
++ BF_MOD_EN_CTRL = 20,
++ BF_FBRPT_DBG_INFO_READ = 23,
++ BF_TXSND_INFO = 24,
++ BF_CMD_TXCMD = 27,
++ BF_CFG_PHY = 28,
++ BF_PROFILE_WRITE_20M_ALL_5X5 = 30,
++};
++
+ struct ra_rate {
+ __le16 wlan_idx;
+ u8 mode;
+@@ -771,17 +806,6 @@ enum {
+ #define MUMIMO_UL BIT(3)
+ #define MUMIMO_DL_CERT BIT(4)
+
+-enum {
+- BF_SOUNDING_ON = 1,
+- BF_PFMU_TAG_READ = 5,
+- BF_STA_REC_READ = 11,
+- BF_HW_EN_UPDATE = 17,
+- BF_MOD_EN_CTRL = 20,
+- BF_FBRPT_DBG_INFO_READ = 23,
+- BF_TXSND_INFO = 24,
+- BF_CFG_PHY = 28,
+-};
+-
+ enum {
+ CMD_BAND_NONE,
+ CMD_BAND_24G,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 30ceb0018..84fbd0fb0 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -486,6 +486,14 @@ struct mt7996_dev {
+ #ifdef CONFIG_MTK_VENDOR
+ bool cert_mode;
+ #endif
++
++#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
++ struct {
++ void *txbf_phase_cal;
++ void *txbf_pfmu_data;
++ void *txbf_pfmu_tag;
++ } test;
++#endif
+ };
+
+ enum {
+@@ -825,7 +833,7 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
+ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
+-int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer);
+ void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
+ int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
+@@ -837,10 +845,12 @@ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig
+ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
+ void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
+ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
++void mt7996_tm_update_channel(struct mt7996_phy *phy);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ int mt7996_dma_rro_init(struct mt7996_dev *dev);
+ #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+
++
+ #endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index e337ae4f7..c7713e8b2 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2958,7 +2958,7 @@ mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
+ {
+ struct mt7996_phy *phy = data;
+
+- return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
++ return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx, 0);
+ }
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
+ mt7996_starec_bf_read_set, "%lld\n");
+@@ -3002,7 +3002,7 @@ mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
+ {
+ struct mt7996_phy *phy = data;
+
+- return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
++ return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx, 0);
+ }
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
+ mt7996_bf_fbk_rpt_set, "%lld\n");
+@@ -3012,7 +3012,7 @@ mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
+ {
+ struct mt7996_phy *phy = data;
+
+- return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
++ return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx, 1);
+ }
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
+ mt7996_bf_pfmu_tag_read_set, "%lld\n");
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index b67d366d4..a9a7db15a 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -295,7 +295,7 @@ __mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
+ return ptlv;
+ }
+
+-int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer)
+ {
+ struct mt7996_dev *dev = phy->dev;
+ #define MT7996_MTK_BF_MAX_SIZE sizeof(struct bf_starec_read)
+@@ -318,9 +318,8 @@ int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
+
+ tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
+ req = (struct bf_pfmu_tag *)tlv;
+-#define BFER 1
+ req->pfmu_id = idx;
+- req->bfer = BFER;
++ req->bfer = bfer;
+ req->band_idx = phy->mt76->band_idx;
+ break;
+ }
+@@ -432,10 +431,61 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
+ return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
+ }
+
++static inline void
++mt7996_ibf_phase_assign(struct mt7996_dev *dev,
++ struct mt7996_ibf_cal_info *cal,
++ struct mt7996_txbf_phase *phase)
++{
++ /* fw return ibf calibrated data with
++ * the mt7996_txbf_phase_info_5g struct for both 2G and 5G.
++ * (return struct mt7992_txbf_phase_info_5g for ibf 2.0)
++ * Therefore, memcpy cannot be used here.
++ */
++ if (get_ibf_version(dev) != IBF_VER_2) {
++ phase_assign(cal->group, v1, m_t0_h, true);
++ phase_assign(cal->group, v1, m_t1_h, true);
++ phase_assign(cal->group, v1, m_t2_h, true);
++ phase_assign(cal->group, v1, m_t2_h_sx2, false);
++ phase_assign_rx_v1(cal->group, v1, r0);
++ phase_assign_rx_v1(cal->group, v1, r1);
++ phase_assign_rx_v1(cal->group, v1, r2);
++ phase_assign_rx_v1(cal->group, v1, r3);
++ phase_assign_rx(cal->group, v1, r2_sx2, false);
++ phase_assign_rx(cal->group, v1, r3_sx2, false);
++ phase_assign(cal->group, v1, r0_reserved, false);
++ phase_assign(cal->group, v1, r1_reserved, false);
++ phase_assign(cal->group, v1, r2_reserved, false);
++ phase_assign(cal->group, v1, r3_reserved, false);
++ phase_assign(cal->group, v1, r2_sx2_reserved, false);
++ phase_assign(cal->group, v1, r3_sx2_reserved, false);
++ } else {
++ phase_assign(cal->group, v2, m_t0_h, true);
++ phase_assign(cal->group, v2, m_t1_h, true);
++ phase_assign(cal->group, v2, m_t2_h, true);
++ if (cal->group) {
++ phase->v2.phase_5g.m_t3_h = cal->v2.phase_5g.m_t3_h;
++ dev_info(dev->mt76.dev, "m_t3_h = %d\n", phase->v2.phase_5g.m_t3_h);
++ }
++ phase_assign_rx_ext(cal->group, v2, r0, true);
++ phase_assign_rx_ext(cal->group, v2, r1, true);
++ phase_assign_rx_ext(cal->group, v2, r2, true);
++ phase_assign_rx_ext(cal->group, v2, r3, true);
++ if (cal->group) {
++ memcpy(&phase->v2.phase_5g.r4, &cal->v2.phase_5g.r4,
++ sizeof(struct txbf_rx_phase_ext));
++ dev_info(dev->mt76.dev, "r4.rx_uh = %d\n", phase->v2.phase_5g.r4.rx_uh);
++ dev_info(dev->mt76.dev, "r4.rx_h = %d\n", phase->v2.phase_5g.r4.rx_h);
++ dev_info(dev->mt76.dev, "r4.rx_mh = %d\n", phase->v2.phase_5g.r4.rx_mh);
++ dev_info(dev->mt76.dev, "r4.rx_m = %d\n", phase->v2.phase_5g.r4.rx_m);
++ dev_info(dev->mt76.dev, "r4.rx_l = %d\n", phase->v2.phase_5g.r4.rx_l);
++ dev_info(dev->mt76.dev, "r4.rx_ul = %d\n", phase->v2.phase_5g.r4.rx_ul);
++ }
++ }
++}
++
+ void
+ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+-#define HE_MODE 3
+ struct mt7996_mcu_bf_basic_event *event;
+
+ event = (struct mt7996_mcu_bf_basic_event *)skb->data;
+@@ -470,13 +520,12 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
+ tag->t1.mob_cal_en);
+
+- if (tag->t1.lm <= HE_MODE) {
++ if (tag->t1.lm <= BF_LM_HE)
+ dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
+ tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
+- } else {
++ else
+ dev_info(dev->mt76.dev, "PartialBW = %d\n",
+ tag->t1.bw_info.partial_bw_info);
+- }
+
+ dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
+ tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
+@@ -728,6 +777,86 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
+
+ break;
+ }
++ case UNI_EVENT_BF_CAL_PHASE: {
++ struct mt7996_ibf_cal_info *cal;
++ struct mt7996_txbf_phase *phase;
++ union {
++ struct mt7996_txbf_phase_out v1;
++ struct mt7992_txbf_phase_out v2;
++ } phase_out;
++ int phase_out_size = sizeof(struct mt7996_txbf_phase_out);
++
++ cal = (struct mt7996_ibf_cal_info *)skb->data;
++ phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++ if (get_ibf_version(dev) == IBF_VER_2)
++ phase_out_size = sizeof(struct mt7992_txbf_phase_out);
++ memcpy(&phase_out, &cal->buf, phase_out_size);
++ switch (cal->cal_type) {
++ case IBF_PHASE_CAL_NORMAL:
++ case IBF_PHASE_CAL_NORMAL_INSTRUMENT:
++ /* Only calibrate group M */
++ if (cal->group_l_m_n != GROUP_M)
++ break;
++ phase = &phase[cal->group];
++ phase->status = cal->status;
++ dev_info(dev->mt76.dev, "Calibrated result = %d\n", phase->status);
++ dev_info(dev->mt76.dev, "Group %d and Group M\n", cal->group);
++ mt7996_ibf_phase_assign(dev, cal, phase);
++ break;
++ case IBF_PHASE_CAL_VERIFY:
++ case IBF_PHASE_CAL_VERIFY_INSTRUMENT:
++ dev_info(dev->mt76.dev, "Verification result = %d\n", cal->status);
++ break;
++ default:
++ break;
++ }
++
++ if (get_ibf_version(dev) == IBF_VER_2) {
++ dev_info(dev->mt76.dev,
++ "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d c4_uh = %d\n",
++ phase_out.v2.c0_uh, phase_out.v2.c1_uh, phase_out.v2.c2_uh,
++ phase_out.v2.c3_uh, phase_out.v2.c4_uh);
++ dev_info(dev->mt76.dev,
++ "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d c4_h = %d\n",
++ phase_out.v2.c0_h, phase_out.v2.c1_h, phase_out.v2.c2_h,
++ phase_out.v2.c3_h, phase_out.v2.c4_h);
++ dev_info(dev->mt76.dev,
++ "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d c4_mh = %d\n",
++ phase_out.v2.c0_mh, phase_out.v2.c1_mh, phase_out.v2.c2_mh,
++ phase_out.v2.c3_mh, phase_out.v2.c4_mh);
++ dev_info(dev->mt76.dev,
++ "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d c4_m = %d\n",
++ phase_out.v2.c0_m, phase_out.v2.c1_m, phase_out.v2.c2_m,
++ phase_out.v2.c3_m, phase_out.v2.c4_m);
++ dev_info(dev->mt76.dev,
++ "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d c4_l = %d\n",
++ phase_out.v2.c0_l, phase_out.v2.c1_l, phase_out.v2.c2_l,
++ phase_out.v2.c3_l, phase_out.v2.c4_l);
++ } else {
++ dev_info(dev->mt76.dev,
++ "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d\n",
++ phase_out.v1.c0_uh, phase_out.v1.c1_uh,
++ phase_out.v1.c2_uh, phase_out.v1.c3_uh);
++ dev_info(dev->mt76.dev,
++ "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d\n",
++ phase_out.v1.c0_h, phase_out.v1.c1_h,
++ phase_out.v1.c2_h, phase_out.v1.c3_h);
++ dev_info(dev->mt76.dev,
++ "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d\n",
++ phase_out.v1.c0_mh, phase_out.v1.c1_mh,
++ phase_out.v1.c2_mh, phase_out.v1.c3_mh);
++ dev_info(dev->mt76.dev,
++ "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d\n",
++ phase_out.v1.c0_m, phase_out.v1.c1_m,
++ phase_out.v1.c2_m, phase_out.v1.c3_m);
++ dev_info(dev->mt76.dev,
++ "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d\n",
++ phase_out.v1.c0_l, phase_out.v1.c1_l,
++ phase_out.v1.c2_l, phase_out.v1.c3_l);
++ }
++
++ break;
++ }
+ default:
+ dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
+ __func__, event->tag);
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 7a4140b57..58d61c517 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -189,6 +189,165 @@ struct bf_txsnd_info {
+ u8 __rsv[2];
+ } __packed;
+
++#define MAX_PHASE_GROUP_NUM 13
++
++struct bf_phase_comp {
++ __le16 tag;
++ __le16 len;
++
++ u8 bw;
++ u8 jp_band;
++ u8 band_idx;
++ bool read_from_e2p;
++ bool disable;
++ u8 group;
++ u8 rsv[2];
++ u8 buf[44];
++} __packed;
++
++struct bf_tx_apply {
++ __le16 tag;
++ __le16 len;
++
++ __le16 wlan_idx;
++ bool ebf;
++ bool ibf;
++ bool mu_txbf;
++ bool phase_cal;
++ u8 rsv[2];
++} __packed;
++
++struct bf_phase_cal {
++ __le16 tag;
++ __le16 len;
++
++ u8 group_l_m_n;
++ u8 group;
++ u8 sx2;
++ u8 cal_type;
++ u8 lna_gain_level;
++ u8 band_idx;
++ u8 version;
++ u8 rsv[1];
++} __packed;
++
++struct bf_txcmd {
++ __le16 tag;
++ __le16 len;
++
++ u8 action;
++ u8 bf_manual;
++ u8 bf_bit;
++ u8 rsv[5];
++} __packed;
++
++struct bf_pfmu_data_all {
++ __le16 tag;
++ __le16 len;
++
++ u8 pfmu_id;
++ u8 band_idx;
++ u8 rsv[2];
++
++ u8 buf[640];
++} __packed;
++
++#define TXBF_DUT_MAC_SUBADDR 0x22
++#define TXBF_GOLDEN_MAC_SUBADDR 0x11
++
++struct mt7996_tm_bf_req {
++ u8 _rsv[4];
++
++ union {
++ struct bf_sounding_on sounding;
++ struct bf_tx_apply tx_apply;
++ struct bf_pfmu_tag pfmu_tag;
++ struct bf_pfmu_data_all pfmu_data_all;
++ struct bf_phase_cal phase_cal;
++ struct bf_phase_comp phase_comp;
++ struct bf_txcmd txcmd;
++ };
++} __packed;
++
++enum tm_trx_mac_type {
++ TM_TRX_MAC_TX = 1,
++ TM_TRX_MAC_RX,
++ TM_TRX_MAC_TXRX,
++ TM_TRX_MAC_TXRX_RXV,
++ TM_TRX_MAC_RXV,
++ TM_TRX_MAC_RX_RXV,
++};
++
++enum tm_trx_param_idx {
++ TM_TRX_PARAM_RSV,
++ /* MAC */
++ TM_TRX_PARAM_SET_TRX,
++ TM_TRX_PARAM_RX_FILTER,
++ TM_TRX_PARAM_RX_FILTER_PKT_LEN,
++ TM_TRX_PARAM_SLOT_TIME,
++ TM_TRX_PARAM_CLEAN_PERSTA_TXQUEUE,
++ TM_TRX_PARAM_AMPDU_WTBL,
++ TM_TRX_PARAM_MU_RX_AID,
++ TM_TRX_PARAM_PHY_MANUAL_TX,
++
++ /* PHY */
++ TM_TRX_PARAM_RX_PATH,
++ TM_TRX_PARAM_TX_STREAM,
++ TM_TRX_PARAM_TSSI_STATUS,
++ TM_TRX_PARAM_DPD_STATUS,
++ TM_TRX_PARAM_RATE_POWER_OFFSET_ON_OFF,
++ TM_TRX_PARAM_THERMO_COMP_STATUS,
++ TM_TRX_PARAM_FREQ_OFFSET,
++ TM_TRX_PARAM_FAGC_RSSI_PATH,
++ TM_TRX_PARAM_PHY_STATUS_COUNT,
++ TM_TRX_PARAM_RXV_INDEX,
++
++ TM_TRX_PARAM_ANTENNA_PORT,
++ TM_TRX_PARAM_THERMAL_ONOFF,
++ TM_TRX_PARAM_TX_POWER_CONTROL_ALL_RF,
++ TM_TRX_PARAM_RATE_POWER_OFFSET,
++ TM_TRX_PARAM_SLT_CMD_TEST,
++ TM_TRX_PARAM_SKU,
++ TM_TRX_PARAM_POWER_PERCENTAGE_ON_OFF,
++ TM_TRX_PARAM_BF_BACKOFF_ON_OFF,
++ TM_TRX_PARAM_POWER_PERCENTAGE_LEVEL,
++ TM_TRX_PARAM_FRTBL_CFG,
++ TM_TRX_PARAM_PREAMBLE_PUNC_ON_OFF,
++
++ TM_TRX_PARAM_MAX_NUM,
++};
++
++enum trx_action {
++ TM_TRX_ACTION_SET,
++ TM_TRX_ACTION_GET,
++};
++
++struct tm_trx_set {
++ u8 type;
++ u8 enable;
++ u8 band_idx;
++ u8 rsv;
++} __packed;
++
++struct mt7996_tm_trx_req {
++ u8 param_num;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++
++ __le16 param_idx;
++ u8 band_idx;
++ u8 testmode_en;
++ u8 action;
++ u8 rsv[3];
++
++ u32 data;
++ struct tm_trx_set set_trx;
++
++ u8 buf[220];
++} __packed;
++
+ struct mt7996_mcu_bf_basic_event {
+ struct mt7996_mcu_rxd rxd;
+
+@@ -394,6 +553,283 @@ struct mt7996_pfmu_tag_event {
+ struct mt7996_pfmu_tag2 t2;
+ };
+
++struct mt7996_pfmu_tag {
++ struct mt7996_pfmu_tag1 t1;
++ struct mt7996_pfmu_tag2 t2;
++};
++
++enum bf_lm_type {
++ BF_LM_LEGACY,
++ BF_LM_HT,
++ BF_LM_VHT,
++ BF_LM_HE,
++ BF_LM_EHT,
++};
++
++struct mt7996_txbf_phase_out {
++ u8 c0_l;
++ u8 c1_l;
++ u8 c2_l;
++ u8 c3_l;
++ u8 c0_m;
++ u8 c1_m;
++ u8 c2_m;
++ u8 c3_m;
++ u8 c0_mh;
++ u8 c1_mh;
++ u8 c2_mh;
++ u8 c3_mh;
++ u8 c0_h;
++ u8 c1_h;
++ u8 c2_h;
++ u8 c3_h;
++ u8 c0_uh;
++ u8 c1_uh;
++ u8 c2_uh;
++ u8 c3_uh;
++};
++
++struct mt7992_txbf_phase_out {
++ u8 c0_l;
++ u8 c1_l;
++ u8 c2_l;
++ u8 c3_l;
++ u8 c4_l;
++ u8 c0_m;
++ u8 c1_m;
++ u8 c2_m;
++ u8 c3_m;
++ u8 c4_m;
++ u8 c0_mh;
++ u8 c1_mh;
++ u8 c2_mh;
++ u8 c3_mh;
++ u8 c4_mh;
++ u8 c0_h;
++ u8 c1_h;
++ u8 c2_h;
++ u8 c3_h;
++ u8 c4_h;
++ u8 c0_uh;
++ u8 c1_uh;
++ u8 c2_uh;
++ u8 c3_uh;
++ u8 c4_uh;
++};
++
++struct txbf_rx_phase {
++ u8 rx_uh;
++ u8 rx_h;
++ u8 rx_m;
++ u8 rx_l;
++ u8 rx_ul;
++};
++
++struct txbf_rx_phase_ext {
++ u8 rx_uh;
++ u8 rx_h;
++ u8 rx_mh;
++ u8 rx_m;
++ u8 rx_l;
++ u8 rx_ul;
++};
++
++struct mt7996_txbf_phase_info_2g {
++ struct txbf_rx_phase r0;
++ struct txbf_rx_phase r1;
++ struct txbf_rx_phase r2;
++ struct txbf_rx_phase r3;
++ struct txbf_rx_phase r2_sx2;
++ struct txbf_rx_phase r3_sx2;
++ u8 m_t0_h;
++ u8 m_t1_h;
++ u8 m_t2_h;
++ u8 m_t2_h_sx2;
++ u8 r0_reserved;
++ u8 r1_reserved;
++ u8 r2_reserved;
++ u8 r3_reserved;
++ u8 r2_sx2_reserved;
++ u8 r3_sx2_reserved;
++};
++
++struct mt7996_txbf_phase_info_5g {
++ struct txbf_rx_phase_ext r0;
++ struct txbf_rx_phase_ext r1;
++ struct txbf_rx_phase_ext r2;
++ struct txbf_rx_phase_ext r3;
++ struct txbf_rx_phase r2_sx2; /* no middle-high in r2_sx2 */
++ struct txbf_rx_phase r3_sx2; /* no middle-high in r3_sx2 */
++ u8 m_t0_h;
++ u8 m_t1_h;
++ u8 m_t2_h;
++ u8 m_t2_h_sx2;
++ u8 r0_reserved;
++ u8 r1_reserved;
++ u8 r2_reserved;
++ u8 r3_reserved;
++ u8 r2_sx2_reserved;
++ u8 r3_sx2_reserved;
++};
++
++struct mt7992_txbf_phase_info_2g {
++ struct txbf_rx_phase_ext r0;
++ struct txbf_rx_phase_ext r1;
++ struct txbf_rx_phase_ext r2;
++ struct txbf_rx_phase_ext r3;
++ u8 m_t0_h;
++ u8 m_t1_h;
++ u8 m_t2_h;
++};
++
++struct mt7992_txbf_phase_info_5g {
++ struct txbf_rx_phase_ext r0;
++ struct txbf_rx_phase_ext r1;
++ struct txbf_rx_phase_ext r2;
++ struct txbf_rx_phase_ext r3;
++ struct txbf_rx_phase_ext r4;
++ u8 m_t0_h;
++ u8 m_t1_h;
++ u8 m_t2_h;
++ u8 m_t3_h;
++};
++
++struct mt7996_txbf_phase {
++ u8 status;
++ union {
++ union {
++ struct mt7996_txbf_phase_info_2g phase_2g;
++ struct mt7996_txbf_phase_info_5g phase_5g;
++ } v1;
++ union {
++ struct mt7992_txbf_phase_info_2g phase_2g;
++ struct mt7992_txbf_phase_info_5g phase_5g;
++ } v2;
++ u8 buf[44];
++ };
++};
++
++#define phase_assign(group, v, field, dump, ...) ({ \
++ if (group) { \
++ phase->v.phase_5g.field = cal->v.phase_5g.field; \
++ if (dump) \
++ dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->v.phase_5g.field); \
++ } else { \
++ phase->v.phase_2g.field = cal->v.phase_5g.field; \
++ if (dump) \
++ dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->v.phase_2g.field); \
++ } \
++})
++
++#define phase_assign_rx(group, v, rx, dump, ...) ({ \
++ phase_assign(group, v, rx.rx_uh, dump); \
++ phase_assign(group, v, rx.rx_h, dump); \
++ phase_assign(group, v, rx.rx_m, dump); \
++ phase_assign(group, v, rx.rx_l, dump); \
++ phase_assign(group, v, rx.rx_ul, dump); \
++})
++
++#define phase_assign_rx_ext(group, v, rx, dump, ...) ({ \
++ phase_assign(group, v, rx.rx_uh, dump); \
++ phase_assign(group, v, rx.rx_h, dump); \
++ phase_assign(group, v, rx.rx_mh, dump); \
++ phase_assign(group, v, rx.rx_m, dump); \
++ phase_assign(group, v, rx.rx_l, dump); \
++ phase_assign(group, v, rx.rx_ul, dump); \
++})
++
++#define phase_assign_rx_v1(group, v, rx, ...) ({ \
++ if (group) { \
++ phase_assign(group, v, rx.rx_uh, true); \
++ phase_assign(group, v, rx.rx_h, true); \
++ phase->v.phase_5g.rx.rx_mh = cal->v.phase_5g.rx.rx_mh; \
++ dev_info(dev->mt76.dev, "%s.rx_mh = %d\n", #rx, phase->v.phase_5g.rx.rx_mh); \
++ phase_assign(group, v, rx.rx_m, true); \
++ phase_assign(group, v, rx.rx_l, true); \
++ phase_assign(group, v, rx.rx_ul, true); \
++ } else { \
++ phase_assign_rx(group, v, rx, true, ...); \
++ } \
++})
++
++#define GROUP_L 0
++#define GROUP_M 1
++#define GROUP_H 2
++
++struct mt7996_pfmu_data {
++ __le16 subc_idx;
++ __le16 phi11;
++ __le16 phi21;
++ __le16 phi31;
++};
++
++struct mt7996_pfmu_data_5x5 {
++ __le16 subc_idx;
++ __le16 phi11;
++ __le16 phi21;
++ __le16 phi31;
++ __le16 phi41;
++};
++
++struct mt7996_ibf_cal_info {
++ struct mt7996_mcu_bf_basic_event event;
++
++ u8 category_id;
++ u8 group_l_m_n;
++ u8 group;
++ bool sx2;
++ u8 status;
++ u8 cal_type;
++ u8 nsts;
++ u8 version;
++ union {
++ struct {
++ struct mt7996_txbf_phase_out phase_out;
++ union {
++ struct mt7996_txbf_phase_info_2g phase_2g;
++ struct mt7996_txbf_phase_info_5g phase_5g;
++ };
++ } v1;
++ struct {
++ struct mt7992_txbf_phase_out phase_out;
++ union {
++ struct mt7992_txbf_phase_info_2g phase_2g;
++ struct mt7992_txbf_phase_info_5g phase_5g;
++ };
++ } v2;
++ u8 buf[64];
++ };
++} __packed;
++
++enum {
++ IBF_PHASE_CAL_UNSPEC,
++ IBF_PHASE_CAL_NORMAL,
++ IBF_PHASE_CAL_VERIFY,
++ IBF_PHASE_CAL_NORMAL_INSTRUMENT,
++ IBF_PHASE_CAL_VERIFY_INSTRUMENT,
++};
++
++enum ibf_version {
++ IBF_VER_1,
++ IBF_VER_2 = 3,
++};
++
++static inline int get_ibf_version(struct mt7996_dev *dev)
++{
++ switch (mt76_chip(&dev->mt76)) {
++ case 0x7990:
++ return IBF_VER_1;
++ case 0x7992:
++ default:
++ return IBF_VER_2;
++ }
++}
++
++#define MT7996_TXBF_SUBCAR_NUM 64
++#define MT7996_TXBF_PFMU_DATA_LEN (MT7996_TXBF_SUBCAR_NUM * sizeof(struct mt7996_pfmu_data))
++#define MT7996_TXBF_PFMU_DATA_LEN_5X5 (MT7996_TXBF_SUBCAR_NUM * \
++ sizeof(struct mt7996_pfmu_data_5x5))
++
+ enum {
+ UNI_EVENT_BF_PFMU_TAG = 0x5,
+ UNI_EVENT_BF_PFMU_DATA = 0x7,
+@@ -408,11 +844,6 @@ enum {
+ UNI_EVENT_BF_MAX_NUM
+ };
+
+-enum {
+- UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
+- UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
+-};
+-
+ struct uni_muru_mum_set_group_tbl_entry {
+ __le16 wlan_idx0;
+ __le16 wlan_idx1;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 8ec1dc1c6..263737c52 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -326,6 +326,9 @@ enum offs_rev {
+ #define MT_ARB_SCR_TX_DISABLE BIT(8)
+ #define MT_ARB_SCR_RX_DISABLE BIT(9)
+
++#define MT_ARB_TQSAXM0(_band) MT_WF_ARB(_band, 0x180)
++#define MT_ARB_TQSAXM_ALTX_START_MASK GENMASK(12, 8)
++
+ /* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */
+ #define MT_WF_RMAC_BASE(_band) __BASE(WF_RMAC_BASE, (_band))
+ #define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs))
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 836211f98..5cec1eef6 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -23,6 +23,7 @@ enum {
+ TM_CHANGED_IPI_THRESHOLD,
+ TM_CHANGED_IPI_PERIOD,
+ TM_CHANGED_IPI_RESET,
++ TM_CHANGED_TXBF_ACT,
+
+ /* must be last */
+ NUM_TM_CHANGED
+@@ -41,25 +42,31 @@ static const u8 tm_change_map[] = {
+ [TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
+ [TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
+ [TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
++ [TM_CHANGED_TXBF_ACT] = MT76_TM_ATTR_TXBF_ACT,
+ };
+
+ static void mt7996_tm_ipi_work(struct work_struct *work);
++static int mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx,
++ bool ebf, bool ibf, bool phase_cal);
+
+ static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
+ {
+ static const u32 width_to_bw[][NUM_BW_MAP] = {
+- [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
++ [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, BF_CDBW_40MHZ, 40,
+ FIRST_CONTROL_CHAN_BITMAP_BW40},
+- [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
++ [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, BF_CDBW_80MHZ, 80,
+ FIRST_CONTROL_CHAN_BITMAP_BW80},
+- [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
+- [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
++ [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, BF_CDBW_8080MHZ,
++ 80, 0x0},
++ [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, BF_CDBW_160MHZ, 160,
+ FIRST_CONTROL_CHAN_BITMAP_BW160},
+- [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
+- [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
+- [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
+- [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
+- [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
++ [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, BF_CDBW_5MHZ, 5, 0x0},
++ [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, BF_CDBW_10MHZ, 10, 0x0},
++ [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ, 20, 0x0},
++ [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ,
++ 20, 0x0},
++ [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, BF_CDBW_320MHZ,
++ 320, 0x0},
+ };
+
+ if (width >= ARRAY_SIZE(width_to_bw))
+@@ -68,26 +75,26 @@ static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_m
+ return width_to_bw[width][method];
+ }
+
+-static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
++static u8 mt7996_tm_rate_mapping(u8 tx_rate_mode, enum rate_mapping_type type)
+ {
+- static const u8 rate_to_phy[] = {
+- [MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
+- [MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
+- [MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
+- [MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
+- [MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
+- [MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
+- [MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
+- [MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
+- [MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
+- [MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
+- [MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
++ static const u8 rate_to_phy[][NUM_RATE_MAP] = {
++ [MT76_TM_TX_MODE_CCK] = {MT_PHY_TYPE_CCK, BF_LM_LEGACY},
++ [MT76_TM_TX_MODE_OFDM] = {MT_PHY_TYPE_OFDM, BF_LM_LEGACY},
++ [MT76_TM_TX_MODE_HT] = {MT_PHY_TYPE_HT, BF_LM_HT},
++ [MT76_TM_TX_MODE_VHT] = {MT_PHY_TYPE_VHT, BF_LM_VHT},
++ [MT76_TM_TX_MODE_HE_SU] = {MT_PHY_TYPE_HE_SU, BF_LM_HE},
++ [MT76_TM_TX_MODE_HE_EXT_SU] = {MT_PHY_TYPE_HE_EXT_SU, BF_LM_HE},
++ [MT76_TM_TX_MODE_HE_TB] = {MT_PHY_TYPE_HE_TB, BF_LM_HE},
++ [MT76_TM_TX_MODE_HE_MU] = {MT_PHY_TYPE_HE_MU, BF_LM_HE},
++ [MT76_TM_TX_MODE_EHT_SU] = {MT_PHY_TYPE_EHT_SU, BF_LM_EHT},
++ [MT76_TM_TX_MODE_EHT_TRIG] = {MT_PHY_TYPE_EHT_TRIG, BF_LM_EHT},
++ [MT76_TM_TX_MODE_EHT_MU] = {MT_PHY_TYPE_EHT_MU, BF_LM_EHT},
+ };
+
+ if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
+ return -EINVAL;
+
+- return rate_to_phy[tx_rate_mode];
++ return rate_to_phy[tx_rate_mode][type];
+ }
+
+ static int
+@@ -239,7 +246,7 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
+ }
+
+-static void
++void
+ mt7996_tm_update_channel(struct mt7996_phy *phy)
+ {
+ #define CHAN_FREQ_BW_80P80_TAG (SET_ID(CHAN_FREQ) | BIT(16))
+@@ -303,7 +310,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
+ mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
+ mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
+ mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
+- mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++ mt7996_tm_set(dev, SET_ID(TX_MODE),
++ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
+ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
+
+ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
+@@ -331,7 +339,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
+
+ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
+ mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
+- mt7996_tm_update_channel(phy);
++ if (!td->bf_en)
++ mt7996_tm_update_channel(phy);
+
+ /* trigger firmware to start TX */
+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
+@@ -373,7 +382,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
+ return;
+ }
+
+- mt7996_tm_update_channel(phy);
++ if (!td->bf_en)
++ mt7996_tm_update_channel(phy);
+
+ if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
+ if (td->aid)
+@@ -381,7 +391,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
+ else
+ ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
+ }
+- mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++ mt7996_tm_set(dev, SET_ID(TX_MODE),
++ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
+ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
+ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
+ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
+@@ -405,7 +416,8 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
+
+ if (en) {
+ mt7996_tm_update_channel(phy);
+- mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++ mt7996_tm_set(dev, SET_ID(TX_MODE),
++ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
+ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
+ /* fix payload is OFDM */
+ mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
+@@ -1047,6 +1059,730 @@ mt7996_tm_set_ipi(struct mt7996_phy *phy)
+ return 0;
+ }
+
++static int
++mt7996_tm_set_trx_mac(struct mt7996_phy *phy, u8 type, bool en)
++{
++#define UNI_TM_TRX_CTRL 0
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_trx_req req = {
++ .param_num = 1,
++ .tag = cpu_to_le16(UNI_TM_TRX_CTRL),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .param_idx = cpu_to_le16(TM_TRX_PARAM_SET_TRX),
++ .band_idx = phy->mt76->band_idx,
++ .testmode_en = 1,
++ .action = TM_TRX_ACTION_SET,
++ .set_trx = {
++ .type = type,
++ .enable = en,
++ .band_idx = phy->mt76->band_idx,
++ }
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_TRX_PARAM),
++ &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
++{
++#define EBF_BBP_RX_OFFSET 0x10280
++#define EBF_BBP_RX_ENABLE (BIT(0) | BIT(15))
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ bool enable = val[0];
++ void *phase_cal, *pfmu_data, *pfmu_tag;
++ u8 nss, band_idx = phy->mt76->band_idx;
++ enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
++ u8 sub_addr = td->is_txbf_dut ? TXBF_DUT_MAC_SUBADDR : TXBF_GOLDEN_MAC_SUBADDR;
++ u8 peer_addr = td->is_txbf_dut ? TXBF_GOLDEN_MAC_SUBADDR : TXBF_DUT_MAC_SUBADDR;
++ u8 bss_addr = TXBF_DUT_MAC_SUBADDR;
++ u8 addr[ETH_ALEN] = {0x00, sub_addr, sub_addr, sub_addr, sub_addr, sub_addr};
++ u8 bssid[ETH_ALEN] = {0x00, bss_addr, bss_addr, bss_addr, bss_addr, bss_addr};
++ u8 peer_addrs[ETH_ALEN] = {0x00, peer_addr, peer_addr, peer_addr, peer_addr, peer_addr};
++ struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv;
++
++ if (!enable) {
++ td->bf_en = false;
++ return 0;
++ }
++
++ if (!dev->test.txbf_phase_cal) {
++ phase_cal = devm_kzalloc(dev->mt76.dev,
++ sizeof(struct mt7996_txbf_phase) *
++ MAX_PHASE_GROUP_NUM,
++ GFP_KERNEL);
++ if (!phase_cal)
++ return -ENOMEM;
++
++ dev->test.txbf_phase_cal = phase_cal;
++ }
++
++ if (!dev->test.txbf_pfmu_data) {
++ /* allocate max size for 5x5 pfmu data */
++ pfmu_data = devm_kzalloc(dev->mt76.dev,
++ MT7996_TXBF_PFMU_DATA_LEN_5X5,
++ GFP_KERNEL);
++ if (!pfmu_data)
++ return -ENOMEM;
++
++ dev->test.txbf_pfmu_data = pfmu_data;
++ }
++
++ if (!dev->test.txbf_pfmu_tag) {
++ pfmu_tag = devm_kzalloc(dev->mt76.dev,
++ sizeof(struct mt7996_pfmu_tag), GFP_KERNEL);
++ if (!pfmu_tag)
++ return -ENOMEM;
++
++ dev->test.txbf_pfmu_tag = pfmu_tag;
++ }
++
++ td->bf_en = true;
++ dev->ibf = td->ibf;
++ memcpy(td->addr[0], peer_addrs, ETH_ALEN);
++ memcpy(td->addr[1], addr, ETH_ALEN);
++ memcpy(td->addr[2], bssid, ETH_ALEN);
++ memcpy(phy->monitor_vif->addr, addr, ETH_ALEN);
++ mt7996_tm_set_mac_addr(dev, td->addr[0], SET_ID(DA));
++ mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
++ mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
++
++ /* bss idx & omac idx should be set to band idx for ibf cal */
++ mvif->mt76.idx = band_idx;
++ dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
++ mvif->mt76.omac_idx = band_idx;
++ phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
++
++ mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true);
++ mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true);
++
++ if (td->ibf) {
++ if (td->is_txbf_dut) {
++ /* Enable ITxBF Capability */
++ mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
++ mt7996_tm_set_trx_mac(phy, TM_TRX_MAC_TX, true);
++
++ td->tx_ipg = 999;
++ td->tx_mpdu_len = 1024;
++ td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
++ nss = hweight8(td->tx_antenna_mask);
++ if (nss > 1 && nss <= 4)
++ td->tx_rate_idx = 15 + 8 * (nss - 2);
++ else
++ td->tx_rate_idx = 31;
++ } else {
++ td->tx_antenna_mask = 1;
++ td->tx_mpdu_len = 1024;
++ td->tx_rate_idx = 0;
++ mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
++ dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
++ mt76_rr(dev, EBF_BBP_RX_OFFSET));
++ }
++
++ td->tx_rate_mode = MT76_TM_TX_MODE_HT;
++ td->tx_rate_sgi = 0;
++ /* 5T5R ibf */
++ if (nss == 5) {
++ td->tx_rate_mode = MT76_TM_TX_MODE_VHT;
++ td->tx_rate_idx = 7;
++ }
++ } else {
++ if (td->is_txbf_dut) {
++ /* Enable ETxBF Capability */
++ mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
++ td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
++ td->tx_spe_idx = 24 + phy->mt76->band_idx;
++ if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT ||
++ td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU)
++ mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
++
++ mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
++ mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
++ } else {
++ /* Turn On BBP CR for RX */
++ mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
++ dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
++ mt76_rr(dev, EBF_BBP_RX_OFFSET));
++
++ td->tx_antenna_mask = 1;
++ }
++ width = phy->mt76->chandef.width;
++
++ if (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_MU)
++ td->tx_rate_mode = MT76_TM_TX_MODE_EHT_SU;
++ }
++ mt76_testmode_param_set(td, MT76_TM_ATTR_TX_ANTENNA);
++
++ mt7996_tm_set(dev, SET_ID(TX_MODE),
++ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
++ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
++ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
++ mt7996_tm_set(dev, SET_ID(CBW),
++ mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++ mt7996_tm_set(dev, SET_ID(DBW),
++ mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++ mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
++ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
++ mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
++ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
++ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
++ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(TX_COMMIT));
++
++ return 0;
++}
++
++static inline void
++mt7996_tm_txbf_phase_copy(struct mt7996_dev *dev, void *des, void *src, int group)
++{
++ int phase_size;
++
++ if (group && get_ibf_version(dev) == IBF_VER_1)
++ phase_size = sizeof(struct mt7996_txbf_phase_info_5g);
++ else if (get_ibf_version(dev) == IBF_VER_1)
++ phase_size = sizeof(struct mt7996_txbf_phase_info_2g);
++ else if (group)
++ phase_size = sizeof(struct mt7992_txbf_phase_info_5g);
++ else
++ phase_size = sizeof(struct mt7992_txbf_phase_info_2g);
++
++ memcpy(des, src, phase_size);
++}
++
++static int
++mt7996_tm_txbf_phase_comp(struct mt7996_phy *phy, u16 *val)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_bf_req req = {
++ .phase_comp = {
++ .tag = cpu_to_le16(BF_IBF_PHASE_COMP),
++ .len = cpu_to_le16(sizeof(req.phase_comp)),
++ .bw = val[0],
++ .jp_band = (val[2] == 1) ? 1 : 0,
++ .band_idx = phy->mt76->band_idx,
++ .read_from_e2p = val[3],
++ .disable = val[4],
++ .group = val[2],
++ }
++ };
++ struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++ int group = val[2];
++
++ wait_event_timeout(dev->mt76.tx_wait, phase[group].status != 0, HZ);
++ mt7996_tm_txbf_phase_copy(dev, req.phase_comp.buf, phase[group].buf, group);
++
++ pr_info("ibf cal process: phase comp info\n");
++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
++ &req, sizeof(req), 0);
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
++ sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_profile_tag_write(struct mt7996_phy *phy, u8 pfmu_idx, struct mt7996_pfmu_tag *tag)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_bf_req req = {
++ .pfmu_tag = {
++ .tag = cpu_to_le16(BF_PFMU_TAG_WRITE),
++ .len = cpu_to_le16(sizeof(req.pfmu_tag)),
++ .pfmu_id = pfmu_idx,
++ .bfer = true,
++ .band_idx = phy->mt76->band_idx,
++ }
++ };
++
++ memcpy(req.pfmu_tag.buf, tag, sizeof(*tag));
++ wait_event_timeout(dev->mt76.tx_wait, tag->t1.pfmu_idx != 0, HZ);
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
++ sizeof(req), false);
++}
++
++static int
++mt7996_tm_add_txbf_sta(struct mt7996_phy *phy, u8 pfmu_idx, u8 nr, u8 nc, bool ebf)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct {
++ struct sta_req_hdr hdr;
++ struct sta_rec_bf bf;
++ } __packed req = {
++ .hdr = {
++ .bss_idx = phy->mt76->band_idx,
++ .wlan_idx_lo = to_wcid_lo(phy->mt76->band_idx + 1),
++ .tlv_num = 1,
++ .is_tlv_append = 1,
++ .muar_idx = 0,
++ .wlan_idx_hi = to_wcid_hi(phy->mt76->band_idx + 1),
++ },
++ .bf = {
++ .tag = cpu_to_le16(STA_REC_BF),
++ .len = cpu_to_le16(sizeof(req.bf)),
++ .pfmu = cpu_to_le16(pfmu_idx),
++ .sounding_phy = 1,
++ .bf_cap = ebf,
++ .ncol = nc,
++ .nrow = nr,
++ .ibf_timeout = 0xff,
++ .tx_mode = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY),
++ },
++ };
++ u8 ndp_rate, ndpa_rate, rept_poll_rate, bf_bw;
++
++ if ((td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU ||
++ td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) && !td->ibf) {
++ rept_poll_rate = 0x49;
++ ndpa_rate = 0x49;
++ ndp_rate = 0;
++ } else if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT && !td->ibf) {
++ rept_poll_rate = 0x9;
++ ndpa_rate = 0x9;
++ ndp_rate = 0;
++ } else {
++ rept_poll_rate = 0;
++ ndpa_rate = 0;
++ if (nr == 1)
++ ndp_rate = 8;
++ else if (nr == 2)
++ ndp_rate = 16;
++ else if (nr == 4)
++ ndp_rate = 32;
++ else
++ ndp_rate = 24;
++
++ /* 5T5R ebf profile for ibf cal */
++ if (nr == 4 && td->ibf && ebf) {
++ ndp_rate = 0;
++ ndpa_rate = 11;
++ }
++ }
++
++ bf_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
++ req.bf.ndp_rate = ndp_rate;
++ req.bf.ndpa_rate = ndpa_rate;
++ req.bf.rept_poll_rate = rept_poll_rate;
++ req.bf.bw = bf_bw;
++ req.bf.tx_mode = (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) ? 0xf : req.bf.tx_mode;
++
++ if (ebf) {
++ req.bf.mem[0].row = 0;
++ req.bf.mem[1].row = 1;
++ req.bf.mem[2].row = 2;
++ req.bf.mem[3].row = 3;
++ } else {
++ req.bf.mem[0].row = 4;
++ req.bf.mem[1].row = 5;
++ req.bf.mem[2].row = 6;
++ req.bf.mem[3].row = 7;
++ }
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), &req,
++ sizeof(req), true);
++}
++
++static int
++mt7996_tm_txbf_profile_update(struct mt7996_phy *phy, u16 *val, bool ebf)
++{
++#define MT_ARB_IBF_ENABLE (BIT(0) | GENMASK(9, 8))
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
++ u8 rate, pfmu_idx = val[0], nc = val[2], nr;
++ int ret;
++ bool is_atenl = val[5];
++
++ if (td->tx_antenna_mask == 3)
++ nr = 1;
++ else if (td->tx_antenna_mask == 7)
++ nr = 2;
++ else if (td->tx_antenna_mask == 31)
++ nr = 4;
++ else
++ nr = 3;
++
++ memset(tag, 0, sizeof(*tag));
++ tag->t1.pfmu_idx = pfmu_idx;
++ tag->t1.ebf = ebf;
++ tag->t1.nr = nr;
++ tag->t1.nc = nc;
++ tag->t1.invalid_prof = true;
++ tag->t1.data_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
++ tag->t2.se_idx = td->tx_spe_idx;
++
++ if (ebf) {
++ tag->t1.row_id1 = 0;
++ tag->t1.row_id2 = 1;
++ tag->t1.row_id3 = 2;
++ tag->t1.row_id4 = 3;
++ tag->t1.lm = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_LM);
++ } else {
++ tag->t1.row_id1 = 4;
++ tag->t1.row_id2 = 5;
++ tag->t1.row_id3 = 6;
++ tag->t1.row_id4 = 7;
++ rate = nr == 4 ? td->tx_rate_mode : MT76_TM_TX_MODE_OFDM;
++ tag->t1.lm = mt7996_tm_rate_mapping(rate, RATE_MODE_TO_LM);
++
++ tag->t2.ibf_timeout = 0xff;
++ tag->t2.ibf_nr = nr;
++ tag->t2.ibf_nc = nc;
++ }
++
++ ret = mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
++ if (ret)
++ return ret;
++
++ ret = mt7996_tm_add_txbf_sta(phy, pfmu_idx, nr, nc, ebf);
++ if (ret)
++ return ret;
++
++ if (!is_atenl && !td->ibf) {
++ mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_TQSAXM_ALTX_START_MASK);
++ dev_info(dev->mt76.dev, "Set TX queue start CR for AX management (0x%x) = 0x%x\n",
++ MT_ARB_TQSAXM0(phy->mt76->band_idx),
++ mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx)));
++ } else if (!is_atenl && td->ibf && ebf) {
++ /* iBF's ebf profile update */
++ mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_IBF_ENABLE);
++ dev_info(dev->mt76.dev, "Set TX queue start CR for AX management (0x%x) = 0x%x\n",
++ MT_ARB_TQSAXM0(phy->mt76->band_idx),
++ mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx)));
++ }
++
++ if (!ebf && is_atenl)
++ return mt7996_tm_txbf_apply_tx(phy, 1, false, true, true);
++
++ return 0;
++}
++
++static int
++mt7996_tm_txbf_phase_cal(struct mt7996_phy *phy, u16 *val)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_bf_req req = {
++ .phase_cal = {
++ .tag = cpu_to_le16(BF_PHASE_CALIBRATION),
++ .len = cpu_to_le16(sizeof(req.phase_cal)),
++ .group = val[0],
++ .group_l_m_n = val[1],
++ .sx2 = val[2],
++ .cal_type = val[3],
++ .lna_gain_level = val[4],
++ .band_idx = phy->mt76->band_idx,
++ .version = val[5],
++ },
++ };
++ struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++
++ /* reset phase status before update phase cal data */
++ phase[req.phase_cal.group].status = 0;
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
++ sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_profile_update_all(struct mt7996_phy *phy, u16 *val)
++{
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ u8 nss = hweight8(td->tx_antenna_mask);
++ u16 pfmu_idx = val[0];
++ u16 subc_id = val[1];
++ u16 angle11 = val[2];
++ u16 angle21 = val[3];
++ u16 angle31 = val[4];
++ u16 angle41 = val[5];
++ u16 angle51 = val[6];
++ s16 phi11 = 0, phi21 = 0, phi31 = 0, phi41 = 0;
++ s16 *pfmu_data;
++ int offs = subc_id * sizeof(struct mt7996_pfmu_data) / sizeof(*pfmu_data);
++
++ if (subc_id > MT7996_TXBF_SUBCAR_NUM - 1)
++ return -EINVAL;
++
++ if (nss == 2) {
++ phi11 = (s16)(angle21 - angle11);
++ } else if (nss == 3) {
++ phi11 = (s16)(angle31 - angle11);
++ phi21 = (s16)(angle31 - angle21);
++ } else if (nss == 5) {
++ phi11 = (s16)(angle51 - angle11);
++ phi21 = (s16)(angle51 - angle21);
++ phi31 = (s16)(angle51 - angle31);
++ phi41 = (s16)(angle51 - angle41);
++ offs = subc_id * sizeof(struct mt7996_pfmu_data_5x5) / sizeof(*pfmu_data);
++ } else {
++ phi11 = (s16)(angle41 - angle11);
++ phi21 = (s16)(angle41 - angle21);
++ phi31 = (s16)(angle41 - angle31);
++ }
++
++ pfmu_data = (s16 *)phy->dev->test.txbf_pfmu_data;
++ pfmu_data += offs;
++
++ if (subc_id < 32)
++ pfmu_data[0] = cpu_to_le16(subc_id + 224);
++ else
++ pfmu_data[0] = cpu_to_le16(subc_id - 32);
++
++ pfmu_data[1] = cpu_to_le16(phi11);
++ pfmu_data[2] = cpu_to_le16(phi21);
++ pfmu_data[3] = cpu_to_le16(phi31);
++ if (nss == 5)
++ pfmu_data[4] = cpu_to_le16(phi41);
++
++ if (subc_id == MT7996_TXBF_SUBCAR_NUM - 1) {
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_bf_req req = {
++ .pfmu_data_all = {
++ .tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL_5X5),
++ .len = cpu_to_le16(sizeof(req.pfmu_data_all)),
++ .pfmu_id = pfmu_idx,
++ .band_idx = phy->mt76->band_idx,
++ },
++ };
++ int size = MT7996_TXBF_PFMU_DATA_LEN_5X5;
++
++ if (nss != 5) {
++ size = MT7996_TXBF_PFMU_DATA_LEN;
++ req.pfmu_data_all.tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL);
++ req.pfmu_data_all.len = cpu_to_le16(sizeof(req.pfmu_data_all) -
++ MT7996_TXBF_PFMU_DATA_LEN_5X5 + size);
++ }
++ memcpy(req.pfmu_data_all.buf, dev->test.txbf_pfmu_data, size);
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
++ &req, sizeof(req), true);
++ }
++
++ return 0;
++}
++
++static int
++mt7996_tm_txbf_e2p_update(struct mt7996_phy *phy)
++{
++#define TXBF_PHASE_EEPROM_START_OFFSET 0xc00
++#define TXBF_PHASE_GROUP_EEPROM_OFFSET_VER_1 46
++#define TXBF_PHASE_G0_EEPROM_OFFSET_VER_2 sizeof(struct mt7992_txbf_phase_info_2g)
++#define TXBF_PHASE_GX_EEPROM_OFFSET_VER_2 sizeof(struct mt7992_txbf_phase_info_5g)
++ struct mt7996_txbf_phase *phase, *p;
++ struct mt7996_dev *dev = phy->dev;
++ u8 *eeprom = dev->mt76.eeprom.data;
++ u16 offset;
++ int i;
++
++ offset = TXBF_PHASE_EEPROM_START_OFFSET;
++ phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++ for (i = 0; i < MAX_PHASE_GROUP_NUM; i++) {
++ p = &phase[i];
++
++ if (!p->status)
++ continue;
++
++ /* copy phase cal data to eeprom */
++ mt7996_tm_txbf_phase_copy(dev, eeprom + offset, p->buf, i);
++ if (get_ibf_version(dev) == IBF_VER_1)
++ offset += TXBF_PHASE_GROUP_EEPROM_OFFSET_VER_1;
++ else
++ offset += i ? TXBF_PHASE_GX_EEPROM_OFFSET_VER_2 :
++ TXBF_PHASE_G0_EEPROM_OFFSET_VER_2;
++ }
++
++ return 0;
++}
++
++static int
++mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx, bool ebf,
++ bool ibf, bool phase_cal)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_bf_req req = {
++ .tx_apply = {
++ .tag = cpu_to_le16(BF_DATA_PACKET_APPLY),
++ .len = cpu_to_le16(sizeof(req.tx_apply)),
++ .wlan_idx = cpu_to_le16(wlan_idx),
++ .ebf = ebf,
++ .ibf = ibf,
++ .phase_cal = phase_cal,
++ },
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_set_tx(struct mt7996_phy *phy, u16 *val)
++{
++ bool bf_on = val[0], update = val[3];
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
++ struct mt76_testmode_data *td = &phy->mt76->test;
++
++ if (bf_on) {
++ mt7996_tm_set_rx_frames(phy, false);
++ mt7996_tm_set_tx_frames(phy, false);
++ mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
++ tag->t1.invalid_prof = false;
++ mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
++ td->bf_ever_en = true;
++
++ if (update)
++ mt7996_tm_txbf_apply_tx(phy, 1, 0, 1, 1);
++ } else {
++ if (!td->bf_ever_en) {
++ mt7996_tm_set_rx_frames(phy, false);
++ mt7996_tm_set_tx_frames(phy, false);
++ td->ibf = false;
++ td->ebf = false;
++
++ if (update)
++ mt7996_tm_txbf_apply_tx(phy, 1, 0, 0, 0);
++ } else {
++ td->bf_ever_en = false;
++
++ mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
++ tag->t1.invalid_prof = true;
++ mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
++ }
++ }
++
++ return 0;
++}
++
++static int
++mt7996_tm_trigger_sounding(struct mt7996_phy *phy, u16 *val, bool en)
++{
++ struct mt7996_dev *dev = phy->dev;
++ u8 sounding_mode = val[0];
++ u8 sta_num = val[1];
++ u32 sounding_interval = (u32)val[2] << 2; /* input unit: 4ms */
++ u16 tag = en ? BF_SOUNDING_ON : BF_SOUNDING_OFF;
++ struct mt7996_tm_bf_req req = {
++ .sounding = {
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(sizeof(req.sounding)),
++ .snd_mode = sounding_mode,
++ .sta_num = sta_num,
++ .wlan_id = {
++ cpu_to_le16(val[3]),
++ cpu_to_le16(val[4]),
++ cpu_to_le16(val[5]),
++ cpu_to_le16(val[6])
++ },
++ .snd_period = cpu_to_le32(sounding_interval),
++ },
++ };
++
++ if (sounding_mode > SOUNDING_MODE_MAX)
++ return -EINVAL;
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
++ &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_txcmd(struct mt7996_phy *phy, u16 *val)
++{
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_tm_bf_req req = {
++ .txcmd = {
++ .tag = cpu_to_le16(BF_CMD_TXCMD),
++ .len = cpu_to_le16(sizeof(req.txcmd)),
++ .action = val[0],
++ .bf_manual = val[1],
++ .bf_bit = val[2],
++ },
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_set_txbf(struct mt7996_phy *phy)
++{
++#define TXBF_IS_DUT_MASK BIT(0)
++#define TXBF_IBF_MASK BIT(1)
++ struct mt76_testmode_data *td = &phy->mt76->test;
++ u16 *val = td->txbf_param;
++
++ dev_info(phy->dev->mt76.dev,
++ "ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u, %u, %u\n",
++ td->txbf_act, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
++
++ switch (td->txbf_act) {
++ case MT76_TM_TXBF_ACT_GOLDEN_INIT:
++ case MT76_TM_TXBF_ACT_INIT:
++ case MT76_TM_TX_EBF_ACT_GOLDEN_INIT:
++ case MT76_TM_TX_EBF_ACT_INIT:
++ td->ibf = !u32_get_bits(td->txbf_act, TXBF_IBF_MASK);
++ td->ebf = true;
++ td->is_txbf_dut = !!u32_get_bits(td->txbf_act, TXBF_IS_DUT_MASK);
++ return mt7996_tm_txbf_init(phy, val);
++ case MT76_TM_TXBF_ACT_UPDATE_CH:
++ mt7996_tm_update_channel(phy);
++ break;
++ case MT76_TM_TXBF_ACT_PHASE_COMP:
++ return mt7996_tm_txbf_phase_comp(phy, val);
++ case MT76_TM_TXBF_ACT_TX_PREP:
++ return mt7996_tm_txbf_set_tx(phy, val);
++ case MT76_TM_TXBF_ACT_IBF_PROF_UPDATE:
++ return mt7996_tm_txbf_profile_update(phy, val, false);
++ case MT76_TM_TXBF_ACT_EBF_PROF_UPDATE:
++ return mt7996_tm_txbf_profile_update(phy, val, true);
++ case MT76_TM_TXBF_ACT_PHASE_CAL:
++ return mt7996_tm_txbf_phase_cal(phy, val);
++ case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD:
++ case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL:
++ return mt7996_tm_txbf_profile_update_all(phy, val);
++ case MT76_TM_TXBF_ACT_E2P_UPDATE:
++ return mt7996_tm_txbf_e2p_update(phy);
++ case MT76_TM_TXBF_ACT_APPLY_TX: {
++ u16 wlan_idx = val[0];
++ bool ebf = !!val[1], ibf = !!val[2], phase_cal = !!val[4];
++
++ return mt7996_tm_txbf_apply_tx(phy, wlan_idx, ebf, ibf, phase_cal);
++ }
++ case MT76_TM_TXBF_ACT_TRIGGER_SOUNDING:
++ return mt7996_tm_trigger_sounding(phy, val, true);
++ case MT76_TM_TXBF_ACT_STOP_SOUNDING:
++ memset(val, 0, sizeof(td->txbf_param));
++ return mt7996_tm_trigger_sounding(phy, val, false);
++ case MT76_TM_TXBF_ACT_PROFILE_TAG_READ:
++ case MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE:
++ case MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: {
++ u8 pfmu_idx = val[0];
++ bool bfer = !!val[1];
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
++
++ if (!tag) {
++ dev_err(dev->mt76.dev,
++ "pfmu tag is not initialized!\n");
++ return 0;
++ }
++
++ if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE)
++ return mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
++ else if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_READ)
++ return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, pfmu_idx, bfer);
++
++ tag->t1.invalid_prof = !!val[0];
++
++ return 0;
++ }
++ case MT76_TM_TXBF_ACT_STA_REC_READ:
++ return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, val[0], 0);
++ case MT76_TM_TXBF_ACT_TXCMD:
++ return mt7996_tm_txbf_txcmd(phy, val);
++ default:
++ break;
++ };
++
++ return 0;
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -1086,6 +1822,8 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ mt7996_tm_set_ipi(phy);
+ if (changed & BIT(TM_CHANGED_IPI_RESET))
+ mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
++ if (changed & BIT(TM_CHANGED_TXBF_ACT))
++ mt7996_tm_set_txbf(phy);
+ }
+
+ static int
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index 78662b2ed..f97ccb267 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -27,6 +27,17 @@ enum {
+ FW_CDBW_8080MHZ,
+ };
+
++enum {
++ BF_CDBW_20MHZ,
++ BF_CDBW_40MHZ,
++ BF_CDBW_80MHZ,
++ BF_CDBW_160MHZ,
++ BF_CDBW_320MHZ,
++ BF_CDBW_10MHZ = BF_CDBW_320MHZ,
++ BF_CDBW_5MHZ,
++ BF_CDBW_8080MHZ,
++};
++
+ #define FIRST_CONTROL_CHAN_BITMAP_BW40 0x5555555
+ #define FIRST_CONTROL_CHAN_BITMAP_BW80 0x111111
+ #define FIRST_CONTROL_CHAN_BITMAP_BW160 0x100101
+@@ -34,12 +45,20 @@ enum {
+ enum bw_mapping_method {
+ BW_MAP_NL_TO_FW,
+ BW_MAP_NL_TO_TM,
++ BW_MAP_NL_TO_BF,
+ BW_MAP_NL_TO_MHZ,
+ BW_MAP_NL_TO_CONTROL_BITMAP_5G,
+
+ NUM_BW_MAP,
+ };
+
++enum rate_mapping_type {
++ RATE_MODE_TO_PHY,
++ RATE_MODE_TO_LM,
++
++ NUM_RATE_MAP,
++};
++
+ struct tm_cal_param {
+ __le32 func_data;
+ u8 band_idx;
+diff --git a/testmode.c b/testmode.c
+index 69147f866..b9f710970 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -462,6 +462,44 @@ out:
+ return err;
+ }
+
++static int
++mt76_testmode_txbf_profile_update_all_cmd(struct mt76_phy *phy, struct nlattr **tb, u32 state)
++{
++#define PARAM_UNIT 5
++#define PARAM_UNIT_5X5 6
++ static u8 pfmu_idx;
++ struct mt76_testmode_data *td = &phy->test;
++ struct mt76_dev *dev = phy->dev;
++ struct nlattr *cur;
++ u16 tmp_val[PARAM_UNIT_5X5], *val = td->txbf_param;
++ int idx, rem, ret, i = 0;
++ int param_len = td->tx_antenna_mask == 31 ? PARAM_UNIT_5X5 : PARAM_UNIT;
++
++ memset(td->txbf_param, 0, sizeof(td->txbf_param));
++ nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
++ if (nla_len(cur) != 2)
++ return -EINVAL;
++ idx = i % param_len;
++ tmp_val[idx] = nla_get_u16(cur);
++ if (idx == 1 && (tmp_val[idx] == 0xf0 || tmp_val[idx] == 0xff)) {
++ pfmu_idx = tmp_val[0];
++ return 0;
++ }
++ if (idx == param_len - 1) {
++ val[0] = pfmu_idx;
++ memcpy(val + 1, tmp_val, param_len * sizeof(u16));
++ if (dev->test_ops->set_params) {
++ ret = dev->test_ops->set_params(phy, tb, state);
++ if (ret)
++ return ret;
++ }
++ }
++ i++;
++ }
++
++ return 0;
++}
++
+ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len)
+ {
+@@ -607,6 +645,30 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ }
+ }
+
++ if (tb[MT76_TM_ATTR_TXBF_ACT]) {
++ struct nlattr *cur;
++ int rem, idx = 0;
++
++ if (!tb[MT76_TM_ATTR_TXBF_PARAM] ||
++ mt76_tm_get_u8(tb[MT76_TM_ATTR_TXBF_ACT], &td->txbf_act,
++ 0, MT76_TM_TXBF_ACT_MAX))
++ goto out;
++
++ if (td->txbf_act == MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD) {
++ err = mt76_testmode_txbf_profile_update_all_cmd(phy, tb, state);
++ goto out;
++ }
++
++ memset(td->txbf_param, 0, sizeof(td->txbf_param));
++ nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
++ if (nla_len(cur) != 2 ||
++ idx >= ARRAY_SIZE(td->txbf_param))
++ goto out;
++
++ td->txbf_param[idx++] = nla_get_u16(cur);
++ }
++ }
++
+ if (dev->test_ops->set_params) {
+ err = dev->test_ops->set_params(phy, tb, state);
+ if (err)
+diff --git a/testmode.h b/testmode.h
+index 5d677f8c1..bda7624ad 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -286,6 +286,59 @@ enum mt76_testmode_eeprom_action {
+ MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
+ };
+
++/**
++ * enum mt76_testmode_txbf_act - txbf action
++ *
++ * @MT76_TM_TXBF_ACT_GOLDEN_INIT: init ibf setting for golden device
++ * @MT76_TM_TXBF_ACT_INIT: init ibf setting for DUT
++ * @MT76_TM_TX_EBF_ACT_GOLDEN_INIT: init ebf setting for golden device
++ * @MT76_TM_TX_EBF_ACT_INIT: init ebf setting for DUT
++ * @MT76_TM_TXBF_ACT_UPDATE_CH: update channel info
++ * @MT76_TM_TXBF_ACT_PHASE_COMP: txbf phase compensation
++ * @MT76_TM_TXBF_ACT_TX_PREP: TX preparation for txbf
++ * @MT76_TM_TXBF_ACT_IBF_PROF_UPDATE: update ibf profile (pfmu tag, bf sta record)
++ * @MT76_TM_TXBF_ACT_EBF_PROF_UPDATE: update ebf profile
++ * @MT76_TM_TXBF_ACT_APPLY_TX: apply TX setting for txbf
++ * @MT76_TM_TXBF_ACT_PHASE_CAL: perform txbf phase calibration
++ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL: update bf profile via instrument
++ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD: update bf profile via instrument
++ * @MT76_TM_TXBF_ACT_E2P_UPDATE: write back txbf calibration result to eeprom
++ * @MT76_TM_TXBF_ACT_TRIGGER_SOUNDING: trigger beamformer to send sounding packet
++ * @MT76_TM_TXBF_ACT_STOP_SOUNDING: stop sending sounding packet
++ * @MT76_TM_TXBF_ACT_PROFILE_TAG_READ: read pfmu tag
++ * @MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE: update pfmu tag
++ * @MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: invalidate pfmu tag
++ * @MT76_TM_TXBF_ACT_STA_REC_READ: read bf sta record
++ * @MT76_TM_TXBF_ACT_TXCMD: configure txcmd bf bit manually
++ */
++enum mt76_testmode_txbf_act {
++ MT76_TM_TXBF_ACT_GOLDEN_INIT,
++ MT76_TM_TXBF_ACT_INIT,
++ MT76_TM_TX_EBF_ACT_GOLDEN_INIT,
++ MT76_TM_TX_EBF_ACT_INIT,
++ MT76_TM_TXBF_ACT_UPDATE_CH,
++ MT76_TM_TXBF_ACT_PHASE_COMP,
++ MT76_TM_TXBF_ACT_TX_PREP,
++ MT76_TM_TXBF_ACT_IBF_PROF_UPDATE,
++ MT76_TM_TXBF_ACT_EBF_PROF_UPDATE,
++ MT76_TM_TXBF_ACT_APPLY_TX,
++ MT76_TM_TXBF_ACT_PHASE_CAL,
++ MT76_TM_TXBF_ACT_PROF_UPDATE_ALL,
++ MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD,
++ MT76_TM_TXBF_ACT_E2P_UPDATE,
++ MT76_TM_TXBF_ACT_TRIGGER_SOUNDING,
++ MT76_TM_TXBF_ACT_STOP_SOUNDING,
++ MT76_TM_TXBF_ACT_PROFILE_TAG_READ,
++ MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE,
++ MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID,
++ MT76_TM_TXBF_ACT_STA_REC_READ,
++ MT76_TM_TXBF_ACT_TXCMD,
++
++ /* keep last */
++ NUM_MT76_TM_TXBF_ACT,
++ MT76_TM_TXBF_ACT_MAX = NUM_MT76_TM_TXBF_ACT - 1,
++};
++
+ extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
+
+ #endif
+diff --git a/tools/fields.c b/tools/fields.c
+index 77696ce7b..f793d1a51 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -44,6 +44,30 @@ static const char * const testmode_offchan_bw[] = {
+ [NL80211_CHAN_WIDTH_160] = "160",
+ };
+
++static const char * const testmode_txbf_act[] = {
++ [MT76_TM_TXBF_ACT_GOLDEN_INIT] = "golden_init",
++ [MT76_TM_TXBF_ACT_INIT] = "init",
++ [MT76_TM_TX_EBF_ACT_GOLDEN_INIT] = "ebf_golden_init",
++ [MT76_TM_TX_EBF_ACT_INIT] = "ebf_init",
++ [MT76_TM_TXBF_ACT_UPDATE_CH] = "update_ch",
++ [MT76_TM_TXBF_ACT_PHASE_COMP] = "phase_comp",
++ [MT76_TM_TXBF_ACT_TX_PREP] = "tx_prep",
++ [MT76_TM_TXBF_ACT_IBF_PROF_UPDATE] = "ibf_prof_update",
++ [MT76_TM_TXBF_ACT_EBF_PROF_UPDATE] = "ebf_prof_update",
++ [MT76_TM_TXBF_ACT_APPLY_TX] = "apply_tx",
++ [MT76_TM_TXBF_ACT_PHASE_CAL] = "phase_cal",
++ [MT76_TM_TXBF_ACT_PROF_UPDATE_ALL] = "prof_update",
++ [MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD] = "prof_update_all",
++ [MT76_TM_TXBF_ACT_E2P_UPDATE] = "e2p_update",
++ [MT76_TM_TXBF_ACT_TRIGGER_SOUNDING] = "trigger_sounding",
++ [MT76_TM_TXBF_ACT_STOP_SOUNDING] = "stop_sounding",
++ [MT76_TM_TXBF_ACT_PROFILE_TAG_READ] = "pfmu_tag_read",
++ [MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE] = "pfmu_tag_write",
++ [MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID] = "set_invalid_prof",
++ [MT76_TM_TXBF_ACT_STA_REC_READ] = "sta_rec_read",
++ [MT76_TM_TXBF_ACT_TXCMD] = "txcmd",
++};
++
+ static void print_enum(const struct tm_field *field, struct nlattr *attr)
+ {
+ unsigned int i = nla_get_u8(attr);
+@@ -94,6 +118,17 @@ static void print_s8(const struct tm_field *field, struct nlattr *attr)
+ printf("%d", (int8_t)nla_get_u8(attr));
+ }
+
++static bool parse_u16_hex(const struct tm_field *field, int idx,
++ struct nl_msg *msg, const char *val)
++{
++ return !nla_put_u16(msg, idx, strtoul(val, NULL, 16));
++}
++
++static void print_u16_hex(const struct tm_field *field, struct nlattr *attr)
++{
++ printf("%d", nla_get_u16(attr));
++}
++
+ static bool parse_u32(const struct tm_field *field, int idx,
+ struct nl_msg *msg, const char *val)
+ {
+@@ -399,6 +434,8 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ FIELD(u8, AID, "aid"),
+ FIELD(u8, RU_ALLOC, "ru_alloc"),
+ FIELD(u8, RU_IDX, "ru_idx"),
++ FIELD_ENUM(TXBF_ACT, "txbf_act", testmode_txbf_act),
++ FIELD_ARRAY(u16_hex, TXBF_PARAM, "txbf_param"),
+ FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
+ FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
+ FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch
new file mode 100644
index 0000000..ff7ffa4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch
@@ -0,0 +1,221 @@
+From 2ad633b14de62c86fbc40d1ab2a1d3417a10d2b7 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 22 Sep 2023 12:33:06 +0800
+Subject: [PATCH 047/116] mtk: wifi: mt76: mt7996: add zwdfs cert mode
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/mcu.c | 44 ++++++++++++++++++++++++++++++++------------
+ mt7996/mcu.h | 14 ++++++++++++++
+ mt7996/mt7996.h | 5 +++++
+ mt7996/vendor.c | 37 +++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h | 12 ++++++++++++
+ 5 files changed, 100 insertions(+), 12 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8f3e5ebfd..8756a5df8 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4496,18 +4496,7 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ u8 rx_sel, u8 val)
+ {
+- struct {
+- u8 _rsv[4];
+-
+- __le16 tag;
+- __le16 len;
+-
+- u8 ctrl;
+- u8 rdd_idx;
+- u8 rdd_rx_sel;
+- u8 val;
+- u8 rsv[4];
+- } __packed req = {
++ struct mt7996_rdd_ctrl req = {
+ .tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .ctrl = cmd,
+@@ -4520,6 +4509,37 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ &req, sizeof(req), true);
+ }
+
++int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable_timer)
++{
++ struct mt7996_rdd_ctrl req = {
++ .tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .ctrl = RDD_DISABLE_ZW_TIMER,
++ .rdd_idx = MT_RX_SEL2,
++ .disable_timer = disable_timer,
++ };
++
++ if (!is_mt7996(&dev->mt76) ||
++ (mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) % 2))
++ return 0;
++
++ switch (dev->mt76.region) {
++ case NL80211_DFS_ETSI:
++ req.val = 0;
++ break;
++ case NL80211_DFS_JP:
++ req.val = 2;
++ break;
++ case NL80211_DFS_FCC:
++ default:
++ req.val = 1;
++ break;
++ }
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
++ &req, sizeof(req), true);
++}
++
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 663128176..f5e91a898 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -119,6 +119,20 @@ struct mt7996_mcu_rdd_report {
+ } hw_pulse[32];
+ } __packed;
+
++struct mt7996_rdd_ctrl {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 ctrl;
++ u8 rdd_idx;
++ u8 rdd_rx_sel;
++ u8 val;
++ u8 disable_timer;
++ u8 rsv[3];
++} __packed;
++
+ struct mt7996_mcu_background_chain_ctrl {
+ u8 _rsv[4];
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 84fbd0fb0..7f7a6223b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -523,8 +523,11 @@ enum mt7996_rdd_cmd {
+ RDD_READ_PULSE,
+ RDD_RESUME_BF,
+ RDD_IRQ_OFF,
++ RDD_DISABLE_ZW_TIMER,
+ };
+
++#define RDD_ZW_TIMER_OFF BIT(31)
++
+ static inline struct mt7996_phy *
+ mt7996_hw_phy(struct ieee80211_hw *hw)
+ {
+@@ -666,6 +669,8 @@ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ u8 rx_sel, u8 val);
++int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
++ bool disable_timer);
+ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
+ struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 7ab64471f..ba00ef359 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -108,6 +108,11 @@ rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
+ };
+
++static const struct nla_policy
++background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
++ [MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
++};
++
+ struct mt7996_amnt_data {
+ u8 idx;
+ u8 addr[ETH_ALEN];
+@@ -916,6 +921,27 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
+ return 0;
+ }
+
++static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
++ struct wireless_dev *wdev,
++ const void *data,
++ int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL];
++ int err;
++ u8 background_radar_mode;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX, data, data_len,
++ background_radar_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ background_radar_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE]);
++
++ return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
++}
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ {
+ .info = {
+@@ -1021,6 +1047,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ .policy = rfeature_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
+ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_background_radar_mode_ctrl,
++ .policy = background_radar_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
++ },
+ };
+
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 2ee1339a5..7c812f914 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -14,6 +14,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ };
+
+@@ -127,6 +128,17 @@ enum mtk_vendor_attr_wireless_dump {
+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
+ };
+
++enum mtk_vendor_attr_background_radar_ctrl {
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
++};
++
+ enum bw_sig {
+ BW_SIGNALING_DISABLE,
+ BW_SIGNALING_STATIC,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-wifi-mt76-testmode-add-channel-68-96.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-wifi-mt76-testmode-add-channel-68-96.patch
new file mode 100644
index 0000000..3aa9441
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-wifi-mt76-testmode-add-channel-68-96.patch
@@ -0,0 +1,250 @@
+From 097d8d847d49f831848e990e60091d10bd9c59a5 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 11 Sep 2023 14:43:07 +0800
+Subject: [PATCH 048/116] mtk: wifi: mt76: testmode: add channel 68 & 96
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Add all the channel between 68 & 96 since ibf 5g channel group 3 will use channel 84.
+Also, "mtk: wifi: mt76: testmode: add channel 68 & 96" can be
+merged into to "mtk: wifi: mt76: testmode: add basic testmode support"
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Fix 5g channel list size
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mac80211.c | 9 +++++++++
+ mt7996/eeprom.c | 49 +++++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/eeprom.h | 2 ++
+ mt7996/mcu.c | 10 +++++++++-
+ mt7996/testmode.c | 15 ++++++++++++---
+ mt7996/testmode.h | 6 +++---
+ 6 files changed, 82 insertions(+), 9 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 0590aaf2e..8091a60e0 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -34,6 +34,15 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
+ CHAN5G(60, 5300),
+ CHAN5G(64, 5320),
+
++ CHAN5G(68, 5340),
++ CHAN5G(72, 5360),
++ CHAN5G(76, 5380),
++ CHAN5G(80, 5400),
++ CHAN5G(84, 5420),
++ CHAN5G(88, 5440),
++ CHAN5G(92, 5460),
++ CHAN5G(96, 5480),
++
+ CHAN5G(100, 5500),
+ CHAN5G(104, 5520),
+ CHAN5G(108, 5540),
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index f97d76cc4..acf5bc179 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -18,6 +18,17 @@ const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
+ CHAN2G(11, 2462)
+ };
+
++const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
++ CHAN5G(68, 5340),
++ CHAN5G(72, 5360),
++ CHAN5G(76, 5380),
++ CHAN5G(80, 5400),
++ CHAN5G(84, 5420),
++ CHAN5G(88, 5440),
++ CHAN5G(92, 5460),
++ CHAN5G(96, 5480)
++};
++
+ const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
+ CHAN5G(50, 5250),
+ CHAN5G(114, 5570),
+@@ -44,6 +55,7 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
+ };
+
+ const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
++const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
+ const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
+ const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
+ const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+@@ -168,8 +180,8 @@ mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
+ if (band == NL80211_BAND_2GHZ)
+ dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
+ else if (band == NL80211_BAND_5GHZ)
+- dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+- dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++ dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
++ DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+ else
+ dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+ (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
+@@ -431,6 +443,39 @@ out:
+ return ret;
+ }
+
++static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
++{
++#define MT76_CHANNELS_5GHZ_SIZE 36 /* ARRAY_SIZE(mt76_channels_5ghz) */
++#define MT76_CHANNELS_6GHZ_SIZE 59 /* ARRAY_SIZE(mt76_channels_6ghz) */
++
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_2G] = ARRAY_SIZE(dpd_2g_ch_list_bw20);
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G_SKIP] = ARRAY_SIZE(dpd_5g_skip_ch_list);
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G] = MT76_CHANNELS_5GHZ_SIZE -
++ DPD_CH_NUM(BW20_5G_SKIP);
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw160);
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = MT76_CHANNELS_6GHZ_SIZE;
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw160);
++
++ switch (mt76_chip(&dev->mt76)) {
++ case 0x7990:
++ dev->prek.rev = mt7996_prek_rev;
++ /* 5g & 6g bw 80 dpd channel list is not used */
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
++ break;
++ case 0x7992:
++ dev->prek.rev = mt7992_prek_rev;
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw80);
++ /* 6g is not used in current sku */
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = 0;
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_6G] = 0;
++ dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = 0;
++ break;
++ default:
++ dev->prek.rev = mt7996_prek_rev;
++ break;
++ }
++}
++
+ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
+ {
+ struct mt76_dev *mdev = &dev->mt76;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 03a4fd07d..9a15b4462 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -67,6 +67,8 @@ enum mt7996_eeprom_field {
+
+ extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
+ extern const u32 dpd_2g_bw20_ch_num;
++extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
++extern const u32 dpd_5g_skip_ch_num;
+ extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
+ extern const u32 dpd_5g_bw160_ch_num;
+ extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8756a5df8..97bf2969e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3766,7 +3766,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ chan_list_size = mphy->sband_5g.sband.n_channels;
+ base_offset += dpd_size_2g;
+ if (bw == NL80211_CHAN_WIDTH_160) {
+- base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++ base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
++ DPD_PER_CH_BW20_SIZE;
+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ cal_id = RF_DPD_FLAT_5G_MEM_CAL;
+ chan_list = dpd_5g_ch_list_bw160;
+@@ -3775,6 +3776,9 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+ channel -= 2;
+ }
++ if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
++ channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++ return 0;
+ break;
+ case NL80211_BAND_6GHZ:
+ dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
+@@ -3814,6 +3818,10 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ if (idx == chan_list_size)
+ return -EINVAL;
+
++ if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
++ channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++ idx -= dpd_5g_skip_ch_num;
++
+ cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
+
+ for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 5cec1eef6..95d3bde03 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -531,6 +531,11 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
+ memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
+
+ for (i = 0; i < channel_size; i++) {
++ if (chan_list[i].band == NL80211_BAND_5GHZ &&
++ chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
++ chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++ continue;
++
+ memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
+ chandef->width = width;
+
+@@ -612,7 +617,8 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
+ if (ret)
+ return ret;
+- wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++ wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
++ DPD_PER_CH_BW20_SIZE;
+ wait_event_timeout(mdev->mcu.wait,
+ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
+
+@@ -868,6 +874,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
+ const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
+ u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
+ u16 first_control = 0, control_chan = chandef->chan->hw_value;
++ bool not_first;
+
+ bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
+ if (!bitmap)
+@@ -877,7 +884,9 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
+ offset = width_mhz / 10 - 2;
+
+ for (i = 0; i < size; i++) {
+- if (!((1 << i) & bitmap))
++ not_first = (chandef->width != NL80211_CHAN_WIDTH_160) ?
++ (i % bitmap) : (i >= 32) || !((1 << i) & bitmap);
++ if (not_first)
+ continue;
+
+ if (control_chan >= chan[i].hw_value)
+@@ -886,7 +895,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
+ break;
+ }
+
+- if (i == size || first_control == 0)
++ if (first_control == 0)
+ return control_chan;
+
+ return first_control + offset;
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index f97ccb267..ba1767aed 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -38,9 +38,9 @@ enum {
+ BF_CDBW_8080MHZ,
+ };
+
+-#define FIRST_CONTROL_CHAN_BITMAP_BW40 0x5555555
+-#define FIRST_CONTROL_CHAN_BITMAP_BW80 0x111111
+-#define FIRST_CONTROL_CHAN_BITMAP_BW160 0x100101
++#define FIRST_CONTROL_CHAN_BITMAP_BW40 2
++#define FIRST_CONTROL_CHAN_BITMAP_BW80 4
++#define FIRST_CONTROL_CHAN_BITMAP_BW160 0x10010101
+
+ enum bw_mapping_method {
+ BW_MAP_NL_TO_FW,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch
new file mode 100644
index 0000000..7279514
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch
@@ -0,0 +1,598 @@
+From 70f6d9e12225fb10db626a41cd0cd81439f2c814 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 12 Oct 2023 16:17:33 +0800
+Subject: [PATCH 049/116] mtk: wifi: mt76: testmode: add kite testmode support
+
+Add Kite testmode support
+1. avoid entering connac 2 testmode flow in kite
+2. refactor prek implementation for handling chip difference
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 63 +++++++++++++-----------------
+ mt7996/eeprom.h | 81 +++++++++++++++++++++++++++------------
+ mt7996/mcu.c | 48 ++++++++++++++---------
+ mt7996/mt7996.h | 18 ++++++++-
+ mt7996/testmode.c | 97 ++++++++++++++++++++++++++++-------------------
+ testmode.c | 11 ++++--
+ 6 files changed, 198 insertions(+), 120 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index acf5bc179..215d81e31 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -29,12 +29,39 @@ const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
+ CHAN5G(96, 5480)
+ };
+
++const struct ieee80211_channel dpd_5g_ch_list_bw80[] = {
++ CHAN5G(42, 5210),
++ CHAN5G(58, 5290),
++ CHAN5G(106, 5530),
++ CHAN5G(122, 5610),
++ CHAN5G(138, 5690),
++ CHAN5G(155, 5775),
++ CHAN5G(171, 5855)
++};
++
+ const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
+ CHAN5G(50, 5250),
+ CHAN5G(114, 5570),
+ CHAN5G(163, 5815)
+ };
+
++const struct ieee80211_channel dpd_6g_ch_list_bw80[] = {
++ CHAN6G(7, 5985),
++ CHAN6G(23, 6065),
++ CHAN6G(39, 6145),
++ CHAN6G(55, 6225),
++ CHAN6G(71, 6305),
++ CHAN6G(87, 6385),
++ CHAN6G(103, 6465),
++ CHAN6G(119, 6545),
++ CHAN6G(135, 6625),
++ CHAN6G(151, 6705),
++ CHAN6G(167, 6785),
++ CHAN6G(183, 6865),
++ CHAN6G(199, 6945),
++ CHAN6G(215, 7025)
++};
++
+ const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
+ CHAN6G(15, 6025),
+ CHAN6G(47, 6185),
+@@ -54,12 +81,6 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
+ CHAN6G(191, 6905)
+ };
+
+-const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
+-const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
+-const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
+-const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
+-const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+-
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+ u8 *eeprom = dev->mt76.eeprom.data;
+@@ -159,36 +180,6 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ }
+ }
+
+-int
+-mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
+-{
+- /* handle different sku */
+- static const u8 band_to_idx[] = {
+- [NL80211_BAND_2GHZ] = MT_BAND0,
+- [NL80211_BAND_5GHZ] = MT_BAND1,
+- [NL80211_BAND_6GHZ] = MT_BAND2,
+- };
+- struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
+- struct mt76_phy *mphy;
+- int dpd_size;
+-
+- if (!phy)
+- return 0;
+-
+- mphy = phy->mt76;
+-
+- if (band == NL80211_BAND_2GHZ)
+- dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
+- else if (band == NL80211_BAND_5GHZ)
+- dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
+- DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+- else
+- dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+- (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
+-
+- return dpd_size;
+-}
+-
+ static int
+ mt7996_eeprom_load_bin(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 9a15b4462..fa9c31e7a 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -45,36 +45,69 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CAL_DPD GENMASK(5, 3)
+
+ #define MT_EE_CAL_UNIT 1024
+-#define MT_EE_CAL_GROUP_SIZE_2G (4 * MT_EE_CAL_UNIT)
+-#define MT_EE_CAL_GROUP_SIZE_5G (45 * MT_EE_CAL_UNIT)
+-#define MT_EE_CAL_GROUP_SIZE_6G (125 * MT_EE_CAL_UNIT)
+-#define MT_EE_CAL_ADCDCOC_SIZE_2G (4 * 4)
+-#define MT_EE_CAL_ADCDCOC_SIZE_5G (4 * 4)
+-#define MT_EE_CAL_ADCDCOC_SIZE_6G (4 * 5)
+-#define MT_EE_CAL_GROUP_SIZE (MT_EE_CAL_GROUP_SIZE_2G + \
+- MT_EE_CAL_GROUP_SIZE_5G + \
+- MT_EE_CAL_GROUP_SIZE_6G + \
+- MT_EE_CAL_ADCDCOC_SIZE_2G + \
+- MT_EE_CAL_ADCDCOC_SIZE_5G + \
+- MT_EE_CAL_ADCDCOC_SIZE_6G)
+-
+-#define DPD_PER_CH_LEGACY_SIZE (4 * MT_EE_CAL_UNIT)
+-#define DPD_PER_CH_MEM_SIZE (13 * MT_EE_CAL_UNIT)
+-#define DPD_PER_CH_OTFG0_SIZE (2 * MT_EE_CAL_UNIT)
+-#define DPD_PER_CH_BW20_SIZE (DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
+-#define DPD_PER_CH_GT_BW20_SIZE (DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
+-#define MT_EE_CAL_DPD_SIZE (780 * MT_EE_CAL_UNIT)
++
++enum mt7996_prek_rev {
++ GROUP_SIZE_2G,
++ GROUP_SIZE_5G,
++ GROUP_SIZE_6G,
++ ADCDCOC_SIZE_2G,
++ ADCDCOC_SIZE_5G,
++ ADCDCOC_SIZE_6G,
++ DPD_LEGACY_SIZE,
++ DPD_MEM_SIZE,
++ DPD_OTFG0_SIZE,
++};
++
++static const u32 mt7996_prek_rev[] = {
++ [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
++ [GROUP_SIZE_5G] = 45 * MT_EE_CAL_UNIT,
++ [GROUP_SIZE_6G] = 125 * MT_EE_CAL_UNIT,
++ [ADCDCOC_SIZE_2G] = 4 * 4,
++ [ADCDCOC_SIZE_5G] = 4 * 4,
++ [ADCDCOC_SIZE_6G] = 4 * 5,
++ [DPD_LEGACY_SIZE] = 4 * MT_EE_CAL_UNIT,
++ [DPD_MEM_SIZE] = 13 * MT_EE_CAL_UNIT,
++ [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
++};
++
++/* kite 2/5g config */
++static const u32 mt7992_prek_rev[] = {
++ [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
++ [GROUP_SIZE_5G] = 110 * MT_EE_CAL_UNIT,
++ [GROUP_SIZE_6G] = 0,
++ [ADCDCOC_SIZE_2G] = 4 * 4,
++ [ADCDCOC_SIZE_5G] = 4 * 5,
++ [ADCDCOC_SIZE_6G] = 0,
++ [DPD_LEGACY_SIZE] = 5 * MT_EE_CAL_UNIT,
++ [DPD_MEM_SIZE] = 16 * MT_EE_CAL_UNIT,
++ [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
++};
+
+ extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
+-extern const u32 dpd_2g_bw20_ch_num;
+ extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
+-extern const u32 dpd_5g_skip_ch_num;
++extern const struct ieee80211_channel dpd_5g_ch_list_bw80[];
+ extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
+-extern const u32 dpd_5g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw80[];
+ extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
+-extern const u32 dpd_6g_bw160_ch_num;
+ extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
+-extern const u32 dpd_6g_bw320_ch_num;
++
++#define PREK(id) (dev->prek.rev[(id)])
++#define DPD_CH_NUM(_type) (dev->prek.dpd_ch_num[DPD_CH_NUM_##_type])
++#define MT_EE_CAL_GROUP_SIZE (PREK(GROUP_SIZE_2G) + PREK(GROUP_SIZE_5G) + \
++ PREK(GROUP_SIZE_6G) + PREK(ADCDCOC_SIZE_2G) + \
++ PREK(ADCDCOC_SIZE_5G) + PREK(ADCDCOC_SIZE_6G))
++#define DPD_PER_CH_BW20_SIZE (PREK(DPD_LEGACY_SIZE) + PREK(DPD_OTFG0_SIZE))
++#define DPD_PER_CH_GT_BW20_SIZE (PREK(DPD_MEM_SIZE) + PREK(DPD_OTFG0_SIZE))
++#define MT_EE_CAL_DPD_SIZE_2G (DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE)
++#define MT_EE_CAL_DPD_SIZE_5G (DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE + \
++ DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE + \
++ DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE)
++#define MT_EE_CAL_DPD_SIZE_6G (DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE + \
++ DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE + \
++ DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE + \
++ DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE)
++#define MT_EE_CAL_DPD_SIZE (MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G + \
++ MT_EE_CAL_DPD_SIZE_6G)
+
+ #define RF_DPD_FLAT_CAL BIT(28)
+ #define RF_PRE_CAL BIT(29)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 97bf2969e..3e92a2742 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3735,13 +3735,11 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ enum nl80211_chan_width bw = chandef->width;
+ const struct ieee80211_channel *chan_list;
+ u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
+- u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
++ u32 per_chan_size = DPD_PER_CH_BW20_SIZE;
+ u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
+ u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
+ int idx, i, ret;
+-
+- dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
+- dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++ bool has_skip_ch = (band == NL80211_BAND_5GHZ);
+
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+@@ -3757,27 +3755,35 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ return 0;
+ cal_id = RF_DPD_FLAT_CAL;
+ chan_list = dpd_2g_ch_list_bw20;
+- chan_list_size = dpd_2g_bw20_ch_num;
++ chan_list_size = DPD_CH_NUM(BW20_2G);
+ break;
+ case NL80211_BAND_5GHZ:
+ dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
+ cal_id = RF_DPD_FLAT_5G_CAL;
+ chan_list = mphy->sband_5g.sband.channels;
+ chan_list_size = mphy->sband_5g.sband.n_channels;
+- base_offset += dpd_size_2g;
++ base_offset += MT_EE_CAL_DPD_SIZE_2G;
+ if (bw == NL80211_CHAN_WIDTH_160) {
+- base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
+- DPD_PER_CH_BW20_SIZE;
++ base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE +
++ DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ cal_id = RF_DPD_FLAT_5G_MEM_CAL;
+ chan_list = dpd_5g_ch_list_bw160;
+- chan_list_size = dpd_5g_bw160_ch_num;
++ chan_list_size = DPD_CH_NUM(BW160_5G);
++ has_skip_ch = false;
++ } else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
++ base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
++ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++ cal_id = RF_DPD_FLAT_5G_MEM_CAL;
++ chan_list = dpd_5g_ch_list_bw80;
++ chan_list_size = DPD_CH_NUM(BW80_5G);
++ has_skip_ch = false;
+ } else if (bw > NL80211_CHAN_WIDTH_20) {
+ /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+ channel -= 2;
+ }
+ if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
+- channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++ channel <= dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
+ return 0;
+ break;
+ case NL80211_BAND_6GHZ:
+@@ -3785,20 +3791,27 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ cal_id = RF_DPD_FLAT_6G_CAL;
+ chan_list = mphy->sband_6g.sband.channels;
+ chan_list_size = mphy->sband_6g.sband.n_channels;
+- base_offset += dpd_size_2g + dpd_size_5g;
++ base_offset += MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G;
+ if (bw == NL80211_CHAN_WIDTH_160) {
+ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
+ chan_list = dpd_6g_ch_list_bw160;
+- chan_list_size = dpd_6g_bw160_ch_num;
+- } else if (bw == NL80211_CHAN_WIDTH_320) {
++ chan_list_size = DPD_CH_NUM(BW160_6G);
++ } else if (is_mt7996(&dev->mt76) && bw == NL80211_CHAN_WIDTH_320) {
+ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+- dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++ DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE +
++ DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
+ chan_list = dpd_6g_ch_list_bw320;
+- chan_list_size = dpd_6g_bw320_ch_num;
++ chan_list_size = DPD_CH_NUM(BW320_6G);
++ } else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
++ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++ chan_list = dpd_6g_ch_list_bw80;
++ chan_list_size = DPD_CH_NUM(BW80_6G);
+ } else if (bw > NL80211_CHAN_WIDTH_20) {
+ /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+ channel -= 2;
+@@ -3818,9 +3831,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ if (idx == chan_list_size)
+ return -EINVAL;
+
+- if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
+- channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
+- idx -= dpd_5g_skip_ch_num;
++ if (has_skip_ch && channel > dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
++ idx -= DPD_CH_NUM(BW20_5G_SKIP);
+
+ cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 7f7a6223b..0ebeb2403 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -194,6 +194,19 @@ struct mt7996_twt_flow {
+
+ DECLARE_EWMA(avg_signal, 10, 8)
+
++enum mt7996_dpd_ch_num {
++ DPD_CH_NUM_BW20_2G,
++ DPD_CH_NUM_BW20_5G,
++ DPD_CH_NUM_BW20_5G_SKIP,
++ DPD_CH_NUM_BW80_5G,
++ DPD_CH_NUM_BW160_5G,
++ DPD_CH_NUM_BW20_6G,
++ DPD_CH_NUM_BW80_6G,
++ DPD_CH_NUM_BW160_6G,
++ DPD_CH_NUM_BW320_6G,
++ DPD_CH_NUM_TYPE_MAX,
++};
++
+ struct mt7996_sta {
+ struct mt76_wcid wcid; /* must be first */
+
+@@ -463,6 +476,10 @@ struct mt7996_dev {
+
+ void *cal;
+ u32 cur_prek_offset;
++ struct {
++ const u32 *rev;
++ u8 dpd_ch_num[DPD_CH_NUM_TYPE_MAX];
++ } prek;
+
+ struct {
+ u16 table_mask;
+@@ -599,7 +616,6 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ struct ieee80211_channel *chan);
+ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
+-int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
+ int mt7996_dma_init(struct mt7996_dev *dev);
+ void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 95d3bde03..9fa4edcd6 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -434,7 +434,7 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
+ static int
+ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ {
+- u8 *eeprom;
++ u8 *eeprom, do_precal;
+ u32 i, group_size, dpd_size, size, offs, *pre_cal;
+ int ret = 0;
+ struct mt7996_dev *dev = phy->dev;
+@@ -462,6 +462,9 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ dpd_size = MT_EE_CAL_DPD_SIZE;
+ size = group_size + dpd_size;
+ offs = MT_EE_DO_PRE_CAL;
++ do_precal = (MT_EE_WIFI_CAL_GROUP_2G * !!PREK(GROUP_SIZE_2G)) |
++ (MT_EE_WIFI_CAL_GROUP_5G * !!PREK(GROUP_SIZE_5G)) |
++ (MT_EE_WIFI_CAL_GROUP_6G * !!PREK(GROUP_SIZE_6G));
+
+ switch (state) {
+ case MT76_TM_STATE_GROUP_PREK:
+@@ -476,13 +479,10 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
+ 30 * HZ);
+
+- if (ret) {
++ if (ret)
+ dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
+- return ret;
+- }
+-
+- if (!ret)
+- eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
++ else
++ eeprom[offs] |= do_precal;
+ break;
+ case MT76_TM_STATE_GROUP_PREK_DUMP:
+ pre_cal = (u32 *)dev->cal;
+@@ -520,10 +520,12 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
+ struct mt76_phy *mphy = phy->mt76;
+ struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
+ struct ieee80211_channel chan_backup;
+- int i, ret;
++ int i, ret, skip_ch_num = DPD_CH_NUM(BW20_5G_SKIP);
+
+ if (!chan_list)
+ return -EOPNOTSUPP;
++ if (!channel_size)
++ return 0;
+
+ req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
+
+@@ -533,7 +535,7 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
+ for (i = 0; i < channel_size; i++) {
+ if (chan_list[i].band == NL80211_BAND_5GHZ &&
+ chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
+- chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++ chan_list[i].hw_value <= dpd_5g_skip_ch_list[skip_ch_num - 1].hw_value)
+ continue;
+
+ memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
+@@ -602,11 +604,11 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ switch (state) {
+ case MT76_TM_STATE_DPD_2G:
+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
+- dpd_2g_bw20_ch_num,
++ DPD_CH_NUM(BW20_2G),
+ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
+- wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
+- wait_event_timeout(mdev->mcu.wait,
+- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++ wait_on_prek_offset += DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
+
+ do_precal = MT_EE_WIFI_CAL_DPD_2G;
+ break;
+@@ -617,18 +619,27 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
+ if (ret)
+ return ret;
+- wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
+- DPD_PER_CH_BW20_SIZE;
+- wait_event_timeout(mdev->mcu.wait,
+- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++ wait_on_prek_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
++
++ /* 5g channel bw80 calibration */
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw80,
++ DPD_CH_NUM(BW80_5G),
++ NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_5G_MEM_CAL);
++ if (ret)
++ return ret;
++ wait_on_prek_offset += DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
+
+ /* 5g channel bw160 calibration */
+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
+- dpd_5g_bw160_ch_num,
++ DPD_CH_NUM(BW160_5G),
+ NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
+- wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+- wait_event_timeout(mdev->mcu.wait,
+- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++ wait_on_prek_offset += DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
+
+ do_precal = MT_EE_WIFI_CAL_DPD_5G;
+ break;
+@@ -639,27 +650,37 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
+ if (ret)
+ return ret;
+- wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
+- wait_event_timeout(mdev->mcu.wait,
+- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++ wait_on_prek_offset += DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
++
++ /* 6g channel bw80 calibration */
++ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw80,
++ DPD_CH_NUM(BW80_6G),
++ NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_6G_MEM_CAL);
++ if (ret)
++ return ret;
++ wait_on_prek_offset += DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
+
+ /* 6g channel bw160 calibration */
+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
+- dpd_6g_bw160_ch_num,
++ DPD_CH_NUM(BW160_6G),
+ NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
+ if (ret)
+ return ret;
+- wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+- wait_event_timeout(mdev->mcu.wait,
+- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++ wait_on_prek_offset += DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
+
+ /* 6g channel bw320 calibration */
+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
+- dpd_6g_bw320_ch_num,
++ DPD_CH_NUM(BW320_6G),
+ NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
+- wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+- wait_event_timeout(mdev->mcu.wait,
+- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++ wait_on_prek_offset += DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE;
++ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++ 30 * HZ);
+
+ do_precal = MT_EE_WIFI_CAL_DPD_6G;
+ break;
+@@ -732,9 +753,9 @@ mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int
+ eeprom = dev->mt76.eeprom.data;
+ offs = MT_EE_DO_PRE_CAL;
+
+- dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
+- dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
+- dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++ dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
++ dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
++ dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
+
+ switch (type) {
+ case PREK_SYNC_ALL:
+@@ -810,9 +831,9 @@ mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *
+ u8 *pre_cal;
+
+ pre_cal = dev->cal;
+- dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
+- dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
+- dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++ dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
++ dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
++ dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
+
+ cal_idx = le32_to_cpu(data->cal_idx);
+ cal_type = le32_to_cpu(data->cal_type);
+diff --git a/testmode.c b/testmode.c
+index b9f710970..2dd184e48 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -37,6 +37,11 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ };
+ EXPORT_SYMBOL_GPL(mt76_tm_policy);
+
++static inline bool mt76_testmode_offload(struct mt76_dev *dev)
++{
++ return is_mt7996(dev) || is_mt7992(dev);
++}
++
+ void mt76_testmode_tx_pending(struct mt76_phy *phy)
+ {
+ struct mt76_testmode_data *td = &phy->test;
+@@ -197,7 +202,7 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
+ u8 max_nss = hweight8(phy->antenna_mask);
+ int ret;
+
+- if (is_mt7996(phy->dev))
++ if (mt76_testmode_offload(phy->dev))
+ return 0;
+
+ ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
+@@ -293,7 +298,7 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
+ td->tx_done = 0;
+ td->tx_pending = td->tx_count;
+
+- if (!is_mt7996(dev))
++ if (!mt76_testmode_offload(dev))
+ mt76_worker_schedule(&dev->tx_worker);
+ }
+
+@@ -303,7 +308,7 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
+ struct mt76_testmode_data *td = &phy->test;
+ struct mt76_dev *dev = phy->dev;
+
+- if (is_mt7996(dev) && dev->test_ops->tx_stop) {
++ if (mt76_testmode_offload(dev) && dev->test_ops->tx_stop) {
+ dev->test_ops->tx_stop(phy);
+ return;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch
new file mode 100644
index 0000000..87a2270
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch
@@ -0,0 +1,43 @@
+From 77ec17101af8185206e06bc70fce00fd9b1d178f Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 14 Nov 2023 11:27:06 +0800
+Subject: [PATCH 050/116] mtk: wifi: mt76: mt7996: assign DEAUTH to ALTX queue
+ for CERT
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+CR-Id: WCNCR00289305
+---
+ mt7996/mac.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 70f0c56cc..727d1fb80 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -753,6 +753,8 @@ static void
+ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ struct sk_buff *skb, struct ieee80211_key_conf *key)
+ {
++ struct mt76_phy *mphy =
++ mt76_dev_phy(&dev->mt76, le32_get_bits(txwi[1], MT_TXD1_TGID));
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+@@ -762,6 +764,14 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ u8 fc_type, fc_stype;
+ u32 val;
+
++ if (ieee80211_is_cert_mode(mphy->hw) && ieee80211_is_deauth(fc)) {
++ /* In WPA3 cert TC-4.8.1, the deauth must be transmitted without
++ * considering PSM bit
++ */
++ txwi[0] &= ~cpu_to_le32(MT_TXD0_Q_IDX);
++ txwi[0] |= cpu_to_le32(FIELD_PREP(MT_TXD0_Q_IDX, MT_LMAC_ALTX0));
++ }
++
+ if (ieee80211_is_action(fc) &&
+ mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+ mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch
new file mode 100644
index 0000000..98055c0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch
@@ -0,0 +1,155 @@
+From 0eb032654d67a0afcd486a730270bc7e069e1f99 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 22 Nov 2023 22:42:09 +0800
+Subject: [PATCH 051/116] mtk: wifi: mt76: mt7996: add no_beacon vendor command
+ for cert
+
+Add the vendor command to disable/enable beacon
+
+[Usage]
+hostapd_cli -i <interface> no_beacon <value>
+<value>
+0: enable beacon
+1: disable beacon
+
+CR-ID: WCNCR00240597
+Change-Id: Ia16707317135aeb02d6a5f6d50983e5174cc9e77
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ mt7996/mcu.c | 11 +++++++++++
+ mt7996/mt7996.h | 1 +
+ mt7996/vendor.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h | 12 ++++++++++++
+ 4 files changed, 65 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3e92a2742..2b12d5c12 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5100,4 +5100,15 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ break;
+ }
+ }
++
++void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct ieee80211_hw *hw = mvif->phy->mt76->hw;
++ u8 val = *((u8 *)data);
++
++ vif->bss_conf.enable_beacon = val;
++
++ mt7996_mcu_add_beacon(hw, vif, val);
++}
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 0ebeb2403..1bba5450f 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -835,6 +835,7 @@ void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
+ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
+ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
+ int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
++void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+ #endif
+
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index ba00ef359..31688c373 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -113,6 +113,11 @@ background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
+ [MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
+ };
+
++static const struct nla_policy
++beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
++ [MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
++};
++
+ struct mt7996_amnt_data {
+ u8 idx;
+ u8 addr[ETH_ALEN];
+@@ -942,6 +947,31 @@ static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
+ return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
+ }
+
++static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
++ struct wireless_dev *wdev,
++ const void *data,
++ int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL];
++ int err;
++ u8 val8;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_BEACON_CTRL_MAX, data, data_len,
++ beacon_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]) {
++ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]);
++ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7996_set_beacon_vif, &val8);
++ }
++
++ return 0;
++}
++
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ {
+ .info = {
+@@ -1058,6 +1088,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ .policy = background_radar_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
+ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_beacon_ctrl,
++ .policy = beacon_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
++ },
+ };
+
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 7c812f914..0d1ef3228 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++ MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -227,6 +228,17 @@ enum mtk_vendor_attr_pp_ctrl {
+ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
+ };
+
++enum mtk_vendor_attr_beacon_ctrl {
++ MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
++ MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
++};
++
+ #endif
+
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch
new file mode 100644
index 0000000..6aaed8b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch
@@ -0,0 +1,290 @@
+From f33f7b6e8e9f4b8af0b9c7d4de2726556c45c566 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 24 Nov 2023 09:49:08 +0800
+Subject: [PATCH 052/116] mtk: wifi: mt76: mt7996: add adie efuse merge support
+
+Merge adie-dependent parameters in efuse into eeprom after FT.
+Note that Eagle BE14000 is not considered yet.
+Add efuse dump command.
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Ib088b90147c75d7437f40dd3569e3584c6ff9ab0
+---
+ mt7996/debugfs.c | 41 +++++++++++++
+ mt7996/eeprom.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.c | 6 +-
+ mt7996/testmode.c | 8 ++-
+ 4 files changed, 195 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 3074202bb..f3a520bef 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -894,6 +894,46 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
+ mt7996_fw_debug_muru_disable_get,
+ mt7996_fw_debug_muru_disable_set, "%lld\n");
+
++static ssize_t
++mt7996_efuse_get(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct mt7996_dev *dev = file->private_data;
++ struct mt76_dev *mdev = &dev->mt76;
++ u8 *buff = mdev->otp.data;
++ int i;
++ ssize_t ret;
++ u32 block_num;
++
++ mdev->otp.size = MT7996_EEPROM_SIZE;
++ if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444)
++ mdev->otp.size += 3 * MT_EE_CAL_UNIT;
++
++ if (!mdev->otp.data) {
++ mdev->otp.data = devm_kzalloc(mdev->dev, mdev->otp.size, GFP_KERNEL);
++ if (!mdev->otp.data)
++ return -ENOMEM;
++
++ block_num = DIV_ROUND_UP(mdev->otp.size, MT7996_EEPROM_BLOCK_SIZE);
++ for (i = 0; i < block_num; i++) {
++ buff = mdev->otp.data + i * MT7996_EEPROM_BLOCK_SIZE;
++ ret = mt7996_mcu_get_eeprom(dev, i * MT7996_EEPROM_BLOCK_SIZE, buff);
++ if (ret && ret != -EINVAL)
++ return ret;
++ }
++ }
++
++ ret = simple_read_from_buffer(user_buf, count, ppos, mdev->otp.data, mdev->otp.size);
++
++ return ret;
++}
++
++static const struct file_operations mt7996_efuse_ops = {
++ .read = mt7996_efuse_get,
++ .open = simple_open,
++ .llseek = default_llseek,
++};
++
+ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -920,6 +960,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
+ mt7996_twt_stats);
+ debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
++ debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
+
+ if (phy->mt76->cap.has_5ghz) {
+ debugfs_create_u32("dfs_hw_pattern", 0400, dir,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 215d81e31..3cdd58529 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -496,6 +496,146 @@ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
+ return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
+ }
+
++static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
++{
++#define MT_EE_CAL_FREE_MAX_SIZE 30
++#define MT_EE_7977BN_OFFSET (0x1200 - 0x500)
++#define MT_EE_END_OFFSET 0xffff
++ enum adie_type {
++ ADIE_7975,
++ ADIE_7976,
++ ADIE_7977,
++ ADIE_7978,
++ ADIE_7979,
++ };
++ static const u16 adie_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
++ [ADIE_7975] = {0x5cd, 0x5cf, 0x5d1, 0x5d3, 0x6c0, 0x6c1, 0x6c2, 0x6c3,
++ 0x7a1, 0x7a6, 0x7a8, 0x7aa, -1},
++ [ADIE_7976] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++ 0x70, 0x71, 0x790, 0x791, 0x794, 0x795, 0x7a6, 0x7a8, 0x7aa, -1},
++ [ADIE_7977] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++ 0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, -1},
++ [ADIE_7978] = {0x91, 0x95, 0x100, 0x102, 0x104, 0x106, 0x107,
++ 0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10e, 0x110, -1},
++ [ADIE_7979] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++ 0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7e, 0x80, -1},
++ };
++ static const u16 eep_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
++ [ADIE_7975] = {0x451, 0x453, 0x455, 0x457, 0x44c, 0x44d, 0x44e, 0x44f,
++ 0xba1, 0xba6, 0xba8, 0xbaa, -1},
++ [ADIE_7976] = {0x44c, 0x44d, 0x44e, 0x44f, 0x450,
++ 0x451, 0x453, 0x455, 0x457, 0x459,
++ 0x470, 0x471, 0xb90, 0xb91, 0xb94, 0xb95,
++ 0xba6, 0xba8, 0xbaa, -1},
++ [ADIE_7977] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250,
++ 0x1251, 0x1253, 0x1255, 0x1257, 0x1259,
++ 0x1269, 0x126a, 0x127a, 0x127b, 0x127c, 0x127d, 0x127e, -1},
++ [ADIE_7978] = {0xb91, 0xb95, 0x480, 0x482, 0x484, 0x486, 0x487, 0x488, 0x489,
++ 0x48a, 0x48b, 0x48c, 0x48e, 0x490, -1},
++ [ADIE_7979] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250, 0x1251,
++ 0x1253, 0x1255, 0x1257, 0x1259, 0x1269, 0x126a,
++ 0x127a, 0x127b, 0x127c, 0x127e, 0x1280, -1},
++ };
++ static const u16 adie_base_7996[] = {
++ 0x400, 0x1e00, 0x1200
++ };
++ static const u16 adie_base_7992[] = {
++ 0x400, 0x1200, 0x0
++ };
++ static const u16 *adie_offs[__MT_MAX_BAND];
++ static const u16 *eep_offs[__MT_MAX_BAND];
++ static const u16 *adie_base;
++ u8 *eeprom = dev->mt76.eeprom.data;
++ u8 buf[MT7996_EEPROM_BLOCK_SIZE];
++ int adie_id, band, i, ret;
++
++ switch (mt76_chip(&dev->mt76)) {
++ case 0x7990:
++ adie_base = adie_base_7996;
++ /* adie 0 */
++ if (dev->fem_type == MT7996_FEM_INT)
++ adie_id = ADIE_7975;
++ else
++ adie_id = ADIE_7976;
++ adie_offs[0] = adie_offs_list[adie_id];
++ eep_offs[0] = eep_offs_list[adie_id];
++
++ /* adie 1 */
++ if (dev->chip_sku != MT7996_SKU_404) {
++ adie_offs[1] = adie_offs_list[ADIE_7977];
++ eep_offs[1] = eep_offs_list[ADIE_7977];
++ }
++
++ /* adie 2 */
++ adie_offs[2] = adie_offs_list[ADIE_7977];
++ eep_offs[2] = eep_offs_list[ADIE_7977];
++ break;
++ case 0x7992:
++ adie_base = adie_base_7992;
++ /* adie 0 */
++ if (dev->chip_sku == MT7992_SKU_44 &&
++ dev->fem_type != MT7996_FEM_EXT)
++ adie_id = ADIE_7975;
++ else if (dev->chip_sku == MT7992_SKU_24)
++ adie_id = ADIE_7978;
++ else
++ adie_id = ADIE_7976;
++ adie_offs[0] = adie_offs_list[adie_id];
++ eep_offs[0] = eep_offs_list[adie_id];
++
++ /* adie 1 */
++ if (dev->chip_sku == MT7992_SKU_44 &&
++ dev->fem_type != MT7996_FEM_INT)
++ adie_id = ADIE_7977;
++ else if (dev->chip_sku != MT7992_SKU_23)
++ adie_id = ADIE_7979;
++ else
++ break;
++ adie_offs[1] = adie_offs_list[adie_id];
++ eep_offs[1] = eep_offs_list[adie_id];
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ for (band = 0; band < __MT_MAX_BAND; band++) {
++ u16 adie_offset, eep_offset;
++ u32 block_num, prev_block_num = -1;
++
++ if (!adie_offs[band])
++ continue;
++
++ for (i = 0; i < MT_EE_CAL_FREE_MAX_SIZE; i++) {
++ adie_offset = adie_offs[band][i] + adie_base[band];
++ eep_offset = eep_offs[band][i];
++ block_num = adie_offset / MT7996_EEPROM_BLOCK_SIZE;
++
++ if (adie_offs[band][i] == MT_EE_END_OFFSET)
++ break;
++
++ if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444 &&
++ band == MT_BAND1)
++ eep_offset -= MT_EE_7977BN_OFFSET;
++
++ if (prev_block_num != block_num) {
++ ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf);
++ if (ret) {
++ if (ret != -EINVAL)
++ return ret;
++
++ prev_block_num = -1;
++ continue;
++ }
++ }
++
++ eeprom[eep_offset] = buf[adie_offset % MT7996_EEPROM_BLOCK_SIZE];
++ prev_block_num = block_num;
++ }
++ }
++
++ return 0;
++}
++
+ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ int ret;
+@@ -512,6 +652,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
++ ret = mt7996_apply_cal_free_data(dev);
++ if (ret)
++ return ret;
++
+ ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
+ if (ret < 0)
+ return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 2b12d5c12..6053242cd 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3606,7 +3606,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ };
+ struct sk_buff *skb;
+ bool valid;
+- int ret;
++ int ret = 0;
+ u8 *buf = read_buf;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+@@ -3624,11 +3624,13 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+
+ skb_pull(skb, 48);
+ memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
++ } else {
++ ret = -EINVAL;
+ }
+
+ dev_kfree_skb(skb);
+
+- return 0;
++ return ret;
+ }
+
+ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 9fa4edcd6..784a8bea4 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -2116,8 +2116,12 @@ mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
+ memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
+
+ ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
+- if (ret < 0)
+- return ret;
++ if (ret) {
++ if (ret != -EINVAL)
++ return ret;
++
++ memset(read_buf, 0, MT76_TM_EEPROM_BLOCK_SIZE);
++ }
+
+ if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
+ continue;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
new file mode 100644
index 0000000..a8214cc
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
@@ -0,0 +1,169 @@
+From bc31813e3f470888713d7072e2563cc1562c27a8 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 5 Dec 2023 16:48:33 +0800
+Subject: [PATCH 053/116] mtk: wifi: mt7996: add Eagle 2adie TBTC (BE14000)
+ support
+
+Add fwdl/default eeprom load support for Eagle 2 adie TBTC
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Add Eagle 2adie TBTC efuse merge
+Add Eagle 2adie TBTC group prek size
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 8 ++++++--
+ mt7996/eeprom.h | 12 ++++++++++++
+ mt7996/init.c | 6 ++++++
+ mt7996/mcu.c | 5 +++++
+ mt7996/mt7996.h | 8 ++++++++
+ mt7996/regs.h | 1 +
+ 6 files changed, 38 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 3cdd58529..5eb862914 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -158,6 +158,8 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ case 0x7990:
+ if (dev->chip_sku == MT7996_SKU_404)
+ return MT7996_EEPROM_DEFAULT_404;
++ else if (dev->chip_sku == MT7996_SKU_233)
++ return MT7996_EEPROM_DEFAULT_233;
+
+ if (dev->fem_type == MT7996_FEM_INT)
+ return MT7996_EEPROM_DEFAULT_INT;
+@@ -450,6 +452,8 @@ static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7990:
+ dev->prek.rev = mt7996_prek_rev;
++ if (dev->chip_sku == MT7996_SKU_233)
++ dev->prek.rev = mt7996_prek_rev_233;
+ /* 5g & 6g bw 80 dpd channel list is not used */
+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+ break;
+@@ -553,7 +557,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
+ case 0x7990:
+ adie_base = adie_base_7996;
+ /* adie 0 */
+- if (dev->fem_type == MT7996_FEM_INT)
++ if (dev->fem_type == MT7996_FEM_INT && dev->chip_sku != MT7996_SKU_233)
+ adie_id = ADIE_7975;
+ else
+ adie_id = ADIE_7976;
+@@ -561,7 +565,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
+ eep_offs[0] = eep_offs_list[adie_id];
+
+ /* adie 1 */
+- if (dev->chip_sku != MT7996_SKU_404) {
++ if (dev->chip_sku == MT7996_SKU_444) {
+ adie_offs[1] = adie_offs_list[ADIE_7977];
+ eep_offs[1] = eep_offs_list[ADIE_7977];
+ }
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index fa9c31e7a..43c9783c0 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -70,6 +70,18 @@ static const u32 mt7996_prek_rev[] = {
+ [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
+ };
+
++static const u32 mt7996_prek_rev_233[] = {
++ [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
++ [GROUP_SIZE_5G] = 44 * MT_EE_CAL_UNIT,
++ [GROUP_SIZE_6G] = 100 * MT_EE_CAL_UNIT,
++ [ADCDCOC_SIZE_2G] = 4 * 4,
++ [ADCDCOC_SIZE_5G] = 4 * 4,
++ [ADCDCOC_SIZE_6G] = 4 * 5,
++ [DPD_LEGACY_SIZE] = 4 * MT_EE_CAL_UNIT,
++ [DPD_MEM_SIZE] = 13 * MT_EE_CAL_UNIT,
++ [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
++};
++
+ /* kite 2/5g config */
+ static const u32 mt7992_prek_rev[] = {
+ [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index d4b0a72eb..cb0032290 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -916,6 +916,12 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
+
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7990:
++ if (FIELD_GET(MT_PAD_GPIO_2ADIE_TBTC, val)) {
++ dev->chip_sku = MT7996_SKU_233;
++ dev->fem_type = MT7996_FEM_INT;
++ return 0;
++ }
++
+ adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
+ if (adie_comb <= 1)
+ dev->chip_sku = MT7996_SKU_444;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6053242cd..e70932760 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -23,6 +23,11 @@
+ _fw = MT7992_##name; \
+ break; \
+ case 0x7990: \
++ if ((_dev)->chip_sku == MT7996_SKU_233) \
++ _fw = MT7996_##name##_233; \
++ else \
++ _fw = MT7996_##name; \
++ break; \
+ default: \
+ _fw = MT7996_##name; \
+ break; \
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 1bba5450f..72865c8ef 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -35,6 +35,12 @@
+ #define MT7996_FIRMWARE_WM_TM "mediatek/mt7996/mt7996_wm_tm.bin"
+ #define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin"
+
++#define MT7996_FIRMWARE_WA_233 "mediatek/mt7996/mt7996_wa_233.bin"
++#define MT7996_FIRMWARE_WM_233 "mediatek/mt7996/mt7996_wm_233.bin"
++#define MT7996_FIRMWARE_DSP_233 MT7996_FIRMWARE_DSP
++#define MT7996_FIRMWARE_WM_TM_233 "mediatek/mt7996/mt7996_wm_tm_233.bin"
++#define MT7996_ROM_PATCH_233 "mediatek/mt7996/mt7996_rom_patch_233.bin"
++
+ #define MT7992_FIRMWARE_WA "mediatek/mt7996/mt7992_wa.bin"
+ #define MT7992_FIRMWARE_WM "mediatek/mt7996/mt7992_wm.bin"
+ #define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin"
+@@ -55,6 +61,7 @@
+
+ #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
+ #define MT7996_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7996_eeprom_2i5i6i.bin"
++#define MT7996_EEPROM_DEFAULT_233 "mediatek/mt7996/mt7996_eeprom_233.bin"
+ #define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
+ #define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom_2i5i.bin"
+ #define MT7992_EEPROM_DEFAULT_EXT "mediatek/mt7996/mt7992_eeprom_2e5e.bin"
+@@ -122,6 +129,7 @@ enum mt7996_fem_type {
+ enum mt7996_sku_type {
+ MT7996_SKU_404,
+ MT7996_SKU_444,
++ MT7996_SKU_233,
+ };
+
+ enum mt7992_sku_type {
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 263737c52..91159c635 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -666,6 +666,7 @@ enum offs_rev {
+
+ #define MT_PAD_GPIO 0x700056f0
+ #define MT_PAD_GPIO_ADIE_COMB GENMASK(16, 15)
++#define MT_PAD_GPIO_2ADIE_TBTC BIT(19)
+ #define MT_PAD_GPIO_ADIE_COMB_7992 GENMASK(17, 16)
+ #define MT_PAD_GPIO_ADIE_NUM_7992 BIT(15)
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch
new file mode 100644
index 0000000..4668cc9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch
@@ -0,0 +1,82 @@
+From c7587ccb5403436628fa4568c4f1aadeb322cb81 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 22 Dec 2023 17:27:10 +0800
+Subject: [PATCH 054/116] mtk: wifi: mt76: mt7996: add background radar hw cap
+ check
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/debugfs.c | 5 +++++
+ mt7996/init.c | 7 ++++---
+ mt7996/mt7996.h | 20 ++++++++++++++++++++
+ 3 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index f3a520bef..6c5fbc78c 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -262,6 +262,11 @@ mt7996_rdd_monitor(struct seq_file *s, void *data)
+
+ mutex_lock(&dev->mt76.mutex);
+
++ if (!mt7996_get_background_radar_cap(dev)) {
++ seq_puts(s, "no background radar capability\n");
++ goto out;
++ }
++
+ if (!cfg80211_chandef_valid(chandef)) {
+ ret = -EINVAL;
+ goto out;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index cb0032290..6430bfa46 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -404,9 +404,10 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
+- if (!mdev->dev->of_node ||
+- !of_property_read_bool(mdev->dev->of_node,
+- "mediatek,disable-radar-background"))
++ if (mt7996_get_background_radar_cap(phy->dev) &&
++ (!mdev->dev->of_node ||
++ !of_property_read_bool(mdev->dev->of_node,
++ "mediatek,disable-radar-background")))
+ wiphy_ext_feature_set(wiphy,
+ NL80211_EXT_FEATURE_RADAR_BACKGROUND);
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 72865c8ef..c73701df1 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -605,6 +605,26 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
+ return band == MT_BAND0 || band == MT_BAND2;
+ }
+
++static inline bool
++mt7996_get_background_radar_cap(struct mt7996_dev *dev)
++{
++ switch (mt76_chip(&dev->mt76)) {
++ case 0x7990:
++ if (dev->chip_sku == MT7996_SKU_233)
++ return 0;
++ break;
++ case 0x7992:
++ if (dev->chip_sku == MT7992_SKU_23 ||
++ dev->chip_sku == MT7992_SKU_24)
++ return 0;
++ break;
++ default:
++ break;
++ }
++
++ return 1;
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch
new file mode 100644
index 0000000..e692eea
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch
@@ -0,0 +1,99 @@
+From 2f7296b87ad9175b6761aa3db3d88a8f67d45fe7 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 19 Mar 2024 17:33:49 +0800
+Subject: [PATCH 055/116] mtk: wifi: mt76: mt7996: add fallback in case of
+ missing precal data
+
+Align Wi-Fi 6 upstream changes
+https://github.com/openwrt/mt76/commit/2135e201e7a9339e018d4e2d4a33c73266e674d7
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 30 +++++++++++++++++++++---------
+ mt7996/init.c | 2 +-
+ mt7996/main.c | 2 +-
+ 3 files changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 5eb862914..9987bdab2 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -487,17 +487,31 @@ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
+ size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
+
+ dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
+- if (!dev->cal)
+- return -ENOMEM;
++ if (!dev->cal) {
++ ret = -ENOMEM;
++ goto fail;
++ }
+
+- if (dev->bin_file_mode)
+- return mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
++ if (dev->bin_file_mode) {
++ ret = mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
++ if (ret)
++ goto fail;
++ }
+
+ ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
+ if (!ret)
+- return ret;
++ return 0;
++
++ ret = mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
++ if (!ret)
++ return 0;
++
++fail:
++ dev_warn(dev->mt76.dev, "Failed to load precal data: %d\n", ret);
++ devm_kfree(dev->mt76.dev, dev->cal);
++ dev->cal = NULL;
+
+- return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
++ return ret;
+ }
+
+ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
+@@ -652,9 +666,7 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
+- ret = mt7996_eeprom_load_precal(dev);
+- if (ret)
+- return ret;
++ mt7996_eeprom_load_precal(dev);
+
+ ret = mt7996_apply_cal_free_data(dev);
+ if (ret)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 6430bfa46..c10f6675d 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1010,7 +1010,7 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ if (ret < 0)
+ return ret;
+
+- if (dev->flash_mode) {
++ if (dev->cal) {
+ ret = mt7996_mcu_apply_group_cal(dev);
+ if (ret)
+ return ret;
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 2f1dd0fb7..aef08a882 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -345,7 +345,7 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+
+ mt76_set_channel(phy->mt76);
+
+- if (dev->flash_mode) {
++ if (dev->cal) {
+ ret = mt7996_mcu_apply_tx_dpd(phy);
+ if (ret)
+ goto out;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch
new file mode 100644
index 0000000..0aa0f55
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch
@@ -0,0 +1,94 @@
+From 8353ce159acb7a91db3d9df1591b1d0882b11063 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 27 Mar 2024 17:50:16 +0800
+Subject: [PATCH 056/116] mtk: wifi: mt76: mt7996: add kite part number support
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Ib06648398f18b47c324e18b476a57444d929608f
+---
+ mt7996/eeprom.c | 35 +++++++++++++++++++++++------------
+ 1 file changed, 23 insertions(+), 12 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 9987bdab2..51455d877 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -316,26 +316,39 @@ out:
+ return ret;
+ }
+
+-static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
++static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_phy *phy,
++ u8 *path, u8 *rx_path, u8 *nss)
+ {
+ #define MODE_HE_ONLY BIT(0)
++#define STREAM_MASK GENMASK(2, 0)
++#define STREAM_OFFSET 1
++#define TX_PATH_OFFSET 10
++#define RX_PATH_OFFSET 19
+ #define WTBL_SIZE_GROUP GENMASK(31, 28)
++#define GET_STREAM_CAP(offs) ({ \
++ typeof(offs) _offs = (offs); \
++ ((cap & (STREAM_MASK << _offs)) >> _offs); \
++})
++ struct mt7996_dev *dev = phy->dev;
+ u32 cap = 0;
+ int ret;
++ u8 band_offs = phy->mt76->band_idx * hweight8(STREAM_MASK);
+
+ ret = mt7996_mcu_get_chip_config(dev, &cap);
+ if (ret)
+ return ret;
+
+- cap = 0x4b249248; /* internal hardcode */
++ dev->has_eht = true;
+ if (cap) {
+ dev->has_eht = !(cap & MODE_HE_ONLY);
+ dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
++ *nss = min_t(u8, *nss, GET_STREAM_CAP(STREAM_OFFSET + band_offs));
++ *path = min_t(u8, *path, GET_STREAM_CAP(TX_PATH_OFFSET + band_offs));
++ *rx_path = min_t(u8, *rx_path, GET_STREAM_CAP(RX_PATH_OFFSET + band_offs));
+ }
+
+- if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4 ||
+- is_mt7992(&dev->mt76))
+- dev->wtbl_size_group = 2; /* set default */
++ if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4)
++ dev->wtbl_size_group = is_mt7996(&dev->mt76) ? 4 : 2; /* set default */
+
+ return 0;
+ }
+@@ -379,13 +392,15 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ {
++ struct mt76_phy *mphy = phy->mt76;
+ u8 path, rx_path, nss, band_idx = phy->mt76->band_idx;
+ u8 *eeprom = dev->mt76.eeprom.data;
+- struct mt76_phy *mphy = phy->mt76;
+- int max_path = 5, max_nss = 4;
+- int ret;
++ int ret, max_path = 5, max_nss = 4;
+
+ mt7996_parse_eeprom_stream(eeprom, band_idx, &path, &rx_path, &nss);
++ ret = mt7996_eeprom_parse_efuse_hw_cap(phy, &path, &rx_path, &nss);
++ if (ret)
++ return ret;
+
+ if (!path || path > max_path)
+ path = max_path;
+@@ -405,10 +420,6 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] +
+ hweight16(mphy->chainmask);
+
+- ret = mt7996_eeprom_parse_efuse_hw_cap(dev);
+- if (ret)
+- return ret;
+-
+ return mt7996_eeprom_parse_band_config(phy);
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
new file mode 100644
index 0000000..68ec2b5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
@@ -0,0 +1,619 @@
+From 7883011e4646c3c69b1f181d3a7de98e811c68fb Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 19:49:22 +0800
+Subject: [PATCH 057/116] mtk: wifi: mt76: revert page_poll for kernel 5.4
+
+This reverts commit e8c10835cf062c577ddf426913788c39d30b4bd7.
+
+Change-Id: I4e5764fc545087f691fb4c2f43e7a9cefd1e1657
+---
+ dma.c | 75 ++++++++++++++++++++++++++-------------------------
+ mac80211.c | 56 --------------------------------------
+ mt76.h | 22 +--------------
+ mt7915/main.c | 26 +++++++-----------
+ usb.c | 43 ++++++++++++++---------------
+ wed.c | 50 ++++++++++++++++++++++------------
+ 6 files changed, 104 insertions(+), 168 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 66c000ef0..33a84f5fa 100644
+--- a/dma.c
++++ b/dma.c
+@@ -178,7 +178,7 @@ mt76_free_pending_rxwi(struct mt76_dev *dev)
+ local_bh_disable();
+ while ((t = __mt76_get_rxwi(dev)) != NULL) {
+ if (t->ptr)
+- mt76_put_page_pool_buf(t->ptr, false);
++ skb_free_frag(t->ptr);
+ kfree(t);
+ }
+ local_bh_enable();
+@@ -450,9 +450,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ if (!t)
+ return NULL;
+
+- dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr,
+- SKB_WITH_OVERHEAD(q->buf_size),
+- page_pool_get_dma_dir(q->page_pool));
++ dma_unmap_single(dev->dma_dev, t->dma_addr,
++ SKB_WITH_OVERHEAD(q->buf_size),
++ DMA_FROM_DEVICE);
+
+ buf = t->ptr;
+ t->dma_addr = 0;
+@@ -462,9 +462,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ if (drop)
+ *drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
+ } else {
+- dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
+- SKB_WITH_OVERHEAD(q->buf_size),
+- page_pool_get_dma_dir(q->page_pool));
++ dma_unmap_single(dev->dma_dev, e->dma_addr[0],
++ SKB_WITH_OVERHEAD(q->buf_size),
++ DMA_FROM_DEVICE);
+ }
+
+ done:
+@@ -638,7 +638,8 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ bool allow_direct)
+ {
+ int len = SKB_WITH_OVERHEAD(q->buf_size);
+- int frames = 0;
++ int frames = 0, offset = q->buf_offset;
++ dma_addr_t addr;
+
+ if (!q->ndesc)
+ return 0;
+@@ -647,28 +648,29 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+
+ while (q->queued < q->ndesc - 1) {
+ struct mt76_queue_buf qbuf = {};
+- enum dma_data_direction dir;
+- dma_addr_t addr;
+- int offset;
+ void *buf = NULL;
+
+ if (mt76_queue_is_wed_rro_ind(q))
+ goto done;
+
+- buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
++ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
+ if (!buf)
+ break;
+
+- addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
+- dir = page_pool_get_dma_dir(q->page_pool);
+- dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
++ addr = dma_map_single(dev->dma_dev, buf, len, DMA_FROM_DEVICE);
++ if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
++ skb_free_frag(buf);
++ break;
++ }
+
+- qbuf.addr = addr + q->buf_offset;
++ qbuf.addr = addr + offset;
+ done:
+- qbuf.len = len - q->buf_offset;
++ qbuf.len = len - offset;
+ qbuf.skip_unmap = false;
+ if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
+- mt76_put_page_pool_buf(buf, allow_direct);
++ dma_unmap_single(dev->dma_dev, addr, len,
++ DMA_FROM_DEVICE);
++ skb_free_frag(buf);
+ break;
+ }
+ frames++;
+@@ -722,10 +724,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ if (!q->entry)
+ return -ENOMEM;
+
+- ret = mt76_create_page_pool(dev, q);
+- if (ret)
+- return ret;
+-
+ ret = mt76_wed_dma_setup(dev, q, false);
+ if (ret)
+ return ret;
+@@ -744,6 +742,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ static void
+ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ {
++ struct page *page;
+ void *buf;
+ bool more;
+
+@@ -759,7 +758,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ break;
+
+ if (!mt76_queue_is_wed_rro(q))
+- mt76_put_page_pool_buf(buf, false);
++ skb_free_frag(buf);
+ } while (1);
+
+ spin_lock_bh(&q->lock);
+@@ -769,6 +768,16 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ }
+
+ spin_unlock_bh(&q->lock);
++
++ if (mt76_queue_is_wed_rx(q))
++ return;
++
++ if (!q->rx_page.va)
++ return;
++
++ page = virt_to_page(q->rx_page.va);
++ __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++ memset(&q->rx_page, 0, sizeof(q->rx_page));
+ }
+
+ static void
+@@ -791,15 +800,10 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+ /* reset WED rx queues */
+ mt76_wed_dma_setup(dev, q, true);
+
+- if (mt76_queue_is_wed_tx_free(q))
+- return;
+-
+- if (mtk_wed_device_active(&dev->mmio.wed) &&
+- mt76_queue_is_wed_rro(q))
+- return;
+-
+- mt76_dma_sync_idx(dev, q);
+- mt76_dma_rx_fill(dev, q, false);
++ if (!mt76_queue_is_wed_tx_free(q)) {
++ mt76_dma_sync_idx(dev, q);
++ mt76_dma_rx_fill(dev, q, false);
++ }
+ }
+
+ static void
+@@ -816,7 +820,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+
+ skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size);
+ } else {
+- mt76_put_page_pool_buf(data, allow_direct);
++ skb_free_frag(data);
+ }
+
+ if (more)
+@@ -891,7 +895,6 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ goto free_frag;
+
+ skb_reserve(skb, q->buf_offset);
+- skb_mark_for_recycle(skb);
+
+ *(u32 *)skb->cb = info;
+
+@@ -907,7 +910,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ continue;
+
+ free_frag:
+- mt76_put_page_pool_buf(data, allow_direct);
++ skb_free_frag(data);
+ }
+
+ mt76_dma_rx_fill(dev, q, true);
+@@ -1010,8 +1013,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+
+ netif_napi_del(&dev->napi[i]);
+ mt76_dma_rx_cleanup(dev, q);
+-
+- page_pool_destroy(q->page_pool);
+ }
+
+ if (mtk_wed_device_active(&dev->mmio.wed))
+diff --git a/mac80211.c b/mac80211.c
+index 8091a60e0..f7b9ba6a0 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -565,47 +565,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
+ }
+ EXPORT_SYMBOL_GPL(mt76_unregister_phy);
+
+-int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q)
+-{
+- struct page_pool_params pp_params = {
+- .order = 0,
+- .flags = PP_FLAG_PAGE_FRAG,
+- .nid = NUMA_NO_NODE,
+- .dev = dev->dma_dev,
+- };
+- int idx = q - dev->q_rx;
+-
+- switch (idx) {
+- case MT_RXQ_MAIN:
+- case MT_RXQ_BAND1:
+- case MT_RXQ_BAND2:
+- pp_params.pool_size = 256;
+- break;
+- default:
+- pp_params.pool_size = 16;
+- break;
+- }
+-
+- if (mt76_is_mmio(dev)) {
+- /* rely on page_pool for DMA mapping */
+- pp_params.flags |= PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
+- pp_params.dma_dir = DMA_FROM_DEVICE;
+- pp_params.max_len = PAGE_SIZE;
+- pp_params.offset = 0;
+- }
+-
+- q->page_pool = page_pool_create(&pp_params);
+- if (IS_ERR(q->page_pool)) {
+- int err = PTR_ERR(q->page_pool);
+-
+- q->page_pool = NULL;
+- return err;
+- }
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(mt76_create_page_pool);
+-
+ struct mt76_dev *
+ mt76_alloc_device(struct device *pdev, unsigned int size,
+ const struct ieee80211_ops *ops,
+@@ -1818,21 +1777,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
+ }
+ EXPORT_SYMBOL_GPL(mt76_ethtool_worker);
+
+-void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index)
+-{
+-#ifdef CONFIG_PAGE_POOL_STATS
+- struct page_pool_stats stats = {};
+- int i;
+-
+- mt76_for_each_q_rx(dev, i)
+- page_pool_get_stats(dev->q_rx[i].page_pool, &stats);
+-
+- page_pool_ethtool_stats_get(data, &stats);
+- *index += page_pool_ethtool_stats_get_count();
+-#endif
+-}
+-EXPORT_SYMBOL_GPL(mt76_ethtool_page_pool_stats);
+-
+ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
+ {
+ struct ieee80211_hw *hw = phy->hw;
+diff --git a/mt76.h b/mt76.h
+index 11cbb2d28..e21c6537f 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -251,7 +251,7 @@ struct mt76_queue {
+
+ dma_addr_t desc_dma;
+ struct sk_buff *rx_head;
+- struct page_pool *page_pool;
++ struct page_frag_cache rx_page;
+ };
+
+ struct mt76_mcu_ops {
+@@ -1606,7 +1606,6 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
+ return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout);
+ }
+
+-void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index);
+ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
+ struct mt76_sta_stats *stats, bool eht);
+ int mt76_skb_adjust_pad(struct sk_buff *skb, int pad);
+@@ -1752,25 +1751,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+ struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+ struct mt76_txwi_cache *r, dma_addr_t phys);
+-int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q);
+-static inline void mt76_put_page_pool_buf(void *buf, bool allow_direct)
+-{
+- struct page *page = virt_to_head_page(buf);
+-
+- page_pool_put_full_page(page->pp, page, allow_direct);
+-}
+-
+-static inline void *
+-mt76_get_page_pool_buf(struct mt76_queue *q, u32 *offset, u32 size)
+-{
+- struct page *page;
+-
+- page = page_pool_dev_alloc_frag(q->page_pool, offset, size);
+- if (!page)
+- return NULL;
+-
+- return page_address(page) + *offset;
+-}
+
+ static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
+ {
+diff --git a/mt7915/main.c b/mt7915/main.c
+index b16a63366..ad91fc3c8 100644
+--- a/mt7915/main.c
++++ b/mt7915/main.c
+@@ -1402,22 +1402,19 @@ void mt7915_get_et_strings(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data)
+ {
+- if (sset != ETH_SS_STATS)
+- return;
+-
+- memcpy(data, mt7915_gstrings_stats, sizeof(mt7915_gstrings_stats));
+- data += sizeof(mt7915_gstrings_stats);
+- page_pool_ethtool_stats_get_strings(data);
++ if (sset == ETH_SS_STATS)
++ memcpy(data, mt7915_gstrings_stats,
++ sizeof(mt7915_gstrings_stats));
+ }
+
+ static
+ int mt7915_get_et_sset_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset)
+ {
+- if (sset != ETH_SS_STATS)
+- return 0;
++ if (sset == ETH_SS_STATS)
++ return MT7915_SSTATS_LEN;
+
+- return MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
++ return 0;
+ }
+
+ static void mt7915_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+@@ -1445,7 +1442,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
+ .idx = mvif->mt76.idx,
+ };
+ /* See mt7915_ampdu_stat_read_phy, etc */
+- int i, ei = 0, stats_size;
++ int i, ei = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+@@ -1557,12 +1554,9 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
+ return;
+
+ ei += wi.worker_stat_count;
+-
+- mt76_ethtool_page_pool_stats(&dev->mt76, &data[ei], &ei);
+-
+- stats_size = MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
+- if (ei != stats_size)
+- dev_err(dev->mt76.dev, "ei: %d size: %d", ei, stats_size);
++ if (ei != MT7915_SSTATS_LEN)
++ dev_err(dev->mt76.dev, "ei: %d MT7915_SSTATS_LEN: %d",
++ ei, (int)MT7915_SSTATS_LEN);
+ }
+
+ static void
+diff --git a/usb.c b/usb.c
+index dc690d1cd..058f2d124 100644
+--- a/usb.c
++++ b/usb.c
+@@ -319,27 +319,29 @@ mt76u_set_endpoints(struct usb_interface *intf,
+
+ static int
+ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
+- int nsgs)
++ int nsgs, gfp_t gfp)
+ {
+ int i;
+
+ for (i = 0; i < nsgs; i++) {
++ struct page *page;
+ void *data;
+ int offset;
+
+- data = mt76_get_page_pool_buf(q, &offset, q->buf_size);
++ data = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
+ if (!data)
+ break;
+
+- sg_set_page(&urb->sg[i], virt_to_head_page(data), q->buf_size,
+- offset);
++ page = virt_to_head_page(data);
++ offset = data - page_address(page);
++ sg_set_page(&urb->sg[i], page, q->buf_size, offset);
+ }
+
+ if (i < nsgs) {
+ int j;
+
+ for (j = nsgs; j < urb->num_sgs; j++)
+- mt76_put_page_pool_buf(sg_virt(&urb->sg[j]), false);
++ skb_free_frag(sg_virt(&urb->sg[j]));
+ urb->num_sgs = i;
+ }
+
+@@ -352,16 +354,15 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
+
+ static int
+ mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
+- struct urb *urb, int nsgs)
++ struct urb *urb, int nsgs, gfp_t gfp)
+ {
+ enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
+- int offset;
+
+ if (qid == MT_RXQ_MAIN && dev->usb.sg_en)
+- return mt76u_fill_rx_sg(dev, q, urb, nsgs);
++ return mt76u_fill_rx_sg(dev, q, urb, nsgs, gfp);
+
+ urb->transfer_buffer_length = q->buf_size;
+- urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size);
++ urb->transfer_buffer = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
+
+ return urb->transfer_buffer ? 0 : -ENOMEM;
+ }
+@@ -399,7 +400,7 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
+ if (err)
+ return err;
+
+- return mt76u_refill_rx(dev, q, e->urb, sg_size);
++ return mt76u_refill_rx(dev, q, e->urb, sg_size, GFP_KERNEL);
+ }
+
+ static void mt76u_urb_free(struct urb *urb)
+@@ -407,10 +408,10 @@ static void mt76u_urb_free(struct urb *urb)
+ int i;
+
+ for (i = 0; i < urb->num_sgs; i++)
+- mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
++ skb_free_frag(sg_virt(&urb->sg[i]));
+
+ if (urb->transfer_buffer)
+- mt76_put_page_pool_buf(urb->transfer_buffer, false);
++ skb_free_frag(urb->transfer_buffer);
+
+ usb_free_urb(urb);
+ }
+@@ -546,8 +547,6 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
+ len -= data_len;
+ nsgs++;
+ }
+-
+- skb_mark_for_recycle(skb);
+ dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
+
+ return nsgs;
+@@ -613,7 +612,7 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+
+ count = mt76u_process_rx_entry(dev, urb, q->buf_size);
+ if (count > 0) {
+- err = mt76u_refill_rx(dev, q, urb, count);
++ err = mt76u_refill_rx(dev, q, urb, count, GFP_ATOMIC);
+ if (err < 0)
+ break;
+ }
+@@ -664,10 +663,6 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
+ struct mt76_queue *q = &dev->q_rx[qid];
+ int i, err;
+
+- err = mt76_create_page_pool(dev, q);
+- if (err)
+- return err;
+-
+ spin_lock_init(&q->lock);
+ q->entry = devm_kcalloc(dev->dev,
+ MT_NUM_RX_ENTRIES, sizeof(*q->entry),
+@@ -696,6 +691,7 @@ EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue);
+ static void
+ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ {
++ struct page *page;
+ int i;
+
+ for (i = 0; i < q->ndesc; i++) {
+@@ -705,8 +701,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ mt76u_urb_free(q->entry[i].urb);
+ q->entry[i].urb = NULL;
+ }
+- page_pool_destroy(q->page_pool);
+- q->page_pool = NULL;
++
++ if (!q->rx_page.va)
++ return;
++
++ page = virt_to_page(q->rx_page.va);
++ __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++ memset(&q->rx_page, 0, sizeof(q->rx_page));
+ }
+
+ static void mt76u_free_rx(struct mt76_dev *dev)
+diff --git a/wed.c b/wed.c
+index f89e45375..8eca4d818 100644
+--- a/wed.c
++++ b/wed.c
+@@ -9,8 +9,12 @@
+ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
+ {
+ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
++ u32 length;
+ int i;
+
++ length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
++ sizeof(struct skb_shared_info));
++
+ for (i = 0; i < dev->rx_token_size; i++) {
+ struct mt76_txwi_cache *t;
+
+@@ -18,7 +22,9 @@ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
+ if (!t || !t->ptr)
+ continue;
+
+- mt76_put_page_pool_buf(t->ptr, false);
++ dma_unmap_single(dev->dma_dev, t->dma_addr,
++ wed->wlan.rx_size, DMA_FROM_DEVICE);
++ __free_pages(virt_to_page(t->ptr), get_order(length));
+ t->ptr = NULL;
+
+ mt76_put_rxwi(dev, t);
+@@ -33,33 +39,45 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ {
+ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc;
+- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+- int i, len = SKB_WITH_OVERHEAD(q->buf_size);
+- struct mt76_txwi_cache *t = NULL;
++ u32 length;
++ int i;
++
++ length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
++ sizeof(struct skb_shared_info));
+
+ for (i = 0; i < size; i++) {
+- enum dma_data_direction dir;
++ struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
+ dma_addr_t addr;
+- u32 offset;
++ struct page *page;
+ int token;
+- void *buf;
++ void *ptr;
+
+- t = mt76_get_rxwi(dev);
+ if (!t)
+ goto unmap;
+
+- buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
+- if (!buf)
++ page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
++ if (!page) {
++ mt76_put_rxwi(dev, t);
+ goto unmap;
++ }
+
+- addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
+- dir = page_pool_get_dma_dir(q->page_pool);
+- dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
++ addr = dma_map_single(dev->dma_dev, ptr,
++ wed->wlan.rx_size,
++ DMA_TO_DEVICE);
++
++ if (unlikely(dma_mapping_error(dev->dev, addr))) {
++ skb_free_frag(ptr);
++ mt76_put_rxwi(dev, t);
++ goto unmap;
++ }
+
+ desc->buf0 = cpu_to_le32(addr);
+- token = mt76_rx_token_consume(dev, buf, t, addr);
++ token = mt76_rx_token_consume(dev, ptr, t, addr);
+ if (token < 0) {
+- mt76_put_page_pool_buf(buf, false);
++ dma_unmap_single(dev->dma_dev, addr,
++ wed->wlan.rx_size, DMA_TO_DEVICE);
++ __free_pages(page, get_order(length));
++ mt76_put_rxwi(dev, t);
+ goto unmap;
+ }
+
+@@ -74,8 +92,6 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ return 0;
+
+ unmap:
+- if (t)
+- mt76_put_rxwi(dev, t);
+ mt76_wed_release_rx_buf(wed);
+
+ return -ENOMEM;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-wifi-mt76-rework-wed-rx-flow.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-wifi-mt76-rework-wed-rx-flow.patch
new file mode 100644
index 0000000..5672878
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-wifi-mt76-rework-wed-rx-flow.patch
@@ -0,0 +1,542 @@
+From ce4b2f430de33a73028d24acac09c0db3ce335e6 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 13:37:23 +0800
+Subject: [PATCH 058/116] mtk: wifi: mt76: rework wed rx flow
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Change-Id: Icd787345c811cb5ad30d9c7c1c5f9e5298bd3be6
+---
+ dma.c | 125 +++++++++++++++++++++++++++++++-----------------
+ mac80211.c | 2 +-
+ mt76.h | 25 ++++++----
+ mt7915/mmio.c | 3 +-
+ mt7915/mt7915.h | 1 +
+ tx.c | 16 +++----
+ wed.c | 57 ++++++++++++++--------
+ 7 files changed, 144 insertions(+), 85 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 33a84f5fa..c54187bd6 100644
+--- a/dma.c
++++ b/dma.c
+@@ -64,17 +64,17 @@ mt76_alloc_txwi(struct mt76_dev *dev)
+ return t;
+ }
+
+-static struct mt76_txwi_cache *
++static struct mt76_rxwi_cache *
+ mt76_alloc_rxwi(struct mt76_dev *dev)
+ {
+- struct mt76_txwi_cache *t;
++ struct mt76_rxwi_cache *r;
+
+- t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC);
+- if (!t)
++ r = kzalloc(L1_CACHE_ALIGN(sizeof(*r)), GFP_ATOMIC);
++ if (!r)
+ return NULL;
+
+- t->ptr = NULL;
+- return t;
++ r->ptr = NULL;
++ return r;
+ }
+
+ static struct mt76_txwi_cache *
+@@ -93,20 +93,20 @@ __mt76_get_txwi(struct mt76_dev *dev)
+ return t;
+ }
+
+-static struct mt76_txwi_cache *
++static struct mt76_rxwi_cache *
+ __mt76_get_rxwi(struct mt76_dev *dev)
+ {
+- struct mt76_txwi_cache *t = NULL;
++ struct mt76_rxwi_cache *r = NULL;
+
+- spin_lock_bh(&dev->wed_lock);
++ spin_lock_bh(&dev->lock);
+ if (!list_empty(&dev->rxwi_cache)) {
+- t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache,
++ r = list_first_entry(&dev->rxwi_cache, struct mt76_rxwi_cache,
+ list);
+- list_del(&t->list);
++ list_del(&r->list);
+ }
+- spin_unlock_bh(&dev->wed_lock);
++ spin_unlock_bh(&dev->lock);
+
+- return t;
++ return r;
+ }
+
+ static struct mt76_txwi_cache *
+@@ -120,13 +120,13 @@ mt76_get_txwi(struct mt76_dev *dev)
+ return mt76_alloc_txwi(dev);
+ }
+
+-struct mt76_txwi_cache *
++struct mt76_rxwi_cache *
+ mt76_get_rxwi(struct mt76_dev *dev)
+ {
+- struct mt76_txwi_cache *t = __mt76_get_rxwi(dev);
++ struct mt76_rxwi_cache *r = __mt76_get_rxwi(dev);
+
+- if (t)
+- return t;
++ if (r)
++ return r;
+
+ return mt76_alloc_rxwi(dev);
+ }
+@@ -145,14 +145,14 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+ EXPORT_SYMBOL_GPL(mt76_put_txwi);
+
+ void
+-mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
++mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r)
+ {
+- if (!t)
++ if (!r)
+ return;
+
+- spin_lock_bh(&dev->wed_lock);
+- list_add(&t->list, &dev->rxwi_cache);
+- spin_unlock_bh(&dev->wed_lock);
++ spin_lock_bh(&dev->lock);
++ list_add(&r->list, &dev->rxwi_cache);
++ spin_unlock_bh(&dev->lock);
+ }
+ EXPORT_SYMBOL_GPL(mt76_put_rxwi);
+
+@@ -173,13 +173,13 @@ mt76_free_pending_txwi(struct mt76_dev *dev)
+ void
+ mt76_free_pending_rxwi(struct mt76_dev *dev)
+ {
+- struct mt76_txwi_cache *t;
++ struct mt76_rxwi_cache *r;
+
+ local_bh_disable();
+- while ((t = __mt76_get_rxwi(dev)) != NULL) {
+- if (t->ptr)
+- skb_free_frag(t->ptr);
+- kfree(t);
++ while ((r = __mt76_get_rxwi(dev)) != NULL) {
++ if (r->ptr)
++ skb_free_frag(r->ptr);
++ kfree(r);
+ }
+ local_bh_enable();
+ }
+@@ -225,10 +225,10 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
+
+ static int
+ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+- struct mt76_queue_buf *buf, void *data)
++ struct mt76_queue_buf *buf, void *data,
++ struct mt76_rxwi_cache *rxwi)
+ {
+ struct mt76_queue_entry *entry = &q->entry[q->head];
+- struct mt76_txwi_cache *txwi = NULL;
+ struct mt76_desc *desc;
+ int idx = q->head;
+ u32 buf1 = 0, ctrl;
+@@ -249,13 +249,15 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ #endif
+
+ if (mt76_queue_is_wed_rx(q)) {
+- txwi = mt76_get_rxwi(dev);
+- if (!txwi)
+- return -ENOMEM;
++ if (!rxwi) {
++ rxwi = mt76_get_rxwi(dev);
++ if (!rxwi)
++ return -ENOMEM;
++ }
+
+- rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr);
++ rx_token = mt76_rx_token_consume(dev, data, rxwi, buf->addr);
+ if (rx_token < 0) {
+- mt76_put_rxwi(dev, txwi);
++ mt76_put_rxwi(dev, rxwi);
+ return -ENOMEM;
+ }
+
+@@ -271,7 +273,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ done:
+ entry->dma_addr[0] = buf->addr;
+ entry->dma_len[0] = buf->len;
+- entry->txwi = txwi;
++ entry->rxwi = rxwi;
+ entry->buf = data;
+ entry->wcid = 0xffff;
+ entry->skip_buf1 = true;
+@@ -420,7 +422,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
+
+ static void *
+ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+- int *len, u32 *info, bool *more, bool *drop)
++ int *len, u32 *info, bool *more, bool *drop, bool flush)
+ {
+ struct mt76_queue_entry *e = &q->entry[idx];
+ struct mt76_desc *desc = &q->desc[idx];
+@@ -445,20 +447,53 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+
+ if (mt76_queue_is_wed_rx(q)) {
+ u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
+- struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token);
++ struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
+
+- if (!t)
++ if (!r)
+ return NULL;
+
+- dma_unmap_single(dev->dma_dev, t->dma_addr,
++ dma_unmap_single(dev->dma_dev, r->dma_addr,
+ SKB_WITH_OVERHEAD(q->buf_size),
+ DMA_FROM_DEVICE);
+
+- buf = t->ptr;
+- t->dma_addr = 0;
+- t->ptr = NULL;
++ if (flush) {
++ buf = r->ptr;
++ r->dma_addr = 0;
++ r->ptr = NULL;
++
++ mt76_put_rxwi(dev, r);
++ } else {
++ struct mt76_queue_buf qbuf;
++
++ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++ if (!buf)
++ return NULL;
++
++ memcpy(buf, r->ptr, SKB_WITH_OVERHEAD(q->buf_size));
++
++ r->dma_addr = dma_map_single(dev->dma_dev, r->ptr,
++ SKB_WITH_OVERHEAD(q->buf_size),
++ DMA_FROM_DEVICE);
++ if (unlikely(dma_mapping_error(dev->dma_dev, r->dma_addr))) {
++ skb_free_frag(r->ptr);
++ mt76_put_rxwi(dev, r);
++ return NULL;
++ }
++
++ qbuf.addr = r->dma_addr;
++ qbuf.len = SKB_WITH_OVERHEAD(q->buf_size);
++ qbuf.skip_unmap = false;
++
++ if (mt76_dma_add_rx_buf(dev, q, &qbuf, r->ptr, r) < 0) {
++ dma_unmap_single(dev->dma_dev, r->dma_addr,
++ SKB_WITH_OVERHEAD(q->buf_size),
++ DMA_FROM_DEVICE);
++ skb_free_frag(r->ptr);
++ mt76_put_rxwi(dev, r);
++ return NULL;
++ }
++ }
+
+- mt76_put_rxwi(dev, t);
+ if (drop)
+ *drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
+ } else {
+@@ -495,7 +530,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ q->tail = (q->tail + 1) % q->ndesc;
+ q->queued--;
+
+- return mt76_dma_get_buf(dev, q, idx, len, info, more, drop);
++ return mt76_dma_get_buf(dev, q, idx, len, info, more, drop, flush);
+ }
+
+ static int
+@@ -667,7 +702,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ done:
+ qbuf.len = len - offset;
+ qbuf.skip_unmap = false;
+- if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
++ if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf, NULL) < 0) {
+ dma_unmap_single(dev->dma_dev, addr, len,
+ DMA_FROM_DEVICE);
+ skb_free_frag(buf);
+diff --git a/mac80211.c b/mac80211.c
+index f7b9ba6a0..c7b222837 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -595,7 +595,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&dev->cc_lock);
+ spin_lock_init(&dev->status_lock);
+- spin_lock_init(&dev->wed_lock);
+ mutex_init(&dev->mutex);
+ init_waitqueue_head(&dev->tx_wait);
+
+@@ -628,6 +627,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ INIT_LIST_HEAD(&dev->txwi_cache);
+ INIT_LIST_HEAD(&dev->rxwi_cache);
+ dev->token_size = dev->drv->token_size;
++ dev->rx_token_size = dev->drv->rx_token_size;
+
+ for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
+ skb_queue_head_init(&dev->rx_skb[i]);
+diff --git a/mt76.h b/mt76.h
+index e21c6537f..aecd0a7cd 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -205,6 +205,7 @@ struct mt76_queue_entry {
+ };
+ union {
+ struct mt76_txwi_cache *txwi;
++ struct mt76_rxwi_cache *rxwi;
+ struct urb *urb;
+ int buf_sz;
+ };
+@@ -416,12 +417,16 @@ struct mt76_txwi_cache {
+ struct list_head list;
+ dma_addr_t dma_addr;
+
+- union {
+- struct sk_buff *skb;
+- void *ptr;
+- };
+-
+ unsigned long jiffies;
++
++ struct sk_buff *skb;
++};
++
++struct mt76_rxwi_cache {
++ struct list_head list;
++ dma_addr_t dma_addr;
++
++ void *ptr;
+ };
+
+ struct mt76_rx_tid {
+@@ -509,6 +514,7 @@ struct mt76_driver_ops {
+ u16 txwi_size;
+ u16 token_size;
+ u8 mcs_rates;
++ u16 rx_token_size;
+
+ void (*update_survey)(struct mt76_phy *phy);
+
+@@ -886,7 +892,6 @@ struct mt76_dev {
+
+ struct ieee80211_hw *hw;
+
+- spinlock_t wed_lock;
+ spinlock_t lock;
+ spinlock_t cc_lock;
+
+@@ -1568,8 +1573,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
+ }
+
+ void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+-void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+-struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
++void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r);
++struct mt76_rxwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
+ void mt76_free_pending_rxwi(struct mt76_dev *dev);
+ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+ struct napi_struct *napi);
+@@ -1748,9 +1753,9 @@ struct mt76_txwi_cache *
+ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
+ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
+ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+-struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
++struct mt76_rxwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+- struct mt76_txwi_cache *r, dma_addr_t phys);
++ struct mt76_rxwi_cache *r, dma_addr_t phys);
+
+ static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
+ {
+diff --git a/mt7915/mmio.c b/mt7915/mmio.c
+index 6004d64f5..5938bd9f2 100644
+--- a/mt7915/mmio.c
++++ b/mt7915/mmio.c
+@@ -714,7 +714,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+ wed->wlan.reset = mt7915_mmio_wed_reset;
+ wed->wlan.reset_complete = mt76_wed_reset_complete;
+
+- dev->mt76.rx_token_size = wed->wlan.rx_npkt;
++ dev->mt76.rx_token_size += wed->wlan.rx_npkt;
+
+ if (mtk_wed_device_attach(wed))
+ return 0;
+@@ -921,6 +921,7 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev,
+ SURVEY_INFO_TIME_RX |
+ SURVEY_INFO_TIME_BSS_RX,
+ .token_size = MT7915_TOKEN_SIZE,
++ .rx_token_size = MT7915_RX_TOKEN_SIZE;
+ .tx_prepare_skb = mt7915_tx_prepare_skb,
+ .tx_complete_skb = mt76_connac_tx_complete_skb,
+ .rx_skb = mt7915_queue_rx_skb,
+diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
+index a30d08eb0..f1e2c93a4 100644
+--- a/mt7915/mt7915.h
++++ b/mt7915/mt7915.h
+@@ -62,6 +62,7 @@
+ #define MT7915_EEPROM_BLOCK_SIZE 16
+ #define MT7915_HW_TOKEN_SIZE 4096
+ #define MT7915_TOKEN_SIZE 8192
++#define MT7915_RX_TOKEN_SIZE 4096
+
+ #define MT7915_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */
+ #define MT7915_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
+diff --git a/tx.c b/tx.c
+index ab42f69b8..46dae6e0a 100644
+--- a/tx.c
++++ b/tx.c
+@@ -851,16 +851,16 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ EXPORT_SYMBOL_GPL(mt76_token_consume);
+
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+- struct mt76_txwi_cache *t, dma_addr_t phys)
++ struct mt76_rxwi_cache *r, dma_addr_t phys)
+ {
+ int token;
+
+ spin_lock_bh(&dev->rx_token_lock);
+- token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size,
++ token = idr_alloc(&dev->rx_token, r, 0, dev->rx_token_size,
+ GFP_ATOMIC);
+ if (token >= 0) {
+- t->ptr = ptr;
+- t->dma_addr = phys;
++ r->ptr = ptr;
++ r->dma_addr = phys;
+ }
+ spin_unlock_bh(&dev->rx_token_lock);
+
+@@ -897,15 +897,15 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
+ }
+ EXPORT_SYMBOL_GPL(mt76_token_release);
+
+-struct mt76_txwi_cache *
++struct mt76_rxwi_cache *
+ mt76_rx_token_release(struct mt76_dev *dev, int token)
+ {
+- struct mt76_txwi_cache *t;
++ struct mt76_rxwi_cache *r;
+
+ spin_lock_bh(&dev->rx_token_lock);
+- t = idr_remove(&dev->rx_token, token);
++ r = idr_remove(&dev->rx_token, token);
+ spin_unlock_bh(&dev->rx_token_lock);
+
+- return t;
++ return r;
+ }
+ EXPORT_SYMBOL_GPL(mt76_rx_token_release);
+diff --git a/wed.c b/wed.c
+index 8eca4d818..0a0b5c05c 100644
+--- a/wed.c
++++ b/wed.c
+@@ -9,28 +9,45 @@
+ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
+ {
+ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+- u32 length;
++ struct page *page;
+ int i;
+
+- length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
+- sizeof(struct skb_shared_info));
+-
+ for (i = 0; i < dev->rx_token_size; i++) {
+- struct mt76_txwi_cache *t;
++ struct mt76_rxwi_cache *r;
+
+- t = mt76_rx_token_release(dev, i);
+- if (!t || !t->ptr)
++ r = mt76_rx_token_release(dev, i);
++ if (!r || !r->ptr)
+ continue;
+
+- dma_unmap_single(dev->dma_dev, t->dma_addr,
++ dma_unmap_single(dev->dma_dev, r->dma_addr,
+ wed->wlan.rx_size, DMA_FROM_DEVICE);
+- __free_pages(virt_to_page(t->ptr), get_order(length));
+- t->ptr = NULL;
++ skb_free_frag(r->ptr);
++ r->ptr = NULL;
+
+- mt76_put_rxwi(dev, t);
++ mt76_put_rxwi(dev, r);
+ }
+
+ mt76_free_pending_rxwi(dev);
++
++ mt76_for_each_q_rx(dev, i) {
++ struct mt76_queue *q = &dev->q_rx[i];
++
++ if (mt76_queue_is_wed_rx(q)) {
++ if (!q->rx_page.va)
++ continue;
++
++ page = virt_to_page(q->rx_page.va);
++ __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++ memset(&q->rx_page, 0, sizeof(q->rx_page));
++ }
++ }
++
++ if (!wed->rx_buf_ring.rx_page.va)
++ return;
++
++ page = virt_to_page(wed->rx_buf_ring.rx_page.va);
++ __page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias);
++ memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page));
+ }
+ EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf);
+
+@@ -46,18 +63,18 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ sizeof(struct skb_shared_info));
+
+ for (i = 0; i < size; i++) {
+- struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
++ struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
+ dma_addr_t addr;
+ struct page *page;
+ int token;
+ void *ptr;
+
+- if (!t)
++ if (!r)
+ goto unmap;
+
+- page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
+- if (!page) {
+- mt76_put_rxwi(dev, t);
++ ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
++ if (!ptr) {
++ mt76_put_rxwi(dev, r);
+ goto unmap;
+ }
+
+@@ -67,17 +84,17 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+
+ if (unlikely(dma_mapping_error(dev->dev, addr))) {
+ skb_free_frag(ptr);
+- mt76_put_rxwi(dev, t);
++ mt76_put_rxwi(dev, r);
+ goto unmap;
+ }
+
+ desc->buf0 = cpu_to_le32(addr);
+- token = mt76_rx_token_consume(dev, ptr, t, addr);
++ token = mt76_rx_token_consume(dev, ptr, r, addr);
+ if (token < 0) {
+ dma_unmap_single(dev->dma_dev, addr,
+ wed->wlan.rx_size, DMA_TO_DEVICE);
+- __free_pages(page, get_order(length));
+- mt76_put_rxwi(dev, t);
++ skb_free_frag(ptr);
++ mt76_put_rxwi(dev, r);
+ goto unmap;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch
new file mode 100644
index 0000000..9634bc9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch
@@ -0,0 +1,38 @@
+From d4491420eea3a761e14335aa8bd57eb1b9deee51 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 19 Apr 2023 17:13:41 +0800
+Subject: [PATCH 059/116] mtk: wifi: mt76: wed: change wed token init size to
+ adapt wed3.0
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ tx.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/tx.c b/tx.c
+index 46dae6e0a..e2795067c 100644
+--- a/tx.c
++++ b/tx.c
+@@ -827,12 +827,16 @@ EXPORT_SYMBOL_GPL(__mt76_set_tx_blocked);
+
+ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ {
+- int token;
++ int token, start = 0;
++
++ if (mtk_wed_device_active(&dev->mmio.wed))
++ start = dev->mmio.wed.wlan.nbuf;
+
+ spin_lock_bh(&dev->token_lock);
+
+- token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
+- if (token >= 0)
++ token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
++ GFP_ATOMIC);
++ if (token >= start)
+ dev->token_count++;
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-wifi-mt76-add-random-early-drop-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-wifi-mt76-add-random-early-drop-support.patch
new file mode 100644
index 0000000..ace759b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-wifi-mt76-add-random-early-drop-support.patch
@@ -0,0 +1,316 @@
+From 6441b05d35d5b721fa16f6d529a1ca3be225ca26 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 19 Apr 2023 18:32:41 +0800
+Subject: [PATCH 060/116] mtk: wifi: mt76: add random early drop support
+
+---
+ mt7996/debugfs.c | 1 +
+ mt7996/mac.c | 7 ++++
+ mt7996/mcu.c | 81 ++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/mcu.h | 4 ++-
+ mt7996/mt7996.h | 5 ++-
+ mt7996/mtk_debugfs.c | 23 +++++++++++++
+ mt7996/mtk_mcu.c | 26 ++++++++++++++
+ mt7996/mtk_mcu.h | 24 +++++++++++++
+ 8 files changed, 167 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 6c5fbc78c..8d639372e 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -634,6 +634,7 @@ mt7996_tx_stats_show(struct seq_file *file, void *data)
+ seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts);
+ seq_printf(file, "Tx success: %8u (MPDUs)\n", success);
+ seq_printf(file, "Tx PER: %u%%\n", per);
++ seq_printf(file, "Tx RED drop: %8u\n", phy->red_drop);
+
+ mt7996_txbf_stat_read_phy(phy, file);
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 727d1fb80..78e35aa6b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1176,6 +1176,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+
+ wcid->stats.tx_retries += tx_retries;
+ wcid->stats.tx_failed += tx_failed;
++
++ if (FIELD_GET(MT_TXFREE_INFO_STAT, info) == 2) {
++ struct mt7996_phy *mphy =
++ __mt7996_phy(dev, wcid->phy_idx);
++
++ mphy->red_drop++;
++ }
+ continue;
+ }
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index e70932760..3205f0f72 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3147,8 +3147,8 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
+- return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+- MCU_WA_PARAM_RED, 0, 0);
++ return mt7996_mcu_red_config(dev,
++ mtk_wed_device_active(&dev->mt76.mmio.wed));
+ }
+
+ int mt7996_mcu_init(struct mt7996_dev *dev)
+@@ -3180,6 +3180,83 @@ out:
+ skb_queue_purge(&dev->mt76.mcu.res_q);
+ }
+
++static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
++{
++#define RED_TOKEN_SRC_CNT 4
++#define RED_TOKEN_CONFIG 2
++ struct {
++ __le32 arg0;
++ __le32 arg1;
++ __le32 arg2;
++
++ u8 mode;
++ u8 version;
++ u8 _rsv[4];
++ __le16 len;
++
++ __le16 tcp_offset;
++ __le16 priority_offset;
++ __le16 token_per_src[RED_TOKEN_SRC_CNT];
++ __le16 token_thr_per_src[RED_TOKEN_SRC_CNT];
++
++ u8 _rsv2[604];
++ } __packed req = {
++ .arg0 = cpu_to_le32(MCU_WA_PARAM_RED_CONFIG),
++
++ .mode = RED_TOKEN_CONFIG,
++ .len = cpu_to_le16(sizeof(req) - sizeof(__le32) * 3),
++
++ .tcp_offset = cpu_to_le16(200),
++ .priority_offset = cpu_to_le16(255),
++ };
++ u8 i;
++
++ for (i = 0; i < RED_TOKEN_SRC_CNT; i++) {
++ req.token_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
++ req.token_thr_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
++ }
++
++ if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
++ req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
++ cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
++ &req, sizeof(req), false);
++}
++
++int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable)
++{
++#define RED_DISABLE 0
++#define RED_BY_WA_ENABLE 2
++ struct {
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++ u8 enable;
++ u8 __rsv2[3];
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_VOW_RED_ENABLE),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .enable = enable ? RED_BY_WA_ENABLE : RED_DISABLE,
++ };
++ int ret;
++
++ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
++ sizeof(req), true);
++
++ if (ret)
++ return ret;
++
++ ret = mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
++ MCU_WA_PARAM_RED_EN, enable, 0);
++
++ if (ret || !enable)
++ return ret;
++
++ return mt7996_mcu_wa_red_config(dev);
++}
++
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
+ {
+ struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index f5e91a898..ca78cd506 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -346,8 +346,9 @@ enum {
+ enum {
+ MCU_WA_PARAM_PDMA_RX = 0x04,
+ MCU_WA_PARAM_CPU_UTIL = 0x0b,
+- MCU_WA_PARAM_RED = 0x0e,
++ MCU_WA_PARAM_RED_EN = 0x0e,
+ MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
++ MCU_WA_PARAM_RED_CONFIG = 0x40,
+ };
+
+ enum mcu_mmps_mode {
+@@ -920,6 +921,7 @@ enum {
+ UNI_VOW_DRR_CTRL,
+ UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
+ UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
++ UNI_VOW_RED_ENABLE = 0x18,
+ };
+
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c73701df1..453d22462 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -354,6 +354,7 @@ struct mt7996_phy {
+ u16 punct_bitmap;
+
+ struct mt7996_scs_ctrl scs_ctrl;
++ u32 red_drop;
+
+ bool sku_limit_en;
+ bool sku_path_en;
+@@ -723,6 +724,7 @@ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
+ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val);
+ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
++int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
+ int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+@@ -896,11 +898,12 @@ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
+ void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
+ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
+ void mt7996_tm_update_channel(struct mt7996_phy *phy);
++
++int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ int mt7996_dma_rro_init(struct mt7996_dev *dev);
+ #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+
+-
+ #endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index c7713e8b2..28b920e11 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3074,6 +3074,27 @@ static int mt7996_muru_prot_thr_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
+ mt7996_muru_prot_thr_set, "%lld\n");
+
++static int
++mt7996_red_config_set(void *data, u64 val)
++{
++ struct mt7996_dev *dev = data;
++
++ return mt7996_mcu_red_config(dev, !!val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_red_config, NULL,
++ mt7996_red_config_set, "%lld\n");
++
++static int
++mt7996_vow_drr_dbg(void *data, u64 val)
++{
++ struct mt7996_dev *dev = data;
++
++ return mt7996_mcu_set_vow_drr_dbg(dev, (u32)val);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
++ mt7996_vow_drr_dbg, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -3151,6 +3172,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ mt7996_wtbl_read);
+
+ debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
++ debugfs_create_file("red", 0200, dir, dev, &fops_red_config);
++ debugfs_create_file("vow_drr_dbg", 0200, dir, dev, &fops_vow_drr_dbg);
+
+ debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
+ debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index a9a7db15a..aed32e982 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1316,4 +1316,30 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
+ sizeof(req), false);
+ }
+
++int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
++{
++#define MT7996_VOW_DEBUG_MODE 0xe
++ struct {
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++ u8 __rsv2[4];
++ __le32 action;
++ __le32 val;
++ u8 __rsv3[8];
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .action = cpu_to_le32(MT7996_VOW_DEBUG_MODE),
++ .val = cpu_to_le32(val),
++ };
++
++ if (val & ~VOW_DRR_DBG_FLAGS)
++ return -EINVAL;
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
++ sizeof(req), true);
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 58d61c517..2cffc8937 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -1138,6 +1138,30 @@ enum muru_vendor_ctrl {
+ MU_CTRL_DL_USER_CNT,
+ MU_CTRL_UL_USER_CNT,
+ };
++
++enum {
++ VOW_DRR_DBG_DUMP_BMP = BIT(0),
++ VOW_DRR_DBG_EST_AT_PRINT = BIT(1),
++ VOW_DRR_DBG_ADJ_GLOBAL_THLD = BIT(21),
++ VOW_DRR_DBG_PRN_LOUD = BIT(22),
++ VOW_DRR_DBG_PRN_ADJ_STA = BIT(23),
++ VOW_DRR_DBG_FIX_CR = GENMASK(27, 24),
++ VOW_DRR_DBG_CLR_FIX_CR = BIT(28),
++ VOW_DRR_DBG_DISABLE = BIT(29),
++ VOW_DRR_DBG_DUMP_CR = BIT(30),
++ VOW_DRR_DBG_PRN = BIT(31)
++};
++
++#define VOW_DRR_DBG_FLAGS (VOW_DRR_DBG_DUMP_BMP | \
++ VOW_DRR_DBG_EST_AT_PRINT | \
++ VOW_DRR_DBG_ADJ_GLOBAL_THLD | \
++ VOW_DRR_DBG_PRN_LOUD | \
++ VOW_DRR_DBG_PRN_ADJ_STA | \
++ VOW_DRR_DBG_FIX_CR | \
++ VOW_DRR_DBG_CLR_FIX_CR | \
++ VOW_DRR_DBG_DISABLE | \
++ VOW_DRR_DBG_DUMP_CR | \
++ VOW_DRR_DBG_PRN)
+ #endif
+
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
new file mode 100644
index 0000000..2a00de3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
@@ -0,0 +1,96 @@
+From 86ac4d3c181a20f2cf093d2502d8a6e4f1054a3d Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 18 May 2023 15:01:47 +0800
+Subject: [PATCH 061/116] mtk: wifi: mt76: mt7996: reset addr_elem when delete
+ ba
+
+The old addr element info may be used when the signature is not equel to
+0xff, and sta will find error SDP cause the SDP/SDL=0 issue.
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+Change-Id: I12fb27e28b2c0310f824e66af6103b4ceba3503e
+
+1. without this patch will delete wrong session id when delete ba.
+Due to fw change the cmd format.
+https://gerrit.mediatek.inc/c/neptune/firmware/bora/wifi/custom/+/7969193
+
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+CR-Id: WCNCR00259516
+Change-Id: I456a5d3eb2320a2c40cf57247ba63083a6d50b2e
+---
+ mt76.h | 1 +
+ mt7996/mcu.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/mt76.h b/mt76.h
+index aecd0a7cd..b3a5f9c12 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -444,6 +444,7 @@ struct mt76_rx_tid {
+ u16 nframes;
+
+ u8 num;
++ u16 session_id;
+
+ u8 started:1, stopped:1, timer_pending:1;
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index ca78cd506..3c4ff7a5c 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -298,6 +298,52 @@ struct mt7996_mcu_thermal_notify {
+ u8 __rsv2[4];
+ } __packed;
+
++struct mt7996_mcu_rro_event {
++ struct mt7996_mcu_rxd rxd;
++
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++} __packed;
++
++struct mt7996_mcu_rro_ba {
++ __le16 tag;
++ __le16 len;
++
++ __le16 wlan_id;
++ u8 tid;
++ u8 __rsv1;
++ __le32 status;
++ __le16 session_id;
++ u8 __rsv2[2];
++} __packed;
++
++struct mt7996_mcu_rro_ba_del_chk_done {
++ __le16 tag;
++ __le16 len;
++
++ __le16 session_id;
++ __le16 mld_id;
++ u8 tid;
++ u8 __rsv[3];
++} __packed;
++
++enum {
++ UNI_RRO_BA_SESSION_STATUS = 0,
++ UNI_RRO_BA_SESSION_TBL = 1,
++ UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
++ UNI_RRO_BA_SESSION_MAX_NUM
++};
++
++struct mt7996_mcu_rro_del_ba {
++ struct mt7996_mcu_rro_event event;
++
++ u8 wlan_idx;
++ u8 tid;
++ u8 __rsv2[2];
++};
++
+ enum mt7996_chan_mib_offs {
+ UNI_MIB_OBSS_AIRTIME = 26,
+ UNI_MIB_NON_WIFI_TIME = 27,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch
new file mode 100644
index 0000000..9e64c36
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch
@@ -0,0 +1,69 @@
+From d5ccc55676b9a010e69111f73ecea5d2305c17f5 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Fri, 6 Oct 2023 14:01:41 +0800
+Subject: [PATCH 062/116] mtk: wifi: mt76: wed: change pcie0 R5 to pcie1 to get
+ 6G ICS
+
+Change-Id: I23a94e3e4b797b513a303b13e4c50e0a0d72bffb
+---
+ mt7996/dma.c | 4 ++++
+ mt7996/init.c | 6 ++----
+ mt7996/mmio.c | 5 ++++-
+ 3 files changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 759a58e8e..5d85e9ea2 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -538,6 +538,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ if (mt7996_band_valid(dev, MT_BAND2)) {
+ /* rx data queue for mt7996 band2 */
+ rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
++ if (mtk_wed_device_active(wed_hif2) && mtk_wed_get_rx_capa(wed_hif2)) {
++ dev->mt76.q_rx[MT_RXQ_BAND2].flags = MT_WED_Q_RX(0);
++ dev->mt76.q_rx[MT_RXQ_BAND2].wed = wed_hif2;
++ }
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ MT_RXQ_ID(MT_RXQ_BAND2),
+ MT7996_RX_RING_SIZE,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index c10f6675d..de5122f7d 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -649,10 +649,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ goto error;
+
+ if (wed == &dev->mt76.mmio.wed_hif2 && mtk_wed_device_active(wed)) {
+- u32 irq_mask = dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2;
+-
+- mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask);
+- mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, irq_mask);
++ mt76_wr(dev, MT_INT_PCIE1_MASK_CSR, MT_INT_TX_RX_DONE_EXT);
++ mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
+ }
+
+ return 0;
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 8fe56ed96..a082ccae8 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -527,12 +527,15 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+ dev->mt76.mmio.irqmask);
+ if (intr1 & MT_INT_RX_TXFREE_EXT)
+ napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]);
++
++ if (intr1 & MT_INT_RX_DONE_BAND2_EXT)
++ napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]);
+ }
+
+ if (mtk_wed_device_active(wed)) {
+ mtk_wed_device_irq_set_mask(wed, 0);
+ intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask);
+- intr |= (intr1 & ~MT_INT_RX_TXFREE_EXT);
++ intr |= (intr1 & ~MT_INT_TX_RX_DONE_EXT);
+ } else {
+ mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ if (dev->hif2)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch
new file mode 100644
index 0000000..173c70d
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch
@@ -0,0 +1,42 @@
+From 8697ea1b612b2c57459b12b2fdd812d6dd31dd18 Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Tue, 23 May 2023 12:06:29 +0800
+Subject: [PATCH 063/116] mtk: wifi: mt76: add SER support for wed3.0
+
+Change-Id: I2711b9dc336fca9a1ae32a8fbf27810a7e27b1e3
+---
+ dma.c | 5 +++--
+ mt7996/mmio.c | 1 +
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index c54187bd6..e5be891cb 100644
+--- a/dma.c
++++ b/dma.c
+@@ -834,8 +834,9 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+
+ /* reset WED rx queues */
+ mt76_wed_dma_setup(dev, q, true);
+-
+- if (!mt76_queue_is_wed_tx_free(q)) {
++ if (!mt76_queue_is_wed_tx_free(q) &&
++ !(mt76_queue_is_wed_rro(q) &&
++ mtk_wed_device_active(&dev->mmio.wed))) {
+ mt76_dma_sync_idx(dev, q);
+ mt76_dma_rx_fill(dev, q, false);
+ }
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index a082ccae8..d29579ff5 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -297,6 +297,7 @@ out:
+
+ return ret;
+ }
++
+ #endif
+
+ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch
new file mode 100644
index 0000000..658863f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch
@@ -0,0 +1,66 @@
+From d810c1768039fafabea801f2c4a5fc517e8cd68d Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 19 Jul 2023 10:55:09 +0800
+Subject: [PATCH 064/116] mtk: wifi: mt76: mt7915: wed: find rx token by
+ physical address
+
+The token id in RxDMAD may be incorrect when it is not the last frame due to
+WED HW bug. Lookup correct token id by physical address in sdp0.
+Add len == 0 check to drop garbage frames
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I4cd90294ad24990826075e92a710cc4e301dcbb7
+---
+ dma.c | 27 +++++++++++++++++++++++++--
+ 1 file changed, 25 insertions(+), 2 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index e5be891cb..1021b3e5d 100644
+--- a/dma.c
++++ b/dma.c
+@@ -446,9 +446,32 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ mt76_dma_should_drop_buf(drop, ctrl, buf1, desc_info);
+
+ if (mt76_queue_is_wed_rx(q)) {
++ u32 id, find = 0;
+ u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
+- struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
++ struct mt76_rxwi_cache *r;
++
++ if (*more) {
++ spin_lock_bh(&dev->rx_token_lock);
++
++ idr_for_each_entry(&dev->rx_token, r, id) {
++ if (r->dma_addr == le32_to_cpu(desc->buf0)) {
++ find = 1;
++ token = id;
++
++ /* Write correct id back to DMA*/
++ u32p_replace_bits(&buf1, id,
++ MT_DMA_CTL_TOKEN);
++ WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
++ break;
++ }
++ }
+
++ spin_unlock_bh(&dev->rx_token_lock);
++ if (!find)
++ return NULL;
++ }
++
++ r = mt76_rx_token_release(dev, token);
+ if (!r)
+ return NULL;
+
+@@ -902,7 +925,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ if (!data)
+ break;
+
+- if (drop)
++ if (drop || (len == 0))
+ goto free_frag;
+
+ if (q->rx_head)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch
new file mode 100644
index 0000000..b48ad31
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch
@@ -0,0 +1,57 @@
+From 4bf3625251b6086c3b57cbef75aa3ef6f3706fe3 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 20 Jul 2023 10:25:50 +0800
+Subject: [PATCH 065/116] mtk: wifi: mt76: mt7996: add dma mask limitation
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ dma.c | 4 ++--
+ wed.c | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 1021b3e5d..da21f6410 100644
+--- a/dma.c
++++ b/dma.c
+@@ -488,7 +488,7 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ } else {
+ struct mt76_queue_buf qbuf;
+
+- buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
+ if (!buf)
+ return NULL;
+
+@@ -711,7 +711,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ if (mt76_queue_is_wed_rro_ind(q))
+ goto done;
+
+- buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
+ if (!buf)
+ break;
+
+diff --git a/wed.c b/wed.c
+index 0a0b5c05c..1c6d53c84 100644
+--- a/wed.c
++++ b/wed.c
+@@ -65,14 +65,14 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ for (i = 0; i < size; i++) {
+ struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
+ dma_addr_t addr;
+- struct page *page;
+ int token;
+ void *ptr;
+
+ if (!r)
+ goto unmap;
+
+- ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
++ ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length,
++ GFP_ATOMIC | GFP_DMA32);
+ if (!ptr) {
+ mt76_put_rxwi(dev, r);
+ goto unmap;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch
new file mode 100644
index 0000000..4089488
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch
@@ -0,0 +1,122 @@
+From 313d5480374cccb0d7414913223021cb7f4171d3 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Fri, 18 Aug 2023 10:17:08 +0800
+Subject: [PATCH 066/116] mtk: wifi: mt76: mt7996: add per bss statistic info
+
+Whenever WED is enabled, unicast traffic might run through HW path.
+As a result, we need to count them using WM event.
+Broadcast and multicast traffic on the other hand, will be counted in mac80211
+as they always go through SW path and thus mac80211 can always see and count them.
+
+| | Tx | Rx |
+|---------|--------------------------------|---------------------------|
+| Unicast | mt76 | mt76 |
+| | __mt7996_stat_to_netdev() | __mt7996_stat_to_netdev() |
+|---------|--------------------------------|---------------------------|
+| BMCast | mac80211 | mac80211 |
+| | __ieee80211_subif_start_xmit() | ieee80211_deliver_skb() |
+---
+ mt7996/init.c | 1 +
+ mt7996/main.c | 1 +
+ mt7996/mcu.c | 40 +++++++++++++++++++++++++++++++++++-----
+ 3 files changed, 37 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index de5122f7d..8b7c27873 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -401,6 +401,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
++ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STAS_COUNT);
+
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index aef08a882..f97898f5f 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -265,6 +265,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ mvif->sta.wcid.phy_idx = band_idx;
+ mvif->sta.wcid.hw_key_idx = -1;
+ mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
++ mvif->sta.vif = mvif;
+ mt76_wcid_init(&mvif->sta.wcid);
+
+ mt7996_mac_wtbl_update(dev, idx,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3205f0f72..568e4d45a 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -524,6 +524,27 @@ mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rat
+ return 0;
+ }
+
++static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
++ struct mt76_wcid *wcid,
++ u32 tx_bytes, u32 rx_bytes,
++ u32 tx_packets, u32 rx_packets)
++{
++ struct mt7996_sta *msta;
++ struct ieee80211_vif *vif;
++ struct wireless_dev *wdev;
++
++ if (wiphy_ext_feature_isset(mphy->hw->wiphy,
++ NL80211_EXT_FEATURE_STAS_COUNT)) {
++ msta = container_of(wcid, struct mt7996_sta, wcid);
++ vif = container_of((void *)msta->vif, struct ieee80211_vif,
++ drv_priv);
++ wdev = ieee80211_vif_to_wdev(vif);
++
++ dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
++ dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
++ }
++}
++
+ static void
+ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -539,7 +560,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ u16 wlan_idx;
+ struct mt76_wcid *wcid;
+ struct mt76_phy *mphy;
+- u32 tx_bytes, rx_bytes;
++ u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
+
+ switch (le16_to_cpu(res->tag)) {
+ case UNI_ALL_STA_TXRX_RATE:
+@@ -567,6 +588,9 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ wcid->stats.tx_bytes += tx_bytes;
+ wcid->stats.rx_bytes += rx_bytes;
+
++ __mt7996_stat_to_netdev(mphy, wcid,
++ tx_bytes, rx_bytes, 0, 0);
++
+ ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
+ ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
+ }
+@@ -578,10 +602,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ if (!wcid)
+ break;
+
+- wcid->stats.tx_packets +=
+- le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
+- wcid->stats.rx_packets +=
+- le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
++ mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
++
++ tx_packets = le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
++ rx_packets = le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
++
++ wcid->stats.tx_packets += tx_packets;
++ wcid->stats.rx_packets += rx_packets;
++
++ __mt7996_stat_to_netdev(mphy, wcid, 0, 0,
++ tx_packets, rx_packets);
+ break;
+ default:
+ break;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch
new file mode 100644
index 0000000..c8e5001
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch
@@ -0,0 +1,39 @@
+From db3c26b1e067aca67455a0246c390dc0271496f3 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 26 Oct 2023 17:27:43 +0800
+Subject: [PATCH 067/116] mtk: wifi: mt76: mt7996: do not report netdev stats
+ on monitor vif
+
+This fixes the following NULL pointer crash when enabling monitor mode:
+[ 205.593158] Call trace:
+[ 205.595597] mt7996_mcu_rx_event+0x4a0/0x6e8 [mt7996e]
+[ 205.600725] mt7996_queue_rx_skb+0x6e4/0xfa0 [mt7996e]
+[ 205.605851] mt76_dma_rx_poll+0x384/0x420 [mt76]
+[ 205.610459] __napi_poll+0x38/0x1c0
+[ 205.613935] napi_threaded_poll+0x80/0xe8
+[ 205.617934] kthread+0x124/0x128
+
+CR-Id: WCNCR00238098
+Change-Id: I66f2449401888255bf8d3edddc1d9f20bd8ba3e7
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mcu.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 568e4d45a..337bcf559 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -540,6 +540,9 @@ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
+ drv_priv);
+ wdev = ieee80211_vif_to_wdev(vif);
+
++ if (vif->type == NL80211_IFTYPE_MONITOR)
++ return;
++
+ dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
+ dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch
new file mode 100644
index 0000000..ed8a05a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch
@@ -0,0 +1,675 @@
+From 58bd9403132eee4438f9e0b67415d772af324585 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Mon, 11 Sep 2023 16:35:15 +0800
+Subject: [PATCH 068/116] mtk: wifi: mt76: mt7996: add support for HW-ATF
+
+---
+ mt7996/debugfs.c | 90 ++++++++++++++++
+ mt7996/init.c | 43 ++++++++
+ mt7996/mac.c | 6 ++
+ mt7996/mcu.c | 265 ++++++++++++++++++++++++++++++++++++++++++-----
+ mt7996/mcu.h | 1 +
+ mt7996/mt7996.h | 96 ++++++++++++++++-
+ 6 files changed, 475 insertions(+), 26 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 8d639372e..f8ba573f2 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -940,6 +940,91 @@ static const struct file_operations mt7996_efuse_ops = {
+ .llseek = default_llseek,
+ };
+
++static int
++mt7996_vow_info_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ struct mt7996_vow_ctrl *vow = &dev->vow;
++ int i;
++
++ seq_printf(s, "VoW ATF Configuration:\n");
++ seq_printf(s, "ATF: %s\n", vow->atf_enable ? "enabled" : "disabled");
++ seq_printf(s, "WATF: %s\n", vow->watf_enable ? "enabled" : "disabled");
++ seq_printf(s, "Airtime Quantums (unit: 256 us)\n");
++ for (i = 0; i < VOW_DRR_QUANTUM_NUM; ++i)
++ seq_printf(s, "\tL%d: %hhu\n", i, vow->drr_quantum[i]);
++ seq_printf(s, "Max Airtime Deficit: %hhu (unit: 256 us)\n", vow->max_deficit);
++
++ return 0;
++}
++
++static int
++mt7996_atf_enable_get(void *data, u64 *val)
++{
++ struct mt7996_phy *phy = data;
++
++ *val = phy->dev->vow.atf_enable;
++
++ return 0;
++}
++
++static int
++mt7996_atf_enable_set(void *data, u64 val)
++{
++ struct mt7996_phy *phy = data;
++ struct mt7996_vow_ctrl *vow = &phy->dev->vow;
++ int ret;
++
++ vow->max_deficit = val ? 64 : 1;
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++ if (ret)
++ return ret;
++
++ vow->atf_enable = !!val;
++ return mt7996_mcu_set_vow_feature_ctrl(phy);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_atf_enable, mt7996_atf_enable_get,
++ mt7996_atf_enable_set, "%llu\n");
++
++static int
++mt7996_airtime_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ struct mt76_dev *mdev = &dev->mt76;
++ struct mt7996_vow_sta_ctrl *vow;
++ struct ieee80211_sta *sta;
++ struct mt7996_sta *msta;
++ struct mt76_wcid *wcid;
++ struct mt76_vif *vif;
++ u64 airtime;
++ u16 i;
++
++ seq_printf(s, "VoW Airtime Information:\n");
++ rcu_read_lock();
++ for (i = 1; i < MT7996_WTBL_STA; ++i) {
++ wcid = rcu_dereference(mdev->wcid[i]);
++ if (!wcid || !wcid->sta)
++ continue;
++
++ msta = container_of(wcid, struct mt7996_sta, wcid);
++ sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
++ vow = &msta->vow;
++ vif = &msta->vif->mt76;
++
++ spin_lock_bh(&vow->lock);
++ airtime = vow->tx_airtime;
++ vow->tx_airtime = 0;
++ spin_unlock_bh(&vow->lock);
++
++ seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
++ sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
++ }
++ rcu_read_unlock();
++
++ return 0;
++}
++
+ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -967,6 +1052,11 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ mt7996_twt_stats);
+ debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
+ debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "vow_info", dir,
++ mt7996_vow_info_read);
++ debugfs_create_file("atf_enable", 0600, dir, phy, &fops_atf_enable);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "airtime", dir,
++ mt7996_airtime_read);
+
+ if (phy->mt76->cap.has_5ghz) {
+ debugfs_create_u32("dfs_hw_pattern", 0400, dir,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 8b7c27873..c382ded01 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -573,6 +573,37 @@ int mt7996_txbf_init(struct mt7996_dev *dev)
+ return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
+ }
+
++static int mt7996_vow_init(struct mt7996_phy *phy)
++{
++ struct mt7996_vow_ctrl *vow = &phy->dev->vow;
++ int ret;
++
++ vow->atf_enable = true;
++ vow->watf_enable = false;
++ vow->max_deficit = 64;
++ vow->sch_type = VOW_SCH_TYPE_FOLLOW_POLICY;
++ vow->sch_policy = VOW_SCH_POLICY_SRR;
++
++ vow->drr_quantum[0] = VOW_DRR_QUANTUM_L0;
++ vow->drr_quantum[1] = VOW_DRR_QUANTUM_L1;
++ vow->drr_quantum[2] = VOW_DRR_QUANTUM_L2;
++ vow->drr_quantum[3] = VOW_DRR_QUANTUM_L3;
++ vow->drr_quantum[4] = VOW_DRR_QUANTUM_L4;
++ vow->drr_quantum[5] = VOW_DRR_QUANTUM_L5;
++ vow->drr_quantum[6] = VOW_DRR_QUANTUM_L6;
++ vow->drr_quantum[7] = VOW_DRR_QUANTUM_L7;
++
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++ if (ret)
++ return ret;
++
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
++ if (ret)
++ return ret;
++
++ return mt7996_mcu_set_vow_feature_ctrl(phy);
++}
++
+ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ enum mt76_band_id band)
+ {
+@@ -645,6 +676,12 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ if (ret)
+ goto error;
+
++ if (mt7996_vow_should_enable(dev)) {
++ ret = mt7996_vow_init(phy);
++ if (ret)
++ goto error;
++ }
++
+ ret = mt7996_init_debugfs(phy);
+ if (ret)
+ goto error;
+@@ -1483,6 +1520,12 @@ int mt7996_register_device(struct mt7996_dev *dev)
+
+ dev->recovery.hw_init_done = true;
+
++ if (mt7996_vow_should_enable(dev)) {
++ ret = mt7996_vow_init(&dev->phy);
++ if (ret)
++ goto error;
++ }
++
+ ret = mt7996_init_debugfs(&dev->phy);
+ if (ret)
+ goto error;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 78e35aa6b..be56abe16 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -103,6 +103,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+ };
+ struct ieee80211_sta *sta;
+ struct mt7996_sta *msta;
++ struct mt7996_vow_sta_ctrl *vow;
+ u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
+ LIST_HEAD(sta_poll_list);
+ int i;
+@@ -161,6 +162,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+
+ sta = container_of((void *)msta, struct ieee80211_sta,
+ drv_priv);
++ vow = &msta->vow;
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ u8 q = mt76_connac_lmac_mapping(i);
+ u32 tx_cur = tx_time[q];
+@@ -171,6 +173,10 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+ continue;
+
+ ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
++
++ spin_lock_bh(&vow->lock);
++ vow->tx_airtime += tx_cur;
++ spin_unlock_bh(&vow->lock);
+ }
+
+ /* get signal strength of resp frames (CTS/BA/ACK) */
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 337bcf559..cde4e0014 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2225,34 +2225,37 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ }
+
+ static int
+-mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
++mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
+ {
+-#define MT_STA_BSS_GROUP 1
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_sta *msta;
+- struct {
+- u8 __rsv1[4];
++ struct mt7996_vow_sta_ctrl *vow = &msta->vow;
++ u8 omac_idx = msta->vif->mt76.omac_idx;
++ int ret;
+
+- __le16 tag;
+- __le16 len;
+- __le16 wlan_idx;
+- u8 __rsv2[2];
+- __le32 action;
+- __le32 val;
+- u8 __rsv3[8];
+- } __packed req = {
+- .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
+- .len = cpu_to_le16(sizeof(req) - 4),
+- .action = cpu_to_le32(MT_STA_BSS_GROUP),
+- .val = cpu_to_le32(mvif->mt76.idx % 16),
+- };
++ /* Assignment of STA BSS group index aligns FW.
++ * Each band has its own BSS group bitmap space.
++ * 0: BSS 0
++ * 4..18: BSS 0x11..0x1f
++ */
++ vow->bss_grp_idx = (omac_idx <= HW_BSSID_MAX)
++ ? omac_idx
++ : HW_BSSID_MAX + omac_idx - EXT_BSSID_START;
++ vow->paused = false;
++ vow->drr_quantum[IEEE80211_AC_VO] = VOW_DRR_QUANTUM_IDX0;
++ vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
++ vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
++ vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
++ vow->tx_airtime = 0;
++ spin_lock_init(&vow->lock);
++
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
++ if (ret)
++ return ret;
+
+- msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+- req.wlan_idx = cpu_to_le16(msta->wcid.idx);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_PAUSE);
++ if (ret)
++ return ret;
+
+- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
+- sizeof(req), true);
++ return mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_ALL);
+ }
+
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+@@ -2308,7 +2311,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
+ }
+
+- ret = mt7996_mcu_add_group(dev, vif, sta);
++ ret = mt7996_mcu_sta_init_vow(mvif->phy, msta);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+@@ -5185,6 +5188,218 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ &req, sizeof(req), false);
+ }
+
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
++ enum vow_drr_ctrl_id id)
++{
++ struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
++ u32 val = 0;
++ struct {
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++ __le16 wlan_idx;
++ u8 band_idx;
++ u8 wmm_idx;
++ __le32 ctrl_id;
++
++ union {
++ __le32 val;
++ u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
++ };
++
++ u8 __rsv2[3];
++ u8 omac_idx;
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
++ .band_idx = phy->mt76->band_idx,
++ .wmm_idx = msta ? msta->vif->mt76.wmm_idx : 0,
++ .ctrl_id = cpu_to_le32(id),
++ .omac_idx = msta ? msta->vif->mt76.omac_idx : 0
++ };
++
++ switch (id) {
++ case VOW_DRR_CTRL_STA_ALL:
++ val |= FIELD_PREP(MT7996_DRR_STA_BSS_GRP_MASK, vow->bss_grp_idx);
++ val |= FIELD_PREP(MT7996_DRR_STA_AC0_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BK]);
++ val |= FIELD_PREP(MT7996_DRR_STA_AC1_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BE]);
++ val |= FIELD_PREP(MT7996_DRR_STA_AC2_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VI]);
++ val |= FIELD_PREP(MT7996_DRR_STA_AC3_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VO]);
++ req.val = cpu_to_le32(val);
++ break;
++ case VOW_DRR_CTRL_STA_BSS_GROUP:
++ req.val = cpu_to_le32(vow->bss_grp_idx);
++ break;
++ case VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND:
++ req.val = cpu_to_le32(phy->dev->vow.max_deficit);
++ break;
++ case VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL:
++ memcpy(req.drr_quantum, phy->dev->vow.drr_quantum, VOW_DRR_QUANTUM_NUM);
++ break;
++ case VOW_DRR_CTRL_STA_PAUSE:
++ req.val = cpu_to_le32(vow->paused);
++ break;
++ default:
++ dev_err(phy->dev->mt76.dev, "Unknown VoW DRR Control ID: %u\n", id);
++ return -EINVAL;
++ }
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
++ &req, sizeof(req), true);
++}
++
++int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
++{
++ struct mt7996_vow_ctrl *vow = &phy->dev->vow;
++ struct {
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++
++ /* DW0 */
++ __le16 apply_bwc_enable_per_grp;
++ __le16 apply_bwc_refill_period : 1;
++ __le16 __rsv2 : 3;
++ __le16 apply_band1_search_rule : 1;
++ __le16 apply_band0_search_rule : 1;
++ __le16 __rsv3 : 3;
++ __le16 apply_watf_enable : 1;
++ __le16 __rsv4 : 2;
++ __le16 apply_grp_no_change_in_txop : 1;
++ __le16 apply_atf_enable : 1;
++ __le16 apply_bwc_token_refill_enable : 1;
++ __le16 apply_bwc_enable : 1;
++
++ /* DW1 */
++ __le16 apply_bwc_check_time_token_per_grp;
++ __le16 __rsv5;
++
++ /* DW2 */
++ __le16 apply_bwc_check_len_token_per_grp;
++ __le16 __rsv6;
++
++ /* DW3 */
++ u8 band_idx;
++ u8 __rsv7[3];
++
++ /* DW4 */
++ __le32 __rsv8;
++
++ /* DW5 */
++ __le16 bwc_enable_per_grp;
++ __le16 bwc_refill_period : 3;
++ __le16 __rsv9 : 1;
++ __le16 band1_search_rule : 1;
++ __le16 band0_search_rule : 1;
++ __le16 __rsv10 : 3;
++ __le16 watf_enable : 1;
++ __le16 __rsv11 : 2;
++ __le16 grp_no_change_in_txop : 1;
++ __le16 atf_enable : 1;
++ __le16 bwc_token_refill_enable : 1;
++ __le16 bwc_enable : 1;
++
++ /* DW6 */
++ __le16 bwc_check_time_token_per_grp;
++ __le16 __rsv12;
++
++ /* DW7 */
++ __le16 bwc_check_len_token_per_grp;
++ __le16 __rsv13;
++
++ /* DW8 */
++ __le32 apply_atf_rts_sta_lock : 1;
++ __le32 atf_rts_sta_lock : 1;
++ __le32 apply_atf_keep_quantum : 1;
++ __le32 atf_keep_quantum : 1;
++ __le32 apply_tx_cnt_mode_ctrl : 1;
++ __le32 tx_cnt_mode_ctrl : 4;
++ __le32 apply_tx_measure_mode_enable : 1;
++ __le32 tx_measure_mode_enable : 1;
++ __le32 apply_backoff_ctrl : 1;
++ __le32 backoff_bound_enable : 1;
++ __le32 backoff_bound : 5;
++ __le32 apply_atf_rts_fail_charge : 1;
++ __le32 atf_rts_fail_charge : 1;
++ __le32 apply_zero_eifs : 1;
++ __le32 zero_eifs : 1;
++ __le32 apply_rx_rifs_enable : 1;
++ __le32 rx_rifs_enable : 1;
++ __le32 apply_vow_ctrl : 1;
++ __le32 vow_ctrl_val : 1;
++ __le32 vow_ctrl_bit : 5;
++ __le32 __rsv14 : 1;
++
++ /* DW9 */
++ __le32 apply_spl_sta_num : 1;
++ __le32 spl_sta_num : 3;
++ __le32 dbg_lvl : 2;
++ __le32 apply_atf_sch_ctrl : 1;
++ __le32 atf_sch_type : 2;
++ __le32 atf_sch_policy : 2;
++ __le32 __rsv15 : 21;
++ } __packed req = {
++ .tag = cpu_to_le16(UNI_VOW_FEATURE_CTRL),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ /* DW0 */
++ .apply_bwc_enable_per_grp = cpu_to_le16(0xffff),
++ .apply_bwc_refill_period = true,
++ .apply_band1_search_rule = true,
++ .apply_band0_search_rule = true,
++ .apply_watf_enable = true,
++ .apply_grp_no_change_in_txop = true,
++ .apply_atf_enable = true,
++ .apply_bwc_token_refill_enable = true,
++ .apply_bwc_enable = true,
++ /* DW1 */
++ .apply_bwc_check_time_token_per_grp = cpu_to_le16(0xffff),
++ /* DW2 */
++ .apply_bwc_check_len_token_per_grp = cpu_to_le16(0xffff),
++ /* DW3 */
++ .band_idx = phy->mt76->band_idx,
++ /* DW5 */
++ .bwc_enable_per_grp = cpu_to_le16(0xffff),
++ .bwc_refill_period = VOW_REFILL_PERIOD_32US,
++ .band1_search_rule = VOW_SEARCH_WMM_FIRST,
++ .band0_search_rule = VOW_SEARCH_WMM_FIRST,
++ .watf_enable = vow->watf_enable,
++ .grp_no_change_in_txop = true,
++ .atf_enable = vow->atf_enable,
++ .bwc_token_refill_enable = true,
++ .bwc_enable = false,
++ /* DW6 */
++ .bwc_check_time_token_per_grp = cpu_to_le16(0x0),
++ /* DW7 */
++ .bwc_check_len_token_per_grp = cpu_to_le16(0x0),
++ /* DW8 */
++ .apply_atf_rts_sta_lock = false,
++ .apply_atf_keep_quantum = true,
++ .atf_keep_quantum = true,
++ .apply_tx_cnt_mode_ctrl = false,
++ .apply_tx_measure_mode_enable = false,
++ .apply_backoff_ctrl = false,
++ .apply_atf_rts_fail_charge = false,
++ .apply_zero_eifs = false,
++ .apply_rx_rifs_enable = false,
++ .apply_vow_ctrl = true,
++ .vow_ctrl_val = true,
++ /* Reset DRR table when SER occurs. */
++ .vow_ctrl_bit = 26,
++ /* DW9 */
++ .apply_spl_sta_num = false,
++ .dbg_lvl = 0,
++ .apply_atf_sch_ctrl = true,
++ .atf_sch_type = vow->sch_type,
++ .atf_sch_policy = vow->sch_policy
++ };
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
++ &req, sizeof(req), true);
++}
++
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 3c4ff7a5c..700330ab5 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -965,6 +965,7 @@ enum {
+
+ enum {
+ UNI_VOW_DRR_CTRL,
++ UNI_VOW_FEATURE_CTRL,
+ UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
+ UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
+ UNI_VOW_RED_ENABLE = 0x18,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 453d22462..c95c12b45 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -114,6 +114,12 @@
+ #define MT7996_RX_MSDU_PAGE_SIZE (128 + \
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+
++#define MT7996_DRR_STA_BSS_GRP_MASK GENMASK(5, 0)
++#define MT7996_DRR_STA_AC0_QNTM_MASK GENMASK(10, 8)
++#define MT7996_DRR_STA_AC1_QNTM_MASK GENMASK(14, 12)
++#define MT7996_DRR_STA_AC2_QNTM_MASK GENMASK(18, 16)
++#define MT7996_DRR_STA_AC3_QNTM_MASK GENMASK(22, 20)
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -215,6 +221,81 @@ enum mt7996_dpd_ch_num {
+ DPD_CH_NUM_TYPE_MAX,
+ };
+
++enum {
++ VOW_SEARCH_AC_FIRST,
++ VOW_SEARCH_WMM_FIRST
++};
++
++enum {
++ VOW_REFILL_PERIOD_1US,
++ VOW_REFILL_PERIOD_2US,
++ VOW_REFILL_PERIOD_4US,
++ VOW_REFILL_PERIOD_8US,
++ VOW_REFILL_PERIOD_16US,
++ VOW_REFILL_PERIOD_32US,
++ VOW_REFILL_PERIOD_64US,
++ VOW_REFILL_PERIOD_128US
++};
++
++/* Default DRR airtime quantum of each level */
++enum {
++ VOW_DRR_QUANTUM_L0 = 6,
++ VOW_DRR_QUANTUM_L1 = 12,
++ VOW_DRR_QUANTUM_L2 = 16,
++ VOW_DRR_QUANTUM_L3 = 20,
++ VOW_DRR_QUANTUM_L4 = 24,
++ VOW_DRR_QUANTUM_L5 = 28,
++ VOW_DRR_QUANTUM_L6 = 32,
++ VOW_DRR_QUANTUM_L7 = 36
++};
++
++enum {
++ VOW_DRR_QUANTUM_IDX0,
++ VOW_DRR_QUANTUM_IDX1,
++ VOW_DRR_QUANTUM_IDX2,
++ VOW_DRR_QUANTUM_IDX3,
++ VOW_DRR_QUANTUM_IDX4,
++ VOW_DRR_QUANTUM_IDX5,
++ VOW_DRR_QUANTUM_IDX6,
++ VOW_DRR_QUANTUM_IDX7,
++ VOW_DRR_QUANTUM_NUM
++};
++
++enum {
++ VOW_SCH_TYPE_FOLLOW_POLICY,
++ VOW_SCH_TYPE_FOLLOW_HW
++};
++
++enum {
++ VOW_SCH_POLICY_SRR, /* Shared Round-Robin */
++ VOW_SCH_POLICY_WRR /* Weighted Round-Robin */
++};
++
++enum vow_drr_ctrl_id {
++ VOW_DRR_CTRL_STA_ALL,
++ VOW_DRR_CTRL_STA_BSS_GROUP,
++ VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND = 0x10,
++ VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL = 0x28,
++ VOW_DRR_CTRL_STA_PAUSE = 0x30
++};
++
++struct mt7996_vow_ctrl {
++ bool atf_enable;
++ bool watf_enable;
++ u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
++ u8 max_deficit;
++ u8 sch_type;
++ u8 sch_policy;
++};
++
++struct mt7996_vow_sta_ctrl {
++ bool paused;
++ u8 bss_grp_idx;
++ u8 drr_quantum[IEEE80211_NUM_ACS];
++ u64 tx_airtime;
++ spinlock_t lock;
++};
++
+ struct mt7996_sta {
+ struct mt76_wcid wcid; /* must be first */
+
+@@ -234,6 +315,8 @@ struct mt7996_sta {
+ u8 flowid_mask;
+ struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT];
+ } twt;
++
++ struct mt7996_vow_sta_ctrl vow;
+ };
+
+ struct mt7996_vif {
+@@ -499,6 +582,7 @@ struct mt7996_dev {
+
+ u8 wtbl_size_group;
+
++ struct mt7996_vow_ctrl vow;
+ #ifdef CONFIG_MTK_DEBUG
+ u16 wlan_idx;
+ struct {
+@@ -739,10 +823,12 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
+-int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
++ enum vow_drr_ctrl_id id);
++int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+@@ -792,6 +878,14 @@ static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
+ return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx);
+ }
+
++static inline bool
++mt7996_vow_should_enable(struct mt7996_dev *dev)
++{
++ return !wiphy_ext_feature_isset(mt76_hw(dev)->wiphy,
++ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS) ||
++ mtk_wed_device_active(&dev->mt76.mmio.wed);
++}
++
+ void mt7996_mac_init(struct mt7996_dev *dev);
+ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
+ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch
new file mode 100644
index 0000000..ed0b185
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch
@@ -0,0 +1,398 @@
+From 2285ea8b0ee712928f6980240c0103fcd2a96033 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 12 Oct 2023 10:04:54 +0800
+Subject: [PATCH 069/116] mtk: wifi: mt76: mt7996: wed: add SER0.5 support w/
+ wed3.0
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+Change-Id: I9b26cdbea6e8ee158a153fd153c2dea77b494f2f
+---
+ dma.c | 9 ++--
+ dma.h | 4 +-
+ mt76.h | 14 ++++--
+ mt792x_dma.c | 6 +--
+ mt7996/dma.c | 20 ++++++--
+ mt7996/init.c | 127 +++++++++++++++++++++++++++++++-----------------
+ mt7996/mac.c | 25 ++++++++++
+ mt7996/mt7996.h | 1 +
+ wed.c | 4 +-
+ 9 files changed, 146 insertions(+), 64 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index da21f6410..e23b744b8 100644
+--- a/dma.c
++++ b/dma.c
+@@ -218,9 +218,9 @@ void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
+ mt76_dma_sync_idx(dev, q);
+ }
+
+-void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
++void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ {
+- __mt76_dma_queue_reset(dev, q, true);
++ __mt76_dma_queue_reset(dev, q, reset);
+ }
+
+ static int
+@@ -540,7 +540,8 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ if (!q->queued)
+ return NULL;
+
+- if (mt76_queue_is_wed_rro_data(q))
++ if (mt76_queue_is_wed_rro_data(q) ||
++ mt76_queue_is_wed_rro_msdu_pg(q))
+ return NULL;
+
+ if (!mt76_queue_is_wed_rro_ind(q)) {
+@@ -792,7 +793,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ return 0;
+ }
+
+- mt76_dma_queue_reset(dev, q);
++ mt76_dma_queue_reset(dev, q, true);
+
+ return 0;
+ }
+diff --git a/dma.h b/dma.h
+index 1de5a2b20..3a8c2e558 100644
+--- a/dma.h
++++ b/dma.h
+@@ -83,12 +83,12 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ bool allow_direct);
+ void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
+ bool reset_idx);
+-void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q);
++void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
+
+ static inline void
+ mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ {
+- dev->queue_ops->reset_q(dev, q);
++ dev->queue_ops->reset_q(dev, q, true);
+ if (mtk_wed_device_active(&dev->mmio.wed))
+ mt76_wed_dma_setup(dev, q, true);
+ }
+diff --git a/mt76.h b/mt76.h
+index b3a5f9c12..e9fc37d99 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -301,7 +301,7 @@ struct mt76_queue_ops {
+
+ void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
+
+- void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q);
++ void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
+ };
+
+ enum mt76_phy_type {
+@@ -1736,8 +1736,13 @@ static inline bool mt76_queue_is_wed_rro_ind(struct mt76_queue *q)
+ static inline bool mt76_queue_is_wed_rro_data(struct mt76_queue *q)
+ {
+ return mt76_queue_is_wed_rro(q) &&
+- (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA ||
+- FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
++ (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA);
++}
++
++static inline bool mt76_queue_is_wed_rro_msdu_pg(struct mt76_queue *q)
++{
++ return mt76_queue_is_wed_rro(q) &&
++ (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
+ }
+
+ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+@@ -1746,7 +1751,8 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+ return false;
+
+ return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX ||
+- mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q);
++ mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q) ||
++ mt76_queue_is_wed_rro_msdu_pg(q);
+
+ }
+
+diff --git a/mt792x_dma.c b/mt792x_dma.c
+index 5cc2d59b7..c224bcc8a 100644
+--- a/mt792x_dma.c
++++ b/mt792x_dma.c
+@@ -181,13 +181,13 @@ mt792x_dma_reset(struct mt792x_dev *dev, bool force)
+
+ /* reset hw queues */
+ for (i = 0; i < __MT_TXQ_MAX; i++)
+- mt76_queue_reset(dev, dev->mphy.q_tx[i]);
++ mt76_queue_reset(dev, dev->mphy.q_tx[i], true);
+
+ for (i = 0; i < __MT_MCUQ_MAX; i++)
+- mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
++ mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
+
+ mt76_for_each_q_rx(&dev->mt76, i)
+- mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++ mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
+
+ mt76_tx_status_check(&dev->mt76, true);
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 5d85e9ea2..d9e1b17ff 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -711,21 +711,31 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ }
+
+ for (i = 0; i < __MT_MCUQ_MAX; i++)
+- mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
++ mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
+
+ mt76_for_each_q_rx(&dev->mt76, i) {
+- if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ if (mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]) ||
+- mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i]))
++ mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i])) {
++ if (force && mt76_queue_is_wed_rro_data(&dev->mt76.q_rx[i]))
++ mt76_queue_reset(dev, &dev->mt76.q_rx[i], false);
+ continue;
++ }
++ }
+
+- mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++ mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
+ }
+
+ mt76_tx_status_check(&dev->mt76, true);
+
+- mt76_for_each_q_rx(&dev->mt76, i)
++ mt76_for_each_q_rx(&dev->mt76, i) {
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed) && force &&
++ (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
++ mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i])))
++ continue;
++
+ mt76_queue_rx_reset(dev, i);
++ }
+
+ mt7996_dma_enable(dev, !force);
+ }
+diff --git a/mt7996/init.c b/mt7996/init.c
+index c382ded01..ff629e8d8 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -735,11 +735,91 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
+ msleep(20);
+ }
+
+-static int mt7996_wed_rro_init(struct mt7996_dev *dev)
++void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ {
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
++ int i;
++
++ if (!dev->has_rro)
++ return;
++
++ if (is_mt7992(&dev->mt76)) {
++ /* set emul 3.0 function */
++ mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
++ MT_RRO_3_0_EMU_CONF_EN_MASK);
++
++ mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
++ dev->wed_rro.addr_elem[0].phy_addr);
++ } else {
++ /* TODO: remove line after WM has set */
++ mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
++
++ /* setup BA bitmap cache address */
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
++ dev->wed_rro.ba_bitmap[0].phy_addr);
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
++ dev->wed_rro.ba_bitmap[1].phy_addr);
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
++
++ /* setup Address element address */
++ for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
++ mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
++ reg += 4;
++ }
++
++ /* setup Address element address - separate address segment mode */
++ mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
++ MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
++ }
++ wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
++ if (is_mt7996(&dev->mt76))
++ wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
++ else
++ wed->wlan.ind_cmd.particular_sid = 1;
++ wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
++ wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
++ wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
++
++ mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
++ mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
++ MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
++
++ /* particular session configure */
++ /* use max session idx + 1 as particular session id */
++ mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
++
++ if (is_mt7992(&dev->mt76)) {
++ reg = MT_RRO_MSDU_PG_SEG_ADDR0;
++
++ mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG,
++ MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN);
++
++ /* setup Msdu page address */
++ for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
++ mt76_wr(dev, reg, dev->wed_rro.msdu_pg[i].phy_addr >> 4);
++ reg += 4;
++ }
++ mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
++ MT_RRO_PARTICULAR_CONFG_EN |
++ FIELD_PREP(MT_RRO_PARTICULAR_SID, 1));
++ } else {
++ mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
++ MT_RRO_PARTICULAR_CONFG_EN |
++ FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
++ }
++ /* interrupt enable */
++ mt76_wr(dev, MT_RRO_HOST_INT_ENA,
++ MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
++#endif
++}
++
++static int mt7996_wed_rro_init(struct mt7996_dev *dev)
++{
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ struct mt7996_wed_rro_addr *addr;
+ void *ptr;
+ int i;
+@@ -799,50 +879,9 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ addr++;
+ }
+
+- /* rro hw init */
+- /* TODO: remove line after WM has set */
+- mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
+-
+- /* setup BA bitmap cache address */
+- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
+- dev->wed_rro.ba_bitmap[0].phy_addr);
+- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
+- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
+- dev->wed_rro.ba_bitmap[1].phy_addr);
+- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
+-
+- /* setup Address element address */
+- for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
+- mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
+- reg += 4;
+- }
+-
+- /* setup Address element address - separate address segment mode */
+- mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
+- MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
+-
+- wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
+- wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
+- wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
+- wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
+- wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
+-
+- mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
+- mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
+- MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
+-
+- /* particular session configure */
+- /* use max session idx + 1 as particular session id */
+- mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
+- mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
+- MT_RRO_PARTICULAR_CONFG_EN |
+- FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
+-
+- /* interrupt enable */
+- mt76_wr(dev, MT_RRO_HOST_INT_ENA,
+- MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
+-
+ /* rro ind cmd queue init */
++ mt7996_rro_hw_init(dev);
++
+ return mt7996_dma_rro_init(dev);
+ #else
+ return 0;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index be56abe16..2ad9f4889 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1764,6 +1764,31 @@ mt7996_mac_restart(struct mt7996_dev *dev)
+ if (ret)
+ goto out;
+
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed) && dev->has_rro) {
++ u32 wed_irq_mask = dev->mt76.mmio.irqmask |
++ MT_INT_RRO_RX_DONE |
++ MT_INT_TX_DONE_BAND2;
++
++ mt7996_rro_hw_init(dev);
++ mt76_for_each_q_rx(&dev->mt76, i) {
++ if (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
++ mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i]))
++ mt76_queue_rx_reset(dev, i);
++ }
++
++ mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
++ mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
++ mt7996_irq_enable(dev, wed_irq_mask);
++ mt7996_irq_disable(dev, 0);
++ }
++
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) {
++ mt76_wr(dev, MT_INT_PCIE1_MASK_CSR,
++ MT_INT_TX_RX_DONE_EXT);
++ mtk_wed_device_start(&dev->mt76.mmio.wed_hif2,
++ MT_INT_TX_RX_DONE_EXT);
++ }
++
+ /* set the necessary init items */
+ ret = mt7996_mcu_set_eeprom(dev);
+ if (ret)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c95c12b45..4090fadfa 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -718,6 +718,7 @@ extern const struct mt76_testmode_ops mt7996_testmode_ops;
+ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ void __iomem *mem_base, u32 device_id);
+ void mt7996_wfsys_reset(struct mt7996_dev *dev);
++void mt7996_rro_hw_init(struct mt7996_dev *dev);
+ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
+ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+ int mt7996_register_device(struct mt7996_dev *dev);
+diff --git a/wed.c b/wed.c
+index 1c6d53c84..61a6badf2 100644
+--- a/wed.c
++++ b/wed.c
+@@ -155,7 +155,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ case MT76_WED_Q_TXFREE:
+ /* WED txfree queue needs ring to be initialized before setup */
+ q->flags = 0;
+- mt76_dma_queue_reset(dev, q);
++ mt76_dma_queue_reset(dev, q, true);
+ mt76_dma_rx_fill(dev, q, false);
+
+ ret = mtk_wed_device_txfree_ring_setup(q->wed, q->regs);
+@@ -184,7 +184,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ break;
+ case MT76_WED_RRO_Q_IND:
+ q->flags &= ~MT_QFLAG_WED;
+- mt76_dma_queue_reset(dev, q);
++ mt76_dma_queue_reset(dev, q, true);
+ mt76_dma_rx_fill(dev, q, false);
+ mtk_wed_device_ind_rx_ring_setup(q->wed, q->regs);
+ break;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch
new file mode 100644
index 0000000..fe42aee
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch
@@ -0,0 +1,212 @@
+From d31f86b70ab12fba91854fe3f4188448638f967d Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Fri, 6 Oct 2023 20:59:42 +0800
+Subject: [PATCH 070/116] mtk: wifi: mt76: mt7996: support backaward
+ compatiable
+
+revert upstream wed trigger mode to polling mode
+
+CR-Id: WCNCR00259341
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+Change-Id: Ifd40df7094052b13e26f42f09908f6404917ad8e
+
+[Description]
+Change the SW token size from 1024 to 15360 according to HW capability.
+
+[Release-log]
+N/A
+
+CR-Id: WCNCR00240772
+Change-Id: I337f41aa80758d00b4c8e7a5cc4d6faeb6f0a4a2
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt76.h | 2 ++
+ mt7996/mac.c | 3 ++-
+ mt7996/mcu.c | 2 +-
+ mt7996/mmio.c | 12 +++++++-----
+ mt7996/mt7996.h | 1 +
+ mt7996/pci.c | 17 +++++++++--------
+ wed.c | 4 ++--
+ 7 files changed, 24 insertions(+), 17 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index e9fc37d99..4009e3c61 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -53,6 +53,8 @@
+
+ #define MT76_TOKEN_FREE_THR 64
+
++#define MT76_WED_SW_TOKEN_SIZE 15360
++
+ #define MT_QFLAG_WED_RING GENMASK(1, 0)
+ #define MT_QFLAG_WED_TYPE GENMASK(4, 2)
+ #define MT_QFLAG_WED BIT(5)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 2ad9f4889..8396e6def 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1777,7 +1777,7 @@ mt7996_mac_restart(struct mt7996_dev *dev)
+ }
+
+ mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
+- mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
++ mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask, false);
+ mt7996_irq_enable(dev, wed_irq_mask);
+ mt7996_irq_disable(dev, 0);
+ }
+@@ -2009,6 +2009,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
+
+ mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
+ true);
++
+ mt7996_irq_enable(dev, wed_irq_mask);
+ mt7996_irq_disable(dev, 0);
+ }
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index cde4e0014..1a45c31b3 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3254,7 +3254,7 @@ static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
+
+ if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
+ req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
+- cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
++ cpu_to_le16(MT7996_SW_TOKEN_SIZE);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
+ &req, sizeof(req), false);
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index d29579ff5..1c2cea2ec 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -14,7 +14,7 @@
+ #include "../trace.h"
+ #include "../dma.h"
+
+-static bool wed_enable;
++static bool wed_enable = true;
+ module_param(wed_enable, bool, 0644);
+
+ static const struct __base mt7996_reg_base[] = {
+@@ -347,7 +347,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ }
+
+ wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
+- wed->wlan.wpdma_rx = wed->wlan.phy_base + hif1_ofs +
++ wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
+ MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
+ MT7996_RXQ_BAND0 * MT_RING_SIZE;
+
+@@ -362,7 +362,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+
+ wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG;
+
+- wed->wlan.wpdma_rx = wed->wlan.phy_base +
++ wed->wlan.wpdma_rx[0] = wed->wlan.phy_base +
+ MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
+ MT7996_RXQ_BAND0 * MT_RING_SIZE;
+
+@@ -404,8 +404,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
+ }
+
+- wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE;
+- wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf;
++ wed->wlan.nbuf = MT7996_TOKEN_SIZE;
++ wed->wlan.token_start = 0;
+
+ wed->wlan.amsdu_max_subframes = 8;
+ wed->wlan.amsdu_max_len = 1536;
+@@ -426,6 +426,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ *irq = wed->irq;
+ dev->mt76.dma_dev = wed->dev;
+
++ dev->mt76.token_size = MT7996_SW_TOKEN_SIZE;
++
+ return 1;
+ #else
+ return 0;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 4090fadfa..7011f6659 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -73,6 +73,7 @@
+ #define MT7996_EEPROM_BLOCK_SIZE 16
+ #define MT7996_TOKEN_SIZE 16384
+ #define MT7996_HW_TOKEN_SIZE 8192
++#define MT7996_SW_TOKEN_SIZE 15360
+
+ #define MT7996_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */
+ #define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 05830c01c..4e957771f 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -171,7 +171,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+
+ ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
+ if (ret < 0)
+- goto free_hif2_wed_irq_vector;
++ goto free_wed_or_irq_vector;
+
+ if (!ret) {
+ ret = pci_alloc_irq_vectors(hif2_dev, 1, 1,
+@@ -180,14 +180,15 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ goto free_hif2;
+
+ dev->hif2->irq = hif2_dev->irq;
+- hif2_irq = dev->hif2->irq;
++ } else {
++ dev->hif2->irq = irq;
+ }
+
+- ret = devm_request_irq(mdev->dev, hif2_irq, mt7996_irq_handler,
+- IRQF_SHARED, KBUILD_MODNAME "-hif",
+- dev);
++ ret = devm_request_irq(mdev->dev, dev->hif2->irq,
++ mt7996_irq_handler, IRQF_SHARED,
++ KBUILD_MODNAME "-hif", dev);
+ if (ret)
+- goto free_hif2_wed_irq_vector;
++ goto free_hif2_irq_vector;
+
+ mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+ /* master switch of PCIe tnterrupt enable */
+@@ -202,8 +203,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+
+ free_hif2_irq:
+ if (dev->hif2)
+- devm_free_irq(mdev->dev, hif2_irq, dev);
+-free_hif2_wed_irq_vector:
++ devm_free_irq(mdev->dev, dev->hif2->irq, dev);
++free_hif2_irq_vector:
+ if (dev->hif2) {
+ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
+ mtk_wed_device_detach(&dev->mt76.mmio.wed_hif2);
+diff --git a/wed.c b/wed.c
+index 61a6badf2..634c95cf9 100644
+--- a/wed.c
++++ b/wed.c
+@@ -120,7 +120,7 @@ int mt76_wed_offload_enable(struct mtk_wed_device *wed)
+ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+
+ spin_lock_bh(&dev->token_lock);
+- dev->token_size = wed->wlan.token_start;
++ dev->token_size = MT76_WED_SW_TOKEN_SIZE;
+ spin_unlock_bh(&dev->token_lock);
+
+ return !wait_event_timeout(dev->tx_wait, !dev->wed_token_count, HZ);
+@@ -204,7 +204,7 @@ void mt76_wed_offload_disable(struct mtk_wed_device *wed)
+ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+
+ spin_lock_bh(&dev->token_lock);
+- dev->token_size = dev->drv->token_size;
++ dev->token_size = MT76_WED_SW_TOKEN_SIZE;
+ spin_unlock_bh(&dev->token_lock);
+ }
+ EXPORT_SYMBOL_GPL(mt76_wed_offload_disable);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch
new file mode 100644
index 0000000..32897a4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch
@@ -0,0 +1,434 @@
+From 6764f5f9c24ad031431c80834e293b0d2ec12db5 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Fri, 8 Sep 2023 11:57:39 +0800
+Subject: [PATCH 071/116] mtk: wifi: mt76: mt7996: wed: add wed support for
+ mt7992
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+
+Fix incomplete WED initialization for Kite band-1 RX ring.
+
+CR-Id: WCNCR00298425
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Change-Id: I2da06c9f1412f8392d1b55feea3ad8ff48ff90ad
+---
+ mt7996/dma.c | 91 +++++++++++++++++++++++++++++++++----------------
+ mt7996/init.c | 12 +++++++
+ mt7996/mac.c | 4 +++
+ mt7996/mmio.c | 49 ++++++++++++++++++--------
+ mt7996/mt7996.h | 10 +++++-
+ mt7996/pci.c | 10 ++++--
+ mt7996/regs.h | 14 +++++++-
+ 7 files changed, 142 insertions(+), 48 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index d9e1b17ff..d62dc8ba9 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -77,18 +77,23 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ MT7996_RXQ_RRO_BAND0);
+ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND0, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND0,
+ MT7996_RXQ_MSDU_PG_BAND0);
+- RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
+- MT7996_RXQ_TXFREE0);
+- /* band1 */
+- RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
+- MT7996_RXQ_MSDU_PG_BAND1);
+- /* band2 */
+- RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
+- MT7996_RXQ_RRO_BAND2);
+- RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
+- MT7996_RXQ_MSDU_PG_BAND2);
+- RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
+- MT7996_RXQ_TXFREE2);
++ if (is_mt7996(&dev->mt76)) {
++ RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
++ MT7996_RXQ_TXFREE0);
++ /* band1 */
++ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
++ MT7996_RXQ_MSDU_PG_BAND1);
++ /* band2 */
++ RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
++ MT7996_RXQ_RRO_BAND2);
++ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
++ MT7996_RXQ_MSDU_PG_BAND2);
++ RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
++ MT7996_RXQ_TXFREE2);
++ } else {
++ RXQ_CONFIG(MT_RXQ_RRO_BAND1, WFDMA0, MT_INT_RX_DONE_RRO_BAND1,
++ MT7996_RXQ_RRO_BAND1);
++ }
+
+ RXQ_CONFIG(MT_RXQ_RRO_IND, WFDMA0, MT_INT_RX_DONE_RRO_IND,
+ MT7996_RXQ_RRO_IND);
+@@ -146,8 +151,13 @@ static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
+ if (dev->has_rro) {
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs,
+ PREFETCH(0x10));
+- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
+- PREFETCH(0x10));
++ if (is_mt7996(&dev->mt76))
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
++ PREFETCH(0x10));
++ else
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND1) + ofs,
++ PREFETCH(0x10));
++
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs,
+ PREFETCH(0x4));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
+@@ -361,12 +371,16 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ * so, redirect pcie0 rx ring3 interrupt to pcie1
+ */
+ if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+- dev->has_rro)
++ dev->has_rro) {
++ u32 intr = is_mt7996(&dev->mt76) ?
++ MT_WFDMA0_RX_INT_SEL_RING6 :
++ MT_WFDMA0_RX_INT_SEL_RING9;
+ mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
+- MT_WFDMA0_RX_INT_SEL_RING6);
+- else
++ intr);
++ } else {
+ mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
+ MT_WFDMA0_RX_INT_SEL_RING3);
++ }
+ }
+
+ mt7996_dma_start(dev, reset, true);
+@@ -401,7 +415,7 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
+- if (mt7996_band_valid(dev, MT_BAND1)) {
++ if (mt7996_band_valid(dev, MT_BAND1) && is_mt7996(&dev->mt76)) {
+ /* rx msdu page queue for band1 */
+ mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags =
+ MT_WED_RRO_Q_MSDU_PG(1) | MT_QFLAG_WED_RRO_EN;
+@@ -522,7 +536,9 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ return ret;
+
+ /* tx free notify event from WA for band0 */
+- if (mtk_wed_device_active(wed) && !dev->has_rro) {
++ if (mtk_wed_device_active(wed) &&
++ ((is_mt7996(&dev->mt76) && !dev->has_rro) ||
++ (is_mt7992(&dev->mt76)))) {
+ dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
+ dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed;
+ }
+@@ -568,6 +584,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ } else if (mt7996_band_valid(dev, MT_BAND1)) {
+ /* rx data queue for mt7992 band1 */
+ rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1) + hif1_ofs;
++ if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) {
++ dev->mt76.q_rx[MT_RXQ_BAND1].flags = MT_WED_Q_RX(1);
++ dev->mt76.q_rx[MT_RXQ_BAND1].wed = wed;
++ }
++
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1],
+ MT_RXQ_ID(MT_RXQ_BAND1),
+ MT7996_RX_RING_SIZE,
+@@ -601,17 +622,29 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
+- /* tx free notify event from WA for band0 */
+- dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
+- dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
++ if (is_mt7992(&dev->mt76)) {
++ dev->mt76.q_rx[MT_RXQ_RRO_BAND1].flags =
++ MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
++ dev->mt76.q_rx[MT_RXQ_RRO_BAND1].wed = wed;
++ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1],
++ MT_RXQ_ID(MT_RXQ_RRO_BAND1),
++ MT7996_RX_RING_SIZE,
++ MT7996_RX_BUF_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
++ if (ret)
++ return ret;
++ } else {
++ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
++ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
+
+- ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
+- MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
+- MT7996_RX_MCU_RING_SIZE,
+- MT7996_RX_BUF_SIZE,
+- MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
+- if (ret)
+- return ret;
++ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
++ MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
++ MT7996_RX_MCU_RING_SIZE,
++ MT7996_RX_BUF_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
++ if (ret)
++ return ret;
++ }
+
+ if (mt7996_band_valid(dev, MT_BAND2)) {
+ /* rx rro data queue for band2 */
+diff --git a/mt7996/init.c b/mt7996/init.c
+index ff629e8d8..e29cebeb9 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -813,6 +813,7 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ /* interrupt enable */
+ mt76_wr(dev, MT_RRO_HOST_INT_ENA,
+ MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
++
+ #endif
+ }
+
+@@ -865,6 +866,17 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ dev->wed_rro.addr_elem[i].phy_addr;
+ }
+
++ for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
++ ptr = dmam_alloc_coherent(dev->mt76.dma_dev, MT7996_RRO_MSDU_PG_SIZE_PER_CR,
++ &dev->wed_rro.msdu_pg[i].phy_addr,
++ GFP_KERNEL);
++ if (!ptr)
++ return -ENOMEM;
++ dev->wed_rro.msdu_pg[i].ptr = ptr;
++
++ memset(dev->wed_rro.msdu_pg[i].ptr, 0, MT7996_RRO_MSDU_PG_SIZE_PER_CR);
++ }
++
+ ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
+ MT7996_RRO_WINDOW_MAX_LEN * sizeof(*addr),
+ &dev->wed_rro.session.phy_addr,
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 8396e6def..b4703804b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2007,6 +2007,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
+
+ mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
+
++ if (is_mt7992(&dev->mt76) && dev->has_rro)
++ mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
++ MT_RRO_3_0_EMU_CONF_EN_MASK);
++
+ mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
+ true);
+
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 1c2cea2ec..3f0692c79 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -313,7 +313,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+
+ dev->has_rro = true;
+
+- hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++ if (dev->hif2)
++ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+
+ if (hif2)
+ wed = &dev->mt76.mmio.wed_hif2;
+@@ -348,8 +349,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+
+ wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
+ wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
+- MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
+- MT7996_RXQ_BAND0 * MT_RING_SIZE;
++ MT_RXQ_RING_BASE(MT7996_RXQ_BAND2) +
++ MT7996_RXQ_BAND2 * MT_RING_SIZE;
+
+ wed->wlan.id = 0x7991;
+ wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
+@@ -369,9 +370,19 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base +
+ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) +
+ MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE;
+- wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
+- MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
+- MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
++ if (is_mt7996(&dev->mt76)) {
++ wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
++ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
++ MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
++ } else {
++ wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
++ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
++ MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
++ wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
++ MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
++ MT7996_RXQ_BAND1 * MT_RING_SIZE;
++ }
++
+ wed->wlan.wpdma_rx_pg = wed->wlan.phy_base +
+ MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) +
+ MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE;
+@@ -381,10 +392,14 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
+
+ wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1;
+- wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
+-
+ wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1;
+- wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
++ if (is_mt7996(&dev->mt76)) {
++ wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
++ wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
++ } else {
++ wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND1) - 1;
++ wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND1) - 1;
++ }
+
+ wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1;
+ wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1;
+@@ -392,14 +407,20 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+
+ wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1;
+ wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1;
+- if (dev->has_rro) {
+- wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
+- MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
+- wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
++ if (is_mt7996(&dev->mt76)) {
++ if (dev->has_rro) {
++ wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++ MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
++ } else {
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
++ wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++ MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
++ }
+ } else {
+ wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
+ wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
+- MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
++ MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
+ }
+ dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
+ }
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 7011f6659..929a077b9 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -121,6 +121,10 @@
+ #define MT7996_DRR_STA_AC2_QNTM_MASK GENMASK(18, 16)
+ #define MT7996_DRR_STA_AC3_QNTM_MASK GENMASK(22, 20)
+
++/* RRO 3.1 */
++#define MT7996_RRO_MSDU_PG_CR_CNT 8
++#define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -180,7 +184,7 @@ enum mt7996_rxq_id {
+ MT7996_RXQ_BAND1 = 5, /* for mt7992 */
+ MT7996_RXQ_BAND2 = 5,
+ MT7996_RXQ_RRO_BAND0 = 8,
+- MT7996_RXQ_RRO_BAND1 = 8,/* unused */
++ MT7996_RXQ_RRO_BAND1 = 9,
+ MT7996_RXQ_RRO_BAND2 = 6,
+ MT7996_RXQ_MSDU_PG_BAND0 = 10,
+ MT7996_RXQ_MSDU_PG_BAND1 = 11,
+@@ -546,6 +550,10 @@ struct mt7996_dev {
+ void *ptr;
+ dma_addr_t phy_addr;
+ } session;
++ struct {
++ void *ptr;
++ dma_addr_t phy_addr;
++ } msdu_pg[MT7996_RRO_MSDU_PG_CR_CNT];
+
+ struct work_struct work;
+ struct list_head poll_list;
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 4e957771f..f0d3f199c 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -107,7 +107,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ struct pci_dev *hif2_dev;
+ struct mt7996_hif *hif2;
+ struct mt7996_dev *dev;
+- int irq, hif2_irq, ret;
++ int irq, ret;
+ struct mt76_dev *mdev;
+
+ hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
+@@ -143,6 +143,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ mdev = &dev->mt76;
+ mt7996_wfsys_reset(dev);
+ hif2 = mt7996_pci_init_hif2(pdev);
++ if (hif2)
++ dev->hif2 = hif2;
+
+ ret = mt7996_mmio_wed_init(dev, pdev, false, &irq);
+ if (ret < 0)
+@@ -167,9 +169,11 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+
+ if (hif2) {
+ hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
+- dev->hif2 = hif2;
++ ret = 0;
++
++ if (is_mt7996(&dev->mt76))
++ ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
+
+- ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
+ if (ret < 0)
+ goto free_wed_or_irq_vector;
+
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 91159c635..e6427a351 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -77,6 +77,8 @@ enum offs_rev {
+ #define MT_RRO_BA_BITMAP_BASE1 MT_RRO_TOP(0xC)
+ #define WF_RRO_AXI_MST_CFG MT_RRO_TOP(0xB8)
+ #define WF_RRO_AXI_MST_CFG_DIDX_OK BIT(12)
++
++#define MT_RRO_ADDR_ARRAY_BASE0 MT_RRO_TOP(0x30)
+ #define MT_RRO_ADDR_ARRAY_BASE1 MT_RRO_TOP(0x34)
+ #define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE BIT(31)
+
+@@ -97,6 +99,14 @@ enum offs_rev {
+
+ #define MT_RRO_ADDR_ELEM_SEG_ADDR0 MT_RRO_TOP(0x400)
+
++#define MT_RRO_3_0_EMU_CONF MT_RRO_TOP(0x600)
++#define MT_RRO_3_0_EMU_CONF_EN_MASK BIT(11)
++
++#define MT_RRO_3_1_GLOBAL_CONFIG MT_RRO_TOP(0x604)
++#define MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN BIT(0)
++
++#define MT_RRO_MSDU_PG_SEG_ADDR0 MT_RRO_TOP(0x620)
++
+ #define MT_RRO_ACK_SN_CTRL MT_RRO_TOP(0x50)
+ #define MT_RRO_ACK_SN_CTRL_SN_MASK GENMASK(27, 16)
+ #define MT_RRO_ACK_SN_CTRL_SESSION_MASK GENMASK(11, 0)
+@@ -402,6 +412,7 @@ enum offs_rev {
+ #define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154)
+ #define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3)
+ #define MT_WFDMA0_RX_INT_SEL_RING6 BIT(6)
++#define MT_WFDMA0_RX_INT_SEL_RING9 BIT(9)
+
+ #define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4)
+
+@@ -503,13 +514,14 @@ enum offs_rev {
+ #define MT_INT_RX_DONE_WA_EXT BIT(3) /* for mt7992 */
+ #define MT_INT_RX_DONE_WA_TRI BIT(3)
+ #define MT_INT_RX_TXFREE_MAIN BIT(17)
++#define MT_INT_RX_TXFREE_BAND1 BIT(15)
+ #define MT_INT_RX_TXFREE_TRI BIT(15)
+ #define MT_INT_RX_DONE_BAND2_EXT BIT(23)
+ #define MT_INT_RX_TXFREE_EXT BIT(26)
+ #define MT_INT_MCU_CMD BIT(29)
+
+ #define MT_INT_RX_DONE_RRO_BAND0 BIT(16)
+-#define MT_INT_RX_DONE_RRO_BAND1 BIT(16)
++#define MT_INT_RX_DONE_RRO_BAND1 BIT(17)
+ #define MT_INT_RX_DONE_RRO_BAND2 BIT(14)
+ #define MT_INT_RX_DONE_RRO_IND BIT(11)
+ #define MT_INT_RX_DONE_MSDU_PG_BAND0 BIT(18)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch
new file mode 100644
index 0000000..3cb13db
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch
@@ -0,0 +1,177 @@
+From 435235e314311be77ae5746d1fed1f5a89f0e947 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 13 Sep 2023 17:35:43 +0800
+Subject: [PATCH 072/116] mtk: wifi: mt76: mt7992: wed: add 2pcie one wed
+ support
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ mt7996/dma.c | 13 +++++++++++--
+ mt7996/mmio.c | 7 +++----
+ mt7996/mtk_debug.h | 5 +++++
+ mt7996/mtk_debugfs.c | 25 ++++++++++++++++++-------
+ mt7996/regs.h | 2 ++
+ 5 files changed, 39 insertions(+), 13 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index d62dc8ba9..c23b0d651 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -355,6 +355,13 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ MT_WFDMA_HOST_CONFIG_PDMA_BAND |
+ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++ is_mt7992(&dev->mt76)) {
++ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_PDMA_BAND |
++ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++ }
++
+ /* AXI read outstanding number */
+ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
+ MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
+@@ -374,7 +381,8 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ dev->has_rro) {
+ u32 intr = is_mt7996(&dev->mt76) ?
+ MT_WFDMA0_RX_INT_SEL_RING6 :
+- MT_WFDMA0_RX_INT_SEL_RING9;
++ MT_WFDMA0_RX_INT_SEL_RING9 |
++ MT_WFDMA0_RX_INT_SEL_RING5;
+ mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
+ intr);
+ } else {
+@@ -630,10 +638,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ MT_RXQ_ID(MT_RXQ_RRO_BAND1),
+ MT7996_RX_RING_SIZE,
+ MT7996_RX_BUF_SIZE,
+- MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
++ MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + hif1_ofs);
+ if (ret)
+ return ret;
+ } else {
++ /* tx free notify event from WA for band0 */
+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
+
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 3f0692c79..91567a04b 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -375,10 +375,10 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
+ MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
+ } else {
+- wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
++ wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
+ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
+ MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
+- wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
++ wed->wlan.wpdma_rx[1] = wed->wlan.phy_base + hif1_ofs +
+ MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
+ MT7996_RXQ_BAND1 * MT_RING_SIZE;
+ }
+@@ -516,10 +516,9 @@ void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
+ if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ mtk_wed_device_irq_set_mask(&mdev->mmio.wed,
+ mdev->mmio.irqmask);
+- if (mtk_wed_device_active(&mdev->mmio.wed_hif2)) {
++ if (mtk_wed_device_active(&mdev->mmio.wed_hif2))
+ mtk_wed_device_irq_set_mask(&mdev->mmio.wed_hif2,
+ mdev->mmio.irqmask);
+- }
+ } else {
+ mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
+ mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
+diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
+index 27d8f1cb2..da2a60723 100644
+--- a/mt7996/mtk_debug.h
++++ b/mt7996/mtk_debug.h
+@@ -561,6 +561,11 @@ struct queue_desc {
+ #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
+ #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
+ #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x590) // 8590
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x594) // 8594
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x598) // 8598
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x59c) // 859C
++
+ //MCU DMA
+ //#define WF_WFDMA_MCU_DMA0_BASE 0x02000
+ #define WF_WFDMA_MCU_DMA0_BASE 0x54000000
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 28b920e11..27ad9432f 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -559,14 +559,22 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
+ dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
+- dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
+- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++ if (is_mt7996(&dev->mt76))
++ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++ else
++ dump_dma_rx_ring_info(s, dev, "R6:TxDone0(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
+ dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
+ dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
+- dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
+- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++ if (is_mt7996(&dev->mt76))
++ dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++ else
++ dump_dma_rx_ring_info(s, dev, "R9:BUF0(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
+ dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
+ dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
+@@ -584,15 +592,18 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
+ dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
+-
+ dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
+ dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
+- dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
+- WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
++ if (is_mt7996(&dev->mt76))
++ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
+ dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
++ if (is_mt7992(&dev->mt76))
++ dump_dma_rx_ring_info(s, dev, "R9:BUF1(MAC2H)", "Both",
++ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR);
+ }
+
+ /* MCU DMA information */
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index e6427a351..cbd71706c 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -411,6 +411,7 @@ enum offs_rev {
+
+ #define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154)
+ #define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3)
++#define MT_WFDMA0_RX_INT_SEL_RING5 BIT(5)
+ #define MT_WFDMA0_RX_INT_SEL_RING6 BIT(6)
+ #define MT_WFDMA0_RX_INT_SEL_RING9 BIT(9)
+
+@@ -451,6 +452,7 @@ enum offs_rev {
+
+ #define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30)
+ #define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0)
++#define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 BIT(21)
+ #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1 BIT(22)
+
+ #define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch
new file mode 100644
index 0000000..82e5096
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch
@@ -0,0 +1,32 @@
+From 4bc413bf6205f65522cc18e00c64773d70faefdf Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Mon, 6 Nov 2023 10:16:34 +0800
+Subject: [PATCH 073/116] mtk: wifi: mt76: mt7996: Remove wed rro ring add napi
+ at init state
+
+without this patch. rro ring will add napi at initial state. once rro ring add napi, it will have chance to be used by host driver. if host driver accessed the ring data, it will cause some issue.
+
+CR-Id: WCNCR00259341
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+---
+ dma.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/dma.c b/dma.c
+index e23b744b8..38701c71e 100644
+--- a/dma.c
++++ b/dma.c
+@@ -1017,6 +1017,10 @@ mt76_dma_init(struct mt76_dev *dev,
+ init_completion(&dev->mmio.wed_reset_complete);
+
+ mt76_for_each_q_rx(dev, i) {
++ if (mtk_wed_device_active(&dev->mmio.wed) &&
++ mt76_queue_is_wed_rro(&dev->q_rx[i]))
++ continue;
++
+ netif_napi_add(&dev->napi_dev, &dev->napi[i], poll);
+ mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
+ napi_enable(&dev->napi[i]);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
new file mode 100644
index 0000000..46633a2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
@@ -0,0 +1,34 @@
+From 2287fce6adb7e62918bf06792acb13f1eac3f1f9 Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Wed, 29 Nov 2023 13:56:52 +0800
+Subject: [PATCH 074/116] mtk: wifi: mt76: mt7996: Remove wed_stop during L1
+ SER
+
+Align logan L1 SER flow. During L1 SER, didn't need to close wed interrupt.
+
+CR-Id: WCNCR00259341
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/mac.c | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index b4703804b..0805251e8 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1950,12 +1950,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
+ wiphy_name(dev->mt76.hw->wiphy));
+
+- if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
+- mtk_wed_device_stop(&dev->mt76.mmio.wed_hif2);
+-
+- if (mtk_wed_device_active(&dev->mt76.mmio.wed))
+- mtk_wed_device_stop(&dev->mt76.mmio.wed);
+-
+ ieee80211_stop_queues(mt76_hw(dev));
+ if (phy2)
+ ieee80211_stop_queues(phy2->mt76->hw);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch
new file mode 100644
index 0000000..b319c54
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch
@@ -0,0 +1,87 @@
+From 5fd50b8df6409278d15377d1a6b58381e677c080 Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Wed, 29 Nov 2023 15:51:04 +0800
+Subject: [PATCH 075/116] mtk: wifi: mt76: mt7996: Refactor rro del ba command
+ format
+
+1. remove unused struct
+2. refactor upstream del ba command format
+
+CR-Id: WCNCR00259516
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+Change-Id: Iab75446a54dd0bf3aa95dc80f32d7d80b6f7fc8b
+---
+ mt7996/mcu.h | 50 +++-----------------------------------------------
+ 1 file changed, 3 insertions(+), 47 deletions(-)
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 700330ab5..a58f52d0c 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -273,7 +273,9 @@ struct mt7996_mcu_wed_rro_ba_delete_event {
+ __le16 len;
+
+ __le16 session_id;
+- u8 __rsv2[2];
++ __le16 mld_id;
++ u8 tid;
++ u8 __rsv[3];
+ } __packed;
+
+ enum {
+@@ -298,52 +300,6 @@ struct mt7996_mcu_thermal_notify {
+ u8 __rsv2[4];
+ } __packed;
+
+-struct mt7996_mcu_rro_event {
+- struct mt7996_mcu_rxd rxd;
+-
+- u8 __rsv1[4];
+-
+- __le16 tag;
+- __le16 len;
+-} __packed;
+-
+-struct mt7996_mcu_rro_ba {
+- __le16 tag;
+- __le16 len;
+-
+- __le16 wlan_id;
+- u8 tid;
+- u8 __rsv1;
+- __le32 status;
+- __le16 session_id;
+- u8 __rsv2[2];
+-} __packed;
+-
+-struct mt7996_mcu_rro_ba_del_chk_done {
+- __le16 tag;
+- __le16 len;
+-
+- __le16 session_id;
+- __le16 mld_id;
+- u8 tid;
+- u8 __rsv[3];
+-} __packed;
+-
+-enum {
+- UNI_RRO_BA_SESSION_STATUS = 0,
+- UNI_RRO_BA_SESSION_TBL = 1,
+- UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
+- UNI_RRO_BA_SESSION_MAX_NUM
+-};
+-
+-struct mt7996_mcu_rro_del_ba {
+- struct mt7996_mcu_rro_event event;
+-
+- u8 wlan_idx;
+- u8 tid;
+- u8 __rsv2[2];
+-};
+-
+ enum mt7996_chan_mib_offs {
+ UNI_MIB_OBSS_AIRTIME = 26,
+ UNI_MIB_NON_WIFI_TIME = 27,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch
new file mode 100644
index 0000000..3591d4e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch
@@ -0,0 +1,786 @@
+From 226977cba476d426936788b86986dbf5b8e71920 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Fri, 17 Nov 2023 18:08:06 +0800
+Subject: [PATCH 076/116] mtk: wifi: mt76: mt7996: get airtime and RSSI via MCU
+ commands
+
+Direct access to WTBL for airtime and RSSI may cause synchronization issue with FW.
+Moreover, frequent access to WTBL, whenever TX-Free-Done event is received, leads to heavy CPU overheads.
+Therefore, indirect access to WTBL, through FW, with lower frequence is performed.
+
+Change-Id: I978e7432603742fae9c753f055ff3087cf6b632c
+CR-Id: WCNCR00298425
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76.h | 20 +++++
+ mt76_connac_mcu.h | 14 +++-
+ mt7996/debugfs.c | 17 ++---
+ mt7996/mac.c | 145 ++++++-----------------------------
+ mt7996/mcu.c | 177 +++++++++++++++++++++++++++++++++++++++++--
+ mt7996/mcu.h | 32 +++++++-
+ mt7996/mt7996.h | 26 ++++++-
+ mt7996/mtk_debugfs.c | 71 +++++++++++++++++
+ mt7996/regs.h | 2 +
+ 9 files changed, 361 insertions(+), 143 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 4009e3c61..c3c35841f 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -332,11 +332,15 @@ struct mt76_sta_stats {
+ u32 tx_packets; /* unit: MSDU */
+ u32 tx_retries;
+ u32 tx_failed;
++ u32 tx_total_mpdu_cnt;
++ u32 tx_failed_mpdu_cnt;
++ u64 tx_airtime;
+ /* WED RX */
+ u64 rx_bytes;
+ u32 rx_packets;
+ u32 rx_errors;
+ u32 rx_drops;
++ u64 rx_airtime;
+ };
+
+ enum mt76_wcid_flags {
+@@ -1335,6 +1339,22 @@ static inline int mt76_decr(int val, int size)
+
+ u8 mt76_ac_to_hwq(u8 ac);
+
++static inline u8
++mt76_ac_to_tid(u8 ac)
++{
++ static const u8 ac_to_tid[] = {
++ [IEEE80211_AC_BE] = 0,
++ [IEEE80211_AC_BK] = 1,
++ [IEEE80211_AC_VI] = 4,
++ [IEEE80211_AC_VO] = 6
++ };
++
++ if (WARN_ON(ac >= IEEE80211_NUM_ACS))
++ return 0;
++
++ return ac_to_tid[ac];
++}
++
+ static inline struct ieee80211_txq *
+ mtxq_to_txq(struct mt76_txq *mtxq)
+ {
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 885e5883e..45fef88b9 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1369,11 +1369,23 @@ enum {
+ UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT,
+ };
+
++enum UNI_PER_STA_INFO_TAG {
++ UNI_PER_STA_RSSI,
++ UNI_PER_STA_CONTENTION_RX_RATE,
++ UNI_PER_STA_PER,
++ UNI_PER_STA_SNR,
++ UNI_PER_STA_TX_RATE,
++ UNI_PER_STA_TX_CNT,
++ UNI_PER_STA_TID_SN_GET,
++ UNI_PER_STA_TID_SN_SET,
++ UNI_PER_STA_MAX_NUM
++};
++
+ enum UNI_ALL_STA_INFO_TAG {
+ UNI_ALL_STA_TXRX_RATE,
+ UNI_ALL_STA_TX_STAT,
+ UNI_ALL_STA_TXRX_ADM_STAT,
+- UNI_ALL_STA_TXRX_AIR_TIME,
++ UNI_ALL_STA_TXRX_AIRTIME,
+ UNI_ALL_STA_DATA_TX_RETRY_COUNT,
+ UNI_ALL_STA_GI_MODE,
+ UNI_ALL_STA_TXRX_MSDU_COUNT,
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index f8ba573f2..e26de48c6 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -992,12 +992,11 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ {
+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
+ struct mt76_dev *mdev = &dev->mt76;
+- struct mt7996_vow_sta_ctrl *vow;
++ struct mt76_sta_stats *stats;
+ struct ieee80211_sta *sta;
+ struct mt7996_sta *msta;
+ struct mt76_wcid *wcid;
+ struct mt76_vif *vif;
+- u64 airtime;
+ u16 i;
+
+ seq_printf(s, "VoW Airtime Information:\n");
+@@ -1009,16 +1008,16 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+
+ msta = container_of(wcid, struct mt7996_sta, wcid);
+ sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+- vow = &msta->vow;
+ vif = &msta->vif->mt76;
++ stats = &wcid->stats;
+
+- spin_lock_bh(&vow->lock);
+- airtime = vow->tx_airtime;
+- vow->tx_airtime = 0;
+- spin_unlock_bh(&vow->lock);
++ seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
++ "TxAirtime: %llu\tRxAirtime: %llu\n",
++ sta->addr, i, vif->band_idx, vif->omac_idx,
++ stats->tx_airtime, stats->rx_airtime);
+
+- seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
+- sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
++ stats->tx_airtime = 0;
++ stats->rx_airtime = 0;
+ }
+ rcu_read_unlock();
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 0805251e8..782594cd7 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -12,8 +12,6 @@
+ #include "mcu.h"
+ #include "vendor.h"
+
+-#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
+-
+ static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
+ .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
+ .radar_pattern = {
+@@ -93,110 +91,6 @@ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
+ return MT_WTBL_LMAC_OFFS(wcid, dw);
+ }
+
+-static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+-{
+- static const u8 ac_to_tid[] = {
+- [IEEE80211_AC_BE] = 0,
+- [IEEE80211_AC_BK] = 1,
+- [IEEE80211_AC_VI] = 4,
+- [IEEE80211_AC_VO] = 6
+- };
+- struct ieee80211_sta *sta;
+- struct mt7996_sta *msta;
+- struct mt7996_vow_sta_ctrl *vow;
+- u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
+- LIST_HEAD(sta_poll_list);
+- int i;
+-
+- spin_lock_bh(&dev->mt76.sta_poll_lock);
+- list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list);
+- spin_unlock_bh(&dev->mt76.sta_poll_lock);
+-
+- rcu_read_lock();
+-
+- while (true) {
+- bool clear = false;
+- u32 addr, val;
+- u16 idx;
+- s8 rssi[4];
+-
+- spin_lock_bh(&dev->mt76.sta_poll_lock);
+- if (list_empty(&sta_poll_list)) {
+- spin_unlock_bh(&dev->mt76.sta_poll_lock);
+- break;
+- }
+- msta = list_first_entry(&sta_poll_list,
+- struct mt7996_sta, wcid.poll_list);
+- list_del_init(&msta->wcid.poll_list);
+- spin_unlock_bh(&dev->mt76.sta_poll_lock);
+-
+- idx = msta->wcid.idx;
+-
+- /* refresh peer's airtime reporting */
+- addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20);
+-
+- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+- u32 tx_last = msta->airtime_ac[i];
+- u32 rx_last = msta->airtime_ac[i + 4];
+-
+- msta->airtime_ac[i] = mt76_rr(dev, addr);
+- msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
+-
+- tx_time[i] = msta->airtime_ac[i] - tx_last;
+- rx_time[i] = msta->airtime_ac[i + 4] - rx_last;
+-
+- if ((tx_last | rx_last) & BIT(30))
+- clear = true;
+-
+- addr += 8;
+- }
+-
+- if (clear) {
+- mt7996_mac_wtbl_update(dev, idx,
+- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+- memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
+- }
+-
+- if (!msta->wcid.sta)
+- continue;
+-
+- sta = container_of((void *)msta, struct ieee80211_sta,
+- drv_priv);
+- vow = &msta->vow;
+- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+- u8 q = mt76_connac_lmac_mapping(i);
+- u32 tx_cur = tx_time[q];
+- u32 rx_cur = rx_time[q];
+- u8 tid = ac_to_tid[i];
+-
+- if (!tx_cur && !rx_cur)
+- continue;
+-
+- ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
+-
+- spin_lock_bh(&vow->lock);
+- vow->tx_airtime += tx_cur;
+- spin_unlock_bh(&vow->lock);
+- }
+-
+- /* get signal strength of resp frames (CTS/BA/ACK) */
+- addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34);
+- val = mt76_rr(dev, addr);
+-
+- rssi[0] = to_rssi(GENMASK(7, 0), val);
+- rssi[1] = to_rssi(GENMASK(15, 8), val);
+- rssi[2] = to_rssi(GENMASK(23, 16), val);
+- rssi[3] = to_rssi(GENMASK(31, 14), val);
+-
+- msta->ack_signal =
+- mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
+-
+- ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
+- }
+-
+- rcu_read_unlock();
+-}
+-
+ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif, bool enable)
+ {
+@@ -1206,8 +1100,6 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ }
+ }
+
+- mt7996_mac_sta_poll(dev);
+-
+ if (wake)
+ mt76_set_tx_blocked(&dev->mt76, false);
+
+@@ -2379,31 +2271,42 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+
+ void mt7996_mac_work(struct work_struct *work)
+ {
+- struct mt7996_phy *phy;
+- struct mt76_phy *mphy;
+-
+- mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
+- mac_work.work);
+- phy = mphy->priv;
++ struct mt76_phy *mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
++ mac_work.work);
++ struct mt7996_phy *phy = mphy->priv;
++ struct mt76_dev *mdev = mphy->dev;
+
+- mutex_lock(&mphy->dev->mutex);
++ mutex_lock(&mdev->mutex);
+
+ mt76_update_survey(mphy);
+ if (++mphy->mac_work_count == 5) {
++ int i;
++
+ mphy->mac_work_count = 0;
+
+ mt7996_mac_update_stats(phy);
+
+- mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_RATE);
+- if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+- mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_ADM_STAT);
+- mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_MSDU_COUNT);
++ /* Update DEV-wise information only in
++ * the MAC work of the first band running.
++ */
++ for (i = MT_BAND0; i <= mphy->band_idx; ++i) {
++ if (i == mphy->band_idx) {
++ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
++ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
++ mt7996_mcu_get_rssi(mdev);
++ if (mtk_wed_device_active(&mdev->mmio.wed)) {
++ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
++ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
++ }
++ } else if (mt7996_band_valid(phy->dev, i) &&
++ test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
++ break;
+ }
+ }
+
+- mutex_unlock(&mphy->dev->mutex);
++ mutex_unlock(&mdev->mutex);
+
+- mt76_tx_status_check(mphy->dev, false);
++ mt76_tx_status_check(mdev, false);
+
+ ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+ MT7996_WATCHDOG_TIME);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1a45c31b3..2bee4a59c 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -563,7 +563,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ u16 wlan_idx;
+ struct mt76_wcid *wcid;
+ struct mt76_phy *mphy;
+- u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
++ struct ieee80211_sta *sta;
++ u32 tx_bytes, rx_bytes, tx_airtime, rx_airtime, tx_packets, rx_packets;
+
+ switch (le16_to_cpu(res->tag)) {
+ case UNI_ALL_STA_TXRX_RATE:
+@@ -584,7 +585,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ break;
+
+ mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
+- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
++ for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ac++) {
+ tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
+ rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
+
+@@ -616,6 +617,24 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ __mt7996_stat_to_netdev(mphy, wcid, 0, 0,
+ tx_packets, rx_packets);
+ break;
++ case UNI_ALL_STA_TXRX_AIRTIME:
++ wlan_idx = le16_to_cpu(res->airtime[i].wlan_idx);
++ wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
++ sta = wcid_to_sta(wcid);
++ if (!sta)
++ continue;
++
++ for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ++ac) {
++ u8 lmac_ac = mt76_connac_lmac_mapping(ac);
++ tx_airtime = le32_to_cpu(res->airtime[i].tx[lmac_ac]);
++ rx_airtime = le32_to_cpu(res->airtime[i].rx[lmac_ac]);
++
++ wcid->stats.tx_airtime += tx_airtime;
++ wcid->stats.rx_airtime += rx_airtime;
++ ieee80211_sta_register_airtime(sta, mt76_ac_to_tid(ac),
++ tx_airtime, rx_airtime);
++ }
++ break;
+ default:
+ break;
+ }
+@@ -2244,8 +2263,6 @@ mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
+ vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
+ vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
+ vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
+- vow->tx_airtime = 0;
+- spin_lock_init(&vow->lock);
+
+ ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
+ if (ret)
+@@ -4839,9 +4856,155 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val)
+ sizeof(req), true);
+ }
+
+-int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
++int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
++ u16 sta_num, u16 *sta_list)
++{
++#define PER_STA_INFO_MAX_NUM 90
++ struct mt7996_mcu_per_sta_info_event *res;
++ struct mt76_wcid *wcid;
++ struct sk_buff *skb;
++ u16 wlan_idx;
++ int i, ret;
++ struct {
++ u8 __rsv1;
++ u8 unsolicit;
++ u8 __rsv2[2];
++
++ __le16 tag;
++ __le16 len;
++ __le16 sta_num;
++ u8 __rsv3[2];
++ __le16 sta_list[PER_STA_INFO_MAX_NUM];
++ } __packed req = {
++ .unsolicit = 0,
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .sta_num = cpu_to_le16(sta_num)
++ };
++
++ if (sta_num > PER_STA_INFO_MAX_NUM)
++ return -EINVAL;
++
++ for (i = 0; i < sta_num; ++i)
++ req.sta_list[i] = cpu_to_le16(sta_list[i]);
++
++ ret = mt76_mcu_send_and_get_msg(dev, MCU_WM_UNI_CMD(PER_STA_INFO),
++ &req, sizeof(req), true, &skb);
++ if (ret)
++ return ret;
++
++ res = (struct mt7996_mcu_per_sta_info_event *)skb->data;
++ if (le16_to_cpu(res->tag) != tag) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ rcu_read_lock();
++ switch (tag) {
++ case UNI_PER_STA_RSSI:
++ for (i = 0; i < sta_num; ++i) {
++ struct mt7996_sta *msta;
++ struct mt76_phy *phy;
++ s8 rssi[4];
++ u8 *rcpi;
++
++ wlan_idx = le16_to_cpu(res->rssi[i].wlan_idx);
++ wcid = rcu_dereference(dev->wcid[wlan_idx]);
++ if (wcid) {
++ rcpi = res->rssi[i].rcpi;
++ rssi[0] = to_rssi(MT_PRXV_RCPI0, rcpi[0]);
++ rssi[1] = to_rssi(MT_PRXV_RCPI0, rcpi[1]);
++ rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
++ rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
++
++ msta = container_of(wcid, struct mt7996_sta, wcid);
++ phy = msta->vif->phy->mt76;
++ msta->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
++ ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
++ } else {
++ ret = -EINVAL;
++ dev_err(dev->dev, "Failed to update RSSI for "
++ "invalid WCID: %hu\n", wlan_idx);
++ }
++ }
++ break;
++ case UNI_PER_STA_TX_CNT:
++ for (i = 0; i < sta_num; ++i) {
++ wlan_idx = le16_to_cpu(res->tx_cnt[i].wlan_idx);
++ wcid = rcu_dereference(dev->wcid[wlan_idx]);
++ if (wcid) {
++ wcid->stats.tx_total_mpdu_cnt +=
++ le32_to_cpu(res->tx_cnt[i].total);
++ wcid->stats.tx_failed_mpdu_cnt +=
++ le32_to_cpu(res->tx_cnt[i].failed);
++ } else {
++ ret = -EINVAL;
++ dev_err(dev->dev, "Failed to update TX MPDU counts "
++ "for invalid WCID: %hu\n", wlan_idx);
++ }
++ }
++ break;
++ default:
++ ret = -EINVAL;
++ dev_err(dev->dev, "Unknown UNI_PER_STA_INFO_TAG: %d\n", tag);
++ }
++ rcu_read_unlock();
++out:
++ dev_kfree_skb(skb);
++ return ret;
++}
++
++int mt7996_mcu_get_rssi(struct mt76_dev *dev)
++{
++ u16 sta_list[PER_STA_INFO_MAX_NUM];
++ LIST_HEAD(sta_poll_list);
++ struct mt7996_sta *msta;
++ int i, ret;
++ bool empty = false;
++
++ spin_lock_bh(&dev->sta_poll_lock);
++ list_splice_init(&dev->sta_poll_list, &sta_poll_list);
++ spin_unlock_bh(&dev->sta_poll_lock);
++
++ while (!empty) {
++ for (i = 0; i < PER_STA_INFO_MAX_NUM; ++i) {
++ spin_lock_bh(&dev->sta_poll_lock);
++ if (list_empty(&sta_poll_list)) {
++ spin_unlock_bh(&dev->sta_poll_lock);
++
++ if (i == 0)
++ return 0;
++
++ empty = true;
++ break;
++ }
++ msta = list_first_entry(&sta_poll_list,
++ struct mt7996_sta,
++ wcid.poll_list);
++ list_del_init(&msta->wcid.poll_list);
++ spin_unlock_bh(&dev->sta_poll_lock);
++
++ sta_list[i] = msta->wcid.idx;
++ }
++
++ ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
++ i, sta_list);
++ if (ret) {
++ /* Add STAs, whose RSSI has not been updated,
++ * back to polling list.
++ */
++ spin_lock_bh(&dev->sta_poll_lock);
++ list_splice(&sta_poll_list, &dev->sta_poll_list);
++ spin_unlock_bh(&dev->sta_poll_lock);
++ break;
++ }
++ }
++
++ return ret;
++}
++
++int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag)
+ {
+- struct mt7996_dev *dev = phy->dev;
+ struct {
+ u8 _rsv[4];
+
+@@ -4852,7 +5015,7 @@ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
+ .len = cpu_to_le16(sizeof(req) - 4),
+ };
+
+- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO),
++ return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(ALL_STA_INFO),
+ &req, sizeof(req), false);
+ }
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index a58f52d0c..e64812c6d 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -199,6 +199,31 @@ struct mt7996_mcu_mib {
+ __le64 data;
+ } __packed;
+
++struct per_sta_rssi {
++ __le16 wlan_idx;
++ u8 __rsv[2];
++ u8 rcpi[4];
++} __packed;
++
++struct per_sta_tx_cnt {
++ __le16 wlan_idx;
++ u8 __rsv[2];
++ __le32 total;
++ __le32 failed;
++} __packed;
++
++struct mt7996_mcu_per_sta_info_event {
++ u8 __rsv[4];
++
++ __le16 tag;
++ __le16 len;
++
++ union {
++ struct per_sta_rssi rssi[0];
++ struct per_sta_tx_cnt tx_cnt[0];
++ };
++} __packed;
++
+ struct all_sta_trx_rate {
+ __le16 wlan_idx;
+ u8 __rsv1[2];
+@@ -237,13 +262,18 @@ struct mt7996_mcu_all_sta_info_event {
+ __le32 tx_bytes[IEEE80211_NUM_ACS];
+ __le32 rx_bytes[IEEE80211_NUM_ACS];
+ } adm_stat[0] __packed;
+-
+ struct {
+ __le16 wlan_idx;
+ u8 rsv[2];
+ __le32 tx_msdu_cnt;
+ __le32 rx_msdu_cnt;
+ } msdu_cnt[0] __packed;
++ struct {
++ __le16 wlan_idx;
++ u8 __rsv[2];
++ __le32 tx[IEEE80211_NUM_ACS];
++ __le32 rx[IEEE80211_NUM_ACS];
++ } airtime[0] __packed;
+ } __packed;
+ } __packed;
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 929a077b9..a0cc8f37d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -125,6 +125,8 @@
+ #define MT7996_RRO_MSDU_PG_CR_CNT 8
+ #define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
+
++#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -297,8 +299,6 @@ struct mt7996_vow_sta_ctrl {
+ bool paused;
+ u8 bss_grp_idx;
+ u8 drr_quantum[IEEE80211_NUM_ACS];
+- u64 tx_airtime;
+- spinlock_t lock;
+ };
+
+ struct mt7996_sta {
+@@ -307,7 +307,6 @@ struct mt7996_sta {
+ struct mt7996_vif *vif;
+
+ struct list_head rc_list;
+- u32 airtime_ac[8];
+
+ int ack_signal;
+ struct ewma_avg_signal avg_ack_signal;
+@@ -404,6 +403,21 @@ struct mt7996_air_monitor_ctrl {
+ };
+ #endif
+
++struct mt7996_rro_ba_session {
++ u32 ack_sn :12;
++ u32 win_sz :3;
++ u32 bn :1;
++ u32 last_in_sn :12;
++ u32 bc :1;
++ u32 bd :1;
++ u32 sat :1;
++ u32 cn :1;
++ u32 within_cnt :12;
++ u32 to_sel :3;
++ u32 rsv :1;
++ u32 last_in_rxtime :12;
++};
++
+ struct mt7996_phy {
+ struct mt76_phy *mt76;
+ struct mt7996_dev *dev;
+@@ -599,6 +613,7 @@ struct mt7996_dev {
+ u32 fw_dbg_module;
+ u8 fw_dbg_lv;
+ u32 bcn_total_cnt[__MT_MAX_BAND];
++ u32 sid;
+ } dbg;
+ const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
+@@ -824,7 +839,10 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+-int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
++int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
++ u16 sta_num, u16 *sta_list);
++int mt7996_mcu_get_rssi(struct mt76_dev *dev);
++int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
+ int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 27ad9432f..5fb6078c1 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3106,6 +3106,69 @@ mt7996_vow_drr_dbg(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
+ mt7996_vow_drr_dbg, "%lld\n");
+
++static int
++mt7996_rro_session_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ struct mt7996_rro_ba_session *tbl;
++ u32 value[2];
++
++ mt76_wr(dev, MT_RRO_DBG_RD_CTRL, MT_RRO_DBG_RD_EXEC +
++ (dev->dbg.sid >> 1) + 0x200);
++
++ if (dev->dbg.sid & 0x1) {
++ value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
++ value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(3));
++ } else {
++ value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(0));
++ value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(1));
++ }
++
++ tbl = (struct mt7996_rro_ba_session *)&value[0];
++
++ seq_printf(s, " seid %d:\nba session table DW0:%08x DW2:%08x\n",
++ dev->dbg.sid, value[0], value[1]);
++
++ seq_printf(s, "ack_sn = 0x%x, last_in_sn = 0x%x, sat/bn/bc/bd/cn = %d/%d/%d/%d/%d\n",
++ tbl->ack_sn, tbl->last_in_sn, tbl->sat, tbl->bn, tbl->bc, tbl->bd, tbl->cn);
++
++ seq_printf(s, "within_cnt = %d, to_sel = %d, last_in_rxtime = %d\n",
++ tbl->within_cnt, tbl->to_sel, tbl->last_in_rxtime);
++
++ return 0;
++}
++
++static int
++mt7996_show_rro_mib(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u32 reg[12];
++
++ seq_printf(s, "RRO mib Info:\n");
++
++ reg[0] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(0));
++ reg[1] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(1));
++ reg[2] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(2));
++ reg[3] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(3));
++ reg[4] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(4));
++ reg[5] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(5));
++ reg[6] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(6));
++ reg[7] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(7));
++ reg[8] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(8));
++ reg[9] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(9));
++ reg[10] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(10));
++ reg[11] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(11));
++
++ seq_printf(s, "STEP_ONE/WITHIN/SURPASS = %x/%x/%x\n", reg[0], reg[3], reg[4]);
++ seq_printf(s, "REPEAT/OLDPKT/BAR = %x/%x/%x\n", reg[1], reg[2], reg[5]);
++ seq_printf(s, "SURPASS with big gap = %x\n", reg[6]);
++ seq_printf(s, "DISCONNECT/INVALID = %x/%x\n", reg[7], reg[8]);
++ seq_printf(s, "TO(Step one)/TO(flush all) = %x/%x\n", reg[9], reg[10]);
++ seq_printf(s, "buf ran out = %x\n", reg[11]);
++
++ return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -3205,6 +3268,14 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+
+ debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
+
++ if (dev->has_rro) {
++ debugfs_create_u32("rro_sid", 0600, dir, &dev->dbg.sid);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "rro_sid_info", dir,
++ mt7996_rro_session_read);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "rro_mib", dir,
++ mt7996_show_rro_mib);
++ }
++
+ return 0;
+ }
+
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index cbd71706c..a001d9fd6 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -122,6 +122,8 @@ enum offs_rev {
+ #define MT_MCU_INT_EVENT_DMA_INIT BIT(1)
+ #define MT_MCU_INT_EVENT_RESET_DONE BIT(3)
+
++#define WF_RRO_TOP_STATISTIC(_n) MT_RRO_TOP(0x180 + _n * 0x4)
++
+ /* PLE */
+ #define MT_PLE_BASE 0x820c0000
+ #define MT_PLE(ofs) (MT_PLE_BASE + (ofs))
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch
new file mode 100644
index 0000000..187b38d
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch
@@ -0,0 +1,220 @@
+From e6455a415fefb896c325db624d03725a56eaa3f5 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 4 Jan 2024 09:47:00 +0800
+Subject: [PATCH 077/116] mtk: wifi: mt76: mt7996: add support for WMM PBC
+ configuration
+
+Query per-AC-queue packet statistics from WA, and determine if multi-AC transmission is ongoing.
+If it is, enable WMM mode in WA. Otherwise, disable WMM mode.
+
+CR-Id: WCNCR00298425
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Change-Id: I1852a45dc0e64780af5f091fd749ceab59697dbb
+---
+ mt76_connac_mcu.h | 2 ++
+ mt7996/init.c | 2 ++
+ mt7996/mac.c | 4 +++
+ mt7996/mcu.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h | 15 +++++++++
+ mt7996/mt7996.h | 4 +++
+ 6 files changed, 105 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 45fef88b9..12fdc6e12 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1026,6 +1026,7 @@ enum {
+ MCU_EXT_EVENT_ASSERT_DUMP = 0x23,
+ MCU_EXT_EVENT_RDD_REPORT = 0x3a,
+ MCU_EXT_EVENT_CSA_NOTIFY = 0x4f,
++ MCU_EXT_EVENT_BSS_ACQ_PKT_CNT = 0x52,
+ MCU_EXT_EVENT_WA_TX_STAT = 0x74,
+ MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
+ MCU_EXT_EVENT_MURU_CTRL = 0x9f,
+@@ -1224,6 +1225,7 @@ enum {
+ MCU_EXT_CMD_TXDPD_CAL = 0x60,
+ MCU_EXT_CMD_CAL_CACHE = 0x67,
+ MCU_EXT_CMD_RED_ENABLE = 0x68,
++ MCU_EXT_CMD_PKT_BUDGET_CTRL = 0x6c,
+ MCU_EXT_CMD_CP_SUPPORT = 0x75,
+ MCU_EXT_CMD_SET_RADAR_TH = 0x7c,
+ MCU_EXT_CMD_SET_RDD_PATTERN = 0x7d,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index e29cebeb9..f4111460c 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1536,6 +1536,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ INIT_WORK(&dev->dump_work, mt7996_mac_dump_work);
+ mutex_init(&dev->dump_mutex);
+
++ INIT_WORK(&dev->wmm_pbc_work, mt7996_mcu_wmm_pbc_work);
++
+ ret = mt7996_init_hardware(dev);
+ if (ret)
+ return ret;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 782594cd7..e3758ff13 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2298,6 +2298,10 @@ void mt7996_mac_work(struct work_struct *work)
+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
+ }
++
++ if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
++ BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
++ dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
+ } else if (mt7996_band_valid(phy->dev, i) &&
+ test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
+ break;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 2bee4a59c..6fb9f81f0 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -669,6 +669,82 @@ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
+ phy->throttle_state = n->duty_percent;
+ }
+
++void mt7996_mcu_wmm_pbc_work(struct work_struct *work)
++{
++#define WMM_PBC_QUEUE_NUM 5
++#define WMM_PBC_BSS_ALL 0xff
++#define WMM_PBC_WLAN_IDX_ALL 0xffff
++#define WMM_PBC_BOUND_DEFAULT 0xffff
++#define WMM_PBC_LOW_BOUND_VO 1900
++#define WMM_PBC_LOW_BOUND_VI 1900
++#define WMM_PBC_LOW_BOUND_BE 1500
++#define WMM_PBC_LOW_BOUND_BK 900
++#define WMM_PBC_LOW_BOUND_MGMT 32
++ struct mt7996_dev *dev = container_of(work, struct mt7996_dev, wmm_pbc_work);
++ struct {
++ u8 bss_idx;
++ u8 queue_num;
++ __le16 wlan_idx;
++ u8 band_idx;
++ u8 __rsv[3];
++ struct {
++ __le16 low;
++ __le16 up;
++ } __packed bound[WMM_PBC_QUEUE_NUM];
++ } __packed req = {
++ .bss_idx = WMM_PBC_BSS_ALL,
++ .queue_num = WMM_PBC_QUEUE_NUM,
++ .wlan_idx = cpu_to_le16(WMM_PBC_WLAN_IDX_ALL),
++ .band_idx = dev->mphy.band_idx,
++ };
++ int i, ret;
++
++#define pbc_acq_low_bound_config(_ac, _bound) \
++ req.bound[mt76_connac_lmac_mapping(_ac)].low = dev->wmm_pbc_enable ? cpu_to_le16(_bound) : 0
++ pbc_acq_low_bound_config(IEEE80211_AC_VO, WMM_PBC_LOW_BOUND_VO);
++ pbc_acq_low_bound_config(IEEE80211_AC_VI, WMM_PBC_LOW_BOUND_VI);
++ pbc_acq_low_bound_config(IEEE80211_AC_BE, WMM_PBC_LOW_BOUND_BE);
++ pbc_acq_low_bound_config(IEEE80211_AC_BK, WMM_PBC_LOW_BOUND_BK);
++ req.bound[4].low = dev->wmm_pbc_enable
++ ? cpu_to_le16(WMM_PBC_LOW_BOUND_MGMT) : 0;
++
++ for (i = 0; i < WMM_PBC_QUEUE_NUM; ++i)
++ req.bound[i].up = cpu_to_le16(WMM_PBC_BOUND_DEFAULT);
++
++ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(PKT_BUDGET_CTRL),
++ &req, sizeof(req), true);
++ if (ret)
++ dev_err(dev->mt76.dev, "Failed to configure WMM PBC.\n");
++}
++
++static void
++mt7996_mcu_rx_bss_acq_pkt_cnt(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++ struct mt7996_mcu_bss_acq_pkt_cnt_event *event = (struct mt7996_mcu_bss_acq_pkt_cnt_event *)skb->data;
++ u32 bitmap = le32_to_cpu(event->bss_bitmap);
++ u64 sum[IEEE80211_NUM_ACS] = {0};
++ u8 ac_cnt = 0;
++ int i, j;
++
++ for (i = 0; (i < BSS_ACQ_PKT_CNT_BSS_NUM) && (bitmap & (1 << i)); ++i) {
++ for (j = IEEE80211_AC_VO; j < IEEE80211_NUM_ACS; ++j)
++ sum[j] += le32_to_cpu(event->bss[i].cnt[mt76_connac_lmac_mapping(j)]);
++ }
++
++ for (i = IEEE80211_AC_VO; i < IEEE80211_NUM_ACS; ++i) {
++ if (sum[i] > WMM_PKT_THRESHOLD)
++ ++ac_cnt;
++ }
++
++ if (ac_cnt > 1 && !dev->wmm_pbc_enable) {
++ dev->wmm_pbc_enable = true;
++ queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
++ } else if (ac_cnt <= 1 && dev->wmm_pbc_enable) {
++ dev->wmm_pbc_enable = false;
++ queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
++ }
++}
++
+ static void
+ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -678,6 +754,8 @@ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ case MCU_EXT_EVENT_FW_LOG_2_HOST:
+ mt7996_mcu_rx_log_message(dev, skb);
+ break;
++ case MCU_EXT_EVENT_BSS_ACQ_PKT_CNT:
++ mt7996_mcu_rx_bss_acq_pkt_cnt(dev, skb);
+ default:
+ break;
+ }
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index e64812c6d..d24874a4b 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -375,10 +375,25 @@ enum {
+ MCU_WA_PARAM_CMD_DEBUG,
+ };
+
++#define BSS_ACQ_PKT_CNT_BSS_NUM 24
++#define BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL 0x00ffffff
++#define BSS_ACQ_PKT_CNT_READ_CLR BIT(31)
++#define WMM_PKT_THRESHOLD 100
++
++struct mt7996_mcu_bss_acq_pkt_cnt_event {
++ struct mt7996_mcu_rxd rxd;
++
++ __le32 bss_bitmap;
++ struct {
++ __le32 cnt[IEEE80211_NUM_ACS];
++ } __packed bss[BSS_ACQ_PKT_CNT_BSS_NUM];
++} __packed;
++
+ enum {
+ MCU_WA_PARAM_PDMA_RX = 0x04,
+ MCU_WA_PARAM_CPU_UTIL = 0x0b,
+ MCU_WA_PARAM_RED_EN = 0x0e,
++ MCU_WA_PARAM_BSS_ACQ_PKT_CNT = 0x12,
+ MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
+ MCU_WA_PARAM_RED_CONFIG = 0x40,
+ };
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index a0cc8f37d..19965393e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -606,6 +606,9 @@ struct mt7996_dev {
+ u8 wtbl_size_group;
+
+ struct mt7996_vow_ctrl vow;
++
++ bool wmm_pbc_enable;
++ struct work_struct wmm_pbc_work;
+ #ifdef CONFIG_MTK_DEBUG
+ u16 wlan_idx;
+ struct {
+@@ -857,6 +860,7 @@ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+ enum vow_drr_ctrl_id id);
+ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
++void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
+
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch
new file mode 100644
index 0000000..eb0ca10
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch
@@ -0,0 +1,330 @@
+From 2d0f8b5d4e08fb55a6e9ea3f05faab7afbfd65a1 Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Thu, 1 Feb 2024 10:32:42 +0800
+Subject: [PATCH 078/116] mtk: wifi: mt76: mt7996: eagle support extra
+ option_type
+
+1. eagle + mt7988d option_type 2 support
+2. eagle single pcie support
+
+CR-Id: WCNCR00259516
+Change-Id: Ib8e741b3c9eba3c796704351259f926c1e4e9d69
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+
+1. adjust pcie outstanding value by pcie speed. not no longer by option_type.
+
+CR-Id: WCNCR00259516
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+Change-Id: I1c8fa6769aa0b192b32738aec0b14c86572a493b
+(cherry picked from commit 31d64e0f571eb06ed67f4916bc12fbcfe1263c47)
+---
+ mt7996/dma.c | 51 +++++++++++++++++++++++++++++++++----
+ mt7996/init.c | 67 ++++++++++++++++++++++++++++++++++++++-----------
+ mt7996/main.c | 15 +++++++++--
+ mt7996/mt7996.h | 5 ++++
+ mt7996/pci.c | 2 +-
+ mt7996/regs.h | 5 ++++
+ 6 files changed, 123 insertions(+), 22 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index c23b0d651..3dc0e8a1d 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -12,12 +12,20 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc,
+ {
+ struct mt7996_dev *dev = phy->dev;
+ u32 flags = 0;
++ int i;
++
++ if (phy->mt76->band_idx == MT_BAND1 && !dev->hif2 && is_mt7996(&dev->mt76)) {
++ phy->mt76->q_tx[0] = phy->mt76->dev->phys[MT_BAND0]->q_tx[0];
++ for (i = 1; i <= MT_TXQ_PSD; i++)
++ phy->mt76->q_tx[i] = phy->mt76->q_tx[0];
++ return 0;
++ }
+
+ if (mtk_wed_device_active(wed)) {
+ ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
+ idx -= MT_TXQ_ID(0);
+
+- if (phy->mt76->band_idx == MT_BAND2)
++ if (wed == &dev->mt76.mmio.wed_hif2)
+ flags = MT_WED_Q_TX(0);
+ else
+ flags = MT_WED_Q_TX(idx);
+@@ -102,8 +110,20 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ /* data tx queue */
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ if (is_mt7996(&dev->mt76)) {
+- TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+- TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++ if (dev->hif2) {
++ if (dev->option_type == 2) {
++ /* bn1:ring21 bn2:ring19 */
++ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++ } else {
++ /* default bn1:ring19 bn2:ring21 */
++ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++ }
++ } else {
++ /* single pcie bn0/1:ring18 bn2:ring19 */
++ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++ }
+ } else {
+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+ }
+@@ -352,8 +372,20 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE);
+
+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+- MT_WFDMA_HOST_CONFIG_PDMA_BAND |
+- MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
++ MT_WFDMA_HOST_CONFIG_PDMA_BAND);
++
++ mt76_clear(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
++ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
++ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
++
++ if (dev->option_type == 2)
++ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
++ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++ else
++ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+
+ if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+ is_mt7992(&dev->mt76)) {
+@@ -366,6 +398,15 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
+ MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
+
++ if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
++ (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
++ mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
++ WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
++ FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x3));
++ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
++ MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
++ FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, 0x3));
++ }
+ /* WFDMA rx threshold */
+ mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_45_TH + hif1_ofs, 0xc000c);
+ mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_67_TH + hif1_ofs, 0x10008);
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f4111460c..1e7cd5235 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -511,7 +511,7 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
+ void mt7996_mac_init(struct mt7996_dev *dev)
+ {
+ #define HIF_TXD_V2_1 0x21
+- int i;
++ int i, rx_path_type, rro_bypass, txfree_path;
+
+ mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
+
+@@ -525,22 +525,45 @@ void mt7996_mac_init(struct mt7996_dev *dev)
+ }
+
+ /* rro module init */
+- if (is_mt7996(&dev->mt76))
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
+- else
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE,
+- dev->hif2 ? 7 : 0);
++ switch (dev->option_type) {
++ case 2:
++ /* eagle + 7988d */
++ rx_path_type = 3;
++ rro_bypass = dev->has_rro ? 1 : 3;
++ txfree_path = dev->has_rro ? 0 : 1;
++ break;
++ case 3:
++ /* eagle + Airoha */
++ rx_path_type = 6;
++ rro_bypass = dev->has_rro ? 1 : 3;
++ txfree_path = dev->has_rro ? 0 : 1;
++ break;
++ case 4:
++ /* Bollinger */
++ rx_path_type = 2;
++ rro_bypass = dev->has_rro ? 1 : 3;
++ txfree_path = dev->has_rro ? 0 : 1;
++ break;
++ default:
++ if (is_mt7996(&dev->mt76))
++ rx_path_type = 2;
++ else
++ rx_path_type = 7;
++
++ rro_bypass = dev->has_rro ? 1 : 3;
++ txfree_path = dev->has_rro ? 0 : 1;
++ break;
++ }
++
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, dev->hif2 ? rx_path_type : 0);
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, rro_bypass);
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, txfree_path);
+
+ if (dev->has_rro) {
+ u16 timeout;
+
+ timeout = mt76_rr(dev, MT_HW_REV) == MT_HW_REV1 ? 512 : 128;
+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_FLUSH_TIMEOUT, timeout);
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 1);
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 0);
+- } else {
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
+ }
+
+ mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+@@ -618,9 +641,22 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ if (phy)
+ return 0;
+
+- if (is_mt7996(&dev->mt76) && band == MT_BAND2 && dev->hif2) {
+- hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+- wed = &dev->mt76.mmio.wed_hif2;
++ if (is_mt7996(&dev->mt76) && dev->hif2) {
++ switch (dev->option_type) {
++ case 2:
++ /* eagle + 7988d */
++ if (band == MT_BAND1) {
++ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++ wed = &dev->mt76.mmio.wed_hif2;
++ }
++ break;
++ default:
++ if (band == MT_BAND2) {
++ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++ wed = &dev->mt76.mmio.wed_hif2;
++ }
++ break;
++ }
+ }
+
+ mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band);
+@@ -1059,6 +1095,9 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
+ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ {
+ int ret, idx;
++ struct device_node *np = dev->mt76.dev->of_node;
++
++ of_property_read_u32(np, "option_type", &dev->option_type);
+
+ mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
+ if (is_mt7992(&dev->mt76)) {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f97898f5f..445f86221 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1606,8 +1606,19 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+
+- if (phy != &dev->phy && phy->mt76->band_idx == MT_BAND2)
+- wed = &dev->mt76.mmio.wed_hif2;
++ if (phy != &dev->phy && dev->hif2) {
++ switch (dev->option_type) {
++ case 2:
++ /* eagle + 7988d */
++ if (phy->mt76->band_idx == MT_BAND1)
++ wed = &dev->mt76.mmio.wed_hif2;
++ break;
++ default:
++ if (phy->mt76->band_idx == MT_BAND2)
++ wed = &dev->mt76.mmio.wed_hif2;
++ break;
++ }
++ }
+
+ if (!mtk_wed_device_active(wed))
+ return -ENODEV;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 19965393e..3f1f9b362 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -8,6 +8,7 @@
+
+ #include <linux/interrupt.h>
+ #include <linux/ktime.h>
++#include <linux/pci.h>
+ #include "../mt76_connac.h"
+ #include "regs.h"
+
+@@ -349,6 +350,8 @@ struct mt7996_hif {
+ struct device *dev;
+ void __iomem *regs;
+ int irq;
++ enum pci_bus_speed speed;
++ enum pcie_link_width width;
+ };
+
+ struct mt7996_scs_ctrl {
+@@ -579,6 +582,8 @@ struct mt7996_dev {
+ u8 eeprom_mode;
+ u32 bg_nxt_freq;
+
++ u32 option_type;
++
+ bool ibf;
+ u8 fw_debug_wm;
+ u8 fw_debug_wa;
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index f0d3f199c..24d69d4dc 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -5,7 +5,6 @@
+
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/pci.h>
+
+ #include "mt7996.h"
+ #include "mac.h"
+@@ -93,6 +92,7 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
+ hif->dev = &pdev->dev;
+ hif->regs = pcim_iomap_table(pdev)[0];
+ hif->irq = pdev->irq;
++ pcie_bandwidth_available(pdev, NULL, &hif->speed, &hif->width);
+ spin_lock_bh(&hif_lock);
+ list_add(&hif->list, &hif_list);
+ spin_unlock_bh(&hif_lock);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index a001d9fd6..a0e4b3e11 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -435,6 +435,7 @@ enum offs_rev {
+ #define WF_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0)
+ #define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD BIT(18)
+ #define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE BIT(14)
++#define WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK GENMASK(27, 24)
+
+ #define WF_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4)
+ #define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE BIT(31)
+@@ -454,6 +455,7 @@ enum offs_rev {
+
+ #define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30)
+ #define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0)
++#define MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 BIT(20)
+ #define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 BIT(21)
+ #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1 BIT(22)
+
+@@ -463,6 +465,9 @@ enum offs_rev {
+ #define MT_WFDMA_AXI_R2A_CTRL MT_WFDMA_EXT_CSR(0x500)
+ #define MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK GENMASK(4, 0)
+
++#define MT_WFDMA_AXI_R2A_CTRL2 MT_WFDMA_EXT_CSR(0x508)
++#define MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK GENMASK(31, 28)
++
+ #define MT_PCIE_RECOG_ID 0xd7090
+ #define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0)
+ #define MT_PCIE_RECOG_ID_SEM BIT(31)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch
new file mode 100644
index 0000000..56ae4e8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch
@@ -0,0 +1,115 @@
+From d3513854d4eaa55fabcef09ed6841ee817b00851 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 27 Jul 2023 19:35:32 +0800
+Subject: [PATCH 079/116] mtk: wifi: mt76: mt7996: support enable/disable
+ thermal protection mechanism
+
+This commit adds a new debugfs thermal_enable to enable/disable thermal
+protection mechanism. The purpose of this commit is for autotest to
+verify thermal protection mechanism.
+
+[Usage]
+Enable thermal protection: echo 1 > thermal_enable
+Disable thermal protection: echo 0 > thermal_enable
+
+Please note that if you re-enable thermal protection mechanism, all the
+configuration values will be retained from the exising configuration,
+rather than using the default values.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/main.c | 1 +
+ mt7996/mt7996.h | 1 +
+ mt7996/mtk_debugfs.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 47 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 445f86221..f7ea49f18 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -91,6 +91,7 @@ int mt7996_run(struct ieee80211_hw *hw)
+ #ifdef CONFIG_MTK_DEBUG
+ phy->sr_enable = true;
+ phy->enhanced_sr_enable = true;
++ phy->thermal_protection_enable = true;
+
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 3f1f9b362..3069e9070 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -488,6 +488,7 @@ struct mt7996_phy {
+ #ifdef CONFIG_MTK_DEBUG
+ bool sr_enable:1;
+ bool enhanced_sr_enable:1;
++ bool thermal_protection_enable:1;
+ #endif
+ };
+
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 5fb6078c1..ee72c0e51 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3169,6 +3169,49 @@ mt7996_show_rro_mib(struct seq_file *s, void *data)
+ return 0;
+ }
+
++static int
++mt7996_thermal_enable_get(void *data, u64 *enable)
++{
++ struct mt7996_phy *phy = data;
++
++ *enable = phy->thermal_protection_enable;
++
++ return 0;
++}
++
++static int
++mt7996_thermal_enable_set(void *data, u64 action)
++{
++ struct mt7996_phy *phy = data;
++ int ret;
++ u8 throttling;
++
++ if (action > 1)
++ return -EINVAL;
++
++ if (!!action == phy->thermal_protection_enable)
++ return 0;
++
++ ret = mt7996_mcu_set_thermal_protect(phy, !!action);
++ if (ret)
++ return ret;
++
++ if (!!!action)
++ goto out;
++
++ throttling = MT7996_THERMAL_THROTTLE_MAX - phy->cdev_state;
++ ret = mt7996_mcu_set_thermal_throttling(phy, throttling);
++ if (ret)
++ return ret;
++
++out:
++ phy->thermal_protection_enable = !!action;
++
++ return 0;
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
++ mt7996_thermal_enable_set, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -3276,6 +3319,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ mt7996_show_rro_mib);
+ }
+
++ debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
++
+ return 0;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch
new file mode 100644
index 0000000..dcd5544
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch
@@ -0,0 +1,118 @@
+From 5c0d266ff3446fe53d2fa9203e930350607ab7dc Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 4 Jan 2024 19:53:37 +0800
+Subject: [PATCH 080/116] mtk: wifi: mt76: mt7996: support thermal recal debug
+ command
+
+Add support thermal recal debug command.
+
+Usage:
+$ echo val > debugfs/thermal_recal
+
+The val can be the following values:
+0 = disable
+1 = enable
+2 = manual trigger
+
+CR-Id: WCNCR00261410
+Change-Id: Ief064633dd7ab0faeb298ac3902ca1b399e70365
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt76_connac_mcu.h | 1 +
+ mt7996/mt7996.h | 1 +
+ mt7996/mtk_debugfs.c | 17 +++++++++++++++++
+ mt7996/mtk_mcu.c | 21 +++++++++++++++++++++
+ 4 files changed, 40 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 12fdc6e12..151183d08 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1285,6 +1285,7 @@ enum {
+ MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ MCU_UNI_CMD_PRECAL_RESULT = 0x47,
++ MCU_UNI_CMD_THERMAL_CAL = 0x4c,
+ MCU_UNI_CMD_RRO = 0x57,
+ MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+ MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 3069e9070..69bcf78f5 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1032,6 +1032,7 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
+ void mt7996_tm_update_channel(struct mt7996_phy *phy);
+
+ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
++int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index ee72c0e51..ddb4a7ca2 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3212,6 +3212,22 @@ out:
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
+ mt7996_thermal_enable_set, "%lld\n");
+
++static int
++mt7996_thermal_recal_set(void *data, u64 val)
++{
++#define THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER 2
++#define THERMAL_DEBUG_MODE_RECAL 1
++ struct mt7996_dev *dev = data;
++
++ if (val > THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER)
++ return -EINVAL;
++
++ return mt7996_mcu_thermal_debug(dev, THERMAL_DEBUG_MODE_RECAL, val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_recal, NULL,
++ mt7996_thermal_recal_set, "%llu\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -3320,6 +3336,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ }
+
+ debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
++ debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
+
+ return 0;
+ }
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index aed32e982..aa44b4710 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1342,4 +1342,25 @@ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
+ sizeof(req), true);
+ }
+
++int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action)
++{
++ struct {
++ u8 __rsv1[4];
++
++ __le16 tag;
++ __le16 len;
++
++ u8 mode;
++ u8 action;
++ u8 __rsv2[2];
++ } __packed req = {
++ .tag = cpu_to_le16(mode),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .mode = mode,
++ .action = action,
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(THERMAL_CAL), &req,
++ sizeof(req), true);
++}
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch
new file mode 100644
index 0000000..663b42a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch
@@ -0,0 +1,327 @@
+From 3945c5fb09e36f31863f019dc12d242aa4e3644c Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Tue, 19 Mar 2024 13:16:12 +0800
+Subject: [PATCH 081/116] mtk: wifi: mt76: mt7996: add kite two pcie with two
+ wed support
+
+CR-Id: WCNCR00259516
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/dma.c | 68 ++++++++++++++++++++++++++++++++++++++-------------
+ mt7996/init.c | 54 +++++++++++++++++++++++-----------------
+ mt7996/main.c | 5 ++--
+ mt7996/mmio.c | 15 ++++++++++--
+ mt7996/pci.c | 5 ++--
+ mt7996/regs.h | 1 +
+ 6 files changed, 101 insertions(+), 47 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 3dc0e8a1d..a2490fa77 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -108,8 +108,8 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ }
+
+ /* data tx queue */
+- TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ if (is_mt7996(&dev->mt76)) {
++ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ if (dev->hif2) {
+ if (dev->option_type == 2) {
+ /* bn1:ring21 bn2:ring19 */
+@@ -125,7 +125,15 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+ }
+ } else {
+- TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++ if (dev->hif2) {
++ /* bn0:ring18 bn1:ring21 */
++ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
++ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++ } else {
++ /* single pcie bn0:ring18 bn1:ring19 */
++ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
++ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++ }
+ }
+
+ /* mcu tx queue */
+@@ -285,8 +293,11 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ if (mt7996_band_valid(dev, MT_BAND0))
+ irq_mask |= MT_INT_BAND0_RX_DONE;
+
+- if (mt7996_band_valid(dev, MT_BAND1))
++ if (mt7996_band_valid(dev, MT_BAND1)) {
+ irq_mask |= MT_INT_BAND1_RX_DONE;
++ if (is_mt7992(&dev->mt76) && dev->hif2)
++ irq_mask |= MT_INT_RX_TXFREE_BAND1_EXT;
++ }
+
+ if (mt7996_band_valid(dev, MT_BAND2))
+ irq_mask |= MT_INT_BAND2_RX_DONE;
+@@ -379,27 +390,46 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
+ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+
+- if (dev->option_type == 2)
+- mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+- MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
+- MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
+- else
+- mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+- MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+-
+- if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+- is_mt7992(&dev->mt76)) {
++ switch (dev->option_type) {
++ case 2:
++ /* eagle + 7988d */
++ if (is_mt7996(&dev->mt76))
++ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
++ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++ else
++ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++ break;
++ case 3:
+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+- MT_WFDMA_HOST_CONFIG_PDMA_BAND |
+- MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++ MT_WFDMA_HOST_CONFIG_BAND0_PCIE1);
++
++ break;
++ default:
++ if (is_mt7996(&dev->mt76))
++ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
++ else
++ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++ break;
+ }
+
+ /* AXI read outstanding number */
+ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
+ MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
+
+- if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
+- (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
++ if (dev->hif2->speed < PCIE_SPEED_5_0GT ||
++ (dev->hif2->speed == PCIE_SPEED_5_0GT && dev->hif2->width < 2)) {
++ mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
++ WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
++ FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x1));
++ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
++ MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
++ FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, 0x1));
++ } else if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
++ (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
+ mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
+ WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
+ FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x3));
+@@ -648,6 +678,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+
+ /* tx free notify event from WA for mt7992 band1 */
+ rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs;
++ if (mtk_wed_device_active(wed_hif2)) {
++ dev->mt76.q_rx[MT_RXQ_BAND1_WA].flags = MT_WED_Q_TXFREE;
++ dev->mt76.q_rx[MT_RXQ_BAND1_WA].wed = wed_hif2;
++ }
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA],
+ MT_RXQ_ID(MT_RXQ_BAND1_WA),
+ MT7996_RX_MCU_RING_SIZE,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 1e7cd5235..768979ef7 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -525,39 +525,46 @@ void mt7996_mac_init(struct mt7996_dev *dev)
+ }
+
+ /* rro module init */
++ rx_path_type = is_mt7996(&dev->mt76) ? 2 : 7;
++ rro_bypass = is_mt7996(&dev->mt76) ? 1 : 2;
++ txfree_path = is_mt7996(&dev->mt76) ? 0: 1;
++
+ switch (dev->option_type) {
+ case 2:
+- /* eagle + 7988d */
+- rx_path_type = 3;
+- rro_bypass = dev->has_rro ? 1 : 3;
+- txfree_path = dev->has_rro ? 0 : 1;
++ if (is_mt7996(&dev->mt76)) {
++ /* eagle + 7988d */
++ rx_path_type = 3;
++ rro_bypass = 1;
++ txfree_path = 0;
++ }
+ break;
+ case 3:
+- /* eagle + Airoha */
+- rx_path_type = 6;
+- rro_bypass = dev->has_rro ? 1 : 3;
+- txfree_path = dev->has_rro ? 0 : 1;
++ /* Airoha */
++ if (is_mt7996(&dev->mt76)) {
++ rx_path_type = 6;
++ rro_bypass = 1;
++ txfree_path = 0;
++ } else {
++ rx_path_type = 8;
++ rro_bypass = 2;
++ txfree_path = 1;
++ }
+ break;
+ case 4:
+- /* Bollinger */
+- rx_path_type = 2;
+- rro_bypass = dev->has_rro ? 1 : 3;
+- txfree_path = dev->has_rro ? 0 : 1;
++ if (is_mt7996(&dev->mt76)) {
++ /* Bollinger */
++ rx_path_type = 2;
++ rro_bypass = 1;
++ txfree_path = 0;
++ }
+ break;
+ default:
+- if (is_mt7996(&dev->mt76))
+- rx_path_type = 2;
+- else
+- rx_path_type = 7;
+-
+- rro_bypass = dev->has_rro ? 1 : 3;
+- txfree_path = dev->has_rro ? 0 : 1;
+ break;
+ }
+
+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, dev->hif2 ? rx_path_type : 0);
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, rro_bypass);
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, txfree_path);
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, dev->has_rro ? rro_bypass : 3);
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, dev->has_rro ? txfree_path : 1);
+
+ if (dev->has_rro) {
+ u16 timeout;
+@@ -641,7 +648,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ if (phy)
+ return 0;
+
+- if (is_mt7996(&dev->mt76) && dev->hif2) {
++ if (dev->hif2) {
+ switch (dev->option_type) {
+ case 2:
+ /* eagle + 7988d */
+@@ -651,7 +658,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ }
+ break;
+ default:
+- if (band == MT_BAND2) {
++ if ((is_mt7996(&dev->mt76) && band == MT_BAND2) ||
++ (is_mt7992(&dev->mt76) && band == MT_BAND1)) {
+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+ wed = &dev->mt76.mmio.wed_hif2;
+ }
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f7ea49f18..2fe9bf28f 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1607,7 +1607,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+
+- if (phy != &dev->phy && dev->hif2) {
++ if (dev->hif2) {
+ switch (dev->option_type) {
+ case 2:
+ /* eagle + 7988d */
+@@ -1615,7 +1615,8 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ wed = &dev->mt76.mmio.wed_hif2;
+ break;
+ default:
+- if (phy->mt76->band_idx == MT_BAND2)
++ if ((is_mt7996(&dev->mt76) && phy->mt76->band_idx == MT_BAND2) ||
++ (is_mt7992(&dev->mt76) && phy->mt76->band_idx == MT_BAND1))
+ wed = &dev->mt76.mmio.wed_hif2;
+ break;
+ }
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 91567a04b..6028182e1 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -336,10 +336,16 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ MT_TXQ_RING_BASE(0) +
+ MT7996_TXQ_BAND2 * MT_RING_SIZE;
+ if (dev->has_rro) {
++ u8 rxq_id = is_mt7996(&dev->mt76) ?
++ MT7996_RXQ_TXFREE2 : MT7996_RXQ_MCU_WA_EXT;
++
+ wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
+ MT_RXQ_RING_BASE(0) +
+- MT7996_RXQ_TXFREE2 * MT_RING_SIZE;
+- wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
++ rxq_id * MT_RING_SIZE;
++ if (is_mt7996(&dev->mt76))
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
++ else
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_BAND1_EXT) - 1;
+ } else {
+ wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
+ MT_RXQ_RING_BASE(0) +
+@@ -423,6 +429,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
+ }
+ dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
++ if(dev->hif2 && is_mt7992(&dev->mt76))
++ wed->wlan.id = 0x7992;
+ }
+
+ wed->wlan.nbuf = MT7996_TOKEN_SIZE;
+@@ -553,6 +561,9 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+
+ if (intr1 & MT_INT_RX_DONE_BAND2_EXT)
+ napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]);
++
++ if (is_mt7992(&dev->mt76) && (intr1 & MT_INT_RX_TXFREE_BAND1_EXT))
++ napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1_WA]);
+ }
+
+ if (mtk_wed_device_active(wed)) {
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 24d69d4dc..382b6a898 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -110,7 +110,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ int irq, ret;
+ struct mt76_dev *mdev;
+
+- hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
++ hif2_enable |= (id->device == 0x7990 || id->device == 0x7991 || id->device == 0x799a);
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+@@ -171,8 +171,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
+ ret = 0;
+
+- if (is_mt7996(&dev->mt76))
+- ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
++ ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
+
+ if (ret < 0)
+ goto free_wed_or_irq_vector;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index a0e4b3e11..e18935172 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -525,6 +525,7 @@ enum offs_rev {
+ #define MT_INT_RX_TXFREE_MAIN BIT(17)
+ #define MT_INT_RX_TXFREE_BAND1 BIT(15)
+ #define MT_INT_RX_TXFREE_TRI BIT(15)
++#define MT_INT_RX_TXFREE_BAND1_EXT BIT(19) /* for mt7992 two PCIE*/
+ #define MT_INT_RX_DONE_BAND2_EXT BIT(23)
+ #define MT_INT_RX_TXFREE_EXT BIT(26)
+ #define MT_INT_MCU_CMD BIT(29)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0082-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0082-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
new file mode 100644
index 0000000..bcb03ab
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0082-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
@@ -0,0 +1,630 @@
+From a5d6a71a81b1bd7daadd810a601615a293beea80 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 11 Jan 2024 08:55:13 +0800
+Subject: [PATCH 082/116] mtk: wifi: mt76: mt7992: add support to enable index
+ FW log for ConsysPlanet
+
+Add support to enable and record index FW log, which is the input for ConsysPlanet, via mt76-test command.
+
+Usage:
+1. Foreground logging
+ 1) Start: mt76-test phy0 idxlog
+ 2) Stop: Ctrl + C
+2. Background logging
+ 1) Start: mt76-test phy0 idxlog &
+ 2) Stop: killall mt76-test
+3. Logging with FW Parser
+ 1) Start Ethernet recording of FW Parser.
+ 2) Start: mt76-test phy0 idxlog <PC's IP Address>
+ 3) Stop: Ctrl + C
+ 4) Stop FW Parser.
+
+Log Files
+- FW Log: FW text message
+ - Location: /tmp/log/clog_(timestamp)/WIFI_FW_(timestamp).clog
+- Driver Log: log message printed at driver layer
+ - Location: /tmp/log/clog_(timestamp)/WIFI_KERNEL_(timestamp).clog
+
+CR-Id: WCNCR00298425
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Change-Id: I5d72c844e920cdcbaed4c65f734de8f041e6f384
+---
+ mt7996/debugfs.c | 90 +++++++++++++++++--
+ mt7996/mac.c | 10 ++-
+ mt7996/mcu.c | 34 +++++++-
+ mt7996/mcu.h | 4 +-
+ mt7996/mt7996.h | 3 +
+ tools/fwlog.c | 218 ++++++++++++++++++++++++++++++++++------------
+ tools/main.c | 2 +
+ tools/mt76-test.h | 3 +
+ 8 files changed, 295 insertions(+), 69 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index e26de48c6..837758611 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -430,8 +430,8 @@ create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
+ {
+ struct dentry *f;
+
+- f = debugfs_create_file("fwlog_data", mode, parent, buf,
+- &relay_file_operations);
++ f = debugfs_create_file(filename[0] == 'f' ? "fwlog_data" : "idxlog_data",
++ mode, parent, buf, &relay_file_operations);
+ if (IS_ERR(f))
+ return NULL;
+
+@@ -522,6 +522,53 @@ mt7996_fw_debug_bin_get(void *data, u64 *val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get,
+ mt7996_fw_debug_bin_set, "%lld\n");
+
++static int
++mt7996_idxlog_enable_get(void *data, u64 *val)
++{
++ struct mt7996_dev *dev = data;
++
++ *val = dev->idxlog_enable;
++
++ return 0;
++}
++
++static int
++mt7996_idxlog_enable_set(void *data, u64 val)
++{
++ static struct rchan_callbacks relay_cb = {
++ .create_buf_file = create_buf_file_cb,
++ .remove_buf_file = remove_buf_file_cb,
++ };
++ struct mt7996_dev *dev = data;
++
++ if (dev->idxlog_enable == !!val)
++ return 0;
++
++ if (!dev->relay_idxlog) {
++ dev->relay_idxlog = relay_open("idxlog_data", dev->debugfs_dir,
++ 1500, 512, &relay_cb, NULL);
++ if (!dev->relay_idxlog)
++ return -ENOMEM;
++ }
++
++ dev->idxlog_enable = !!val;
++
++ if (val) {
++ int ret = mt7996_mcu_fw_time_sync(&dev->mt76);
++ if (ret)
++ return ret;
++
++ /* Reset relay channel only when it is not being written to. */
++ relay_reset(dev->relay_idxlog);
++ }
++
++ return mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM,
++ val ? MCU_FW_LOG_RELAY_IDX : 0);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_idxlog_enable, mt7996_idxlog_enable_get,
++ mt7996_idxlog_enable_set, "%llu\n");
++
+ static int
+ mt7996_fw_util_wa_show(struct seq_file *file, void *data)
+ {
+@@ -1042,6 +1089,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
+ debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
+ debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
++ debugfs_create_file("idxlog_enable", 0600, dir, dev, &fops_idxlog_enable);
+ /* TODO: wm fw cpu utilization */
+ debugfs_create_file("fw_util_wa", 0400, dir, dev,
+ &mt7996_fw_util_wa_fops);
+@@ -1108,6 +1156,32 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
+ spin_unlock_irqrestore(&lock, flags);
+ }
+
++static void
++mt7996_debugfs_write_idxlog(struct mt7996_dev *dev, const void *data, int len)
++{
++ static DEFINE_SPINLOCK(lock);
++ unsigned long flags;
++ void *dest;
++
++ if (!dev->relay_idxlog)
++ return;
++
++ spin_lock_irqsave(&lock, flags);
++
++ dest = relay_reserve(dev->relay_idxlog, len + 4);
++ if (!dest)
++ dev_err(dev->mt76.dev, "Failed to reserve slot in %s\n",
++ dev->relay_idxlog->base_filename);
++ else {
++ *(u32 *)dest = len;
++ dest += 4;
++ memcpy(dest, data, len);
++ relay_flush(dev->relay_idxlog);
++ }
++
++ spin_unlock_irqrestore(&lock, flags);
++}
++
+ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len)
+ {
+ struct {
+@@ -1132,11 +1206,15 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
+ {
+- if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC)
+- return false;
++ bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
+
+- if (dev->relay_fwlog)
+- mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
++ if (is_fwlog) {
++ if (dev->relay_fwlog)
++ mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
++ } else if (dev->relay_idxlog)
++ mt7996_debugfs_write_idxlog(dev, data, len);
++ else
++ return false;
+
+ return true;
+ }
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index e3758ff13..8c444423b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2279,11 +2279,9 @@ void mt7996_mac_work(struct work_struct *work)
+ mutex_lock(&mdev->mutex);
+
+ mt76_update_survey(mphy);
+- if (++mphy->mac_work_count == 5) {
++ if (++mphy->mac_work_count % 5 == 0) {
+ int i;
+
+- mphy->mac_work_count = 0;
+-
+ mt7996_mac_update_stats(phy);
+
+ /* Update DEV-wise information only in
+@@ -2302,6 +2300,12 @@ void mt7996_mac_work(struct work_struct *work)
+ if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
+ BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
+ dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
++
++ if (mphy->mac_work_count == 100) {
++ if (phy->dev->idxlog_enable && mt7996_mcu_fw_time_sync(mdev))
++ dev_err(mdev->dev, "Failed to synchronize time with FW.\n");
++ mphy->mac_work_count = 0;
++ }
+ } else if (mt7996_band_valid(phy->dev, i) &&
+ test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
+ break;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6fb9f81f0..3a376c9ac 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -400,6 +400,7 @@ static void
+ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+ #define UNI_EVENT_FW_LOG_FORMAT 0
++#define UNI_EVENT_FW_LOG_MEMORY 1
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ const char *data = (char *)&rxd[1] + 4, *type;
+ struct tlv *tlv = (struct tlv *)data;
+@@ -411,7 +412,8 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
+ goto out;
+ }
+
+- if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT)
++ if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT &&
++ le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_MEMORY)
+ return;
+
+ data += sizeof(*tlv) + 4;
+@@ -3184,6 +3186,36 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level)
+ sizeof(data), false);
+ }
+
++int mt7996_mcu_fw_time_sync(struct mt76_dev *dev)
++{
++ struct {
++ u8 _rsv[4];
++
++ __le16 tag;
++ __le16 len;
++ __le32 sec;
++ __le32 usec;
++ } data = {
++ .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_TIME_SYNC),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ };
++ struct timespec64 ts;
++ struct tm tm;
++
++ ktime_get_real_ts64(&ts);
++ data.sec = cpu_to_le32((u32)ts.tv_sec);
++ data.usec = cpu_to_le32((u32)(ts.tv_nsec / 1000));
++
++ /* Dump synchronized time for ConsysPlanet to parse. */
++ time64_to_tm(ts.tv_sec, 0, &tm);
++ dev_info(dev->dev, "%ld-%02d-%02d %02d:%02d:%02d.%ld UTC\n",
++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
++ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec / 1000);
++
++ return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(WSYS_CONFIG), &data,
++ sizeof(data), true);
++}
++
+ static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled)
+ {
+ struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index d24874a4b..814072e3a 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -357,7 +357,8 @@ enum {
+ MCU_FW_LOG_WM,
+ MCU_FW_LOG_WA,
+ MCU_FW_LOG_TO_HOST,
+- MCU_FW_LOG_RELAY = 16
++ MCU_FW_LOG_RELAY = 16,
++ MCU_FW_LOG_RELAY_IDX = 40
+ };
+
+ enum {
+@@ -950,6 +951,7 @@ enum {
+ UNI_WSYS_CONFIG_FW_LOG_CTRL,
+ UNI_WSYS_CONFIG_FW_DBG_CTRL,
+ UNI_CMD_CERT_CFG = 6,
++ UNI_WSYS_CONFIG_FW_TIME_SYNC, /* UNI_CMD_FW_TIME_SYNC in FW */
+ };
+
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 69bcf78f5..d03d3d94c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -591,9 +591,11 @@ struct mt7996_dev {
+ u8 fw_debug_bin;
+ u16 fw_debug_seq;
+ bool fw_debug_muru_disable;
++ bool idxlog_enable;
+
+ struct dentry *debugfs_dir;
+ struct rchan *relay_fwlog;
++ struct rchan *relay_idxlog;
+
+ void *cal;
+ u32 cur_prek_offset;
+@@ -845,6 +847,7 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
+ int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
+ int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
++int mt7996_mcu_fw_time_sync(struct mt76_dev *dev);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+diff --git a/tools/fwlog.c b/tools/fwlog.c
+index 3c6a61d71..0e2de2dc2 100644
+--- a/tools/fwlog.c
++++ b/tools/fwlog.c
+@@ -29,10 +29,9 @@ static const char *debugfs_path(const char *phyname, const char *file)
+ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ {
+ FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
+-
+ if (!f) {
+- fprintf(stderr, "Could not open fw_debug_bin file\n");
+- return 1;
++ perror("fopen");
++ return -1;
+ }
+
+ if (en && val)
+@@ -47,6 +46,21 @@ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ return 0;
+ }
+
++static int mt76_set_idxlog_enable(const char *phyname, bool enable)
++{
++ FILE *f = fopen(debugfs_path(phyname, "idxlog_enable"), "w");
++ if (!f) {
++ perror("fopen");
++ return -1;
++ }
++
++ fprintf(f, "%hhu", enable);
++
++ fclose(f);
++
++ return 0;
++}
++
+ int read_retry(int fd, void *buf, int len)
+ {
+ int out_len = 0;
+@@ -80,105 +94,193 @@ static void handle_signal(int sig)
+ done = true;
+ }
+
+-int mt76_fwlog(const char *phyname, int argc, char **argv)
++static int mt76_log_socket(struct sockaddr_in *remote, char *ip)
+ {
+-#define BUF_SIZE 1504
+ struct sockaddr_in local = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = INADDR_ANY,
+ };
+- struct sockaddr_in remote = {
+- .sin_family = AF_INET,
+- .sin_port = htons(55688),
+- };
+- char *buf = calloc(BUF_SIZE, sizeof(char));
+- int ret = 0;
+- /* int yes = 1; */
+- int s, fd;
+-
+- if (argc < 1) {
+- fprintf(stderr, "need destination address\n");
+- return 1;
+- }
++ int s, ret;
+
+- if (!inet_aton(argv[0], &remote.sin_addr)) {
+- fprintf(stderr, "invalid destination address\n");
+- return 1;
++ remote->sin_family = AF_INET;
++ remote->sin_port = htons(55688);
++ if (!inet_aton(ip, &remote->sin_addr)) {
++ fprintf(stderr, "Invalid destination IP address: %s\n", ip);
++ return -EINVAL;
+ }
+
+ s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0) {
+ perror("socket");
+- return 1;
++ return s;
+ }
+
+- /* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
+- if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
++ ret = bind(s, (struct sockaddr *)&local, sizeof(local));
++ if (ret) {
+ perror("bind");
+- return 1;
++ close(s);
++ return ret;
+ }
+
+- if (mt76_set_fwlog_en(phyname, true, argv[1]))
+- return 1;
++ return s;
++}
++
++static int mt76_log_relay(int in_fd, int out_fd, struct sockaddr_in *remote)
++{
++ char *buf = malloc(FWLOG_BUF_SIZE);
++ int ret = 0;
+
+- fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
+- if (fd < 0) {
+- fprintf(stderr, "Could not open fwlog_data file: %s\n", strerror(errno));
+- ret = 1;
+- goto out;
++ if (!buf) {
++ perror("malloc");
++ return -ENOMEM;
+ }
+
+ signal(SIGTERM, handle_signal);
+ signal(SIGINT, handle_signal);
+ signal(SIGQUIT, handle_signal);
+
+- while (1) {
++ while (!done) {
+ struct pollfd pfd = {
+- .fd = fd,
+- .events = POLLIN | POLLHUP | POLLERR,
++ .fd = in_fd,
++ .events = POLLIN,
+ };
+ uint32_t len;
+- int r;
+-
+- if (done)
+- break;
++ int rc;
+
+ poll(&pfd, 1, -1);
+
+- r = read_retry(fd, &len, sizeof(len));
+- if (r < 0)
++ rc = read_retry(in_fd, &len, sizeof(len));
++ if (rc < 0) {
++ if (!done) {
++ fprintf(stderr, "Failed to read relay file.\n");
++ ret = -1;
++ }
+ break;
+-
+- if (!r)
++ }
++ if (!rc)
+ continue;
+
+- if (len > BUF_SIZE) {
+- fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
+- ret = 1;
++ if (len > FWLOG_BUF_SIZE) {
++ fprintf(stderr, "Log size was too large: %u bytes\n", len);
++ ret = -ENOMEM;
+ break;
+ }
+
+- if (done)
++ rc = read_retry(in_fd, buf, len);
++ if (rc < 0) {
++ if (!done) {
++ fprintf(stderr, "Failed to read relay file.\n");
++ ret = -1;
++ }
+ break;
+-
+- r = read_retry(fd, buf, len);
+- if (done)
++ }
++ if (rc != len) {
++ fprintf(stderr, "Expected log size: %u bytes\n", len);
++ fprintf(stderr, "Read log size: %u bytes\n", rc);
++ ret = -EIO;
+ break;
++ }
+
+- if (r != len) {
+- fprintf(stderr, "Short read: %d < %d\n", r, len);
+- ret = 1;
++ if (remote)
++ rc = sendto(out_fd, buf, len, 0, (struct sockaddr *)remote, sizeof(*remote));
++ else
++ rc = write(out_fd, buf, len);
++ if (rc < 0) {
++ perror("sendto/write");
++ ret = -1;
+ break;
+ }
++ }
++
++ free(buf);
++
++ return ret;
++}
++
++int mt76_fwlog(const char *phyname, int argc, char **argv)
++{
++ struct sockaddr_in remote;
++ int in_fd, out_fd, ret;
++
++ if (argc < 1) {
++ fprintf(stderr, "need destination address\n");
++ return -EINVAL;
++ }
++
++ out_fd = mt76_log_socket(&remote, argv[0]);
++ if (out_fd < 0)
++ return out_fd;
++
++ ret = mt76_set_fwlog_en(phyname, true, argv[1]);
++ if (ret)
++ goto close;
+
+- /* send buf */
+- sendto(s, buf, len, 0, (struct sockaddr *)&remote, sizeof(remote));
++ in_fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
++ if (in_fd < 0) {
++ perror("open");
++ goto disable;
+ }
+
+- close(fd);
++ if (mt76_log_relay(in_fd, out_fd, &remote))
++ fprintf(stderr, "Failed to relay FW log.\n");
+
+-out:
+- mt76_set_fwlog_en(phyname, false, NULL);
++ close(in_fd);
++disable:
++ ret = mt76_set_fwlog_en(phyname, false, NULL);
++close:
++ close(out_fd);
++
++ return ret;
++}
++
++int mt76_idxlog(const char *phyname, int argc, char **argv)
++{
++#define IDXLOG_FILE_PATH "/tmp/log/WIFI_FW.clog"
++ struct sockaddr_in remote;
++ int in_fd, out_fd, ret;
++
++ if (argc) {
++ out_fd = mt76_log_socket(&remote, argv[0]);
++ if (out_fd < 0)
++ return out_fd;
++ } else {
++ out_fd = open(IDXLOG_FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR);
++ if (out_fd < 0) {
++ perror("open");
++ return -1;
++ }
++ }
++
++ ret = mt76_set_idxlog_enable(phyname, true);
++ if (ret)
++ goto close;
++
++ in_fd = open(debugfs_path(phyname, "idxlog_data"), O_RDONLY);
++ if (in_fd < 0) {
++ perror("open");
++ goto disable;
++ }
++
++ if (mt76_log_relay(in_fd, out_fd, argc ? &remote : NULL))
++ fprintf(stderr, "Failed to relay index log.\n");
++
++ close(in_fd);
++disable:
++ ret = mt76_set_idxlog_enable(phyname, false);
++close:
++ close(out_fd);
++
++ if (argc)
++ system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
++ "clog_dir=/tmp/log/clog_${timestamp};"
++ "mkdir ${clog_dir};"
++ "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
++ else
++ system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
++ "clog_dir=/tmp/log/clog_${timestamp};"
++ "mkdir ${clog_dir};"
++ "mv /tmp/log/WIFI_FW.clog ${clog_dir}/WIFI_FW_${timestamp}.clog;"
++ "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
+
+ return ret;
+ }
+diff --git a/tools/main.c b/tools/main.c
+index 699a9eea6..e9e255679 100644
+--- a/tools/main.c
++++ b/tools/main.c
+@@ -198,6 +198,8 @@ int main(int argc, char **argv)
+ ret = mt76_eeprom(phy, argc, argv);
+ else if (!strcmp(cmd, "fwlog"))
+ ret = mt76_fwlog(phyname, argc, argv);
++ else if (!strcmp(cmd, "idxlog"))
++ ret = mt76_idxlog(phyname, argc, argv);
+ else
+ usage();
+
+diff --git a/tools/mt76-test.h b/tools/mt76-test.h
+index d2fafa862..b9d508c5c 100644
+--- a/tools/mt76-test.h
++++ b/tools/mt76-test.h
+@@ -22,6 +22,8 @@
+ #define EEPROM_FILE_PATH_FMT "/tmp/mt76-test-%s"
+ #define EEPROM_PART_SIZE 20480
+
++#define FWLOG_BUF_SIZE 1504
++
+ struct nl_msg;
+ struct nlattr;
+
+@@ -61,5 +63,6 @@ extern unsigned char *eeprom_data;
+ void usage(void);
+ int mt76_eeprom(int phy, int argc, char **argv);
+ int mt76_fwlog(const char *phyname, int argc, char **argv);
++int mt76_idxlog(const char *phyname, int argc, char **argv);
+
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0083-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0083-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch
new file mode 100644
index 0000000..bb90a0e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0083-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch
@@ -0,0 +1,452 @@
+From afcc6594ef97e51bdbb707b7e9aeb8dfb7202ef1 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 3 Nov 2023 21:44:45 +0800
+Subject: [PATCH 083/116] wifi: mt76: mt7996: implement and switch to hw scan
+ callbacks
+
+To support MLO, hw_scan callbacks are mandatory. However, the
+firmware of AP-segment doesn't support hw_scan commands, so we need
+to implement it in the driver.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+If the cfg80211_scan_request contains an unicast BSSID, the probe
+request should be unicast.
+This works for ML probe request, which should be unicast.
+
+Change-Id: Ic096b10e4a82211be4c5c435d3e34e0a76ea3b9e
+Co-developed-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Co-developed-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c | 3 +-
+ mt76.h | 2 +
+ mt7996/init.c | 5 ++
+ mt7996/mac.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/main.c | 95 ++++++++++++++++++++++++++++----
+ mt7996/mcu.c | 3 +-
+ mt7996/mt7996.h | 11 +++-
+ 7 files changed, 248 insertions(+), 12 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index c7b222837..4fad03dd9 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -820,7 +820,7 @@ bool mt76_has_tx_pending(struct mt76_phy *phy)
+ }
+ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
+
+-static struct mt76_channel_state *
++struct mt76_channel_state *
+ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ {
+ struct mt76_sband *msband;
+@@ -836,6 +836,7 @@ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ idx = c - &msband->sband.channels[0];
+ return &msband->chan[idx];
+ }
++EXPORT_SYMBOL_GPL(mt76_channel_state);
+
+ void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time)
+ {
+diff --git a/mt76.h b/mt76.h
+index c3c35841f..0abb8ebf5 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -1477,6 +1477,8 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
+ enum ieee80211_frame_release_type reason,
+ bool more_data);
+ bool mt76_has_tx_pending(struct mt76_phy *phy);
++struct mt76_channel_state *
++mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c);
+ void mt76_set_channel(struct mt76_phy *phy);
+ void mt76_update_survey(struct mt76_phy *phy);
+ void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time);
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 768979ef7..952356823 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -457,6 +457,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+
+ wiphy->available_antennas_rx = phy->mt76->antenna_mask;
+ wiphy->available_antennas_tx = phy->mt76->antenna_mask;
++
++ wiphy->max_scan_ssids = 4;
++ wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ }
+
+ static void
+@@ -677,6 +680,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ mphy->dev->phys[band] = mphy;
+
+ INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work);
++ INIT_DELAYED_WORK(&phy->scan_work, mt7996_scan_work);
+
+ ret = mt7996_eeprom_parse_hw_cap(dev, phy);
+ if (ret)
+@@ -1574,6 +1578,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ dev->mt76.phy.priv = &dev->phy;
+ INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
+ INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
++ INIT_DELAYED_WORK(&dev->phy.scan_work, mt7996_scan_work);
+ INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
+ INIT_LIST_HEAD(&dev->sta_rc_list);
+ INIT_LIST_HEAD(&dev->twt_list);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 8c444423b..4e9dd2c1f 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2693,3 +2693,144 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+ dev->twt.table_mask &= ~BIT(flow->table_id);
+ dev->twt.n_agrt--;
+ }
++
++static void
++mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
++ const u8 *dst)
++{
++ struct ieee80211_hw *hw = phy->mt76->hw;
++ struct cfg80211_scan_request *req = phy->scan_req;
++ struct ieee80211_vif *vif = phy->scan_vif;
++ struct mt7996_vif *mvif;
++ struct mt76_wcid *wcid;
++ struct ieee80211_tx_info *info;
++ struct sk_buff *skb;
++
++ if (!req || !vif)
++ return;
++
++ mvif = (struct mt7996_vif *)vif->drv_priv;
++ wcid = &mvif->sta.wcid;
++
++ skb = ieee80211_probereq_get(hw, vif->addr,
++ ssid->ssid, ssid->ssid_len, req->ie_len);
++ if (!skb)
++ return;
++
++ if (is_unicast_ether_addr(dst)) {
++ struct ieee80211_hdr_3addr *hdr =
++ (struct ieee80211_hdr_3addr *)skb->data;
++ memcpy(hdr->addr1, dst, ETH_ALEN);
++ memcpy(hdr->addr3, dst, ETH_ALEN);
++ }
++
++ info = IEEE80211_SKB_CB(skb);
++ if (req->no_cck)
++ info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
++
++ if (req->ie_len)
++ skb_put_data(skb, req->ie, req->ie_len);
++
++ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
++
++ rcu_read_lock();
++ if (!ieee80211_tx_prepare_skb(hw, vif, skb,
++ phy->scan_chan->band,
++ NULL)) {
++ rcu_read_unlock();
++ ieee80211_free_txskb(hw, skb);
++ return;
++ }
++
++ local_bh_disable();
++ mt76_tx(phy->mt76, NULL, wcid, skb);
++ local_bh_enable();
++
++ rcu_read_unlock();
++}
++
++void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
++{
++ struct cfg80211_scan_info info = {
++ .aborted = aborted,
++ };
++
++ ieee80211_scan_completed(phy->mt76->hw, &info);
++ phy->scan_chan = NULL;
++ phy->scan_req = NULL;
++ phy->scan_vif = NULL;
++ clear_bit(MT76_SCANNING, &phy->mt76->state);
++}
++
++static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
++{
++ bool *has_sta = data;
++
++ if (*has_sta)
++ return;
++ *has_sta = true;
++}
++
++void mt7996_scan_work(struct work_struct *work)
++{
++ struct mt7996_phy *phy = container_of(work, struct mt7996_phy, scan_work.work);
++ struct ieee80211_hw *hw = phy->mt76->hw;
++ struct cfg80211_scan_request *req = phy->scan_req;
++ struct cfg80211_chan_def chandef = {};
++ int duration;
++ bool has_sta = false, active_scan = false;
++
++ mutex_lock(&phy->dev->mt76.mutex);
++ if (phy->scan_chan_idx >= req->n_channels) {
++ mt7996_scan_complete(phy, false);
++ mutex_unlock(&phy->dev->mt76.mutex);
++
++ mt7996_set_channel(phy, &hw->conf.chandef);
++
++ return;
++ }
++
++ ieee80211_iterate_stations_atomic(hw, mt7996_scan_check_sta, &has_sta);
++
++ /* go back to operating channel */
++ if (has_sta && phy->scan_chan) {
++ phy->scan_chan = NULL;
++ mutex_unlock(&phy->dev->mt76.mutex);
++
++ mt7996_set_channel(phy, &phy->mt76->chandef);
++
++ ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
++
++ return;
++ }
++
++ wiphy_info(hw->wiphy, "hw scan %d MHz\n",
++ req->channels[phy->scan_chan_idx]->center_freq);
++
++ phy->scan_chan = req->channels[phy->scan_chan_idx++];
++
++ if (!req->n_ssids ||
++ (phy->scan_chan->flags & (IEEE80211_CHAN_NO_IR |
++ IEEE80211_CHAN_RADAR))) {
++ duration = HZ / 9; /* ~110 ms */
++ } else {
++ duration = HZ / 16; /* ~60 ms */
++ active_scan = true;
++ }
++
++ cfg80211_chandef_create(&chandef, phy->scan_chan, NL80211_CHAN_HT20);
++ mutex_unlock(&phy->dev->mt76.mutex);
++
++ mt7996_set_channel(phy, &chandef);
++
++ if (active_scan) {
++ int i;
++
++ mutex_lock(&phy->dev->mt76.mutex);
++ for (i = 0; i < req->n_ssids; i++)
++ mt7996_scan_send_probe(phy, &req->ssids[i], req->bssid);
++ mutex_unlock(&phy->dev->mt76.mutex);
++ }
++
++ ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
++}
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 2fe9bf28f..c8f1b1097 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -312,6 +312,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ int idx = msta->wcid.idx;
+
++ cancel_delayed_work_sync(&phy->scan_work);
++
+ mt7996_mcu_add_sta(dev, vif, NULL, false, false);
+ mt7996_mcu_add_bss_info(phy, vif, false);
+
+@@ -323,6 +325,10 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+
+ mutex_lock(&dev->mt76.mutex);
++
++ if (test_bit(MT76_SCANNING, &phy->mt76->state))
++ mt7996_scan_complete(phy, true);
++
+ dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
+ phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
+ mutex_unlock(&dev->mt76.mutex);
+@@ -335,7 +341,33 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
+ }
+
+-int mt7996_set_channel(struct mt7996_phy *phy)
++static void ___mt7996_set_channel(struct mt7996_phy *phy,
++ struct cfg80211_chan_def *chandef)
++{
++ struct mt76_dev *mdev = phy->mt76->dev;
++ struct mt76_phy *mphy = phy->mt76;
++ bool offchannel = phy->scan_chan != NULL;
++ int timeout = HZ / 5;
++
++ wait_event_timeout(mdev->tx_wait, !mt76_has_tx_pending(mphy), timeout);
++ mt76_update_survey(mphy);
++
++ if (mphy->chandef.chan->center_freq != chandef->chan->center_freq ||
++ mphy->chandef.width != chandef->width)
++ mphy->dfs_state = MT_DFS_STATE_UNKNOWN;
++
++ mphy->chandef = *chandef;
++ mphy->chan_state = mt76_channel_state(mphy, chandef->chan);
++
++ if (!offchannel)
++ mphy->main_chan = chandef->chan;
++
++ if (chandef->chan != mphy->main_chan)
++ memset(mphy->chan_state, 0, sizeof(*mphy->chan_state));
++}
++
++static int __mt7996_set_channel(struct mt7996_phy *phy,
++ struct cfg80211_chan_def *chandef)
+ {
+ struct mt7996_dev *dev = phy->dev;
+ int ret;
+@@ -345,7 +377,7 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ mutex_lock(&dev->mt76.mutex);
+ set_bit(MT76_RESET, &phy->mt76->state);
+
+- mt76_set_channel(phy->mt76);
++ ___mt7996_set_channel(phy, chandef);
+
+ if (dev->cal) {
+ ret = mt7996_mcu_apply_tx_dpd(phy);
+@@ -381,6 +413,19 @@ out:
+ return ret;
+ }
+
++int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
++{
++ int ret;
++
++ ieee80211_stop_queues(phy->mt76->hw);
++ ret = __mt7996_set_channel(phy, chandef);
++ if (ret)
++ return ret;
++ ieee80211_wake_queues(phy->mt76->hw);
++
++ return 0;
++}
++
+ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+@@ -477,11 +522,7 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ if (ret)
+ return ret;
+
+- ieee80211_stop_queues(hw);
+- ret = mt7996_set_channel(phy);
+- if (ret)
+- return ret;
+- ieee80211_wake_queues(hw);
++ mt7996_set_channel(phy, &hw->conf.chandef);
+ }
+
+ if (changed & (IEEE80211_CONF_CHANGE_POWER |
+@@ -1648,6 +1689,42 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+
+ #endif
+
++static int
++mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_scan_request *hw_req)
++{
++ struct cfg80211_scan_request *req = &hw_req->req;
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++
++ mutex_lock(&phy->dev->mt76.mutex);
++ if (WARN_ON(phy->scan_req || phy->scan_chan)) {
++ mutex_unlock(&phy->dev->mt76.mutex);
++ return -EBUSY;
++ }
++
++ set_bit(MT76_SCANNING, &phy->mt76->state);
++ phy->scan_req = req;
++ phy->scan_vif = vif;
++ phy->scan_chan_idx = 0;
++ mutex_unlock(&phy->dev->mt76.mutex);
++
++ ieee80211_queue_delayed_work(hw, &phy->scan_work, 0);
++
++ return 0;
++}
++
++static void
++mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
++{
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++
++ cancel_delayed_work_sync(&phy->scan_work);
++
++ mutex_lock(&phy->dev->mt76.mutex);
++ mt7996_scan_complete(phy, true);
++ mutex_unlock(&phy->dev->mt76.mutex);
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ .tx = mt7996_tx,
+ .start = mt7996_start,
+@@ -1666,8 +1743,8 @@ const struct ieee80211_ops mt7996_ops = {
+ .ampdu_action = mt7996_ampdu_action,
+ .set_rts_threshold = mt7996_set_rts_threshold,
+ .wake_tx_queue = mt76_wake_tx_queue,
+- .sw_scan_start = mt76_sw_scan,
+- .sw_scan_complete = mt76_sw_scan_complete,
++ .hw_scan = mt7996_hw_scan,
++ .cancel_hw_scan = mt7996_cancel_hw_scan,
+ .release_buffered_frames = mt76_release_buffered_frames,
+ .get_txpower = mt76_get_txpower,
+ .channel_switch_beacon = mt7996_channel_switch_beacon,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3a376c9ac..99cca0dc7 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3775,7 +3775,8 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
+ req.switch_reason = CH_SWITCH_NORMAL;
+ else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL ||
+- phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
++ phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE ||
++ phy->scan_chan)
+ req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
+ else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
+ NL80211_IFTYPE_AP))
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d03d3d94c..fbf1e835b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -458,6 +458,13 @@ struct mt7996_phy {
+ u8 pp_mode;
+ u16 punct_bitmap;
+
++ /* for hw_scan */
++ struct delayed_work scan_work;
++ struct ieee80211_channel *scan_chan;
++ struct cfg80211_scan_request *scan_req;
++ struct ieee80211_vif *scan_vif;
++ int scan_chan_idx;
++
+ struct mt7996_scs_ctrl scs_ctrl;
+ u32 red_drop;
+
+@@ -806,7 +813,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_he_obss_pd *he_obss_pd);
+ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, bool changed);
+-int mt7996_set_channel(struct mt7996_phy *phy);
++int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif);
+ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+@@ -964,6 +971,8 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ struct sk_buff *skb, u32 *info);
+ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+ void mt7996_stats_work(struct work_struct *work);
++void mt7996_scan_work(struct work_struct *work);
++void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
+ int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
+ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
+ void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0084-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0084-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch
new file mode 100644
index 0000000..efe816c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0084-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch
@@ -0,0 +1,384 @@
+From fa5c84eb945cb472d98172876d176bff4a082d67 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 6 Nov 2023 16:17:11 +0800
+Subject: [PATCH 084/116] wifi: mt76: mt7996: implement and switch to chanctx
+ callbacks
+
+To support MLO, chanctx callbacks are mandatory, since one VIF (MLD) could
+operate on multiple channels (links).
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Change-Id: Ie4530a3bc2ac9e51045184d5aecca14118177042
+Co-developed-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/init.c | 22 ++++++
+ mt7996/mac.c | 10 ++-
+ mt7996/main.c | 178 ++++++++++++++++++++++++++++++++++++++++++++----
+ mt7996/mcu.c | 2 +-
+ mt7996/mt7996.h | 17 +++++
+ 5 files changed, 211 insertions(+), 18 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 952356823..6eeec3b8d 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -382,6 +382,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+
+ hw->sta_data_size = sizeof(struct mt7996_sta);
+ hw->vif_data_size = sizeof(struct mt7996_vif);
++ hw->chanctx_data_size = sizeof(struct mt7996_chanctx);
+
+ wiphy->iface_combinations = if_comb;
+ wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+@@ -417,6 +418,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+ ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
++ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+
+ hw->max_tx_fragments = 4;
+
+@@ -637,6 +639,22 @@ static int mt7996_vow_init(struct mt7996_phy *phy)
+ return mt7996_mcu_set_vow_feature_ctrl(phy);
+ }
+
++static void mt7996_init_chanctx(struct mt7996_phy *phy)
++{
++ struct ieee80211_supported_band *sband;
++ struct ieee80211_channel *chan;
++
++ if (phy->mt76->band_idx == MT_BAND2)
++ sband = &phy->mt76->sband_6g.sband;
++ else if (phy->mt76->band_idx == MT_BAND1)
++ sband = &phy->mt76->sband_5g.sband;
++ else
++ sband = &phy->mt76->sband_2g.sband;
++
++ chan = &sband->channels[0];
++ cfg80211_chandef_create(&phy->mt76->chandef, chan, NL80211_CHAN_HT20);
++}
++
+ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ enum mt76_band_id band)
+ {
+@@ -720,6 +738,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ if (ret)
+ goto error;
+
++ mt7996_init_chanctx(phy);
++
+ ret = mt7996_thermal_init(phy);
+ if (ret)
+ goto error;
+@@ -1609,6 +1629,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
++ mt7996_init_chanctx(&dev->phy);
++
+ ret = mt7996_thermal_init(&dev->phy);
+ if (ret)
+ return ret;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 4e9dd2c1f..0879c7735 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2785,7 +2785,10 @@ void mt7996_scan_work(struct work_struct *work)
+ mt7996_scan_complete(phy, false);
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+- mt7996_set_channel(phy, &hw->conf.chandef);
++ if (phy->chanctx)
++ mt7996_set_channel(phy, &phy->chanctx->chandef);
++ else
++ mt7996_set_channel(phy, &phy->mt76->chandef);
+
+ return;
+ }
+@@ -2797,7 +2800,10 @@ void mt7996_scan_work(struct work_struct *work)
+ phy->scan_chan = NULL;
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+- mt7996_set_channel(phy, &phy->mt76->chandef);
++ if (phy->chanctx)
++ mt7996_set_channel(phy, &phy->chanctx->chandef);
++ else
++ mt7996_set_channel(phy, &phy->mt76->chandef);
+
+ ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index c8f1b1097..dd4f3a711 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -76,6 +76,11 @@ int mt7996_run(struct ieee80211_hw *hw)
+ if (ret)
+ goto out;
+
++ /* set a parking channel */
++ ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++ if (ret)
++ goto out;
++
+ ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
+ if (ret)
+ goto out;
+@@ -510,21 +515,6 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ int ret;
+
+- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+- if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
+- ret = mt7996_mcu_edcca_enable(phy, true);
+- if (ret)
+- return ret;
+- }
+-
+- ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
+- phy->mt76->chandef.punctured);
+- if (ret)
+- return ret;
+-
+- mt7996_set_channel(phy, &hw->conf.chandef);
+- }
+-
+ if (changed & (IEEE80211_CONF_CHANGE_POWER |
+ IEEE80211_CONF_CHANGE_CHANNEL)) {
+ ret = mt7996_mcu_set_txpower_sku(phy);
+@@ -1725,6 +1715,158 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ mutex_unlock(&phy->dev->mt76.mutex);
+ }
+
++static int
++mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
++{
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++ int ret;
++
++ wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
++ mutex_lock(&phy->dev->mt76.mutex);
++
++ if (ctx->assigned) {
++ mutex_unlock(&phy->dev->mt76.mutex);
++ return -ENOSPC;
++ }
++
++ ctx->assigned = true;
++ ctx->chandef = conf->def;
++ ctx->phy = phy;
++ if (phy->chanctx) {
++ mutex_unlock(&phy->dev->mt76.mutex);
++ return 0;
++ }
++
++ phy->chanctx = ctx;
++ mutex_unlock(&phy->dev->mt76.mutex);
++
++ if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
++ ret = mt7996_mcu_edcca_enable(phy, true);
++ if (ret)
++ return ret;
++ }
++
++ ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE, ctx->chandef.punctured);
++ if (ret)
++ return ret;
++
++ return mt7996_set_channel(phy, &ctx->chandef);
++}
++
++static void
++mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
++{
++ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++ struct mt7996_phy *phy = ctx->phy;
++
++ wiphy_info(hw->wiphy, "%s: remove %u\n", __func__, conf->def.chan->hw_value);
++ cancel_delayed_work_sync(&phy->scan_work);
++ cancel_delayed_work_sync(&phy->mt76->mac_work);
++
++ mutex_lock(&phy->dev->mt76.mutex);
++ ctx->assigned = false;
++ if (ctx == phy->chanctx)
++ phy->chanctx = NULL;
++ mutex_unlock(&phy->dev->mt76.mutex);
++}
++
++static void
++mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf,
++ u32 changed)
++{
++ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++ struct mt7996_phy *phy = ctx->phy;
++
++ wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
++ if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
++ ctx->chandef = conf->def;
++
++ mt7996_set_channel(phy, &ctx->chandef);
++ }
++}
++
++static int
++mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf,
++ struct ieee80211_chanctx_conf *conf)
++{
++ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++ struct mt7996_phy *phy = ctx->phy;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++
++ wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
++ vif->addr, vif->type, link_conf->link_id,
++ conf->def.chan->center_freq);
++
++ mutex_lock(&phy->dev->mt76.mutex);
++
++ mvif->chanctx = ctx;
++ ctx->nbss_assigned++;
++
++ mutex_unlock(&phy->dev->mt76.mutex);
++
++ return 0;
++}
++
++static void
++mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf,
++ struct ieee80211_chanctx_conf *conf)
++{
++ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++ struct mt7996_phy *phy = ctx->phy;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++
++ wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
++ vif->addr, vif->type, link_conf->link_id,
++ conf->def.chan->center_freq);
++ cancel_delayed_work_sync(&phy->scan_work);
++
++ mutex_lock(&phy->dev->mt76.mutex);
++
++ if (test_bit(MT76_SCANNING, &phy->mt76->state))
++ mt7996_scan_complete(phy, true);
++
++ mvif->chanctx = NULL;
++ ctx->nbss_assigned--;
++
++ mutex_unlock(&phy->dev->mt76.mutex);
++}
++
++static int
++mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
++ struct ieee80211_vif_chanctx_switch *vifs,
++ int n_vifs,
++ enum ieee80211_chanctx_switch_mode mode)
++{
++ struct mt7996_chanctx *old_ctx = mt7996_chanctx_get(vifs->old_ctx);
++ struct mt7996_chanctx *new_ctx = mt7996_chanctx_get(vifs->new_ctx);
++ struct mt7996_phy *phy = old_ctx->phy;
++
++ wiphy_info(hw->wiphy, "%s: old=%d, new=%d\n", __func__, vifs->old_ctx->def.chan->hw_value, vifs->new_ctx->def.chan->hw_value);
++
++ if (new_ctx->nbss_assigned && phy->chanctx == new_ctx) {
++ new_ctx->nbss_assigned += n_vifs;
++ return 0;
++ }
++
++ if (WARN_ON(old_ctx != phy->chanctx))
++ return -EINVAL;
++
++ mutex_lock(&phy->dev->mt76.mutex);
++
++ phy->chanctx = new_ctx;
++ new_ctx->assigned = true;
++ new_ctx->chandef = vifs->new_ctx->def;
++ new_ctx->phy = phy;
++ new_ctx->nbss_assigned += n_vifs;
++
++ mutex_unlock(&phy->dev->mt76.mutex);
++
++ return mt7996_set_channel(phy, &new_ctx->chandef);
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ .tx = mt7996_tx,
+ .start = mt7996_start,
+@@ -1775,4 +1917,10 @@ const struct ieee80211_ops mt7996_ops = {
+ .net_fill_forward_path = mt7996_net_fill_forward_path,
+ .net_setup_tc = mt76_wed_net_setup_tc,
+ #endif
++ .add_chanctx = mt7996_add_chanctx,
++ .remove_chanctx = mt7996_remove_chanctx,
++ .change_chanctx = mt7996_change_chanctx,
++ .assign_vif_chanctx = mt7996_assign_vif_chanctx,
++ .unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
++ .switch_vif_chanctx = mt7996_switch_vif_chanctx,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 99cca0dc7..85077108e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5309,7 +5309,7 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
+ .bitmap = cpu_to_le16(bitmap),
+ };
+
+- if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ ||
++ if (phy->chanctx->chandef.chan->band == NL80211_BAND_2GHZ ||
+ mode > PP_USR_MODE)
+ return 0;
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index fbf1e835b..84aafb404 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -332,6 +332,8 @@ struct mt7996_vif {
+
+ struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ struct cfg80211_bitrate_mask bitrate_mask;
++
++ struct mt7996_chanctx *chanctx;
+ };
+
+ /* crash-dump */
+@@ -421,6 +423,14 @@ struct mt7996_rro_ba_session {
+ u32 last_in_rxtime :12;
+ };
+
++struct mt7996_chanctx {
++ struct cfg80211_chan_def chandef;
++ struct mt7996_phy *phy;
++
++ bool assigned;
++ u8 nbss_assigned;
++};
++
+ struct mt7996_phy {
+ struct mt76_phy *mt76;
+ struct mt7996_dev *dev;
+@@ -464,6 +474,7 @@ struct mt7996_phy {
+ struct cfg80211_scan_request *scan_req;
+ struct ieee80211_vif *scan_vif;
+ int scan_chan_idx;
++ struct mt7996_chanctx *chanctx;
+
+ struct mt7996_scs_ctrl scs_ctrl;
+ u32 red_drop;
+@@ -752,6 +763,12 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
+ return 1;
+ }
+
++static inline struct mt7996_chanctx *
++mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
++{
++ return (struct mt7996_chanctx *)&ctx->drv_priv;
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0085-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0085-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch
new file mode 100644
index 0000000..03615f1
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0085-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch
@@ -0,0 +1,147 @@
+From 3e111fa2be564cc27c213471242f6b6b920e253c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 8 Nov 2023 18:52:26 +0800
+Subject: [PATCH 085/116] wifi: mt76: mt7996: use .sta_state to replace
+ .sta_add and .sta_remove
+
+MAC80211 mostly uses MLD address through TX path, and leaves the header
+translation procedure to driver. To perform address translation, driver
+needs to get the setup link address at early stage (i.e., state 1),
+however, when using .sta_add/.sta_remove callbacks, driver can only get
+the link address at state 3, so it's necessary to switch to .sta_state
+callback to meet this requirement.
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Change-Id: I8a0faef919843f2c7d5ff3256702a3bf8384ea60
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 53 ++++++++++++++++++++-----------------------------
+ mt7996/mmio.c | 1 +
+ mt7996/mt7996.h | 2 ++
+ 3 files changed, 24 insertions(+), 32 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index dd4f3a711..c330dd479 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -784,7 +784,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ u8 band_idx = mvif->phy->mt76->band_idx;
+- int ret, idx;
++ int idx;
+
+ #ifdef CONFIG_MTK_VENDOR
+ struct mt7996_phy *phy = &dev->phy;
+@@ -802,23 +802,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ msta->wcid.phy_idx = band_idx;
+ msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+
+- ewma_avg_signal_init(&msta->avg_ack_signal);
+-
+- mt7996_mac_wtbl_update(dev, idx,
+- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+ #ifdef CONFIG_MTK_VENDOR
+ mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
+ #endif
+
+- ret = mt7996_mcu_add_sta(dev, vif, sta, true, true);
+- if (ret)
+- return ret;
+-
+- ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
+- if (ret)
+- return ret;
+-
+ #ifdef CONFIG_MTK_VENDOR
+ switch (band_idx) {
+ case MT_BAND1:
+@@ -839,6 +826,25 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ return 0;
+ }
+
++void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
++{
++ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++
++ mutex_lock(&dev->mt76.mutex);
++
++ mt7996_mac_wtbl_update(dev, msta->wcid.idx,
++ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++ mt7996_mcu_add_sta(dev, vif, sta, true, true);
++ mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++
++ ewma_avg_signal_init(&msta->avg_ack_signal);
++
++ mutex_unlock(&dev->mt76.mutex);
++}
++
+ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
+@@ -958,22 +964,6 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ return ret;
+ }
+
+-static int
+-mt7996_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST,
+- IEEE80211_STA_NONE);
+-}
+-
+-static int
+-mt7996_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE,
+- IEEE80211_STA_NOTEXIST);
+-}
+-
+ static int
+ mt7996_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+@@ -1877,8 +1867,7 @@ const struct ieee80211_ops mt7996_ops = {
+ .conf_tx = mt7996_conf_tx,
+ .configure_filter = mt7996_configure_filter,
+ .bss_info_changed = mt7996_bss_info_changed,
+- .sta_add = mt7996_sta_add,
+- .sta_remove = mt7996_sta_remove,
++ .sta_state = mt76_sta_state,
+ .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ .sta_rc_update = mt7996_sta_rc_update,
+ .set_key = mt7996_set_key,
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 6028182e1..6abbcb68e 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -655,6 +655,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ .rx_check = mt7996_rx_check,
+ .rx_poll_complete = mt7996_rx_poll_complete,
+ .sta_add = mt7996_mac_sta_add,
++ .sta_assoc = mt7996_mac_sta_assoc,
+ .sta_remove = mt7996_mac_sta_remove,
+ .update_survey = mt7996_update_channel,
+ };
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 84aafb404..2b266d18b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -966,6 +966,8 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ void mt7996_mac_set_coverage_class(struct mt7996_phy *phy);
+ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
++void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta);
+ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ void mt7996_mac_work(struct work_struct *work);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
new file mode 100644
index 0000000..e8d7384
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
@@ -0,0 +1,2320 @@
+From 175bf6b7c7eabfb45f6618b17d19c475a1e0916e Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 24 Nov 2023 11:31:55 +0800
+Subject: [PATCH 086/116] wifi: mt76: mt7996: switch to per-link data structure
+ of vif
+
+Introduce struct mt7996_bss_conf, data structure for per-link BSS.
+Note that mt7996_vif now represents a legacy or MLD device.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/debugfs.c | 10 +-
+ mt7996/init.c | 4 +-
+ mt7996/mac.c | 25 ++-
+ mt7996/main.c | 269 ++++++++++++++++++-----------
+ mt7996/mcu.c | 429 +++++++++++++++++++++++-----------------------
+ mt7996/mt7996.h | 72 +++++---
+ mt7996/testmode.c | 17 +-
+ 7 files changed, 463 insertions(+), 363 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 837758611..06015d686 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -739,7 +739,7 @@ static void
+ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_dev *dev = msta->vif->phy->dev;
++ struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct seq_file *s = data;
+ u8 ac;
+
+@@ -759,7 +759,7 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ GENMASK(11, 0));
+ seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
+ sta->addr, msta->wcid.idx,
+- msta->vif->mt76.wmm_idx, ac, qlen);
++ msta->vif->deflink.mt76.wmm_idx, ac, qlen);
+ }
+ }
+
+@@ -1023,7 +1023,7 @@ mt7996_atf_enable_set(void *data, u64 val)
+ int ret;
+
+ vow->max_deficit = val ? 64 : 1;
+- ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
+ if (ret)
+ return ret;
+
+@@ -1055,7 +1055,7 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+
+ msta = container_of(wcid, struct mt7996_sta, wcid);
+ sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+- vif = &msta->vif->mt76;
++ vif = &msta->vif->deflink.mt76;
+ stats = &wcid->stats;
+
+ seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
+@@ -1230,7 +1230,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
+ #define LONG_PREAMBLE 1
+ struct ieee80211_sta *sta = file->private_data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_dev *dev = msta->vif->phy->dev;
++ struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct ra_rate phy = {};
+ char buf[100];
+ int ret;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 6eeec3b8d..381e1292c 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -628,11 +628,11 @@ static int mt7996_vow_init(struct mt7996_phy *phy)
+ vow->drr_quantum[6] = VOW_DRR_QUANTUM_L6;
+ vow->drr_quantum[7] = VOW_DRR_QUANTUM_L7;
+
+- ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
+ if (ret)
+ return ret;
+
+- ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
+ if (ret)
+ return ret;
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 0879c7735..63b16f92e 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -897,8 +897,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+
+ if (vif) {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf = &mvif->deflink;
+
+- txp->fw.bss_idx = mvif->mt76.idx;
++ txp->fw.bss_idx = mconf->mt76.idx;
+ }
+
+ txp->fw.token = cpu_to_le16(id);
+@@ -1518,12 +1519,15 @@ static void
+ mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+ struct ieee80211_hw *hw = priv;
++ struct ieee80211_bss_conf *conf = &vif->bss_conf;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf = &mvif->deflink;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+- mt7996_mcu_add_beacon(hw, vif, vif->bss_conf.enable_beacon);
++ mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
+ break;
+ default:
+ break;
+@@ -2237,6 +2241,8 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
++ struct ieee80211_bss_conf *conf;
++ struct mt7996_bss_conf *mconf;
+ struct mt7996_sta *msta;
+ u32 changed;
+ LIST_HEAD(list);
+@@ -2253,14 +2259,16 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+
+ sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+ vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
++ conf = &vif->bss_conf;
++ mconf = &msta->vif->deflink;
+
+ if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ IEEE80211_RC_NSS_CHANGED |
+ IEEE80211_RC_BW_CHANGED))
+- mt7996_mcu_add_rate_ctrl(dev, vif, sta, true);
++ mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, true);
+
+ if (changed & IEEE80211_RC_SMPS_CHANGED)
+- mt7996_mcu_set_fixed_field(dev, vif, sta, NULL,
++ mt7996_mcu_set_fixed_field(dev, mconf, sta, NULL,
+ RATE_PARAM_MMPS_UPDATE);
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+@@ -2643,7 +2651,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+
+ flow->sched = true;
+ flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
+- curr_tsf = __mt7996_get_tsf(hw, msta->vif);
++ curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink);
+ div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
+ flow_tsf = curr_tsf + interval - rem;
+ twt_agrt->twt = cpu_to_le64(flow_tsf);
+@@ -2652,7 +2660,8 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ }
+ flow->tsf = le64_to_cpu(twt_agrt->twt);
+
+- if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD))
++ if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
++ MCU_TWT_AGRT_ADD))
+ goto unlock;
+
+ setup_cmd = TWT_SETUP_CMD_ACCEPT;
+@@ -2674,6 +2683,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+ u8 flowid)
+ {
+ struct mt7996_twt_flow *flow;
++ struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+@@ -2684,8 +2694,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+ return;
+
+ flow = &msta->twt.flow[flowid];
+- if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow,
+- MCU_TWT_AGRT_DELETE))
++ if (mt7996_mcu_twt_agrt_update(dev, mconf, flow, MCU_TWT_AGRT_DELETE))
+ return;
+
+ list_del_init(&flow->list);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index c330dd479..9f61082f5 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -205,29 +205,30 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
+ return -1;
+ }
+
+-static void mt7996_init_bitrate_mask(struct ieee80211_vif *vif)
++static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ int i;
+
+- for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) {
+- mvif->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI;
+- mvif->bitrate_mask.control[i].he_gi = 0xff;
+- mvif->bitrate_mask.control[i].he_ltf = 0xff;
+- mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0);
+- memset(mvif->bitrate_mask.control[i].ht_mcs, 0xff,
+- sizeof(mvif->bitrate_mask.control[i].ht_mcs));
+- memset(mvif->bitrate_mask.control[i].vht_mcs, 0xff,
+- sizeof(mvif->bitrate_mask.control[i].vht_mcs));
+- memset(mvif->bitrate_mask.control[i].he_mcs, 0xff,
+- sizeof(mvif->bitrate_mask.control[i].he_mcs));
++ for (i = 0; i < ARRAY_SIZE(mconf->bitrate_mask.control); i++) {
++ mconf->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI;
++ mconf->bitrate_mask.control[i].he_gi = 0xff;
++ mconf->bitrate_mask.control[i].he_ltf = 0xff;
++ mconf->bitrate_mask.control[i].legacy = GENMASK(31, 0);
++ memset(mconf->bitrate_mask.control[i].ht_mcs, 0xff,
++ sizeof(mconf->bitrate_mask.control[i].ht_mcs));
++ memset(mconf->bitrate_mask.control[i].vht_mcs, 0xff,
++ sizeof(mconf->bitrate_mask.control[i].vht_mcs));
++ memset(mconf->bitrate_mask.control[i].he_mcs, 0xff,
++ sizeof(mconf->bitrate_mask.control[i].he_mcs));
+ }
+ }
+
+ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+ {
++ struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf = &mvif->deflink;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt76_txq *mtxq;
+@@ -240,8 +241,8 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ is_zero_ether_addr(vif->addr))
+ phy->monitor_vif = vif;
+
+- mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+- if (mvif->mt76.idx >= mt7996_max_interface_num(dev)) {
++ mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
++ if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
+ ret = -ENOSPC;
+ goto out;
+ }
+@@ -251,19 +252,21 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ ret = -ENOSPC;
+ goto out;
+ }
+- mvif->mt76.omac_idx = idx;
+- mvif->phy = phy;
+- mvif->mt76.band_idx = band_idx;
+- mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
+-
+- ret = mt7996_mcu_add_dev_info(phy, vif, true);
++ mconf->mt76.omac_idx = idx;
++ mconf->vif = mvif;
++ mconf->phy = phy;
++ mconf->mt76.band_idx = band_idx;
++ mconf->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
++ mvif->dev = dev;
++
++ ret = mt7996_mcu_add_dev_info(phy, conf, mconf, true);
+ if (ret)
+ goto out;
+
+- dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
+- phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
++ dev->mt76.vif_mask |= BIT_ULL(mconf->mt76.idx);
++ phy->omac_mask |= BIT_ULL(mconf->mt76.omac_idx);
+
+- idx = MT7996_WTBL_RESERVED - mvif->mt76.idx;
++ idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
+
+ INIT_LIST_HEAD(&mvif->sta.rc_list);
+ INIT_LIST_HEAD(&mvif->sta.wcid.poll_list);
+@@ -283,24 +286,25 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ }
+
+ if (vif->type != NL80211_IFTYPE_AP &&
+- (!mvif->mt76.omac_idx || mvif->mt76.omac_idx > 3))
++ (!mconf->mt76.omac_idx || mconf->mt76.omac_idx > 3))
+ vif->offload_flags = 0;
+ vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
+
+ if (phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ)
+- mvif->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
++ mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
+ else
+- mvif->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
++ mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
+
+- mt7996_init_bitrate_mask(vif);
++ mt7996_init_bitrate_mask(mconf);
+
+- mt7996_mcu_add_bss_info(phy, vif, true);
++ mt7996_mcu_add_bss_info(phy, conf, mconf, true);
+ /* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA
+ * interface, since firmware only records BSSID when the entry is new
+ */
+ if (vif->type != NL80211_IFTYPE_STATION)
+- mt7996_mcu_add_sta(dev, vif, NULL, true, true);
++ mt7996_mcu_add_sta(dev, conf, mconf, NULL, true, true);
+ rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
++ rcu_assign_pointer(mvif->link[0], mconf);
+
+ out:
+ mutex_unlock(&dev->mt76.mutex);
+@@ -311,7 +315,9 @@ out:
+ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+ {
++ struct ieee80211_bss_conf *conf;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
+ struct mt7996_sta *msta = &mvif->sta;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+@@ -319,24 +325,22 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+
+ cancel_delayed_work_sync(&phy->scan_work);
+
+- mt7996_mcu_add_sta(dev, vif, NULL, false, false);
+- mt7996_mcu_add_bss_info(phy, vif, false);
++ mutex_lock(&dev->mt76.mutex);
++
++ conf = link_conf_dereference_protected(vif, 0);
++ mconf = mconf_dereference_protected(mvif, 0);
++ mt7996_mcu_add_sta(dev, conf, mconf, NULL, false, false);
++ mt7996_mcu_add_bss_info(phy, conf, mconf, false);
+
+ if (vif == phy->monitor_vif)
+ phy->monitor_vif = NULL;
+
+- mt7996_mcu_add_dev_info(phy, vif, false);
++ mt7996_mcu_add_dev_info(phy, conf, mconf, false);
+
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+
+- mutex_lock(&dev->mt76.mutex);
+-
+- if (test_bit(MT76_SCANNING, &phy->mt76->state))
+- mt7996_scan_complete(phy, true);
+-
+- dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
+- phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
+- mutex_unlock(&dev->mt76.mutex);
++ dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
++ phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ if (!list_empty(&msta->wcid.poll_list))
+@@ -344,6 +348,9 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+ mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
++ rcu_assign_pointer(mvif->link[0], NULL);
++
++ mutex_unlock(&dev->mt76.mutex);
+ }
+
+ static void ___mt7996_set_channel(struct mt7996_phy *phy,
+@@ -441,6 +448,8 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
+ &mvif->sta;
+ struct mt76_wcid *wcid = &msta->wcid;
++ struct mt7996_bss_conf *mconf;
++ struct ieee80211_bss_conf *conf;
+ u8 *wcid_keyidx = &wcid->hw_key_idx;
+ int idx = key->keyidx;
+ int err = 0;
+@@ -482,9 +491,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+
+ mutex_lock(&dev->mt76.mutex);
+
+- if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) {
+- mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+- mt7996_mcu_add_bss_info(phy, vif, true);
++ conf = link_conf_dereference_protected(vif, 0);
++ mconf = mconf_dereference_protected(mvif, 0);
++ if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
++ mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
++ mt7996_mcu_add_bss_info(phy, conf, mconf, true);
+ }
+
+ if (cmd == SET_KEY) {
+@@ -498,9 +509,9 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ mt76_wcid_key_setup(&dev->mt76, wcid, key);
+
+ if (key->keyidx == 6 || key->keyidx == 7)
+- err = mt7996_mcu_bcn_prot_enable(dev, vif, key);
++ err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, key);
+ else
+- err = mt7996_mcu_add_key(&dev->mt76, vif, key,
++ err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+ &msta->wcid, cmd);
+ out:
+@@ -547,7 +558,9 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ unsigned int link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+ {
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
+ const u8 mq_to_aci[] = {
+ [IEEE80211_AC_VO] = 3,
+ [IEEE80211_AC_VI] = 2,
+@@ -555,10 +568,15 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ [IEEE80211_AC_BK] = 1,
+ };
+
++ mutex_lock(&dev->mt76.mutex);
++ mconf = mconf_dereference_protected(mvif, 0);
++
+ /* firmware uses access class index */
+- mvif->queue_params[mq_to_aci[queue]] = *params;
++ mconf->queue_params[mq_to_aci[queue]] = *params;
+ /* no need to update right away, we'll get BSS_CHANGED_QOS */
+
++ mutex_unlock(&dev->mt76.mutex);
++
+ return 0;
+ }
+
+@@ -619,22 +637,20 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ }
+
+ static void
+-mt7996_update_bss_color(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif,
++mt7996_update_bss_color(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct mt7996_bss_conf *mconf,
+ struct cfg80211_he_bss_color *bss_color)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP: {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-
+- if (mvif->mt76.omac_idx > HW_BSSID_MAX)
++ if (mconf->mt76.omac_idx > HW_BSSID_MAX)
+ return;
+ fallthrough;
+ }
+ case NL80211_IFTYPE_STATION:
+- mt7996_mcu_update_bss_color(dev, vif, bss_color);
++ mt7996_mcu_update_bss_color(dev, mconf, bss_color);
+ break;
+ default:
+ break;
+@@ -642,16 +658,15 @@ mt7996_update_bss_color(struct ieee80211_hw *hw,
+ }
+
+ static u8
+-mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- bool beacon, bool mcast)
++mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, bool beacon, bool mcast)
+ {
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt76_phy *mphy = hw->priv;
++ struct mt76_phy *mphy = mconf->phy->mt76;
+ u16 rate;
+ u8 i, idx;
+
+- rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
++ rate = mt76_connac2_mac_tx_rate_val(mphy, conf->vif, beacon, mcast);
+
+ if (beacon) {
+ struct mt7996_phy *phy = mphy->priv;
+@@ -672,23 +687,22 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ if ((mt76_rates[i].hw_value & GENMASK(7, 0)) == idx)
+ return MT7996_BASIC_RATES_TBL + 2 * i;
+
+- return mvif->basic_rates_idx;
++ return mconf->mt76.basic_rates_idx;
+ }
+
+ static void
+-mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- struct ieee80211_bss_conf *info)
++mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- u8 band = mvif->mt76.band_idx;
++ u8 band = mconf->mt76.band_idx;
+ u32 *mu;
+
+- mu = (u32 *)info->mu_group.membership;
++ mu = (u32 *)conf->mu_group.membership;
+ mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD0(band), mu[0]);
+ mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD1(band), mu[1]);
+
+- mu = (u32 *)info->mu_group.position;
++ mu = (u32 *)conf->mu_group.position;
+ mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS0(band), mu[0]);
+ mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS1(band), mu[1]);
+ mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS2(band), mu[2]);
+@@ -700,20 +714,22 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_bss_conf *info,
+ u64 changed)
+ {
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+
++ mconf = mconf_dereference_protected(mvif, 0);
+ /* station mode uses BSSID to map the wlan entry to a peer,
+ * and then peer references bss_info_rfch to set bandwidth cap.
+ */
+ if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
+ (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
+ (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
+- mt7996_mcu_add_bss_info(phy, vif, true);
+- mt7996_mcu_add_sta(dev, vif, NULL, true,
++ mt7996_mcu_add_bss_info(phy, info, mconf, true);
++ mt7996_mcu_add_sta(dev, info, mconf, NULL, true,
+ !!(changed & BSS_CHANGED_BSSID));
+ }
+
+@@ -725,42 +741,42 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+
+ if (slottime != phy->slottime) {
+ phy->slottime = slottime;
+- mt7996_mcu_set_timing(phy, vif);
++ mt7996_mcu_set_timing(phy, mconf);
+ }
+ }
+
+ if (changed & BSS_CHANGED_MCAST_RATE)
+- mvif->mcast_rates_idx =
+- mt7996_get_rates_table(hw, vif, false, true);
++ mconf->mt76.mcast_rates_idx =
++ mt7996_get_rates_table(hw, info, mconf, false, true);
+
+ if (changed & BSS_CHANGED_BASIC_RATES)
+- mvif->basic_rates_idx =
+- mt7996_get_rates_table(hw, vif, false, false);
++ mconf->mt76.basic_rates_idx =
++ mt7996_get_rates_table(hw, info, mconf, false, false);
+
+ /* ensure that enable txcmd_mode after bss_info */
+ if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
+- mt7996_mcu_set_tx(dev, vif);
++ mt7996_mcu_set_tx(dev, mconf);
+
+ if (changed & BSS_CHANGED_HE_OBSS_PD)
+- mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd);
++ mt7996_mcu_add_obss_spr(phy, mconf, &info->he_obss_pd);
+
+ if (changed & BSS_CHANGED_HE_BSS_COLOR)
+- mt7996_update_bss_color(hw, vif, &info->he_bss_color);
++ mt7996_update_bss_color(hw, vif, mconf, &info->he_bss_color);
+
+ if (changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED)) {
+- mvif->beacon_rates_idx =
+- mt7996_get_rates_table(hw, vif, true, false);
++ mconf->mt76.beacon_rates_idx =
++ mt7996_get_rates_table(hw, info, mconf, true, false);
+
+- mt7996_mcu_add_beacon(hw, vif, info->enable_beacon);
++ mt7996_mcu_add_beacon(hw, info, mconf, info->enable_beacon);
+ }
+
+ if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+ BSS_CHANGED_FILS_DISCOVERY))
+- mt7996_mcu_beacon_inband_discov(dev, vif, changed);
++ mt7996_mcu_beacon_inband_discov(dev, info, mconf, changed);
+
+ if (changed & BSS_CHANGED_MU_GROUPS)
+- mt7996_update_mu_group(hw, vif, info);
++ mt7996_update_mu_group(hw, info, mconf);
+
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -771,9 +787,14 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ struct cfg80211_chan_def *chandef)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
++ struct ieee80211_bss_conf *conf;
+
+ mutex_lock(&dev->mt76.mutex);
+- mt7996_mcu_add_beacon(hw, vif, true);
++ mconf = mconf_dereference_protected(mvif, 0);
++ conf = link_conf_dereference_protected(vif, 0);
++ mt7996_mcu_add_beacon(hw, conf, mconf, true);
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -783,7 +804,8 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- u8 band_idx = mvif->phy->mt76->band_idx;
++ struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
++ u8 band_idx = mconf->phy->mt76->band_idx;
+ int idx;
+
+ #ifdef CONFIG_MTK_VENDOR
+@@ -803,7 +825,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+
+ #ifdef CONFIG_MTK_VENDOR
+- mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
++ mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
+ #endif
+
+ #ifdef CONFIG_MTK_VENDOR
+@@ -830,15 +852,20 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_bss_conf *mconf;
++ struct ieee80211_bss_conf *conf;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ mt7996_mac_wtbl_update(dev, msta->wcid.idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+- mt7996_mcu_add_sta(dev, vif, sta, true, true);
+- mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++ conf = link_conf_dereference_protected(vif, 0);
++ mconf = mconf_dereference_protected(mvif, 0);
++ mt7996_mcu_add_sta(dev, conf, mconf, sta, true, true);
++ mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, false);
+
+ ewma_avg_signal_init(&msta->avg_ack_signal);
+
+@@ -849,10 +876,15 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_bss_conf *mconf;
++ struct ieee80211_bss_conf *conf;
+ int i;
+
+- mt7996_mcu_add_sta(dev, vif, sta, false, false);
++ conf = link_conf_dereference_protected(vif, 0);
++ mconf = mconf_dereference_protected(mvif, 0);
++ mt7996_mcu_add_sta(dev, conf, mconf, sta, false, false);
+
+ mt7996_mac_wtbl_update(dev, msta->wcid.idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+@@ -984,7 +1016,7 @@ mt7996_get_stats(struct ieee80211_hw *hw,
+ return 0;
+ }
+
+-u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
++u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_bss_conf *mconf)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+@@ -996,8 +1028,8 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+- n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+- : mvif->mt76.omac_idx;
++ n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
++ : mconf->mt76.omac_idx;
+ /* TSF software read */
+ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE,
+ MT_LPON_TCR_SW_READ);
+@@ -1012,10 +1044,12 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_bss_conf *mconf;
+ u64 ret;
+
+ mutex_lock(&dev->mt76.mutex);
+- ret = __mt7996_get_tsf(hw, mvif);
++ mconf = mconf_dereference_protected(mvif, 0);
++ ret = __mt7996_get_tsf(hw, mconf);
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+@@ -1028,6 +1062,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_bss_conf *mconf;
+ union {
+ u64 t64;
+ u32 t32[2];
+@@ -1036,8 +1071,9 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mutex_lock(&dev->mt76.mutex);
+
+- n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+- : mvif->mt76.omac_idx;
++ mconf = mconf_dereference_protected(mvif, 0);
++ n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
++ : mconf->mt76.omac_idx;
+ mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+ mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
+ /* TSF software overwrite */
+@@ -1054,6 +1090,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_bss_conf *mconf;
+ union {
+ u64 t64;
+ u32 t32[2];
+@@ -1062,8 +1099,9 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mutex_lock(&dev->mt76.mutex);
+
+- n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+- : mvif->mt76.omac_idx;
++ mconf = mconf_dereference_protected(mvif, 0);
++ n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
++ : mconf->mt76.omac_idx;
+ mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+ mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
+ /* TSF software adjust*/
+@@ -1179,7 +1217,7 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_dev *dev = msta->vif->phy->dev;
++ struct mt7996_dev *dev = msta->vif->dev;
+ u32 *changed = data;
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+@@ -1215,9 +1253,13 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_bss_conf *mconf;
+ u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+
+- mvif->bitrate_mask = *mask;
++ mutex_lock(&dev->mt76.mutex);
++ mconf = mconf_dereference_protected(mvif, 0);
++ mconf->bitrate_mask = *mask;
++ mutex_unlock(&dev->mt76.mutex);
+
+ /* if multiple rates across different preambles are given we can
+ * reconfigure this info with all peers using sta_rec command with
+@@ -1239,14 +1281,20 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ bool enabled)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_bss_conf *mconf;
++
++ mutex_lock(&dev->mt76.mutex);
++ mconf = mconf_dereference_protected(mvif, 0);
+
+ if (enabled)
+ set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
+ else
+ clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
+
+- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
++ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++ mutex_unlock(&dev->mt76.mutex);
+ }
+
+ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+@@ -1255,14 +1303,20 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+ bool enabled)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_bss_conf *mconf;
++
++ mutex_lock(&dev->mt76.mutex);
++ mconf = mconf_dereference_protected(mvif, 0);
+
+ if (enabled)
+ set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+ else
+ clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+
+- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
++ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++ mutex_unlock(&dev->mt76.mutex);
+ }
+
+ static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = {
+@@ -1395,7 +1449,7 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+ struct mt76_ethtool_worker_info *wi = wi_data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+
+- if (msta->vif->mt76.idx != wi->idx)
++ if (msta->vif->deflink.mt76.idx != wi->idx)
+ return;
+
+ mt76_ethtool_worker(wi, &msta->wcid.stats, true);
+@@ -1409,15 +1463,17 @@ void mt7996_get_et_stats(struct ieee80211_hw *hw,
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
+ struct mt76_mib_stats *mib = &phy->mib;
+ struct mt76_ethtool_worker_info wi = {
+ .data = data,
+- .idx = mvif->mt76.idx,
+ };
+ /* See mt7996_ampdu_stat_read_phy, etc */
+ int i, ei = 0;
+
+ mutex_lock(&dev->mt76.mutex);
++ mconf = mconf_dereference_protected(mvif, 0);
++ wi.idx = mconf->mt76.idx,
+
+ mt7996_mac_update_stats(phy);
+
+@@ -1623,6 +1679,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ struct net_device_path *path)
+ {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf = &mvif->deflink;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+@@ -1652,7 +1709,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ path->type = DEV_PATH_MTK_WDMA;
+ path->dev = ctx->dev;
+ path->mtk_wdma.wdma_idx = wed->wdma_idx;
+- path->mtk_wdma.bss = mvif->mt76.idx;
++ path->mtk_wdma.bss = mconf->mt76.idx;
+ path->mtk_wdma.queue = 0;
+ path->mtk_wdma.wcid = msta->wcid.idx;
+
+@@ -1784,6 +1841,7 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ struct mt7996_phy *phy = ctx->phy;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
+
+ wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
+ vif->addr, vif->type, link_conf->link_id,
+@@ -1791,7 +1849,8 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mutex_lock(&phy->dev->mt76.mutex);
+
+- mvif->chanctx = ctx;
++ mconf = mconf_dereference_protected(mvif, 0);
++ mconf->chanctx = ctx;
+ ctx->nbss_assigned++;
+
+ mutex_unlock(&phy->dev->mt76.mutex);
+@@ -1807,6 +1866,7 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ struct mt7996_phy *phy = ctx->phy;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
+
+ wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
+ vif->addr, vif->type, link_conf->link_id,
+@@ -1818,7 +1878,8 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ mt7996_scan_complete(phy, true);
+
+- mvif->chanctx = NULL;
++ mconf = mconf_dereference_protected(mvif, 0);
++ mconf->chanctx = NULL;
+ ctx->nbss_assigned--;
+
+ mutex_unlock(&phy->dev->mt76.mutex);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 85077108e..11700f90f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -117,12 +117,12 @@ mt7996_mcu_get_sta_nss(u16 mcs_map)
+ }
+
+ static void
+-mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
+- u16 mcs_map)
++mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
++ struct mt7996_bss_conf *mconf,
++ __le16 *he_mcs, u16 mcs_map)
+ {
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band;
+- const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs;
++ enum nl80211_band band = mconf->phy->mt76->chandef.chan->band;
++ const u16 *mask = mconf->bitrate_mask.control[band].he_mcs;
+ int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++) {
+@@ -922,8 +922,7 @@ mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
+ }
+
+ static void
+-mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+- struct mt7996_phy *phy)
++mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
+ {
+ static const u8 rlm_ch_band[] = {
+ [NL80211_BAND_2GHZ] = 1,
+@@ -953,8 +952,7 @@ mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+
+ static void
+-mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+- struct mt7996_phy *phy)
++mt7996_mcu_bss_ra_tlv(struct sk_buff *skb)
+ {
+ struct bss_ra_tlv *ra;
+ struct tlv *tlv;
+@@ -966,7 +964,7 @@ mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+
+ static void
+-mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ struct mt7996_phy *phy)
+ {
+ #define DEFAULT_HE_PE_DURATION 4
+@@ -975,16 +973,16 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ struct bss_info_uni_he *he;
+ struct tlv *tlv;
+
+- cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
++ cap = mt76_connac_get_he_phy_cap(phy->mt76, conf->vif);
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
+
+ he = (struct bss_info_uni_he *)tlv;
+- he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
++ he->he_pe_duration = conf->htc_trig_based_pkt_ext;
+ if (!he->he_pe_duration)
+ he->he_pe_duration = DEFAULT_HE_PE_DURATION;
+
+- he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th);
++ he->he_rts_thres = cpu_to_le16(conf->frame_time_rts_th);
+ if (!he->he_rts_thres)
+ he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES);
+
+@@ -994,13 +992,13 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+
+ static void
+-mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ struct mt7996_phy *phy, int enable)
+ {
+ struct bss_info_uni_mbssid *mbssid;
+ struct tlv *tlv;
+
+- if (!vif->bss_conf.bssid_indicator && enable)
++ if (!conf->bssid_indicator && enable)
+ return;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
+@@ -1008,23 +1006,21 @@ mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ mbssid = (struct bss_info_uni_mbssid *)tlv;
+
+ if (enable) {
+- mbssid->max_indicator = vif->bss_conf.bssid_indicator;
+- mbssid->mbss_idx = vif->bss_conf.bssid_index;
++ mbssid->max_indicator = conf->bssid_indicator;
++ mbssid->mbss_idx = conf->bssid_index;
+ mbssid->tx_bss_omac_idx = 0;
+ }
+ }
+
+ static void
+-mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7996_bss_conf *mconf,
+ struct mt7996_phy *phy)
+ {
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct bss_rate_tlv *bmc;
+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ enum nl80211_band band = chandef->chan->band;
+ struct tlv *tlv;
+- u8 idx = mvif->mcast_rates_idx ?
+- mvif->mcast_rates_idx : mvif->basic_rates_idx;
++ u8 idx = mconf->mt76.mcast_rates_idx ?: mconf->mt76.basic_rates_idx;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc));
+
+@@ -1048,9 +1044,9 @@ mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en)
+ }
+
+ static void
+-mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
++mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++ struct mt7996_bss_conf *mconf)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct bss_mld_tlv *mld;
+ struct tlv *tlv;
+
+@@ -1058,33 +1054,30 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+
+ mld = (struct bss_mld_tlv *)tlv;
+ mld->group_mld_id = 0xff;
+- mld->own_mld_id = mvif->mt76.idx;
++ mld->own_mld_id = mconf->mt76.idx;
+ mld->remap_idx = 0xff;
+ }
+
+ static void
+-mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
++mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct mt7996_bss_conf *mconf)
+ {
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct bss_sec_tlv *sec;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec));
+
+ sec = (struct bss_sec_tlv *)tlv;
+- sec->cipher = mvif->cipher;
++ sec->cipher = mconf->mt76.cipher;
+ }
+
+ static int
+-mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+- bool bssid, bool enable)
++mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, bool bssid, bool enable)
+ {
+ #define UNI_MUAR_ENTRY 2
+ struct mt7996_dev *dev = phy->dev;
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START;
+- const u8 *addr = vif->addr;
+-
++ u32 idx = mconf->mt76.omac_idx - REPEATER_BSSID_START;
++ const u8 *addr = bssid ? conf->bssid : conf->vif->addr;
+ struct {
+ struct {
+ u8 band;
+@@ -1109,9 +1102,6 @@ mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ .entry_add = true,
+ };
+
+- if (bssid)
+- addr = vif->bss_conf.bssid;
+-
+ if (enable)
+ memcpy(req.addr, addr, ETH_ALEN);
+
+@@ -1120,10 +1110,8 @@ mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ }
+
+ static void
+-mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
++mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_phy *phy = mvif->phy;
+ struct bss_ifs_time_tlv *ifs_time;
+ struct tlv *tlv;
+ bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ;
+@@ -1149,12 +1137,13 @@ mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+
+ static int
+ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+- struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta,
+ struct mt76_phy *phy, u16 wlan_idx,
+ bool enable)
+ {
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
++ struct ieee80211_vif *vif = conf->vif;
+ struct cfg80211_chan_def *chandef = &phy->chandef;
+ struct mt76_connac_bss_basic_tlv *bss;
+ u32 type = CONNECTION_INFRA_AP;
+@@ -1171,8 +1160,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ if (enable) {
+ rcu_read_lock();
+ if (!sta)
+- sta = ieee80211_find_sta(vif,
+- vif->bss_conf.bssid);
++ sta = ieee80211_find_sta(vif, conf->bssid);
+ /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
+ if (sta) {
+ struct mt76_wcid *wcid;
+@@ -1195,18 +1183,17 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss));
+
+ bss = (struct mt76_connac_bss_basic_tlv *)tlv;
+- bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+- bss->dtim_period = vif->bss_conf.dtim_period;
+ bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
+ bss->sta_idx = cpu_to_le16(sta_wlan_idx);
+ bss->conn_type = cpu_to_le32(type);
+- bss->omac_idx = mvif->omac_idx;
+- bss->band_idx = mvif->band_idx;
+- bss->wmm_idx = mvif->wmm_idx;
++ bss->omac_idx = mconf->mt76.omac_idx;
++ bss->band_idx = mconf->mt76.band_idx;
++ bss->wmm_idx = mconf->mt76.wmm_idx;
+ bss->conn_state = !enable;
+ bss->active = enable;
+
+- idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx;
++ idx = mconf->mt76.omac_idx > EXT_BSSID_START ? HW_BSSID_0 :
++ mconf->mt76.omac_idx;
+ bss->hw_bss_idx = idx;
+
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+@@ -1219,9 +1206,9 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ return 0;
+ }
+
+- memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN);
+- bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+- bss->dtim_period = vif->bss_conf.dtim_period;
++ memcpy(bss->bssid, conf->bssid, ETH_ALEN);
++ bss->bcn_interval = cpu_to_le16(conf->beacon_int);
++ bss->dtim_period = conf->dtim_period;
+ bss->phymode = mt76_connac_get_phy_mode(phy, vif,
+ chandef->chan->band, NULL);
+ bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, vif,
+@@ -1248,63 +1235,64 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
+ }
+
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+- struct ieee80211_vif *vif, int enable)
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, int enable)
+ {
++ struct ieee80211_vif *vif = conf->vif;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = phy->dev;
+ struct sk_buff *skb;
+
+- if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) {
+- mt7996_mcu_muar_config(phy, vif, false, enable);
+- mt7996_mcu_muar_config(phy, vif, true, enable);
++ if (mconf->mt76.omac_idx >= REPEATER_BSSID_START) {
++ mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
++ mt7996_mcu_muar_config(phy, conf, mconf, true, enable);
+ }
+
+- skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ MT7996_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ /* bss_basic must be first */
+- mt7996_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76,
++ mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
+ mvif->sta.wcid.idx, enable);
+- mt7996_mcu_bss_sec_tlv(skb, vif);
++ mt7996_mcu_bss_sec_tlv(skb, mconf);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+ goto out;
+
+ if (enable) {
+- mt7996_mcu_bss_rfch_tlv(skb, vif, phy);
+- mt7996_mcu_bss_bmc_tlv(skb, vif, phy);
+- mt7996_mcu_bss_ra_tlv(skb, vif, phy);
++ mt7996_mcu_bss_rfch_tlv(skb, phy);
++ mt7996_mcu_bss_bmc_tlv(skb, mconf, phy);
++ mt7996_mcu_bss_ra_tlv(skb);
+ mt7996_mcu_bss_txcmd_tlv(skb, true);
+- mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
++ mt7996_mcu_bss_ifs_timing_tlv(skb, phy);
+
+- if (vif->bss_conf.he_support)
+- mt7996_mcu_bss_he_tlv(skb, vif, phy);
++ if (conf->he_support)
++ mt7996_mcu_bss_he_tlv(skb, conf, phy);
+
+ /* this tag is necessary no matter if the vif is MLD */
+- mt7996_mcu_bss_mld_tlv(skb, vif);
++ mt7996_mcu_bss_mld_tlv(skb, vif, mconf);
+ }
+
+- mt7996_mcu_bss_mbssid_tlv(skb, vif, phy, enable);
++ mt7996_mcu_bss_mbssid_tlv(skb, conf, phy, enable);
+
+ out:
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+ }
+
+-int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif)
++int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = phy->dev;
+ struct sk_buff *skb;
+
+- skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ MT7996_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+- mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
++ mt7996_mcu_bss_ifs_timing_tlv(skb, phy);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+@@ -1346,12 +1334,12 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ bool enable)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+- struct mt7996_vif *mvif = msta->vif;
++ struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+
+ if (enable && !params->amsdu)
+ msta->wcid.amsdu = false;
+
+- return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, true);
++ return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
+ }
+
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+@@ -1359,13 +1347,14 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ bool enable)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+- struct mt7996_vif *mvif = msta->vif;
++ struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+
+- return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, false);
++ return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, false);
+ }
+
+ static void
+-mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta)
+ {
+ struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+@@ -1386,9 +1375,9 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ he->he_phy_cap[i] = elem->phy_cap_info[i];
+ }
+
+- if (vif->type == NL80211_IFTYPE_AP &&
++ if (conf->vif->type == NL80211_IFTYPE_AP &&
+ (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
+- u8p_replace_bits(&he->he_phy_cap[1], vif->bss_conf.he_ldpc,
++ u8p_replace_bits(&he->he_phy_cap[1], conf->he_ldpc,
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
+
+ mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
+@@ -1396,16 +1385,16 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ case IEEE80211_STA_RX_BW_160:
+ if (elem->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+- mt7996_mcu_set_sta_he_mcs(sta,
++ mt7996_mcu_set_sta_he_mcs(sta, mconf,
+ &he->max_nss_mcs[CMD_HE_MCS_BW8080],
+ le16_to_cpu(mcs_map.rx_mcs_80p80));
+
+- mt7996_mcu_set_sta_he_mcs(sta,
++ mt7996_mcu_set_sta_he_mcs(sta, mconf,
+ &he->max_nss_mcs[CMD_HE_MCS_BW160],
+ le16_to_cpu(mcs_map.rx_mcs_160));
+ fallthrough;
+ default:
+- mt7996_mcu_set_sta_he_mcs(sta,
++ mt7996_mcu_set_sta_he_mcs(sta, mconf,
+ &he->max_nss_mcs[CMD_HE_MCS_BW80],
+ le16_to_cpu(mcs_map.rx_mcs_80));
+ break;
+@@ -1496,7 +1485,7 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ struct tlv *tlv;
+ #ifdef CONFIG_MTK_VENDOR
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->phy;
++ struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->deflink.phy;
+ #endif
+
+ /* For 6G band, this tlv is necessary to let hw work normally */
+@@ -1553,25 +1542,26 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+
+ static void
+ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
++ struct ieee80211_sta *sta)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_phy *phy = mvif->phy;
++ struct mt7996_phy *phy = mconf->phy;
+ struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ struct sta_rec_muru *muru;
+ struct tlv *tlv;
+
+- if (vif->type != NL80211_IFTYPE_STATION &&
+- vif->type != NL80211_IFTYPE_AP)
++ if (conf->vif->type != NL80211_IFTYPE_STATION &&
++ conf->vif->type != NL80211_IFTYPE_AP)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
+
+ muru = (struct sta_rec_muru *)tlv;
+- muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
+- vif->bss_conf.he_mu_beamformer ||
+- vif->bss_conf.vht_mu_beamformer ||
+- vif->bss_conf.vht_mu_beamformee) &&
++ muru->cfg.mimo_dl_en = (conf->eht_mu_beamformer ||
++ conf->he_mu_beamformer ||
++ conf->vht_mu_beamformer ||
++ conf->vht_mu_beamformee) &&
+ !!(phy->muru_onoff & MUMIMO_DL);
+ muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
+ muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
+@@ -1612,13 +1602,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ }
+
+ static inline bool
+-mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta, bool bfee)
+ {
+ int sts = hweight16(phy->mt76->chainmask);
+
+- if (vif->type != NL80211_IFTYPE_STATION &&
+- vif->type != NL80211_IFTYPE_AP)
++ if (conf->vif->type != NL80211_IFTYPE_STATION &&
++ conf->vif->type != NL80211_IFTYPE_AP)
+ return false;
+
+ if (!bfee && sts < 2)
+@@ -1629,10 +1620,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
+
+ if (bfee)
+- return vif->bss_conf.eht_su_beamformee &&
++ return conf->eht_su_beamformee &&
+ EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
+ else
+- return vif->bss_conf.eht_su_beamformer &&
++ return conf->eht_su_beamformer &&
+ EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
+ }
+
+@@ -1640,10 +1631,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
+
+ if (bfee)
+- return vif->bss_conf.he_su_beamformee &&
++ return conf->he_su_beamformee &&
+ HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]);
+ else
+- return vif->bss_conf.he_su_beamformer &&
++ return conf->he_su_beamformer &&
+ HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
+ }
+
+@@ -1651,10 +1642,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ u32 cap = sta->deflink.vht_cap.cap;
+
+ if (bfee)
+- return vif->bss_conf.vht_su_beamformee &&
++ return conf->vht_su_beamformee &&
+ (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
+ else
+- return vif->bss_conf.vht_su_beamformer &&
++ return conf->vht_su_beamformer &&
+ (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+ }
+
+@@ -1850,10 +1841,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+
+ static void
+ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++ struct ieee80211_bss_conf *conf, struct mt7996_bss_conf *mconf,
++ struct ieee80211_sta *sta)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_phy *phy = mvif->phy;
++ struct mt7996_phy *phy = mconf->phy;
+ int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+ struct sta_rec_bf *bf;
+ struct tlv *tlv;
+@@ -1868,7 +1859,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+- ebf = mt7996_is_ebf_supported(phy, vif, sta, false);
++ ebf = mt7996_is_ebf_supported(phy, conf, mconf, sta, false);
+ if (!ebf && !dev->ibf)
+ return;
+
+@@ -1880,9 +1871,9 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ * ht: iBF only, since mac80211 lacks of eBF support
+ */
+ if (sta->deflink.eht_cap.has_eht && ebf)
+- mt7996_mcu_sta_bfer_eht(sta, vif, phy, bf);
++ mt7996_mcu_sta_bfer_eht(sta, conf->vif, phy, bf);
+ else if (sta->deflink.he_cap.has_he && ebf)
+- mt7996_mcu_sta_bfer_he(sta, vif, phy, bf);
++ mt7996_mcu_sta_bfer_he(sta, conf->vif, phy, bf);
+ else if (sta->deflink.vht_cap.vht_supported)
+ mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf);
+ else if (sta->deflink.ht_cap.ht_supported)
+@@ -1921,10 +1912,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+
+ static void
+ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_phy *phy = mvif->phy;
++ struct mt7996_phy *phy = mconf->phy;
+ int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+ struct sta_rec_bfee *bfee;
+ struct tlv *tlv;
+@@ -1933,7 +1924,7 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+- if (!mt7996_is_ebf_supported(phy, vif, sta, true))
++ if (!mt7996_is_ebf_supported(phy, conf, mconf, sta, true))
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
+@@ -2055,17 +2046,17 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ MCU_WM_UNI_CMD(RA), true);
+ }
+
+-int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta, void *data, u32 field)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct sta_phy_uni *phy = data;
+ struct sta_rec_ra_fixed_uni *ra;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ &msta->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+@@ -2097,12 +2088,13 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ }
+
+ static int
+-mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
+- struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
++ struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
++ struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
+ enum nl80211_band band = chandef->chan->band;
+ struct sta_phy_uni phy = {};
+ int ret, nrates = 0;
+@@ -2144,7 +2136,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+
+ /* fixed single rate */
+ if (nrates == 1) {
+- ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
++ ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+ RATE_PARAM_FIXED_MCS);
+ if (ret)
+ return ret;
+@@ -2166,7 +2158,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ else
+ mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
+
+- ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
++ ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+ RATE_PARAM_FIXED_GI);
+ if (ret)
+ return ret;
+@@ -2174,7 +2166,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+
+ /* fixed HE_LTF */
+ if (mask->control[band].he_ltf != GENMASK(7, 0)) {
+- ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
++ ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+ RATE_PARAM_FIXED_HE_LTF);
+ if (ret)
+ return ret;
+@@ -2185,13 +2177,14 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+
+ static void
+ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
++ struct ieee80211_sta *sta)
+ {
+ #define INIT_RCPI 180
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt76_phy *mphy = mvif->phy->mt76;
++ struct mt76_phy *mphy = mconf->phy->mt76;
+ struct cfg80211_chan_def *chandef = &mphy->chandef;
+- struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
++ struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
+ enum nl80211_band band = chandef->chan->band;
+ struct sta_rec_ra_uni *ra;
+ struct tlv *tlv;
+@@ -2203,7 +2196,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+
+ ra->valid = true;
+ ra->auto_rate = true;
+- ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta);
++ ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, sta);
+ ra->channel = chandef->chan->hw_value;
+ ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ?
+ CMD_CBW_320MHZ : sta->deflink.bandwidth;
+@@ -2242,7 +2235,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ cap |= STA_CAP_TX_STBC;
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+ cap |= STA_CAP_RX_STBC;
+- if (vif->bss_conf.ht_ldpc &&
++ if (conf->ht_ldpc &&
+ (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ cap |= STA_CAP_LDPC;
+
+@@ -2268,7 +2261,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ cap |= STA_CAP_VHT_TX_STBC;
+ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
+ cap |= STA_CAP_VHT_RX_STBC;
+- if (vif->bss_conf.vht_ldpc &&
++ if (conf->vht_ldpc &&
+ (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
+ cap |= STA_CAP_VHT_LDPC;
+
+@@ -2289,15 +2282,16 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi));
+ }
+
+-int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta, bool changed)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct sk_buff *skb;
+ int ret;
+
+- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ &msta->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+@@ -2308,26 +2302,27 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ * update sta_rec_he here.
+ */
+ if (changed)
+- mt7996_mcu_sta_he_tlv(skb, vif, sta);
++ mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
+
+ /* sta_rec_ra accommodates BW, NSS and only MCS range format
+ * i.e 0-{7,8,9} for VHT.
+ */
+- mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta);
++ mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, sta);
+
+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ if (ret)
+ return ret;
+
+- return mt7996_mcu_add_rate_ctrl_fixed(dev, vif, sta);
++ return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, sta);
+ }
+
+ static int
+-mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
++mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
+ {
++ struct mt7996_phy *phy = mconf->phy;
+ struct mt7996_vow_sta_ctrl *vow = &msta->vow;
+- u8 omac_idx = msta->vif->mt76.omac_idx;
++ u8 omac_idx = mconf->mt76.omac_idx;
+ int ret;
+
+ /* Assignment of STA BSS group index aligns FW.
+@@ -2344,20 +2339,22 @@ mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
+ vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
+ vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
+
+- ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
+ if (ret)
+ return ret;
+
+- ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_PAUSE);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_PAUSE);
+ if (ret)
+ return ret;
+
+- return mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_ALL);
++ return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_ALL);
+ }
+
+-int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta, bool enable, bool newly)
++int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
++ bool enable, bool newly)
+ {
++ struct ieee80211_vif *vif = conf->vif;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta;
+ struct sk_buff *skb;
+@@ -2365,7 +2362,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+
+ msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+
+- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ &msta->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+@@ -2387,7 +2384,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ /* starec hdrt mode */
+ mt7996_mcu_sta_hdrt_tlv(dev, skb);
+ /* starec bfer */
+- mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta);
++ mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, sta);
+ /* starec ht */
+ mt7996_mcu_sta_ht_tlv(skb, sta);
+ /* starec vht */
+@@ -2397,18 +2394,18 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ /* starec amsdu */
+ mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
+ /* starec he */
+- mt7996_mcu_sta_he_tlv(skb, vif, sta);
++ mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
+ /* starec he 6g*/
+ mt7996_mcu_sta_he_6g_tlv(skb, sta);
+ /* starec eht */
+ mt7996_mcu_sta_eht_tlv(skb, sta);
+ /* starec muru */
+- mt7996_mcu_sta_muru_tlv(dev, skb, vif, sta);
++ mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, sta);
+ /* starec bfee */
+- mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
++ mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, sta);
+ }
+
+- ret = mt7996_mcu_sta_init_vow(mvif->phy, msta);
++ ret = mt7996_mcu_sta_init_vow(mconf, msta);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+@@ -2463,16 +2460,15 @@ mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
+ return 0;
+ }
+
+-int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+ struct ieee80211_key_conf *key, int mcu_cmd,
+ struct mt76_wcid *wcid, enum set_key_cmd cmd)
+ {
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct sk_buff *skb;
+ int ret;
+
+- skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
+- MT7996_STA_UPDATE_MAX_SIZE);
++ skb = __mt76_connac_mcu_alloc_sta_req(dev, (struct mt76_vif *)mconf,
++ wcid, MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+@@ -2483,17 +2479,18 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
+ }
+
+-static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+- u8 *pn)
++static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, u8 *pn)
+ {
+ #define TSC_TYPE_BIGTK_PN 2
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
+ struct sta_rec_pn_info *pn_info;
+ struct sk_buff *skb, *rskb;
+ struct tlv *tlv;
+ int ret;
+
+- skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &mvif->sta.wcid);
++ skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mvif->sta.wcid);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+@@ -2517,10 +2514,11 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ return 0;
+ }
+
+-int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_key_conf *key)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+@@ -2529,7 +2527,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ sizeof(struct mt7996_mcu_bcn_prot_tlv);
+ int ret;
+
+- skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
++ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, len);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+@@ -2537,7 +2535,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
+
+ bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
+
+- ret = mt7996_mcu_get_pn(dev, vif, pn);
++ ret = mt7996_mcu_get_pn(dev, conf, mconf, pn);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+@@ -2570,10 +2568,10 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+ }
+ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+- struct ieee80211_vif *vif, bool enable)
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, bool enable)
+ {
+ struct mt7996_dev *dev = phy->dev;
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct {
+ struct req_hdr {
+ u8 omac_idx;
+@@ -2589,8 +2587,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ } __packed tlv;
+ } data = {
+ .hdr = {
+- .omac_idx = mvif->mt76.omac_idx,
+- .band_idx = mvif->mt76.band_idx,
++ .omac_idx = mconf->mt76.omac_idx,
++ .band_idx = mconf->mt76.band_idx,
+ },
+ .tlv = {
+ .tag = cpu_to_le16(DEV_INFO_ACTIVE),
+@@ -2599,16 +2597,16 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ },
+ };
+
+- if (mvif->mt76.omac_idx >= REPEATER_BSSID_START)
+- return mt7996_mcu_muar_config(phy, vif, false, enable);
++ if (mconf->mt76.omac_idx >= REPEATER_BSSID_START)
++ return mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
+
+- memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN);
++ memcpy(data.tlv.omac_addr, conf->addr, ETH_ALEN);
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
+ &data, sizeof(data), true);
+ }
+
+ static void
+-mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
++mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
+ struct sk_buff *skb,
+ struct ieee80211_mutable_offsets *offs)
+ {
+@@ -2619,7 +2617,7 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
+ if (!offs->cntdwn_counter_offs[0])
+ return;
+
+- tag = vif->bss_conf.csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC;
++ tag = conf->csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC;
+
+ tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info));
+
+@@ -2629,14 +2627,15 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
+
+ static void
+ mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
+- struct ieee80211_vif *vif, struct bss_bcn_content_tlv *bcn,
++ struct ieee80211_bss_conf *conf,
++ struct bss_bcn_content_tlv *bcn,
+ struct ieee80211_mutable_offsets *offs)
+ {
+ struct bss_bcn_mbss_tlv *mbss;
+ const struct element *elem;
+ struct tlv *tlv;
+
+- if (!vif->bss_conf.bssid_indicator)
++ if (!conf->bssid_indicator)
+ return;
+
+ tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_MBSSID, sizeof(*mbss));
+@@ -2681,7 +2680,7 @@ mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
+ }
+
+ static void
+-mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ struct sk_buff *rskb, struct sk_buff *skb,
+ struct bss_bcn_content_tlv *bcn,
+ struct ieee80211_mutable_offsets *offs)
+@@ -2695,9 +2694,9 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ if (offs->cntdwn_counter_offs[0]) {
+ u16 offset = offs->cntdwn_counter_offs[0];
+
+- if (vif->bss_conf.csa_active)
++ if (conf->csa_active)
+ bcn->csa_ie_pos = cpu_to_le16(offset - 4);
+- if (vif->bss_conf.color_change_active)
++ if (conf->color_change_active)
+ bcn->bcc_ie_pos = cpu_to_le16(offset - 3);
+ }
+
+@@ -2709,11 +2708,11 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ }
+
+ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif, int en)
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, int en)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct ieee80211_mutable_offsets offs;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb, *rskb;
+@@ -2721,15 +2720,15 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ struct bss_bcn_content_tlv *bcn;
+ int len;
+
+- if (vif->bss_conf.nontransmitted)
++ if (conf->nontransmitted)
+ return 0;
+
+- rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++ rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ MT7996_MAX_BSS_OFFLOAD_SIZE);
+ if (IS_ERR(rskb))
+ return PTR_ERR(rskb);
+
+- skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
++ skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, 0);
+ if (!skb) {
+ dev_kfree_skb(rskb);
+ return -EINVAL;
+@@ -2752,9 +2751,9 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ if (!en)
+ goto out;
+
+- mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
+- mt7996_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs);
+- mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs);
++ mt7996_mcu_beacon_cont(dev, conf, rskb, skb, bcn, &offs);
++ mt7996_mcu_beacon_mbss(rskb, skb, conf, bcn, &offs);
++ mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
+ out:
+ dev_kfree_skb(skb);
+ return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+@@ -2762,14 +2761,15 @@ out:
+ }
+
+ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+- struct ieee80211_vif *vif, u32 changed)
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, u32 changed)
+ {
+ #define OFFLOAD_TX_MODE_SU BIT(0)
+ #define OFFLOAD_TX_MODE_MU BIT(1)
+ struct ieee80211_hw *hw = mt76_hw(dev);
++ struct ieee80211_vif *vif = conf->vif;
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
++ struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
+ enum nl80211_band band = chandef->chan->band;
+ struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+ struct bss_inband_discovery_tlv *discov;
+@@ -2779,20 +2779,20 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ u8 *buf, interval;
+ int len;
+
+- if (vif->bss_conf.nontransmitted)
++ if (conf->nontransmitted)
+ return 0;
+
+- rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++ rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ MT7996_MAX_BSS_OFFLOAD_SIZE);
+ if (IS_ERR(rskb))
+ return PTR_ERR(rskb);
+
+ if (changed & BSS_CHANGED_FILS_DISCOVERY) {
+- interval = vif->bss_conf.fils_discovery.max_interval;
++ interval = conf->fils_discovery.max_interval;
+ skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
+ } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
+- vif->bss_conf.unsol_bcast_probe_resp_interval) {
+- interval = vif->bss_conf.unsol_bcast_probe_resp_interval;
++ conf->unsol_bcast_probe_resp_interval) {
++ interval = conf->unsol_bcast_probe_resp_interval;
+ skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
+ }
+
+@@ -3456,7 +3456,7 @@ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
+ MCU_WM_UNI_CMD(RX_HDR_TRANS), true);
+ }
+
+-int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
++int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf)
+ {
+ #define MCU_EDCA_AC_PARAM 0
+ #define WMM_AIFS_SET BIT(0)
+@@ -3465,12 +3465,11 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
+ #define WMM_TXOP_SET BIT(3)
+ #define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \
+ WMM_CW_MAX_SET | WMM_TXOP_SET)
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct {
+ u8 bss_idx;
+ u8 __rsv[3];
+ } __packed hdr = {
+- .bss_idx = mvif->mt76.idx,
++ .bss_idx = mconf->mt76.idx,
+ };
+ struct sk_buff *skb;
+ int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca);
+@@ -3483,7 +3482,7 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+- struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac];
++ struct ieee80211_tx_queue_params *q = &mconf->queue_params[ac];
+ struct edca *e;
+ struct tlv *tlv;
+
+@@ -4496,12 +4495,12 @@ mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy,
+ }
+
+ static int
+-mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = phy->dev;
+- u8 omac = mvif->mt76.omac_idx;
++ u8 omac = mconf->mt76.omac_idx;
+ struct {
+ u8 band_idx;
+ u8 __rsv[3];
+@@ -4573,7 +4572,8 @@ mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy,
+ sizeof(req), true);
+ }
+
+-int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+ {
+ int ret;
+@@ -4607,7 +4607,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ return ret;
+
+ /* Set SR prohibit */
+- ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd);
++ ret = mt7996_mcu_set_obss_spr_siga(phy, mconf, he_obss_pd);
+ if (ret)
+ return ret;
+
+@@ -4615,16 +4615,16 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd);
+ }
+
+-int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
++ struct mt7996_bss_conf *mconf,
+ struct cfg80211_he_bss_color *he_bss_color)
+ {
+ int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv);
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct bss_color_tlv *bss_color;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+- skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
++ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, len);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+@@ -4643,7 +4643,7 @@ int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vi
+ #define TWT_AGRT_PROTECT BIT(2)
+
+ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+- struct mt7996_vif *mvif,
++ struct mt7996_bss_conf *mconf,
+ struct mt7996_twt_flow *flow,
+ int cmd)
+ {
+@@ -4674,12 +4674,12 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .tbl_idx = flow->table_id,
+ .cmd = cmd,
+- .own_mac_idx = mvif->mt76.omac_idx,
++ .own_mac_idx = mconf->mt76.omac_idx,
+ .flowid = flow->id,
+ .peer_id = cpu_to_le16(flow->wcid),
+ .duration = flow->duration,
+- .bss = mvif->mt76.idx,
+- .bss_idx = mvif->mt76.idx,
++ .bss = mconf->mt76.idx,
++ .bss_idx = mconf->mt76.idx,
+ .start_tsf = cpu_to_le64(flow->tsf),
+ .mantissa = flow->mantissa,
+ .exponent = flow->exp,
+@@ -4810,15 +4810,15 @@ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable
+
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta;
+ struct sk_buff *skb;
+
+- msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
++ msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mconf->vif->sta;
+
+- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ &msta->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+@@ -5462,8 +5462,9 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ &req, sizeof(req), false);
+ }
+
+-int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+- enum vow_drr_ctrl_id id)
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
++ struct mt7996_bss_conf *mconf,
++ struct mt7996_sta *msta, enum vow_drr_ctrl_id id)
+ {
+ struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
+ u32 val = 0;
+@@ -5489,9 +5490,9 @@ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
+ .band_idx = phy->mt76->band_idx,
+- .wmm_idx = msta ? msta->vif->mt76.wmm_idx : 0,
++ .wmm_idx = msta ? mconf->mt76.wmm_idx : 0,
+ .ctrl_id = cpu_to_le32(id),
+- .omac_idx = msta ? msta->vif->mt76.omac_idx : 0
++ .omac_idx = msta ? mconf->mt76.omac_idx : 0
+ };
+
+ switch (id) {
+@@ -5679,7 +5680,7 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+ u8 mode, val;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_phy *phy = mvif->phy;
++ struct mt7996_phy *phy = mvif->deflink.phy;
+
+ mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
+ val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
+@@ -5710,11 +5711,11 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct ieee80211_hw *hw = mvif->phy->mt76->hw;
++ struct ieee80211_hw *hw = mvif->deflink.phy->mt76->hw;
+ u8 val = *((u8 *)data);
+
+ vif->bss_conf.enable_beacon = val;
+
+- mt7996_mcu_add_beacon(hw, vif, val);
++ mt7996_mcu_add_beacon(hw, &vif->bss_conf, &mvif->deflink, val);
+ }
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 2b266d18b..6b03ee17f 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -324,18 +324,25 @@ struct mt7996_sta {
+ struct mt7996_vow_sta_ctrl vow;
+ };
+
+-struct mt7996_vif {
++struct mt7996_bss_conf {
+ struct mt76_vif mt76; /* must be first */
+
+- struct mt7996_sta sta;
++ struct mt7996_vif *vif;
+ struct mt7996_phy *phy;
+-
+ struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ struct cfg80211_bitrate_mask bitrate_mask;
+
+ struct mt7996_chanctx *chanctx;
+ };
+
++struct mt7996_vif {
++ struct mt7996_bss_conf deflink;
++ struct mt7996_bss_conf __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
++
++ struct mt7996_sta sta;
++ struct mt7996_dev *dev;
++};
++
+ /* crash-dump */
+ struct mt7996_crash_data {
+ guid_t guid;
+@@ -769,6 +776,13 @@ mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+ return (struct mt7996_chanctx *)&ctx->drv_priv;
+ }
+
++static inline struct mt7996_bss_conf *
++mconf_dereference_protected(struct mt7996_vif *mvif, u8 link_id)
++{
++ return rcu_dereference_protected(mvif->link[link_id],
++ lockdep_is_held(&mvif->dev->mt76.mutex));
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+@@ -779,7 +793,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ void mt7996_wfsys_reset(struct mt7996_dev *dev);
+ void mt7996_rro_hw_init(struct mt7996_dev *dev);
+ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
+-u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
++u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_bss_conf *mconf);
+ int mt7996_register_device(struct mt7996_dev *dev);
+ void mt7996_unregister_device(struct mt7996_dev *dev);
+ const char *mt7996_eeprom_name(struct mt7996_dev *dev);
+@@ -805,37 +819,47 @@ int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
+ int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
+ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+- struct mt7996_vif *mvif,
++ struct mt7996_bss_conf *mconf,
+ struct mt7996_twt_flow *flow,
+ int cmd);
+ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+- struct ieee80211_vif *vif, bool enable);
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, bool enable);
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+- struct ieee80211_vif *vif, int enable);
+-int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta, bool enable, bool newly);
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, int enable);
++int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
++ bool enable, bool newly);
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool add);
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool add);
+-int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
++ struct mt7996_bss_conf *mconf,
+ struct cfg80211_he_bss_color *he_bss_color);
+-int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- int enable);
++int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, int en);
+ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+- struct ieee80211_vif *vif, u32 changed);
+-int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, u32 changed);
++int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_he_obss_pd *he_obss_pd);
+-int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta, bool changed);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+-int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif);
++int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ void *data, u16 version);
+-int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta, void *data, u32 field);
+ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
+ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
+@@ -850,7 +874,7 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
+ const struct mt7996_dfs_pattern *pattern);
+ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
+-int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif);
++int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf);
+ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
+ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
+ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
+@@ -890,8 +914,9 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+-int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+- enum vow_drr_ctrl_id id);
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
++ struct mt7996_bss_conf *mconf,
++ struct mt7996_sta *msta, enum vow_drr_ctrl_id id);
+ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+ void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
+
+@@ -1000,13 +1025,16 @@ void mt7996_update_channel(struct mt76_phy *mphy);
+ int mt7996_init_debugfs(struct mt7996_phy *phy);
+ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len);
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len);
+-int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+ struct ieee80211_key_conf *key, int mcu_cmd,
+ struct mt76_wcid *wcid, enum set_key_cmd cmd);
+-int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_key_conf *key);
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
++ struct mt7996_bss_conf *mconf,
+ struct ieee80211_sta *sta);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 784a8bea4..bf55b4309 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -223,6 +223,7 @@ static void
+ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ {
+ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv;
+ u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
+
+ if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
+@@ -234,8 +235,8 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+
+ mt7996_tm_rf_switch_mode(dev, rf_test_mode);
+
+- mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
+- mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en, false);
++ mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, en);
++ mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, en, false);
+
+ mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
+
+@@ -1179,13 +1180,13 @@ mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
+ mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
+
+ /* bss idx & omac idx should be set to band idx for ibf cal */
+- mvif->mt76.idx = band_idx;
+- dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
+- mvif->mt76.omac_idx = band_idx;
+- phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
++ mvif->deflink.mt76.idx = band_idx;
++ dev->mt76.vif_mask |= BIT_ULL(mvif->deflink.mt76.idx);
++ mvif->deflink.mt76.omac_idx = band_idx;
++ phy->omac_mask |= BIT_ULL(mvif->deflink.mt76.omac_idx);
+
+- mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true);
+- mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true);
++ mt7996_mcu_add_dev_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
++ mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
+
+ if (td->ibf) {
+ if (td->is_txbf_dut) {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0087-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0087-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
new file mode 100644
index 0000000..fb5a898
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0087-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
@@ -0,0 +1,2430 @@
+From c847e82ab99803a3f79be845fba1cbbf2de03bbc Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 27 Nov 2023 10:43:34 +0800
+Subject: [PATCH 087/116] wifi: mt76: mt7996: switch to per-link data structure
+ of sta
+
+Introduce struct mt7996_link_sta, data structure for per-link STA.
+Note that mt7996_sta now represents a peer legacy or MLD device.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7615/mcu.c | 4 +-
+ mt76_connac_mcu.c | 18 +-
+ mt76_connac_mcu.h | 7 +-
+ mt7915/mcu.c | 4 +-
+ mt7925/mcu.c | 4 +-
+ mt7996/debugfs.c | 20 ++-
+ mt7996/mac.c | 109 ++++++------
+ mt7996/main.c | 223 +++++++++++++++---------
+ mt7996/mcu.c | 430 ++++++++++++++++++++++++----------------------
+ mt7996/mt7996.h | 44 +++--
+ mt7996/testmode.c | 6 +-
+ 11 files changed, 482 insertions(+), 387 deletions(-)
+
+diff --git a/mt7615/mcu.c b/mt7615/mcu.c
+index a9310660b..8f4f203ef 100644
+--- a/mt7615/mcu.c
++++ b/mt7615/mcu.c
+@@ -862,8 +862,8 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ else
+ mvif->sta_added = true;
+ }
+- mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, vif, sta, enable,
+- new_entry);
++ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, vif, &sta->deflink,
++ enable, new_entry);
+ if (enable && sta)
+ mt76_connac_mcu_sta_tlv(phy->mt76, sskb, sta, vif, 0,
+ MT76_STA_INFO_STATE_ASSOC);
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 0c7b69352..d83d31441 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -371,9 +371,10 @@ EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_omac_tlv);
+
+ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta,
++ struct ieee80211_link_sta *link_sta,
+ bool enable, bool newly)
+ {
++ struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL;
+ struct sta_rec_basic *basic;
+ struct tlv *tlv;
+ int conn_type;
+@@ -431,7 +432,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ break;
+ }
+
+- memcpy(basic->peer_addr, sta->addr, ETH_ALEN);
++ memcpy(basic->peer_addr, link_sta->addr, ETH_ALEN);
+ basic->qos = sta->wme;
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv);
+@@ -1055,7 +1056,7 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+ return PTR_ERR(skb);
+
+ if (info->sta || !info->offload_fw)
+- mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, info->sta,
++ mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
+ info->enable, info->newly);
+ if (info->sta && info->enable)
+ mt76_connac_mcu_sta_tlv(phy, skb, info->sta,
+@@ -1306,7 +1307,8 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba);
+
+ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+- enum nl80211_band band, struct ieee80211_sta *sta)
++ enum nl80211_band band,
++ struct ieee80211_link_sta *link_sta)
+ {
+ struct mt76_dev *dev = phy->dev;
+ const struct ieee80211_sta_he_cap *he_cap;
+@@ -1317,10 +1319,10 @@ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ if (is_connac_v1(dev))
+ return 0x38;
+
+- if (sta) {
+- ht_cap = &sta->deflink.ht_cap;
+- vht_cap = &sta->deflink.vht_cap;
+- he_cap = &sta->deflink.he_cap;
++ if (link_sta) {
++ ht_cap = &link_sta->ht_cap;
++ vht_cap = &link_sta->vht_cap;
++ he_cap = &link_sta->he_cap;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 151183d08..f7da63658 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1903,8 +1903,8 @@ int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy);
+ int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif);
+ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta, bool enable,
+- bool newly);
++ struct ieee80211_link_sta *link_sta,
++ bool enable, bool newly);
+ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, void *sta_wtbl,
+@@ -2012,7 +2012,8 @@ mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
+ const struct ieee80211_sta_eht_cap *
+ mt76_connac_get_eht_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
+ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+- enum nl80211_band band, struct ieee80211_sta *sta);
++ enum nl80211_band band,
++ struct ieee80211_link_sta *link_sta);
+ u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ enum nl80211_band band);
+
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index 2d017396c..1d1b3cead 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -1504,7 +1504,7 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+
+ ra->valid = true;
+ ra->auto_rate = true;
+- ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta);
++ ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink);
+ ra->channel = chandef->chan->hw_value;
+ ra->bw = sta->deflink.bandwidth;
+ ra->phy.bw = sta->deflink.bandwidth;
+@@ -1669,7 +1669,7 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ return PTR_ERR(skb);
+
+ /* starec basic */
+- mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, sta, enable,
++ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, &sta->deflink, enable,
+ !rcu_access_pointer(dev->mt76.wcid[msta->wcid.idx]));
+ if (!enable)
+ goto out;
+diff --git a/mt7925/mcu.c b/mt7925/mcu.c
+index bd37cb8d7..1cb556302 100644
+--- a/mt7925/mcu.c
++++ b/mt7925/mcu.c
+@@ -1626,7 +1626,7 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
+ return PTR_ERR(skb);
+
+ if (info->sta || !info->offload_fw)
+- mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, info->sta,
++ mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
+ info->enable, info->newly);
+ if (info->sta && info->enable) {
+ mt7925_mcu_sta_phy_tlv(skb, info->vif, info->sta);
+@@ -2092,7 +2092,7 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb,
+ basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_OFDM_INDEX);
+
+ memcpy(basic_req->bssid, vif->bss_conf.bssid, ETH_ALEN);
+- basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, sta);
++ basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, &sta->deflink);
+ basic_req->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+ basic_req->dtim_period = vif->bss_conf.dtim_period;
+ basic_req->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 06015d686..56e219231 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -739,14 +739,15 @@ static void
+ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink = &msta->deflink;
+ struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct seq_file *s = data;
+ u8 ac;
+
+ for (ac = 0; ac < 4; ac++) {
+ u32 qlen, ctrl, val;
+- u32 idx = msta->wcid.idx >> 5;
+- u8 offs = msta->wcid.idx & GENMASK(4, 0);
++ u32 idx = mlink->wcid.idx >> 5;
++ u8 offs = mlink->wcid.idx & GENMASK(4, 0);
+
+ ctrl = BIT(31) | BIT(11) | (ac << 24);
+ val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx));
+@@ -754,11 +755,11 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ if (val & BIT(offs))
+ continue;
+
+- mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx);
++ mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | mlink->wcid.idx);
+ qlen = mt76_get_field(dev, MT_FL_Q3_CTRL,
+ GENMASK(11, 0));
+ seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
+- sta->addr, msta->wcid.idx,
++ sta->addr, mlink->wcid.idx,
+ msta->vif->deflink.mt76.wmm_idx, ac, qlen);
+ }
+ }
+@@ -1041,7 +1042,7 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ struct mt76_dev *mdev = &dev->mt76;
+ struct mt76_sta_stats *stats;
+ struct ieee80211_sta *sta;
+- struct mt7996_sta *msta;
++ struct mt7996_link_sta *mlink;
+ struct mt76_wcid *wcid;
+ struct mt76_vif *vif;
+ u16 i;
+@@ -1053,9 +1054,9 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ if (!wcid || !wcid->sta)
+ continue;
+
+- msta = container_of(wcid, struct mt7996_sta, wcid);
+- sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+- vif = &msta->vif->deflink.mt76;
++ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++ sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
++ vif = &mlink->sta->vif->deflink.mt76;
+ stats = &wcid->stats;
+
+ seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
+@@ -1230,6 +1231,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
+ #define LONG_PREAMBLE 1
+ struct ieee80211_sta *sta = file->private_data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink = &msta->deflink;
+ struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct ra_rate phy = {};
+ char buf[100];
+@@ -1265,7 +1267,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
+ goto out;
+ }
+
+- phy.wlan_idx = cpu_to_le16(msta->wcid.idx);
++ phy.wlan_idx = cpu_to_le16(mlink->wcid.idx);
+ phy.gi = cpu_to_le16(gi);
+ phy.ltf = cpu_to_le16(ltf);
+ phy.ldpc = phy.ldpc ? 7 : 0;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 63b16f92e..3cff43d6b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -54,7 +54,7 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = {
+ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
+ u16 idx, bool unicast)
+ {
+- struct mt7996_sta *sta;
++ struct mt7996_link_sta *mlink;
+ struct mt76_wcid *wcid;
+
+ if (idx >= ARRAY_SIZE(dev->mt76.wcid))
+@@ -67,11 +67,11 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
+ if (!wcid->sta)
+ return NULL;
+
+- sta = container_of(wcid, struct mt7996_sta, wcid);
+- if (!sta->vif)
++ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++ if (!mlink->sta->vif)
+ return NULL;
+
+- return &sta->vif->sta.wcid;
++ return &mlink->wcid;
+ }
+
+ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
+@@ -92,12 +92,11 @@ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
+ }
+
+ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+- struct ieee80211_vif *vif, bool enable)
++ struct mt7996_link_sta *mlink, bool enable)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ u32 addr;
+
+- addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5);
++ addr = mt7996_mac_wtbl_lmac_addr(dev, mlink->wcid.idx, 5);
+ if (enable)
+ mt76_set(dev, addr, BIT(5));
+ else
+@@ -349,7 +348,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ __le16 fc = 0;
+ int idx;
+ u8 hw_aggr = false;
+- struct mt7996_sta *msta = NULL;
++ struct mt7996_link_sta *mlink = NULL;
+
+ hw_aggr = status->aggr;
+ memset(status, 0, sizeof(*status));
+@@ -380,10 +379,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
+
+ if (status->wcid) {
+- msta = container_of(status->wcid, struct mt7996_sta, wcid);
++ mlink = container_of(status->wcid, struct mt7996_link_sta, wcid);
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+- if (list_empty(&msta->wcid.poll_list))
+- list_add_tail(&msta->wcid.poll_list,
++ if (list_empty(&mlink->wcid.poll_list))
++ list_add_tail(&mlink->wcid.poll_list,
+ &dev->mt76.sta_poll_list);
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ }
+@@ -592,7 +591,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ #endif
+ } else {
+ status->flag |= RX_FLAG_8023;
+- mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
++ mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], mlink ? mlink->sta : NULL, skb,
+ *info);
+ }
+
+@@ -942,6 +941,7 @@ static void
+ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ {
+ struct mt7996_sta *msta;
++ struct mt7996_link_sta *mlink;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ u16 fc, tid;
+@@ -970,7 +970,8 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ return;
+
+ msta = (struct mt7996_sta *)sta->drv_priv;
+- if (!test_and_set_bit(tid, &msta->wcid.ampdu_state))
++ mlink = rcu_dereference(msta->link[0]);
++ if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
+ ieee80211_start_tx_ba_session(sta, tid, 0);
+ }
+
+@@ -1048,7 +1049,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ */
+ info = le32_to_cpu(*cur_info);
+ if (info & MT_TXFREE_INFO_PAIR) {
+- struct mt7996_sta *msta;
++ struct mt7996_link_sta *mlink;
+ u16 idx;
+
+ idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
+@@ -1057,10 +1058,10 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ if (!sta)
+ continue;
+
+- msta = container_of(wcid, struct mt7996_sta, wcid);
++ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+ spin_lock_bh(&mdev->sta_poll_lock);
+- if (list_empty(&msta->wcid.poll_list))
+- list_add_tail(&msta->wcid.poll_list,
++ if (list_empty(&mlink->wcid.poll_list))
++ list_add_tail(&mlink->wcid.poll_list,
+ &mdev->sta_poll_list);
+ spin_unlock_bh(&mdev->sta_poll_lock);
+ continue;
+@@ -1265,7 +1266,7 @@ out:
+
+ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ {
+- struct mt7996_sta *msta = NULL;
++ struct mt7996_link_sta *mlink;
+ struct mt76_wcid *wcid;
+ __le32 *txs_data = data;
+ u16 wcidx;
+@@ -1286,16 +1287,15 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ if (!wcid)
+ goto out;
+
+- msta = container_of(wcid, struct mt7996_sta, wcid);
+-
+ mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data);
+
+ if (!wcid->sta)
+ goto out;
+
++ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+- if (list_empty(&msta->wcid.poll_list))
+- list_add_tail(&msta->wcid.poll_list, &dev->mt76.sta_poll_list);
++ if (list_empty(&mlink->wcid.poll_list))
++ list_add_tail(&mlink->wcid.poll_list, &dev->mt76.sta_poll_list);
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+ out:
+@@ -2242,8 +2242,9 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
+ struct ieee80211_bss_conf *conf;
++ struct ieee80211_link_sta *link_sta;
+ struct mt7996_bss_conf *mconf;
+- struct mt7996_sta *msta;
++ struct mt7996_link_sta *mlink;
+ u32 changed;
+ LIST_HEAD(list);
+
+@@ -2251,24 +2252,25 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ list_splice_init(&dev->sta_rc_list, &list);
+
+ while (!list_empty(&list)) {
+- msta = list_first_entry(&list, struct mt7996_sta, rc_list);
+- list_del_init(&msta->rc_list);
+- changed = msta->changed;
+- msta->changed = 0;
++ mlink = list_first_entry(&list, struct mt7996_link_sta, rc_list);
++ list_del_init(&mlink->rc_list);
++ changed = mlink->changed;
++ mlink->changed = 0;
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+- sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+- vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
++ sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
++ link_sta = &sta->deflink;
++ vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
+ conf = &vif->bss_conf;
+- mconf = &msta->vif->deflink;
++ mconf = &mlink->sta->vif->deflink;
+
+ if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ IEEE80211_RC_NSS_CHANGED |
+ IEEE80211_RC_BW_CHANGED))
+- mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, true);
++ mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, true);
+
+ if (changed & IEEE80211_RC_SMPS_CHANGED)
+- mt7996_mcu_set_fixed_field(dev, mconf, sta, NULL,
++ mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink, NULL,
+ RATE_PARAM_MMPS_UPDATE);
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+@@ -2561,7 +2563,7 @@ static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt)
+ }
+
+ static bool
+-mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
++mt7996_mac_twt_param_equal(struct mt7996_link_sta *mlink,
+ struct ieee80211_twt_params *twt_agrt)
+ {
+ u16 type = le16_to_cpu(twt_agrt->req_type);
+@@ -2572,10 +2574,10 @@ mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
+ for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) {
+ struct mt7996_twt_flow *f;
+
+- if (!(msta->twt.flowid_mask & BIT(i)))
++ if (!(mlink->twt.flowid_mask & BIT(i)))
+ continue;
+
+- f = &msta->twt.flow[i];
++ f = &mlink->twt.flow[i];
+ if (f->duration == twt_agrt->min_twt_dur &&
+ f->mantissa == twt_agrt->mantissa &&
+ f->exp == exp &&
+@@ -2594,6 +2596,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ {
+ enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink;
+ struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
+ u16 req_type = le16_to_cpu(twt_agrt->req_type);
+ enum ieee80211_twt_setup_cmd sta_setup_cmd;
+@@ -2605,11 +2608,12 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ goto out;
+
+ mutex_lock(&dev->mt76.mutex);
++ mlink = mlink_dereference_protected(msta, 0);
+
+ if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT)
+ goto unlock;
+
+- if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow))
++ if (hweight8(mlink->twt.flowid_mask) == ARRAY_SIZE(mlink->twt.flow))
+ goto unlock;
+
+ if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) {
+@@ -2618,10 +2622,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ goto unlock;
+ }
+
+- if (mt7996_mac_twt_param_equal(msta, twt_agrt))
++ if (mt7996_mac_twt_param_equal(mlink, twt_agrt))
+ goto unlock;
+
+- flowid = ffs(~msta->twt.flowid_mask) - 1;
++ flowid = ffs(~mlink->twt.flowid_mask) - 1;
+ twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID);
+ twt_agrt->req_type |= le16_encode_bits(flowid,
+ IEEE80211_TWT_REQTYPE_FLOWID);
+@@ -2630,10 +2634,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type);
+ sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type);
+
+- flow = &msta->twt.flow[flowid];
++ flow = &mlink->twt.flow[flowid];
+ memset(flow, 0, sizeof(*flow));
+ INIT_LIST_HEAD(&flow->list);
+- flow->wcid = msta->wcid.idx;
++ flow->wcid = mlink->wcid.idx;
+ flow->table_id = table_id;
+ flow->id = flowid;
+ flow->duration = twt_agrt->min_twt_dur;
+@@ -2651,7 +2655,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+
+ flow->sched = true;
+ flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
+- curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink);
++ curr_tsf = __mt7996_get_tsf(hw, &mlink->sta->vif->deflink);
+ div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
+ flow_tsf = curr_tsf + interval - rem;
+ twt_agrt->twt = cpu_to_le64(flow_tsf);
+@@ -2660,13 +2664,13 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ }
+ flow->tsf = le64_to_cpu(twt_agrt->twt);
+
+- if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
++ if (mt7996_mcu_twt_agrt_update(dev, &mlink->sta->vif->deflink, flow,
+ MCU_TWT_AGRT_ADD))
+ goto unlock;
+
+ setup_cmd = TWT_SETUP_CMD_ACCEPT;
+ dev->twt.table_mask |= BIT(table_id);
+- msta->twt.flowid_mask |= BIT(flowid);
++ mlink->twt.flowid_mask |= BIT(flowid);
+ dev->twt.n_agrt++;
+
+ unlock:
+@@ -2679,26 +2683,25 @@ out:
+ }
+
+ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+- struct mt7996_sta *msta,
+- u8 flowid)
++ struct mt7996_link_sta *mlink, u8 flowid)
+ {
+ struct mt7996_twt_flow *flow;
+- struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
++ struct mt7996_bss_conf *mconf = mconf_dereference_protected(mlink->sta->vif, 0);
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+- if (flowid >= ARRAY_SIZE(msta->twt.flow))
++ if (flowid >= ARRAY_SIZE(mlink->twt.flow))
+ return;
+
+- if (!(msta->twt.flowid_mask & BIT(flowid)))
++ if (!(mlink->twt.flowid_mask & BIT(flowid)))
+ return;
+
+- flow = &msta->twt.flow[flowid];
++ flow = &mlink->twt.flow[flowid];
+ if (mt7996_mcu_twt_agrt_update(dev, mconf, flow, MCU_TWT_AGRT_DELETE))
+ return;
+
+ list_del_init(&flow->list);
+- msta->twt.flowid_mask &= ~BIT(flowid);
++ mlink->twt.flowid_mask &= ~BIT(flowid);
+ dev->twt.table_mask &= ~BIT(flow->table_id);
+ dev->twt.n_agrt--;
+ }
+@@ -2711,7 +2714,7 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ struct cfg80211_scan_request *req = phy->scan_req;
+ struct ieee80211_vif *vif = phy->scan_vif;
+ struct mt7996_vif *mvif;
+- struct mt76_wcid *wcid;
++ struct mt7996_link_sta *mlink;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb;
+
+@@ -2719,7 +2722,6 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ return;
+
+ mvif = (struct mt7996_vif *)vif->drv_priv;
+- wcid = &mvif->sta.wcid;
+
+ skb = ieee80211_probereq_get(hw, vif->addr,
+ ssid->ssid, ssid->ssid_len, req->ie_len);
+@@ -2752,7 +2754,8 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ }
+
+ local_bh_disable();
+- mt76_tx(phy->mt76, NULL, wcid, skb);
++ mlink = rcu_dereference(mvif->sta.link[0]);
++ mt76_tx(phy->mt76, NULL, &mlink->wcid, skb);
+ local_bh_enable();
+
+ rcu_read_unlock();
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 9f61082f5..4069ffb70 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -229,6 +229,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf = &mvif->deflink;
++ struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt76_txq *mtxq;
+@@ -268,14 +269,15 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+
+ idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
+
+- INIT_LIST_HEAD(&mvif->sta.rc_list);
+- INIT_LIST_HEAD(&mvif->sta.wcid.poll_list);
+- mvif->sta.wcid.idx = idx;
+- mvif->sta.wcid.phy_idx = band_idx;
+- mvif->sta.wcid.hw_key_idx = -1;
+- mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
+- mvif->sta.vif = mvif;
+- mt76_wcid_init(&mvif->sta.wcid);
++ INIT_LIST_HEAD(&mlink->rc_list);
++ INIT_LIST_HEAD(&mlink->wcid.poll_list);
++ mlink->wcid.idx = idx;
++ mlink->wcid.phy_idx = band_idx;
++ mlink->wcid.hw_key_idx = -1;
++ mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++ mlink->sta = &mvif->sta;
++ mlink->sta->vif = mvif;
++ mt76_wcid_init(&mlink->wcid);
+
+ mt7996_mac_wtbl_update(dev, idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+@@ -297,14 +299,15 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+
+ mt7996_init_bitrate_mask(mconf);
+
+- mt7996_mcu_add_bss_info(phy, conf, mconf, true);
++ mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+ /* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA
+ * interface, since firmware only records BSSID when the entry is new
+ */
+ if (vif->type != NL80211_IFTYPE_STATION)
+- mt7996_mcu_add_sta(dev, conf, mconf, NULL, true, true);
+- rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
++ mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, true);
++ rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
+ rcu_assign_pointer(mvif->link[0], mconf);
++ rcu_assign_pointer(mvif->sta.link[0], mlink);
+
+ out:
+ mutex_unlock(&dev->mt76.mutex);
+@@ -318,10 +321,10 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_bss_conf *conf;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf;
+- struct mt7996_sta *msta = &mvif->sta;
++ struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+- int idx = msta->wcid.idx;
++ int idx = mlink->wcid.idx;
+
+ cancel_delayed_work_sync(&phy->scan_work);
+
+@@ -329,8 +332,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+
+ conf = link_conf_dereference_protected(vif, 0);
+ mconf = mconf_dereference_protected(mvif, 0);
+- mt7996_mcu_add_sta(dev, conf, mconf, NULL, false, false);
+- mt7996_mcu_add_bss_info(phy, conf, mconf, false);
++ mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
++ mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
+
+ if (vif == phy->monitor_vif)
+ phy->monitor_vif = NULL;
+@@ -343,12 +346,13 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+- if (!list_empty(&msta->wcid.poll_list))
+- list_del_init(&msta->wcid.poll_list);
++ if (!list_empty(&mlink->wcid.poll_list))
++ list_del_init(&mlink->wcid.poll_list);
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+- mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
++ mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+ rcu_assign_pointer(mvif->link[0], NULL);
++ rcu_assign_pointer(mvif->sta.link[0], NULL);
+
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -447,10 +451,10 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
+ &mvif->sta;
+- struct mt76_wcid *wcid = &msta->wcid;
+ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
+ struct ieee80211_bss_conf *conf;
+- u8 *wcid_keyidx = &wcid->hw_key_idx;
++ u8 *wcid_keyidx;
+ int idx = key->keyidx;
+ int err = 0;
+
+@@ -464,6 +468,12 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ return -EOPNOTSUPP;
+
++ mutex_lock(&dev->mt76.mutex);
++ conf = link_conf_dereference_protected(vif, 0);
++ mconf = mconf_dereference_protected(mvif, 0);
++ mlink = mlink_dereference_protected(msta, 0);
++ wcid_keyidx = &mlink->wcid.hw_key_idx;
++
+ /* fall back to sw encryption for unsupported ciphers */
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+@@ -486,16 +496,13 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ default:
++ mutex_unlock(&dev->mt76.mutex);
+ return -EOPNOTSUPP;
+ }
+
+- mutex_lock(&dev->mt76.mutex);
+-
+- conf = link_conf_dereference_protected(vif, 0);
+- mconf = mconf_dereference_protected(mvif, 0);
+ if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
+ mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+- mt7996_mcu_add_bss_info(phy, conf, mconf, true);
++ mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+ }
+
+ if (cmd == SET_KEY) {
+@@ -506,14 +513,14 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ goto out;
+ }
+
+- mt76_wcid_key_setup(&dev->mt76, wcid, key);
++ mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
+
+ if (key->keyidx == 6 || key->keyidx == 7)
+- err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, key);
++ err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
+ else
+ err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+- &msta->wcid, cmd);
++ &mlink->wcid, cmd);
+ out:
+ mutex_unlock(&dev->mt76.mutex);
+
+@@ -716,25 +723,27 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+
+ mconf = mconf_dereference_protected(mvif, 0);
++ mlink = mlink_dereference_protected(&mvif->sta, 0);
+ /* station mode uses BSSID to map the wlan entry to a peer,
+ * and then peer references bss_info_rfch to set bandwidth cap.
+ */
+ if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
+ (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
+ (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
+- mt7996_mcu_add_bss_info(phy, info, mconf, true);
+- mt7996_mcu_add_sta(dev, info, mconf, NULL, true,
++ mt7996_mcu_add_bss_info(phy, info, mconf, mlink, true);
++ mt7996_mcu_add_sta(dev, info, mconf, NULL, mlink, true,
+ !!(changed & BSS_CHANGED_BSSID));
+ }
+
+ if (changed & BSS_CHANGED_ERP_CTS_PROT)
+- mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot);
++ mt7996_mac_enable_rtscts(dev, mlink, info->use_cts_prot);
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ int slottime = info->use_short_slot ? 9 : 20;
+@@ -805,6 +814,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
++ struct mt7996_link_sta *mlink = &msta->deflink;
+ u8 band_idx = mconf->phy->mt76->band_idx;
+ int idx;
+
+@@ -816,13 +826,16 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ if (idx < 0)
+ return -ENOSPC;
+
+- INIT_LIST_HEAD(&msta->rc_list);
+- INIT_LIST_HEAD(&msta->wcid.poll_list);
++ INIT_LIST_HEAD(&mlink->rc_list);
++ INIT_LIST_HEAD(&mlink->wcid.poll_list);
+ msta->vif = mvif;
+- msta->wcid.sta = 1;
+- msta->wcid.idx = idx;
+- msta->wcid.phy_idx = band_idx;
+- msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++ mlink->wcid.sta = 1;
++ mlink->wcid.idx = idx;
++ mlink->wcid.phy_idx = band_idx;
++ mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++ mlink->sta = msta;
++
++ rcu_assign_pointer(msta->link[0], mlink);
+
+ #ifdef CONFIG_MTK_VENDOR
+ mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
+@@ -855,19 +868,25 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
+ struct ieee80211_bss_conf *conf;
++ struct ieee80211_link_sta *link_sta;
+
+ mutex_lock(&dev->mt76.mutex);
+
+- mt7996_mac_wtbl_update(dev, msta->wcid.idx,
+- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+ conf = link_conf_dereference_protected(vif, 0);
+ mconf = mconf_dereference_protected(mvif, 0);
+- mt7996_mcu_add_sta(dev, conf, mconf, sta, true, true);
+- mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, false);
++ link_sta = link_sta_dereference_protected(sta, 0);
++ mlink = mlink_dereference_protected(msta, 0);
+
+- ewma_avg_signal_init(&msta->avg_ack_signal);
++ mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
++ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++ mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
++ mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
++ mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++
++ ewma_avg_signal_init(&mlink->avg_ack_signal);
+
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -879,25 +898,31 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
+ struct ieee80211_bss_conf *conf;
++ struct ieee80211_link_sta *link_sta;
+ int i;
+
+ conf = link_conf_dereference_protected(vif, 0);
+ mconf = mconf_dereference_protected(mvif, 0);
+- mt7996_mcu_add_sta(dev, conf, mconf, sta, false, false);
++ link_sta = link_sta_dereference_protected(sta, 0);
++ mlink = mlink_dereference_protected(msta, 0);
++ mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
+
+- mt7996_mac_wtbl_update(dev, msta->wcid.idx,
++ mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+- for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++)
+- mt7996_mac_twt_teardown_flow(dev, msta, i);
++ for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
++ mt7996_mac_twt_teardown_flow(dev, mlink, i);
+
+ spin_lock_bh(&mdev->sta_poll_lock);
+- if (!list_empty(&msta->wcid.poll_list))
+- list_del_init(&msta->wcid.poll_list);
+- if (!list_empty(&msta->rc_list))
+- list_del_init(&msta->rc_list);
++ if (!list_empty(&mlink->wcid.poll_list))
++ list_del_init(&mlink->wcid.poll_list);
++ if (!list_empty(&mlink->rc_list))
++ list_del_init(&mlink->rc_list);
+ spin_unlock_bh(&mdev->sta_poll_lock);
++
++ rcu_assign_pointer(msta->link[0], NULL);
+ }
+
+ static void mt7996_tx(struct ieee80211_hw *hw,
+@@ -909,19 +934,22 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_vif *vif = info->control.vif;
+ struct mt76_wcid *wcid = &dev->mt76.global_wcid;
++ struct mt7996_link_sta *mlink;
+
+ if (control->sta) {
+- struct mt7996_sta *sta;
++ struct mt7996_sta *msta;
+
+- sta = (struct mt7996_sta *)control->sta->drv_priv;
+- wcid = &sta->wcid;
++ msta = (struct mt7996_sta *)control->sta->drv_priv;
++ mlink = rcu_dereference(msta->link[0]);
++ wcid = &mlink->wcid;
+ }
+
+ if (vif && !control->sta) {
+ struct mt7996_vif *mvif;
+
+ mvif = (struct mt7996_vif *)vif->drv_priv;
+- wcid = &mvif->sta.wcid;
++ mlink = rcu_dereference(mvif->sta.link[0]);
++ wcid = &mlink->wcid;
+ }
+
+ mt76_tx(mphy, control->sta, wcid, skb);
+@@ -948,6 +976,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta = params->sta;
+ struct ieee80211_txq *txq = sta->txq[params->tid];
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink;
+ u16 tid = params->tid;
+ u16 ssn = params->ssn;
+ struct mt76_txq *mtxq;
+@@ -959,14 +988,15 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mtxq = (struct mt76_txq *)txq->drv_priv;
+
+ mutex_lock(&dev->mt76.mutex);
++ mlink = mlink_dereference_protected(msta, 0);
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+- mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn,
++ mt76_rx_aggr_start(&dev->mt76, &mlink->wcid, tid, ssn,
+ params->buf_size);
+ ret = mt7996_mcu_add_rx_ba(dev, params, true);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+- mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
++ mt76_rx_aggr_stop(&dev->mt76, &mlink->wcid, tid);
+ ret = mt7996_mcu_add_rx_ba(dev, params, false);
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+@@ -977,16 +1007,16 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ mtxq->aggr = false;
+- clear_bit(tid, &msta->wcid.ampdu_state);
++ clear_bit(tid, &mlink->wcid.ampdu_state);
+ ret = mt7996_mcu_add_tx_ba(dev, params, false);
+ break;
+ case IEEE80211_AMPDU_TX_START:
+- set_bit(tid, &msta->wcid.ampdu_state);
++ set_bit(tid, &mlink->wcid.ampdu_state);
+ ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ mtxq->aggr = false;
+- clear_bit(tid, &msta->wcid.ampdu_state);
++ clear_bit(tid, &mlink->wcid.ampdu_state);
+ ret = mt7996_mcu_add_tx_ba(dev, params, false);
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+@@ -1165,10 +1195,19 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct station_info *sinfo)
+ {
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct rate_info *txrate = &msta->wcid.rate;
++ struct mt7996_link_sta *mlink;
++ struct rate_info *txrate;
+
++ /* TODO: support per-link rate report */
++ mutex_lock(&dev->mt76.mutex);
++ mlink = mlink_dereference_protected(msta, 0);
++ if (!mlink)
++ goto out;
++
++ txrate = &mlink->wcid.rate;
+ if (txrate->legacy || txrate->flags) {
+ if (txrate->legacy) {
+ sinfo->txrate.legacy = txrate->legacy;
+@@ -1187,44 +1226,52 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ sinfo->txrate.flags = txrate->flags;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+
+- sinfo->tx_failed = msta->wcid.stats.tx_failed;
++ sinfo->tx_failed = mlink->wcid.stats.tx_failed;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+
+- sinfo->tx_retries = msta->wcid.stats.tx_retries;
++ sinfo->tx_retries = mlink->wcid.stats.tx_retries;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+
+- sinfo->ack_signal = (s8)msta->ack_signal;
++ sinfo->ack_signal = (s8)mlink->ack_signal;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
+
+- sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal);
++ sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&mlink->avg_ack_signal);
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
+
+ if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+- sinfo->tx_bytes = msta->wcid.stats.tx_bytes;
++ sinfo->tx_bytes = mlink->wcid.stats.tx_bytes;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
+
+- sinfo->rx_bytes = msta->wcid.stats.rx_bytes;
++ sinfo->rx_bytes = mlink->wcid.stats.rx_bytes;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
+
+- sinfo->tx_packets = msta->wcid.stats.tx_packets;
++ sinfo->tx_packets = mlink->wcid.stats.tx_packets;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
+
+- sinfo->rx_packets = msta->wcid.stats.rx_packets;
++ sinfo->rx_packets = mlink->wcid.stats.rx_packets;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
+ }
++out:
++ mutex_unlock(&dev->mt76.mutex);
+ }
+
+ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink;
+ struct mt7996_dev *dev = msta->vif->dev;
+ u32 *changed = data;
+
++ rcu_read_lock();
++ mlink = rcu_dereference(msta->link[0]);
++
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+- msta->changed |= *changed;
+- if (list_empty(&msta->rc_list))
+- list_add_tail(&msta->rc_list, &dev->sta_rc_list);
++ mlink->changed |= *changed;
++ if (list_empty(&mlink->rc_list))
++ list_add_tail(&mlink->rc_list, &dev->sta_rc_list);
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
++
++ rcu_read_unlock();
+ }
+
+ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+@@ -1238,7 +1285,7 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+
+ if (!msta->vif) {
+ dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
+- sta->addr, msta->wcid.idx);
++ sta->addr, msta->deflink.wcid.idx);
+ return;
+ }
+
+@@ -1284,16 +1331,18 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
+
+ mutex_lock(&dev->mt76.mutex);
+ mconf = mconf_dereference_protected(mvif, 0);
++ mlink = mlink_dereference_protected(msta, 0);
+
+ if (enabled)
+- set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
++ set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+ else
+- clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
++ clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+
+- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -1306,16 +1355,18 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
+
+ mutex_lock(&dev->mt76.mutex);
+ mconf = mconf_dereference_protected(mvif, 0);
++ mlink = mlink_dereference_protected(msta, 0);
+
+ if (enabled)
+- set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
++ set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+ else
+- clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
++ clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+
+- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -1448,11 +1499,12 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+ {
+ struct mt76_ethtool_worker_info *wi = wi_data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink = &msta->deflink;
+
+ if (msta->vif->deflink.mt76.idx != wi->idx)
+ return;
+
+- mt76_ethtool_worker(wi, &msta->wcid.stats, true);
++ mt76_ethtool_worker(wi, &mlink->wcid.stats, true);
+ }
+
+ static
+@@ -1555,10 +1607,12 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
+ u8 flowid)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+- mt7996_mac_twt_teardown_flow(dev, msta, flowid);
++ mlink = mlink_dereference_protected(msta, 0);
++ mt7996_mac_twt_teardown_flow(dev, mlink, flowid);
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -1681,6 +1735,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf = &mvif->deflink;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink = &msta->deflink;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+@@ -1703,7 +1758,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ if (!mtk_wed_device_active(wed))
+ return -ENODEV;
+
+- if (msta->wcid.idx > MT7996_WTBL_STA)
++ if (mlink->wcid.idx > MT7996_WTBL_STA)
+ return -EIO;
+
+ path->type = DEV_PATH_MTK_WDMA;
+@@ -1711,11 +1766,11 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ path->mtk_wdma.wdma_idx = wed->wdma_idx;
+ path->mtk_wdma.bss = mconf->mt76.idx;
+ path->mtk_wdma.queue = 0;
+- path->mtk_wdma.wcid = msta->wcid.idx;
++ path->mtk_wdma.wcid = mlink->wcid.idx;
+
+ if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) &&
+ mtk_wed_is_amsdu_supported(wed))
+- path->mtk_wdma.amsdu = msta->wcid.amsdu;
++ path->mtk_wdma.amsdu = mlink->wcid.amsdu;
+ else
+ path->mtk_wdma.amsdu = 0;
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 11700f90f..6d2b517e0 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -117,13 +117,13 @@ mt7996_mcu_get_sta_nss(u16 mcs_map)
+ }
+
+ static void
+-mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
++mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta,
+ struct mt7996_bss_conf *mconf,
+ __le16 *he_mcs, u16 mcs_map)
+ {
+ enum nl80211_band band = mconf->phy->mt76->chandef.chan->band;
+ const u16 *mask = mconf->bitrate_mask.control[band].he_mcs;
+- int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
++ int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++) {
+ int mcs;
+@@ -166,11 +166,11 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
+ }
+
+ static void
+-mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
+- const u16 *mask)
++mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta,
++ __le16 *vht_mcs, const u16 *mask)
+ {
+- u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map);
+- int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
++ u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map);
++ int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
+ switch (mcs_map & 0x3) {
+@@ -192,13 +192,13 @@ mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
+ }
+
+ static void
+-mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
++mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta, u8 *ht_mcs,
+ const u8 *mask)
+ {
+- int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
++ int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++)
+- ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss];
++ ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss];
+ }
+
+ static int
+@@ -531,14 +531,14 @@ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
+ u32 tx_bytes, u32 rx_bytes,
+ u32 tx_packets, u32 rx_packets)
+ {
+- struct mt7996_sta *msta;
++ struct mt7996_link_sta *mlink;
+ struct ieee80211_vif *vif;
+ struct wireless_dev *wdev;
+
+ if (wiphy_ext_feature_isset(mphy->hw->wiphy,
+ NL80211_EXT_FEATURE_STAS_COUNT)) {
+- msta = container_of(wcid, struct mt7996_sta, wcid);
+- vif = container_of((void *)msta->vif, struct ieee80211_vif,
++ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++ vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif,
+ drv_priv);
+ wdev = ieee80211_vif_to_wdev(vif);
+
+@@ -1236,10 +1236,10 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
+
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ struct ieee80211_bss_conf *conf,
+- struct mt7996_bss_conf *mconf, int enable)
++ struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink, int enable)
+ {
+ struct ieee80211_vif *vif = conf->vif;
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = phy->dev;
+ struct sk_buff *skb;
+
+@@ -1255,7 +1255,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+
+ /* bss_basic must be first */
+ mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
+- mvif->sta.wcid.idx, enable);
++ mlink->wcid.idx, enable);
+ mt7996_mcu_bss_sec_tlv(skb, mconf);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+@@ -1335,9 +1335,10 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+ struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
++ struct mt7996_link_sta *mlink = mlink_dereference_protected(msta, 0);
+
+ if (enable && !params->amsdu)
+- msta->wcid.amsdu = false;
++ mlink->wcid.amsdu = false;
+
+ return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
+ }
+@@ -1355,15 +1356,15 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ static void
+ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta)
++ struct ieee80211_link_sta *link_sta)
+ {
+- struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
++ struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp mcs_map;
+ struct sta_rec_he_v2 *he;
+ struct tlv *tlv;
+ int i = 0;
+
+- if (!sta->deflink.he_cap.has_he)
++ if (!link_sta->he_cap.has_he)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he));
+@@ -1380,21 +1381,21 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ u8p_replace_bits(&he->he_phy_cap[1], conf->he_ldpc,
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
+
+- mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
+- switch (sta->deflink.bandwidth) {
++ mcs_map = link_sta->he_cap.he_mcs_nss_supp;
++ switch (link_sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ if (elem->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+- mt7996_mcu_set_sta_he_mcs(sta, mconf,
++ mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
+ &he->max_nss_mcs[CMD_HE_MCS_BW8080],
+ le16_to_cpu(mcs_map.rx_mcs_80p80));
+
+- mt7996_mcu_set_sta_he_mcs(sta, mconf,
++ mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
+ &he->max_nss_mcs[CMD_HE_MCS_BW160],
+ le16_to_cpu(mcs_map.rx_mcs_160));
+ fallthrough;
+ default:
+- mt7996_mcu_set_sta_he_mcs(sta, mconf,
++ mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
+ &he->max_nss_mcs[CMD_HE_MCS_BW80],
+ le16_to_cpu(mcs_map.rx_mcs_80));
+ break;
+@@ -1404,24 +1405,25 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ }
+
+ static void
+-mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb,
++ struct ieee80211_link_sta *link_sta)
+ {
+ struct sta_rec_he_6g_capa *he_6g;
+ struct tlv *tlv;
+
+- if (!sta->deflink.he_6ghz_capa.capa)
++ if (!link_sta->he_6ghz_capa.capa)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g));
+
+ he_6g = (struct sta_rec_he_6g_capa *)tlv;
+- he_6g->capa = sta->deflink.he_6ghz_capa.capa;
++ he_6g->capa = link_sta->he_6ghz_capa.capa;
+ }
+
+ static void
+-mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
+ {
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
+ struct ieee80211_vif *vif = container_of((void *)msta->vif,
+ struct ieee80211_vif, drv_priv);
+ struct ieee80211_eht_mcs_nss_supp *mcs_map;
+@@ -1429,11 +1431,11 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ struct sta_rec_eht *eht;
+ struct tlv *tlv;
+
+- if (!sta->deflink.eht_cap.has_eht)
++ if (!link_sta->eht_cap.has_eht)
+ return;
+
+- mcs_map = &sta->deflink.eht_cap.eht_mcs_nss_supp;
+- elem = &sta->deflink.eht_cap.eht_cap_elem;
++ mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp;
++ elem = &link_sta->eht_cap.eht_cap_elem;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht));
+
+@@ -1444,7 +1446,7 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]);
+
+ if (vif->type != NL80211_IFTYPE_STATION &&
+- (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] &
++ (link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+@@ -1460,44 +1462,44 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ }
+
+ static void
+-mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
+ {
+ struct sta_rec_ht_uni *ht;
+ struct tlv *tlv;
+
+- if (!sta->deflink.ht_cap.ht_supported)
++ if (!link_sta->ht_cap.ht_supported)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
+
+ ht = (struct sta_rec_ht_uni *)tlv;
+- ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
+- ht->ampdu_param = u8_encode_bits(sta->deflink.ht_cap.ampdu_factor,
++ ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap);
++ ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor,
+ IEEE80211_HT_AMPDU_PARM_FACTOR) |
+- u8_encode_bits(sta->deflink.ht_cap.ampdu_density,
++ u8_encode_bits(link_sta->ht_cap.ampdu_density,
+ IEEE80211_HT_AMPDU_PARM_DENSITY);
+ }
+
+ static void
+-mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
+ {
+ struct sta_rec_vht *vht;
+ struct tlv *tlv;
+ #ifdef CONFIG_MTK_VENDOR
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
+ struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->deflink.phy;
+ #endif
+
+ /* For 6G band, this tlv is necessary to let hw work normally */
+- if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
++ if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
+
+ vht = (struct sta_rec_vht *)tlv;
+- vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
+- vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
+- vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
++ vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap);
++ vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map;
++ vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map;
+ #ifdef CONFIG_MTK_VENDOR
+ vht->rts_bw_sig = phy->rts_bw_sig;
+ #endif
+@@ -1505,9 +1507,10 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+
+ static void
+ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++ struct ieee80211_vif *vif,
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink)
+ {
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct sta_rec_amsdu *amsdu;
+ struct tlv *tlv;
+
+@@ -1516,16 +1519,16 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ vif->type != NL80211_IFTYPE_AP)
+ return;
+
+- if (!sta->deflink.agg.max_amsdu_len)
++ if (!link_sta->agg.max_amsdu_len)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu));
+ amsdu = (struct sta_rec_amsdu *)tlv;
+ amsdu->max_amsdu_num = 8;
+ amsdu->amsdu_en = true;
+- msta->wcid.amsdu = true;
++ mlink->wcid.amsdu = true;
+
+- switch (sta->deflink.agg.max_amsdu_len) {
++ switch (link_sta->agg.max_amsdu_len) {
+ case IEEE80211_MAX_MPDU_LEN_VHT_11454:
+ amsdu->max_mpdu_size =
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
+@@ -1544,10 +1547,10 @@ static void
+ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta)
++ struct ieee80211_link_sta *link_sta)
+ {
+ struct mt7996_phy *phy = mconf->phy;
+- struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
++ struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
+ struct sta_rec_muru *muru;
+ struct tlv *tlv;
+
+@@ -1567,11 +1570,11 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
+ muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
+
+- if (sta->deflink.vht_cap.vht_supported)
++ if (link_sta->vht_cap.vht_supported)
+ muru->mimo_dl.vht_mu_bfee =
+- !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
++ !!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+
+- if (!sta->deflink.he_cap.has_he)
++ if (!link_sta->he_cap.has_he)
+ return;
+
+ muru->mimo_dl.partial_bw_dl_mimo =
+@@ -1604,7 +1607,7 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ static inline bool
+ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta, bool bfee)
++ struct ieee80211_link_sta *link_sta, bool bfee)
+ {
+ int sts = hweight16(phy->mt76->chainmask);
+
+@@ -1615,8 +1618,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ if (!bfee && sts < 2)
+ return false;
+
+- if (sta->deflink.eht_cap.has_eht) {
+- struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
++ if (link_sta->eht_cap.has_eht) {
++ struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
+ struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
+
+ if (bfee)
+@@ -1627,8 +1630,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
+ }
+
+- if (sta->deflink.he_cap.has_he) {
+- struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
++ if (link_sta->he_cap.has_he) {
++ struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
+
+ if (bfee)
+ return conf->he_su_beamformee &&
+@@ -1638,8 +1641,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
+ }
+
+- if (sta->deflink.vht_cap.vht_supported) {
+- u32 cap = sta->deflink.vht_cap.cap;
++ if (link_sta->vht_cap.vht_supported) {
++ u32 cap = link_sta->vht_cap.cap;
+
+ if (bfee)
+ return conf->vht_su_beamformee &&
+@@ -1662,10 +1665,10 @@ mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf)
+ }
+
+ static void
+-mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+- struct sta_rec_bf *bf)
++mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta,
++ struct mt7996_phy *phy, struct sta_rec_bf *bf)
+ {
+- struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs;
++ struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs;
+ u8 n = 0;
+
+ bf->tx_mode = MT_PHY_TYPE_HT;
+@@ -1687,10 +1690,11 @@ mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+ }
+
+ static void
+-mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+- struct sta_rec_bf *bf, bool explicit)
++mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta,
++ struct mt7996_phy *phy, struct sta_rec_bf *bf,
++ bool explicit)
+ {
+- struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
++ struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
+ struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap;
+ u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map);
+ u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
+@@ -1711,23 +1715,24 @@ mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = bf->ncol;
+
+- if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
++ if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
+ bf->nrow = 1;
+ } else {
+ bf->nrow = tx_ant;
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = nss_mcs;
+
+- if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
++ if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
+ bf->ibf_nrow = 1;
+ }
+ }
+
+ static void
+-mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+- struct mt7996_phy *phy, struct sta_rec_bf *bf)
++mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta,
++ struct ieee80211_vif *vif, struct mt7996_phy *phy,
++ struct sta_rec_bf *bf)
+ {
+- struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
++ struct ieee80211_sta_he_cap *pc = &link_sta->he_cap;
+ struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
+ const struct ieee80211_sta_he_cap *vc =
+ mt76_connac_get_he_phy_cap(phy->mt76, vif);
+@@ -1752,7 +1757,7 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = bf->ncol;
+
+- if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160)
++ if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160)
+ return;
+
+ /* go over for 160MHz and 80p80 */
+@@ -1784,10 +1789,11 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ }
+
+ static void
+-mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+- struct mt7996_phy *phy, struct sta_rec_bf *bf)
++mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta,
++ struct ieee80211_vif *vif, struct mt7996_phy *phy,
++ struct sta_rec_bf *bf)
+ {
+- struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
++ struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
+ struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
+ struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp;
+ const struct ieee80211_sta_eht_cap *vc =
+@@ -1810,10 +1816,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = bf->ncol;
+
+- if (sta->deflink.bandwidth < IEEE80211_STA_RX_BW_160)
++ if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160)
+ return;
+
+- switch (sta->deflink.bandwidth) {
++ switch (link_sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]);
+ sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]);
+@@ -1842,7 +1848,7 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ static void
+ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_bss_conf *conf, struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta)
++ struct ieee80211_link_sta *link_sta)
+ {
+ struct mt7996_phy *phy = mconf->phy;
+ int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+@@ -1856,10 +1862,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ };
+ bool ebf;
+
+- if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
++ if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
+ return;
+
+- ebf = mt7996_is_ebf_supported(phy, conf, mconf, sta, false);
++ ebf = mt7996_is_ebf_supported(phy, conf, mconf, link_sta, false);
+ if (!ebf && !dev->ibf)
+ return;
+
+@@ -1870,23 +1876,23 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ * vht: support eBF and iBF
+ * ht: iBF only, since mac80211 lacks of eBF support
+ */
+- if (sta->deflink.eht_cap.has_eht && ebf)
+- mt7996_mcu_sta_bfer_eht(sta, conf->vif, phy, bf);
+- else if (sta->deflink.he_cap.has_he && ebf)
+- mt7996_mcu_sta_bfer_he(sta, conf->vif, phy, bf);
+- else if (sta->deflink.vht_cap.vht_supported)
+- mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf);
+- else if (sta->deflink.ht_cap.ht_supported)
+- mt7996_mcu_sta_bfer_ht(sta, phy, bf);
++ if (link_sta->eht_cap.has_eht && ebf)
++ mt7996_mcu_sta_bfer_eht(link_sta, conf->vif, phy, bf);
++ else if (link_sta->he_cap.has_he && ebf)
++ mt7996_mcu_sta_bfer_he(link_sta, conf->vif, phy, bf);
++ else if (link_sta->vht_cap.vht_supported)
++ mt7996_mcu_sta_bfer_vht(link_sta, phy, bf, ebf);
++ else if (link_sta->ht_cap.ht_supported)
++ mt7996_mcu_sta_bfer_ht(link_sta, phy, bf);
+ else
+ return;
+
+ bf->bf_cap = ebf ? ebf : dev->ibf << 1;
+- bf->bw = sta->deflink.bandwidth;
+- bf->ibf_dbw = sta->deflink.bandwidth;
++ bf->bw = link_sta->bandwidth;
++ bf->ibf_dbw = link_sta->bandwidth;
+ bf->ibf_nrow = tx_ant;
+
+- if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
++ if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
+ bf->ibf_timeout = 0x48;
+ else
+ bf->ibf_timeout = 0x18;
+@@ -1896,7 +1902,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ else
+ bf->mem_20m = matrix[bf->nrow][bf->ncol];
+
+- switch (sta->deflink.bandwidth) {
++ switch (link_sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ case IEEE80211_STA_RX_BW_80:
+ bf->mem_total = bf->mem_20m * 2;
+@@ -1913,7 +1919,8 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ static void
+ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_bss_conf *conf,
+- struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta)
++ struct mt7996_bss_conf *mconf,
++ struct ieee80211_link_sta *link_sta)
+ {
+ struct mt7996_phy *phy = mconf->phy;
+ int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+@@ -1921,22 +1928,22 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct tlv *tlv;
+ u8 nrow = 0;
+
+- if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
++ if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he))
+ return;
+
+- if (!mt7996_is_ebf_supported(phy, conf, mconf, sta, true))
++ if (!mt7996_is_ebf_supported(phy, conf, mconf, link_sta, true))
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
+ bfee = (struct sta_rec_bfee *)tlv;
+
+- if (sta->deflink.he_cap.has_he) {
+- struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
++ if (link_sta->he_cap.has_he) {
++ struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
+
+ nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ pe->phy_cap_info[5]);
+- } else if (sta->deflink.vht_cap.vht_supported) {
+- struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
++ } else if (link_sta->vht_cap.vht_supported) {
++ struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
+
+ nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
+ pc->cap);
+@@ -1973,25 +1980,24 @@ mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb)
+ static void
+ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
++ struct mt7996_link_sta *mlink)
+ {
+ struct sta_rec_hdr_trans *hdr_trans;
+- struct mt76_wcid *wcid;
++ struct mt76_wcid *wcid = &mlink->wcid;
+ struct tlv *tlv;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans));
+ hdr_trans = (struct sta_rec_hdr_trans *)tlv;
+ hdr_trans->dis_rx_hdr_tran = true;
+
++ if (!wcid->sta)
++ return;
++
+ if (vif->type == NL80211_IFTYPE_STATION)
+ hdr_trans->to_ds = true;
+ else
+ hdr_trans->from_ds = true;
+
+- if (!sta)
+- return;
+-
+- wcid = (struct mt76_wcid *)sta->drv_priv;
+ hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
+ if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
+ hdr_trans->to_ds = true;
+@@ -2048,16 +2054,17 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+
+ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta, void *data, u32 field)
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink, void *data,
++ u32 field)
+ {
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct sta_phy_uni *phy = data;
+ struct sta_rec_ra_fixed_uni *ra;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+- &msta->wcid,
++ &mlink->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+@@ -2076,7 +2083,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ ra->phy = *phy;
+ break;
+ case RATE_PARAM_MMPS_UPDATE:
+- ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
++ ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
+ break;
+ default:
+ break;
+@@ -2091,7 +2098,8 @@ static int
+ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta)
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink)
+ {
+ struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
+ struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
+@@ -2115,11 +2123,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ } \
+ } while (0)
+
+- if (sta->deflink.he_cap.has_he) {
++ if (link_sta->he_cap.has_he) {
+ __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
+- } else if (sta->deflink.vht_cap.vht_supported) {
++ } else if (link_sta->vht_cap.vht_supported) {
+ __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
+- } else if (sta->deflink.ht_cap.ht_supported) {
++ } else if (link_sta->ht_cap.ht_supported) {
+ __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
+ } else {
+ nrates = hweight32(mask->control[band].legacy);
+@@ -2136,8 +2144,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+
+ /* fixed single rate */
+ if (nrates == 1) {
+- ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+- RATE_PARAM_FIXED_MCS);
++ ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
++ &phy, RATE_PARAM_FIXED_MCS);
+ if (ret)
+ return ret;
+ }
+@@ -2145,29 +2153,28 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ /* fixed GI */
+ if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
+ mask->control[band].he_gi != GENMASK(7, 0)) {
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ u32 addr;
+
+ /* firmware updates only TXCMD but doesn't take WTBL into
+ * account, so driver should update here to reflect the
+ * actual txrate hardware sends out.
+ */
+- addr = mt7996_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7);
+- if (sta->deflink.he_cap.has_he)
++ addr = mt7996_mac_wtbl_lmac_addr(dev, mlink->wcid.idx, 7);
++ if (link_sta->he_cap.has_he)
+ mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
+ else
+ mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
+
+- ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+- RATE_PARAM_FIXED_GI);
++ ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
++ &phy, RATE_PARAM_FIXED_GI);
+ if (ret)
+ return ret;
+ }
+
+ /* fixed HE_LTF */
+ if (mask->control[band].he_ltf != GENMASK(7, 0)) {
+- ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+- RATE_PARAM_FIXED_HE_LTF);
++ ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
++ &phy, RATE_PARAM_FIXED_HE_LTF);
+ if (ret)
+ return ret;
+ }
+@@ -2179,7 +2186,7 @@ static void
+ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta)
++ struct ieee80211_link_sta *link_sta)
+ {
+ #define INIT_RCPI 180
+ struct mt76_phy *mphy = mconf->phy->mt76;
+@@ -2188,20 +2195,20 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ enum nl80211_band band = chandef->chan->band;
+ struct sta_rec_ra_uni *ra;
+ struct tlv *tlv;
+- u32 supp_rate = sta->deflink.supp_rates[band];
+- u32 cap = sta->wme ? STA_CAP_WMM : 0;
++ u32 supp_rate = link_sta->supp_rates[band];
++ u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
+ ra = (struct sta_rec_ra_uni *)tlv;
+
+ ra->valid = true;
+ ra->auto_rate = true;
+- ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, sta);
++ ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, link_sta);
+ ra->channel = chandef->chan->hw_value;
+- ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ?
+- CMD_CBW_320MHZ : sta->deflink.bandwidth;
++ ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ?
++ CMD_CBW_320MHZ : link_sta->bandwidth;
+ ra->phy.bw = ra->bw;
+- ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
++ ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
+
+ if (supp_rate) {
+ supp_rate &= mask->control[band].legacy;
+@@ -2221,60 +2228,60 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ }
+ }
+
+- if (sta->deflink.ht_cap.ht_supported) {
++ if (link_sta->ht_cap.ht_supported) {
+ ra->supp_mode |= MODE_HT;
+- ra->af = sta->deflink.ht_cap.ampdu_factor;
+- ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
++ ra->af = link_sta->ht_cap.ampdu_factor;
++ ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
+
+ cap |= STA_CAP_HT;
+- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
++ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ cap |= STA_CAP_SGI_20;
+- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
++ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+ cap |= STA_CAP_SGI_40;
+- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
++ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
+ cap |= STA_CAP_TX_STBC;
+- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
++ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+ cap |= STA_CAP_RX_STBC;
+ if (conf->ht_ldpc &&
+- (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
++ (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ cap |= STA_CAP_LDPC;
+
+- mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs,
++ mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs,
+ mask->control[band].ht_mcs);
+ ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
+ }
+
+- if (sta->deflink.vht_cap.vht_supported) {
++ if (link_sta->vht_cap.vht_supported) {
+ u8 af;
+
+ ra->supp_mode |= MODE_VHT;
+ af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+- sta->deflink.vht_cap.cap);
++ link_sta->vht_cap.cap);
+ ra->af = max_t(u8, ra->af, af);
+
+ cap |= STA_CAP_VHT;
+- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
++ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
+ cap |= STA_CAP_VHT_SGI_80;
+- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
++ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
+ cap |= STA_CAP_VHT_SGI_160;
+- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
++ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
+ cap |= STA_CAP_VHT_TX_STBC;
+- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
++ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
+ cap |= STA_CAP_VHT_RX_STBC;
+ if (conf->vht_ldpc &&
+- (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
++ (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
+ cap |= STA_CAP_VHT_LDPC;
+
+- mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs,
++ mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs,
+ mask->control[band].vht_mcs);
+ }
+
+- if (sta->deflink.he_cap.has_he) {
++ if (link_sta->he_cap.has_he) {
+ ra->supp_mode |= MODE_HE;
+ cap |= STA_CAP_HE;
+
+- if (sta->deflink.he_6ghz_capa.capa)
+- ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
++ if (link_sta->he_6ghz_capa.capa)
++ ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+ }
+ ra->sta_cap = cpu_to_le32(cap);
+@@ -2285,14 +2292,14 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta, bool changed)
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink, bool changed)
+ {
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+- &msta->wcid,
++ &mlink->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+@@ -2302,26 +2309,27 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ * update sta_rec_he here.
+ */
+ if (changed)
+- mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
++ mt7996_mcu_sta_he_tlv(skb, conf, mconf, link_sta);
+
+ /* sta_rec_ra accommodates BW, NSS and only MCS range format
+ * i.e 0-{7,8,9} for VHT.
+ */
+- mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, sta);
++ mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, link_sta);
+
+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ if (ret)
+ return ret;
+
+- return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, sta);
++ return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, link_sta, mlink);
+ }
+
+ static int
+-mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
++mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink)
+ {
+ struct mt7996_phy *phy = mconf->phy;
+- struct mt7996_vow_sta_ctrl *vow = &msta->vow;
++ struct mt7996_vow_sta_ctrl *vow = &mlink->vow;
+ u8 omac_idx = mconf->mt76.omac_idx;
+ int ret;
+
+@@ -2339,73 +2347,70 @@ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
+ vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
+ vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
+
+- ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_BSS_GROUP);
+ if (ret)
+ return ret;
+
+- ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_PAUSE);
++ ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_PAUSE);
+ if (ret)
+ return ret;
+
+- return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_ALL);
++ return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_ALL);
+ }
+
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+- struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
+- bool enable, bool newly)
++ struct mt7996_bss_conf *mconf,
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink, bool enable, bool newly)
+ {
+ struct ieee80211_vif *vif = conf->vif;
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_sta *msta;
+ struct sk_buff *skb;
+ int ret;
+
+- msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+-
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+- &msta->wcid,
++ &mlink->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ /* starec basic */
+- mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, sta, enable, newly);
++ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, link_sta, enable, newly);
+
+ if (!enable)
+ goto out;
+
+ /* starec hdr trans */
+- mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
++ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, mlink);
+ /* starec tx proc */
+ mt7996_mcu_sta_tx_proc_tlv(skb);
+
+ /* tag order is in accordance with firmware dependency. */
+- if (sta) {
++ if (link_sta) {
+ /* starec hdrt mode */
+ mt7996_mcu_sta_hdrt_tlv(dev, skb);
+ /* starec bfer */
+- mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, sta);
++ mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, link_sta);
+ /* starec ht */
+- mt7996_mcu_sta_ht_tlv(skb, sta);
++ mt7996_mcu_sta_ht_tlv(skb, link_sta);
+ /* starec vht */
+- mt7996_mcu_sta_vht_tlv(skb, sta);
++ mt7996_mcu_sta_vht_tlv(skb, link_sta);
+ /* starec uapsd */
+- mt76_connac_mcu_sta_uapsd(skb, vif, sta);
++ mt76_connac_mcu_sta_uapsd(skb, vif, link_sta->sta);
+ /* starec amsdu */
+- mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
++ mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, link_sta, mlink);
+ /* starec he */
+- mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
++ mt7996_mcu_sta_he_tlv(skb, conf, mconf, link_sta);
+ /* starec he 6g*/
+- mt7996_mcu_sta_he_6g_tlv(skb, sta);
++ mt7996_mcu_sta_he_6g_tlv(skb, link_sta);
+ /* starec eht */
+- mt7996_mcu_sta_eht_tlv(skb, sta);
++ mt7996_mcu_sta_eht_tlv(skb, link_sta);
+ /* starec muru */
+- mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, sta);
++ mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, link_sta);
+ /* starec bfee */
+- mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, sta);
++ mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, link_sta);
+ }
+
+- ret = mt7996_mcu_sta_init_vow(mconf, msta);
++ ret = mt7996_mcu_sta_init_vow(mconf, mlink);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+@@ -2481,16 +2486,16 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+
+ static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *conf,
+- struct mt7996_bss_conf *mconf, u8 *pn)
++ struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink, u8 *pn)
+ {
+ #define TSC_TYPE_BIGTK_PN 2
+- struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
+ struct sta_rec_pn_info *pn_info;
+ struct sk_buff *skb, *rskb;
+ struct tlv *tlv;
+ int ret;
+
+- skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mvif->sta.wcid);
++ skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mlink->wcid);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+@@ -2517,6 +2522,7 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
+ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink,
+ struct ieee80211_key_conf *key)
+ {
+ struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
+@@ -2535,7 +2541,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+
+ bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
+
+- ret = mt7996_mcu_get_pn(dev, conf, mconf, pn);
++ ret = mt7996_mcu_get_pn(dev, conf, mconf, mlink, pn);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+@@ -4811,21 +4817,18 @@ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta)
++ struct mt7996_link_sta *mlink)
+ {
+- struct mt7996_sta *msta;
+ struct sk_buff *skb;
+
+- msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mconf->vif->sta;
+-
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+- &msta->wcid,
++ &mlink->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ /* starec hdr trans */
+- mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
++ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, mlink);
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+@@ -5014,7 +5017,7 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ switch (tag) {
+ case UNI_PER_STA_RSSI:
+ for (i = 0; i < sta_num; ++i) {
+- struct mt7996_sta *msta;
++ struct mt7996_link_sta *mlink;
+ struct mt76_phy *phy;
+ s8 rssi[4];
+ u8 *rcpi;
+@@ -5028,10 +5031,10 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
+ rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
+
+- msta = container_of(wcid, struct mt7996_sta, wcid);
+- phy = msta->vif->phy->mt76;
+- msta->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
+- ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
++ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++ phy = mlink->sta->vif->deflink.phy->mt76;
++ mlink->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
++ ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
+ } else {
+ ret = -EINVAL;
+ dev_err(dev->dev, "Failed to update RSSI for "
+@@ -5069,7 +5072,7 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
+ {
+ u16 sta_list[PER_STA_INFO_MAX_NUM];
+ LIST_HEAD(sta_poll_list);
+- struct mt7996_sta *msta;
++ struct mt7996_link_sta *mlink;
+ int i, ret;
+ bool empty = false;
+
+@@ -5089,13 +5092,13 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
+ empty = true;
+ break;
+ }
+- msta = list_first_entry(&sta_poll_list,
+- struct mt7996_sta,
++ mlink = list_first_entry(&sta_poll_list,
++ struct mt7996_link_sta,
+ wcid.poll_list);
+- list_del_init(&msta->wcid.poll_list);
++ list_del_init(&mlink->wcid.poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+
+- sta_list[i] = msta->wcid.idx;
++ sta_list[i] = mlink->wcid.idx;
+ }
+
+ ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
+@@ -5385,10 +5388,18 @@ int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
+ void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
+ {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink;
+ struct mt7996_phy *poll_phy = (struct mt7996_phy *) data;
+
+- if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
+- poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
++ mutex_lock(&poll_phy->dev->mt76.mutex);
++ mlink = mlink_dereference_protected(msta, 0);
++ if (!mlink)
++ goto out;
++
++ if (poll_phy->scs_ctrl.sta_min_rssi > mlink->ack_signal)
++ poll_phy->scs_ctrl.sta_min_rssi = mlink->ack_signal;
++out:
++ mutex_unlock(&poll_phy->dev->mt76.mutex);
+ }
+
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work)
+@@ -5464,9 +5475,10 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+
+ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
+ struct mt7996_bss_conf *mconf,
+- struct mt7996_sta *msta, enum vow_drr_ctrl_id id)
++ struct mt7996_link_sta *mlink,
++ enum vow_drr_ctrl_id id)
+ {
+- struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
++ struct mt7996_vow_sta_ctrl *vow = mlink ? &mlink->vow : NULL;
+ u32 val = 0;
+ struct {
+ u8 __rsv1[4];
+@@ -5488,11 +5500,11 @@ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
+ .len = cpu_to_le16(sizeof(req) - 4),
+- .wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
++ .wlan_idx = cpu_to_le16(mlink ? mlink->wcid.idx : 0),
+ .band_idx = phy->mt76->band_idx,
+- .wmm_idx = msta ? mconf->mt76.wmm_idx : 0,
++ .wmm_idx = mlink ? mconf->mt76.wmm_idx : 0,
+ .ctrl_id = cpu_to_le32(id),
+- .omac_idx = msta ? mconf->mt76.omac_idx : 0
++ .omac_idx = mlink ? mconf->mt76.omac_idx : 0
+ };
+
+ switch (id) {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 6b03ee17f..370172037 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -302,10 +302,10 @@ struct mt7996_vow_sta_ctrl {
+ u8 drr_quantum[IEEE80211_NUM_ACS];
+ };
+
+-struct mt7996_sta {
++struct mt7996_link_sta {
+ struct mt76_wcid wcid; /* must be first */
+
+- struct mt7996_vif *vif;
++ struct mt7996_sta *sta;
+
+ struct list_head rc_list;
+
+@@ -324,6 +324,13 @@ struct mt7996_sta {
+ struct mt7996_vow_sta_ctrl vow;
+ };
+
++struct mt7996_sta {
++ struct mt7996_link_sta deflink;
++ struct mt7996_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
++
++ struct mt7996_vif *vif;
++};
++
+ struct mt7996_bss_conf {
+ struct mt76_vif mt76; /* must be first */
+
+@@ -783,6 +790,13 @@ mconf_dereference_protected(struct mt7996_vif *mvif, u8 link_id)
+ lockdep_is_held(&mvif->dev->mt76.mutex));
+ }
+
++static inline struct mt7996_link_sta *
++mlink_dereference_protected(struct mt7996_sta *msta, u8 link_id)
++{
++ return rcu_dereference_protected(msta->link[link_id],
++ lockdep_is_held(&msta->vif->dev->mt76.mutex));
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+@@ -827,10 +841,12 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ struct mt7996_bss_conf *mconf, bool enable);
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ struct ieee80211_bss_conf *conf,
+- struct mt7996_bss_conf *mconf, int enable);
++ struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink, int enable);
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+- struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
+- bool enable, bool newly);
++ struct mt7996_bss_conf *mconf,
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink, bool enable, bool newly);
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool add);
+@@ -852,7 +868,8 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
+ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta, bool changed);
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink, bool changed);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+@@ -860,7 +877,9 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ void *data, u16 version);
+ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta, void *data, u32 field);
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink, void *data,
++ u32 field);
+ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
+ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
+ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
+@@ -916,7 +935,8 @@ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
+ struct mt7996_bss_conf *mconf,
+- struct mt7996_sta *msta, enum vow_drr_ctrl_id id);
++ struct mt7996_link_sta *mlink,
++ enum vow_drr_ctrl_id id);
+ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+ void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
+
+@@ -983,7 +1003,7 @@ void mt7996_mac_reset_counters(struct mt7996_phy *phy);
+ void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy);
+ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band);
+ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+- struct ieee80211_vif *vif, bool enable);
++ struct mt7996_link_sta *mlink, bool enable);
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ struct sk_buff *skb, struct mt76_wcid *wcid,
+ struct ieee80211_key_conf *key, int pid,
+@@ -1001,8 +1021,7 @@ void mt7996_mac_dump_work(struct work_struct *work);
+ void mt7996_mac_sta_rc_work(struct work_struct *work);
+ void mt7996_mac_update_stats(struct mt7996_phy *phy);
+ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+- struct mt7996_sta *msta,
+- u8 flowid);
++ struct mt7996_link_sta *mlink, u8 flowid);
+ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct ieee80211_twt_setup *twt);
+@@ -1031,11 +1050,12 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink,
+ struct ieee80211_key_conf *key);
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta);
++ struct mt7996_link_sta *mlink);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index bf55b4309..ba17f947b 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -235,8 +235,8 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+
+ mt7996_tm_rf_switch_mode(dev, rf_test_mode);
+
+- mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, en);
+- mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, en, false);
++ mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, en);
++ mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, &mvif->sta.deflink, en, false);
+
+ mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
+
+@@ -1186,7 +1186,7 @@ mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
+ phy->omac_mask |= BIT_ULL(mvif->deflink.mt76.omac_idx);
+
+ mt7996_mcu_add_dev_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
+- mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
++ mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, true);
+
+ if (td->ibf) {
+ if (td->is_txbf_dut) {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0088-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0088-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch
new file mode 100644
index 0000000..d52447a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0088-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch
@@ -0,0 +1,91 @@
+From 700547976605b447defd5492837cc123c97aba50 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 29 Nov 2023 11:04:50 +0800
+Subject: [PATCH 088/116] wifi: mt76: extend wcid and sta flow for MLO support
+
+Add link related info to wcid, and split sta connection flow of common
+parts for MLO supported chipsets.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c | 12 +++++++++++-
+ mt76.h | 7 ++++++-
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 4fad03dd9..96fab2320 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1066,6 +1066,10 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+ sizeof(mstat.chain_signal));
+ memcpy(status->chain_signal, mstat.chain_signal,
+ sizeof(mstat.chain_signal));
++ if (mstat.wcid) {
++ status->link_valid = mstat.wcid->link_valid;
++ status->link_id = mstat.wcid->link_id;
++ }
+
+ *sta = wcid_to_sta(mstat.wcid);
+ *hw = mt76_phy_hw(dev, mstat.phy_idx);
+@@ -1374,6 +1378,9 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ if (ret)
+ goto out;
+
++ if (phy->hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)
++ goto out;
++
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ struct mt76_txq *mtxq;
+
+@@ -1403,12 +1410,15 @@ void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+ int i, idx = wcid->idx;
+
+- for (i = 0; i < ARRAY_SIZE(wcid->aggr); i++)
++ for (i = 0; !sta->valid_links && i < ARRAY_SIZE(wcid->aggr); i++)
+ mt76_rx_aggr_stop(dev, wcid, i);
+
+ if (dev->drv->sta_remove)
+ dev->drv->sta_remove(dev, vif, sta);
+
++ if (sta->valid_links)
++ return;
++
+ mt76_wcid_cleanup(dev, wcid);
+
+ mt76_wcid_mask_clear(dev->wcid_mask, idx);
+diff --git a/mt76.h b/mt76.h
+index 0abb8ebf5..b60b5161b 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -381,6 +381,9 @@ struct mt76_wcid {
+ u8 sta:1;
+ u8 amsdu:1;
+ u8 phy_idx:2;
++ u8 link_id:4;
++ bool link_valid;
++ struct mt76_wcid *def_wcid;
+
+ u8 rx_check_pn;
+ u8 rx_key_pn[IEEE80211_NUM_TIDS + 1][6];
+@@ -1366,11 +1369,13 @@ mtxq_to_txq(struct mt76_txq *mtxq)
+ static inline struct ieee80211_sta *
+ wcid_to_sta(struct mt76_wcid *wcid)
+ {
+- void *ptr = wcid;
++ void *ptr;
+
+ if (!wcid || !wcid->sta)
+ return NULL;
+
++ ptr = wcid->def_wcid ?: wcid;
++
+ return container_of(ptr, struct ieee80211_sta, drv_priv);
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0089-wifi-mt76-mt7996-enable-MLO-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0089-wifi-mt76-mt7996-enable-MLO-capability.patch
new file mode 100644
index 0000000..479e720
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0089-wifi-mt76-mt7996-enable-MLO-capability.patch
@@ -0,0 +1,108 @@
+From c7aef97eb798ee5c8e779e5d0514cbe68b41f6a3 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 30 Nov 2023 16:31:17 +0800
+Subject: [PATCH 089/116] wifi: mt76: mt7996: enable MLO capability
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/eeprom.c | 6 ++++++
+ mt7996/init.c | 40 ++++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 44 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 51455d877..0393e93bf 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -387,6 +387,12 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+ break;
+ }
+
++ /* TODO: for MLO, we enable all band capabilities */
++ phy->mt76->cap.has_2ghz = true;
++ phy->mt76->cap.has_5ghz = true;
++ if (is_mt7996(&phy->dev->mt76))
++ phy->mt76->cap.has_6ghz = true;
++
+ return ret;
+ }
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 381e1292c..c6eb6a5c2 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -34,16 +34,45 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ .limits = if_limits,
+ .n_limits = ARRAY_SIZE(if_limits),
+ .max_interfaces = MT7996_MAX_INTERFACES,
+- .num_different_channels = 1,
++ .num_different_channels = 3,
+ .beacon_int_infra_match = true,
++ /*
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80) |
+ BIT(NL80211_CHAN_WIDTH_160),
++ */
+ }
+ };
+
++static const u8 mt7996_if_types_ext_capa[] = {
++ [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
++ [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
++};
++
++static const struct wiphy_iftype_ext_capab mt7996_iftypes_ext_capa[] = {
++ {
++ .iftype = NL80211_IFTYPE_STATION,
++ .extended_capabilities = mt7996_if_types_ext_capa,
++ .extended_capabilities_mask = mt7996_if_types_ext_capa,
++ .extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
++ .mld_capa_and_ops = 2,
++ },
++ {
++ .iftype = NL80211_IFTYPE_AP,
++ .extended_capabilities = mt7996_if_types_ext_capa,
++ .extended_capabilities_mask = mt7996_if_types_ext_capa,
++ .extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
++ .mld_capa_and_ops = 2,
++ /* the max number of simultaneous links is defined as the
++ * maximum number of affiliated APs minus 1.
++ * mt7996 could have 3 links in an MLD AP, so currently
++ * hardcode it to 2.
++ */
++ },
++};
++
+ static ssize_t mt7996_thermal_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+@@ -417,8 +446,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+ ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+- ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
++ // ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
++ ieee80211_hw_set(hw, CONNECTION_MONITOR);
+
+ hw->max_tx_fragments = 4;
+
+@@ -462,6 +492,12 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+
+ wiphy->max_scan_ssids = 4;
+ wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
++
++ /* enable MLO support */
++ wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
++ wiphy->iftype_ext_capab = mt7996_iftypes_ext_capa;
++ wiphy->num_iftype_ext_capab = ARRAY_SIZE(mt7996_iftypes_ext_capa);
++ wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
+ }
+
+ static void
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0090-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0090-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch
new file mode 100644
index 0000000..d6b68ca
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0090-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch
@@ -0,0 +1,590 @@
+From 4cc16942c3f1ec92f9a25802341f456d92dd5cc4 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 23 Nov 2023 18:22:11 +0800
+Subject: [PATCH 090/116] wifi: mt76: mt7996: support multi-link vif links and
+ MLO bss callbacks
+
+Rework add/remove interface functions to add/remove bss_conf functions,
+and also switch to callbacks for MLO bss.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 298 +++++++++++++++++++++++++++++++++++++++---------
+ mt7996/mcu.c | 29 +++--
+ mt7996/mt7996.h | 9 ++
+ 3 files changed, 270 insertions(+), 66 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 4069ffb70..38aae12be 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -205,6 +205,38 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
+ return -1;
+ }
+
++static int get_own_mld_idx(u64 mask, bool group_mld)
++{
++ u8 start, end;
++ int i;
++
++ if (group_mld) {
++ start = 0;
++ end = 15;
++ } else {
++ start = 16;
++ end = 63;
++ }
++
++ i = get_free_idx(mask, start, end);
++ if (i)
++ return i - 1;
++
++ return -1;
++}
++
++static int get_mld_remap_idx(u64 mask)
++{
++ u8 start = 0, end = 15;
++ int i;
++
++ i = get_free_idx(mask, start, end);
++ if (i)
++ return i - 1;
++
++ return -1;
++}
++
+ static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
+ {
+ int i;
+@@ -223,48 +255,108 @@ static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
+ }
+ }
+
+-static int mt7996_add_interface(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif)
++static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf)
+ {
+- struct ieee80211_bss_conf *conf = &vif->bss_conf;
++ struct mt7996_phy *phy = mconf->phy;
++ struct mt7996_dev *dev = phy->dev;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_bss_conf *mconf = &mvif->deflink;
+- struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+- struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ u8 link_id = conf->link_id;
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(&mvif->sta, link_id);
++
++ if (!mlink)
++ return;
++
++ mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
++ mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
++ mt7996_mcu_add_dev_info(phy, conf, mconf, false);
++
++ rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
++ rcu_assign_pointer(mvif->link[link_id], NULL);
++ rcu_assign_pointer(mvif->sta.link[link_id], NULL);
++
++ dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
++ dev->mld_id_mask &= ~BIT_ULL(mconf->own_mld_id);
++ phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
++
++ spin_lock_bh(&dev->mt76.sta_poll_lock);
++ if (!list_empty(&mlink->wcid.poll_list))
++ list_del_init(&mlink->wcid.poll_list);
++ spin_unlock_bh(&dev->mt76.sta_poll_lock);
++
++ mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
++
++ if (mlink != &mvif->sta.deflink)
++ kfree(mlink);
++
++ if (mconf != &mvif->deflink)
++ kfree(mconf);
++}
++
++static int mt7996_add_bss_conf(struct mt7996_phy *phy,
++ struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_dev *dev = phy->dev;
++ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
+ struct mt76_txq *mtxq;
+ u8 band_idx = phy->mt76->band_idx;
+- int idx, ret = 0;
+-
+- mutex_lock(&dev->mt76.mutex);
++ u8 link_id = conf->link_id;
++ int idx, ret;
+
+- if (vif->type == NL80211_IFTYPE_MONITOR &&
+- is_zero_ether_addr(vif->addr))
+- phy->monitor_vif = vif;
++ if (conf != &vif->bss_conf) {
++ mconf = kzalloc(sizeof(*mconf), GFP_KERNEL);
++ if (!mconf)
++ return -ENOMEM;
++ } else {
++ mconf = &mvif->deflink;
++ }
+
+ mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+ if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
+ ret = -ENOSPC;
+- goto out;
++ goto error;
+ }
+
+ idx = get_omac_idx(vif->type, phy->omac_mask);
+ if (idx < 0) {
+ ret = -ENOSPC;
+- goto out;
++ goto error;
++ }
++
++ mconf->own_mld_id = get_own_mld_idx(dev->mld_id_mask, false);
++ if (mconf->own_mld_id < 0) {
++ ret = -ENOSPC;
++ goto error;
+ }
++
+ mconf->mt76.omac_idx = idx;
+ mconf->vif = mvif;
+ mconf->phy = phy;
+ mconf->mt76.band_idx = band_idx;
+ mconf->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
+- mvif->dev = dev;
++ mconf->link_id = link_id;
+
+ ret = mt7996_mcu_add_dev_info(phy, conf, mconf, true);
+ if (ret)
+- goto out;
++ goto error;
++
++ if (ieee80211_vif_is_mld(vif)) {
++ mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
++ if (!mlink) {
++ ret = -ENOMEM;
++ goto error;
++ }
++ } else {
++ mlink = &mvif->sta.deflink;
++ }
+
+ dev->mt76.vif_mask |= BIT_ULL(mconf->mt76.idx);
++ dev->mld_id_mask |= BIT_ULL(mconf->own_mld_id);
+ phy->omac_mask |= BIT_ULL(mconf->mt76.omac_idx);
+
+ idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
+@@ -275,6 +367,9 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ mlink->wcid.phy_idx = band_idx;
+ mlink->wcid.hw_key_idx = -1;
+ mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++ mlink->wcid.def_wcid = &mvif->sta.deflink.wcid;
++ mlink->wcid.link_id = link_id;
++ mlink->wcid.link_valid = ieee80211_vif_is_mld(vif);
+ mlink->sta = &mvif->sta;
+ mlink->sta->vif = mvif;
+ mt76_wcid_init(&mlink->wcid);
+@@ -296,7 +391,6 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
+ else
+ mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
+-
+ mt7996_init_bitrate_mask(mconf);
+
+ mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+@@ -306,10 +400,32 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ if (vif->type != NL80211_IFTYPE_STATION)
+ mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, true);
+ rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
+- rcu_assign_pointer(mvif->link[0], mconf);
+- rcu_assign_pointer(mvif->sta.link[0], mlink);
++ rcu_assign_pointer(mvif->link[link_id], mconf);
++ rcu_assign_pointer(mvif->sta.link[link_id], mlink);
+
+-out:
++ return 0;
++error:
++ mt7996_remove_bss_conf(vif, conf, mconf);
++ return ret;
++}
++
++static int mt7996_add_interface(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ int ret = 0;
++
++ mutex_lock(&dev->mt76.mutex);
++ if (vif->type == NL80211_IFTYPE_MONITOR &&
++ is_zero_ether_addr(vif->addr))
++ phy->monitor_vif = vif;
++
++ mvif->dev = dev;
++ mvif->sta.vif = mvif;
++
++ ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+@@ -321,38 +437,23 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_bss_conf *conf;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf;
+- struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+- int idx = mlink->wcid.idx;
+
+ cancel_delayed_work_sync(&phy->scan_work);
+
+ mutex_lock(&dev->mt76.mutex);
+
+- conf = link_conf_dereference_protected(vif, 0);
+- mconf = mconf_dereference_protected(mvif, 0);
+- mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
+- mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
++ if (test_bit(MT76_SCANNING, &phy->mt76->state))
++ mt7996_scan_complete(phy, true);
+
+ if (vif == phy->monitor_vif)
+ phy->monitor_vif = NULL;
+
+- mt7996_mcu_add_dev_info(phy, conf, mconf, false);
+-
+- rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+-
+- dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
+- phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
+-
+- spin_lock_bh(&dev->mt76.sta_poll_lock);
+- if (!list_empty(&mlink->wcid.poll_list))
+- list_del_init(&mlink->wcid.poll_list);
+- spin_unlock_bh(&dev->mt76.sta_poll_lock);
++ conf = link_conf_dereference_protected(vif, 0);
++ mconf = mconf_dereference_protected(mvif, 0);
+
+- mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+- rcu_assign_pointer(mvif->link[0], NULL);
+- rcu_assign_pointer(mvif->sta.link[0], NULL);
++ mt7996_remove_bss_conf(vif, conf, mconf);
+
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -716,10 +817,31 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS3(band), mu[3]);
+ }
+
+-static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif,
+- struct ieee80211_bss_conf *info,
+- u64 changed)
++static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif, u64 changed)
++{
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++
++ mutex_lock(&dev->mt76.mutex);
++
++ if (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
++ struct ieee80211_bss_conf *conf = &vif->bss_conf;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
++ struct mt7996_link_sta *mlink = mlink_dereference_protected(&mvif->sta, 0);
++
++ mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
++ mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
++ }
++
++ mutex_unlock(&dev->mt76.mutex);
++}
++
++static void mt7996_link_info_changed(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *info,
++ u64 changed)
+ {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf;
+@@ -735,7 +857,6 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ * and then peer references bss_info_rfch to set bandwidth cap.
+ */
+ if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
+- (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
+ (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
+ mt7996_mcu_add_bss_info(phy, info, mconf, mlink, true);
+ mt7996_mcu_add_sta(dev, info, mconf, NULL, mlink, true,
+@@ -1078,7 +1199,7 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ u64 ret;
+
+ mutex_lock(&dev->mt76.mutex);
+- mconf = mconf_dereference_protected(mvif, 0);
++ mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ ret = __mt7996_get_tsf(hw, mconf);
+ mutex_unlock(&dev->mt76.mutex);
+
+@@ -1101,7 +1222,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mutex_lock(&dev->mt76.mutex);
+
+- mconf = mconf_dereference_protected(mvif, 0);
++ mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : mconf->mt76.omac_idx;
+ mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+@@ -1129,7 +1250,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mutex_lock(&dev->mt76.mutex);
+
+- mconf = mconf_dereference_protected(mvif, 0);
++ mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : mconf->mt76.omac_idx;
+ mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+@@ -1304,7 +1425,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+
+ mutex_lock(&dev->mt76.mutex);
+- mconf = mconf_dereference_protected(mvif, 0);
++ mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ mconf->bitrate_mask = *mask;
+ mutex_unlock(&dev->mt76.mutex);
+
+@@ -1524,7 +1645,7 @@ void mt7996_get_et_stats(struct ieee80211_hw *hw,
+ int i, ei = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+- mconf = mconf_dereference_protected(mvif, 0);
++ mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ wi.idx = mconf->mt76.idx,
+
+ mt7996_mac_update_stats(phy);
+@@ -1897,6 +2018,8 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct mt7996_phy *phy = ctx->phy;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_bss_conf *mconf;
++ u8 link_id = link_conf->link_id;
++ int ret;
+
+ wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
+ vif->addr, vif->type, link_conf->link_id,
+@@ -1904,10 +2027,24 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+
+ mutex_lock(&phy->dev->mt76.mutex);
+
+- mconf = mconf_dereference_protected(mvif, 0);
++ /* remove first */
++ if (rcu_access_pointer(mvif->link[link_id]))
++ mt7996_remove_bss_conf(vif, link_conf,
++ mconf_dereference_protected(mvif, link_id));
++
++ ret = mt7996_add_bss_conf(phy, vif, link_conf);
++ if (ret) {
++ mutex_unlock(&phy->dev->mt76.mutex);
++ return ret;
++ }
++
++ mconf = mconf_dereference_protected(mvif, link_id);
+ mconf->chanctx = ctx;
+ ctx->nbss_assigned++;
+
++ if (mt7996_hw_phy(hw) == phy)
++ mvif->master_link_id = link_id;
++
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ return 0;
+@@ -1933,7 +2070,7 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ mt7996_scan_complete(phy, true);
+
+- mconf = mconf_dereference_protected(mvif, 0);
++ mconf = mconf_dereference_protected(mvif, link_conf->link_id);
+ mconf->chanctx = NULL;
+ ctx->nbss_assigned--;
+
+@@ -1973,6 +2110,57 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ return mt7996_set_channel(phy, &new_ctx->chandef);
+ }
+
++static int
++mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ u16 old_links, u16 new_links,
++ struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
++{
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ unsigned long rem = old_links & ~new_links;
++ unsigned int link_id;
++ int ret = 0;
++
++ if (old_links == new_links)
++ return 0;
++
++ mutex_lock(&dev->mt76.mutex);
++
++ /* check if there's legacy bss needed to be removed */
++ if (rcu_access_pointer(mvif->link[0]) == &mvif->deflink)
++ rem |= BIT(0);
++ /* remove first */
++ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++
++ if (!mconf)
++ continue;
++
++ mt7996_remove_bss_conf(vif, old[link_id], mconf);
++ }
++
++ if (!old_links) {
++ mvif->group_mld_id = get_own_mld_idx(dev->mld_id_mask, true);
++ dev->mld_id_mask |= BIT_ULL(mvif->group_mld_id);
++
++ mvif->mld_remap_id = get_mld_remap_idx(dev->mld_remap_id_mask);
++ dev->mld_remap_id_mask |= BIT_ULL(mvif->mld_remap_id);
++ }
++
++ /* fallback to non-MLO interface */
++ if (!new_links) {
++ ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++ dev->mld_id_mask &= ~BIT_ULL(mvif->group_mld_id);
++ dev->mld_remap_id_mask &= ~BIT_ULL(mvif->mld_remap_id);
++ }
++
++ mutex_unlock(&dev->mt76.mutex);
++
++ return ret;
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ .tx = mt7996_tx,
+ .start = mt7996_start,
+@@ -1982,7 +2170,8 @@ const struct ieee80211_ops mt7996_ops = {
+ .config = mt7996_config,
+ .conf_tx = mt7996_conf_tx,
+ .configure_filter = mt7996_configure_filter,
+- .bss_info_changed = mt7996_bss_info_changed,
++ .vif_cfg_changed = mt7996_vif_cfg_changed,
++ .link_info_changed = mt7996_link_info_changed,
+ .sta_state = mt76_sta_state,
+ .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ .sta_rc_update = mt7996_sta_rc_update,
+@@ -2028,4 +2217,5 @@ const struct ieee80211_ops mt7996_ops = {
+ .assign_vif_chanctx = mt7996_assign_vif_chanctx,
+ .unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
+ .switch_vif_chanctx = mt7996_switch_vif_chanctx,
++ .change_vif_links = mt7996_change_vif_links,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6d2b517e0..0eccc112d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1047,15 +1047,23 @@ static void
+ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ struct mt7996_bss_conf *mconf)
+ {
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct bss_mld_tlv *mld;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld));
+-
+ mld = (struct bss_mld_tlv *)tlv;
+- mld->group_mld_id = 0xff;
+- mld->own_mld_id = mconf->mt76.idx;
+- mld->remap_idx = 0xff;
++
++ if (ieee80211_vif_is_mld(vif)) {
++ mld->group_mld_id = mvif->group_mld_id;
++ mld->remap_idx = mvif->mld_remap_id;
++ memcpy(mld->mac_addr, vif->addr, ETH_ALEN);
++ } else {
++ mld->group_mld_id = 0xff;
++ mld->remap_idx = 0xff;
++ }
++
++ mld->own_mld_id = mconf->own_mld_id;
+ }
+
+ static void
+@@ -1136,13 +1144,11 @@ mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
+ }
+
+ static int
+-mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+- struct ieee80211_bss_conf *conf,
+- struct mt7996_bss_conf *mconf,
+- struct ieee80211_sta *sta,
+- struct mt76_phy *phy, u16 wlan_idx,
+- bool enable)
++mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
++ u16 wlan_idx, bool enable)
+ {
++ struct mt76_phy *phy = mconf->phy->mt76;
+ struct ieee80211_vif *vif = conf->vif;
+ struct cfg80211_chan_def *chandef = &phy->chandef;
+ struct mt76_connac_bss_basic_tlv *bss;
+@@ -1254,8 +1260,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ return PTR_ERR(skb);
+
+ /* bss_basic must be first */
+- mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
+- mlink->wcid.idx, enable);
++ mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, mlink->wcid.idx, enable);
+ mt7996_mcu_bss_sec_tlv(skb, mconf);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 370172037..ed8c82e8c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -340,6 +340,9 @@ struct mt7996_bss_conf {
+ struct cfg80211_bitrate_mask bitrate_mask;
+
+ struct mt7996_chanctx *chanctx;
++
++ u8 link_id;
++ u8 own_mld_id;
+ };
+
+ struct mt7996_vif {
+@@ -348,6 +351,10 @@ struct mt7996_vif {
+
+ struct mt7996_sta sta;
+ struct mt7996_dev *dev;
++
++ u8 master_link_id;
++ u8 group_mld_id;
++ u8 mld_remap_id;
+ };
+
+ /* crash-dump */
+@@ -549,6 +556,8 @@ struct mt7996_dev {
+ u16 chainmask;
+ u8 chainshift[__MT_MAX_BAND];
+ u32 hif_idx;
++ u64 mld_id_mask;
++ u64 mld_remap_id_mask;
+
+ struct work_struct init_work;
+ struct work_struct rc_work;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0091-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0091-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch
new file mode 100644
index 0000000..c1eda8b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0091-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch
@@ -0,0 +1,604 @@
+From 9bff2b264652d23bf0dc056f1e76120951c8d833 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 29 Nov 2023 10:12:39 +0800
+Subject: [PATCH 091/116] wifi: mt76: mt7996: support multi-link sta links and
+ MLO sta callbacks
+
+Rework add_sta functions to add_link_sta functions, and support
+.change_sta_links callback.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac_mcu.h | 2 +
+ mt7996/main.c | 309 ++++++++++++++++++++++++++++++++++++----------
+ mt7996/mcu.c | 117 ++++++++++++++++++
+ mt7996/mcu.h | 29 +++++
+ mt7996/mt7996.h | 7 ++
+ 5 files changed, 398 insertions(+), 66 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index f7da63658..d45765beb 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -814,7 +814,9 @@ enum {
+ STA_REC_HE_6G = 0x17,
+ STA_REC_HE_V2 = 0x19,
+ STA_REC_MLD = 0x20,
++ STA_REC_EHT_MLD = 0x21,
+ STA_REC_EHT = 0x22,
++ STA_REC_MLD_TEARDOWN = 0x23,
+ STA_REC_PN_INFO = 0x26,
+ STA_REC_KEY_V3 = 0x27,
+ STA_REC_HDRT = 0x28,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 38aae12be..b193e638e 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -928,42 +928,234 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
++static void mt7996_remove_link_sta(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
++ struct ieee80211_link_sta *link_sta,
++ struct mt7996_link_sta *mlink)
++{
++ struct ieee80211_sta *sta = link_sta->sta;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ int i;
++
++ if (!mlink)
++ return;
++
++ for (i = 0; i < ARRAY_SIZE(mlink->wcid.aggr); i++)
++ mt76_rx_aggr_stop(&dev->mt76, &mlink->wcid, i);
++
++ if (sta->mlo)
++ mt7996_mcu_teardown_mld_sta(dev, mconf, mlink);
++ else
++ mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
++
++ mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
++ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++ for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
++ mt7996_mac_twt_teardown_flow(dev, mlink, i);
++
++ rcu_assign_pointer(mlink->sta->link[mlink->wcid.link_id], NULL);
++
++ spin_lock_bh(&dev->mt76.sta_poll_lock);
++ if (!list_empty(&mlink->wcid.poll_list))
++ list_del_init(&mlink->wcid.poll_list);
++ if (!list_empty(&mlink->rc_list))
++ list_del_init(&mlink->rc_list);
++ spin_unlock_bh(&dev->mt76.sta_poll_lock);
++
++ /* TODO: update primary link */
++ if (sta->valid_links) {
++ if (mlink->wcid.link_id == msta->pri_link)
++ msta->pri_link = msta->sec_link;
++
++ if (sta->valid_links & ~(BIT(msta->pri_link)))
++ msta->sec_link = __ffs(sta->valid_links & ~(BIT(msta->pri_link)));
++ else
++ msta->sec_link = msta->pri_link;
++ }
++
++ mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
++ mt76_wcid_mask_clear(dev->mt76.wcid_mask, mlink->wcid.idx);
++ mt76_wcid_mask_clear(dev->mt76.wcid_phy_mask, mlink->wcid.idx);
++
++ if (mlink != &msta->deflink)
++ kfree(mlink);
++}
++
++static int mt7996_add_link_sta(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct mt7996_bss_conf *mconf,
++ struct ieee80211_link_sta *link_sta, bool assoc)
++{
++ struct ieee80211_sta *sta = link_sta->sta;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ u8 link_id = link_sta->link_id;
++ struct mt7996_link_sta *mlink = NULL;
++ int idx, ret;
++
++ if (!rcu_access_pointer(msta->link[link_id])) {
++ idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
++ if (idx < 0)
++ return -ENOSPC;
++
++ if (sta->mlo) {
++ mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
++ if (!mlink)
++ return -ENOMEM;
++ } else {
++ mlink = &msta->deflink;
++ }
++
++ INIT_LIST_HEAD(&mlink->rc_list);
++ INIT_LIST_HEAD(&mlink->wcid.poll_list);
++ msta->vif = mvif;
++ mlink->wcid.sta = 1;
++ mlink->wcid.idx = idx;
++ mlink->wcid.phy_idx = mconf->phy->mt76->band_idx;
++ mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++ mlink->wcid.def_wcid = &msta->deflink.wcid;
++ mlink->sta = msta;
++ if (sta->valid_links) {
++ mlink->wcid.link_valid = true;
++ mlink->wcid.link_id = link_id;
++ if (sta->valid_links & ~(BIT(msta->pri_link)))
++ msta->sec_link = __ffs(sta->valid_links &
++ ~(BIT(msta->pri_link)));
++ else
++ msta->sec_link = msta->pri_link;
++ }
++
++ rcu_assign_pointer(msta->link[link_id], mlink);
++
++ ewma_signal_init(&mlink->wcid.rssi);
++ if (mconf->phy->mt76->band_idx == MT_BAND1)
++ mt76_wcid_mask_set(dev->mt76.wcid_phy_mask, idx);
++ rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
++ mt76_wcid_init(&mlink->wcid);
++ }
++
++ if (!assoc)
++ return 0;
++
++ if (!mlink)
++ mlink = mlink_dereference_protected(msta, link_id);
++ mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
++ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++ ret = mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
++ if (ret)
++ goto error;
++
++ ret = mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
++ if (ret)
++ goto error;
++
++ ewma_avg_signal_init(&mlink->avg_ack_signal);
++
++ return 0;
++error:
++ mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
++ return ret;
++}
++
++static void
++mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, unsigned long rem)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ unsigned int link_id;
++
++ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
++ struct ieee80211_bss_conf *conf =
++ link_conf_dereference_protected(vif, link_id);
++ struct ieee80211_link_sta *link_sta =
++ link_sta_dereference_protected(sta, link_id);
++
++ mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
++ }
++}
++
++static int
++mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, unsigned long add,
++ bool assoc)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink;
++ unsigned int link_id;
++ int i, ret;
++
++ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct ieee80211_bss_conf *conf =
++ link_conf_dereference_protected(vif, link_id);
++ struct ieee80211_link_sta *link_sta =
++ link_sta_dereference_protected(sta, link_id);
++
++ ret = mt7996_add_link_sta(dev, conf, mconf, link_sta, assoc);
++ if (ret)
++ goto error;
++ }
++
++ if (!assoc)
++ return 0;
++
++ mlink = mlink_dereference_protected(msta, msta->pri_link);
++ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
++ struct mt76_txq *mtxq;
++
++ if (!sta->txq[i])
++ continue;
++ mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
++ mtxq->wcid = mlink->wcid.idx;
++ }
++
++ ret = mt7996_mcu_add_mld_sta(dev, vif, sta, add);
++ if (ret)
++ goto error;
++
++ return 0;
++error:
++ mt7996_mac_sta_remove_links(dev, vif, sta, add);
++ return ret;
++}
++
+ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
+- struct mt7996_link_sta *mlink = &msta->deflink;
+- u8 band_idx = mconf->phy->mt76->band_idx;
+- int idx;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_bss_conf *mconf;
++ u8 link_id = sta->valid_links ? __ffs(sta->valid_links) : 0;
++ unsigned long add = BIT(link_id);
++ int ret;
+
+ #ifdef CONFIG_MTK_VENDOR
+ struct mt7996_phy *phy = &dev->phy;
+ #endif
+
+- idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+- if (idx < 0)
+- return -ENOSPC;
+-
+- INIT_LIST_HEAD(&mlink->rc_list);
+- INIT_LIST_HEAD(&mlink->wcid.poll_list);
+- msta->vif = mvif;
+- mlink->wcid.sta = 1;
+- mlink->wcid.idx = idx;
+- mlink->wcid.phy_idx = band_idx;
+- mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+- mlink->sta = msta;
+-
+- rcu_assign_pointer(msta->link[0], mlink);
++ msta->pri_link = link_id;
++ ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
++ if (ret)
++ return ret;
+
+ #ifdef CONFIG_MTK_VENDOR
++ mconf = mconf_dereference_protected(mvif, link_id);
+ mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
+ #endif
+
+ #ifdef CONFIG_MTK_VENDOR
+- switch (band_idx) {
++ switch (mconf->phy->mt76->band_idx) {
+ case MT_BAND1:
+ phy = mt7996_phy2(dev);
+ break;
+@@ -986,28 +1178,11 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_bss_conf *mconf;
+- struct mt7996_link_sta *mlink;
+- struct ieee80211_bss_conf *conf;
+- struct ieee80211_link_sta *link_sta;
++ unsigned long add = sta->valid_links ?: BIT(0);
+
+ mutex_lock(&dev->mt76.mutex);
+
+- conf = link_conf_dereference_protected(vif, 0);
+- mconf = mconf_dereference_protected(mvif, 0);
+- link_sta = link_sta_dereference_protected(sta, 0);
+- mlink = mlink_dereference_protected(msta, 0);
+-
+- mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
+- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+- mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
+- mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
+- mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+-
+- ewma_avg_signal_init(&mlink->avg_ack_signal);
++ mt7996_mac_sta_add_links(dev, vif, sta, add, true);
+
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -1016,34 +1191,9 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_bss_conf *mconf;
+- struct mt7996_link_sta *mlink;
+- struct ieee80211_bss_conf *conf;
+- struct ieee80211_link_sta *link_sta;
+- int i;
+-
+- conf = link_conf_dereference_protected(vif, 0);
+- mconf = mconf_dereference_protected(mvif, 0);
+- link_sta = link_sta_dereference_protected(sta, 0);
+- mlink = mlink_dereference_protected(msta, 0);
+- mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
+-
+- mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
+- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+- for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
+- mt7996_mac_twt_teardown_flow(dev, mlink, i);
++ unsigned long rem = sta->valid_links ?: BIT(0);
+
+- spin_lock_bh(&mdev->sta_poll_lock);
+- if (!list_empty(&mlink->wcid.poll_list))
+- list_del_init(&mlink->wcid.poll_list);
+- if (!list_empty(&mlink->rc_list))
+- list_del_init(&mlink->rc_list);
+- spin_unlock_bh(&mdev->sta_poll_lock);
+-
+- rcu_assign_pointer(msta->link[0], NULL);
++ mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ }
+
+ static void mt7996_tx(struct ieee80211_hw *hw,
+@@ -2161,6 +2311,32 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ return ret;
+ }
+
++static int
++mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, u16 old_links, u16 new_links)
++{
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ unsigned long add = new_links & ~old_links;
++ unsigned long rem = old_links & ~new_links;
++ int ret = 0;
++
++ mutex_lock(&dev->mt76.mutex);
++
++ if (rem)
++ mt7996_mac_sta_remove_links(dev, vif, sta, rem);
++
++ ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
++ if (ret)
++ goto remove;
++
++ goto out;
++remove:
++ mt7996_mac_sta_remove_links(dev, vif, sta, add);
++out:
++ mutex_unlock(&dev->mt76.mutex);
++ return ret;
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ .tx = mt7996_tx,
+ .start = mt7996_start,
+@@ -2218,4 +2394,5 @@ const struct ieee80211_ops mt7996_ops = {
+ .unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
+ .switch_vif_chanctx = mt7996_switch_vif_chanctx,
+ .change_vif_links = mt7996_change_vif_links,
++ .change_sta_links = mt7996_change_sta_links,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0eccc112d..ed854b473 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2425,6 +2425,123 @@ out:
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+
++static void
++mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
++ struct ieee80211_sta *sta)
++{
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct sta_rec_mld_setup *mld_setup;
++ struct mld_setup_link *mld_setup_link;
++ struct mt7996_link_sta *mlink;
++ struct mt7996_bss_conf *mconf;
++ struct tlv *tlv;
++ unsigned long valid_links = sta->valid_links;
++ unsigned int link_id;
++
++ mlink = mlink_dereference_protected(msta, msta->pri_link);
++ if (!mlink)
++ return;
++
++ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD,
++ sizeof(*mld_setup) +
++ sizeof(struct mld_setup_link) *
++ hweight16(sta->valid_links));
++
++ mld_setup = (struct sta_rec_mld_setup *)tlv;
++ memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN);
++ mld_setup->setup_wcid = cpu_to_le16(mlink->wcid.idx);
++ mld_setup->primary_id = cpu_to_le16(mlink->wcid.idx);
++ if (msta->sec_link != msta->pri_link) {
++ mlink = mlink_dereference_protected(msta, msta->sec_link);
++ if (!mlink)
++ return;
++ }
++ mld_setup->seconed_id = cpu_to_le16(mlink->wcid.idx);
++ mld_setup->link_num = hweight16(sta->valid_links);
++
++ mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ mlink = mlink_dereference_protected(msta, link_id);
++ mconf = mconf_dereference_protected(msta->vif, link_id);
++
++ mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
++ mld_setup_link->bss_idx = mconf->mt76.idx;
++ mld_setup_link++;
++ }
++}
++
++static void
++mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
++ struct ieee80211_sta *sta)
++{
++ struct sta_rec_eht_mld *eht_mld;
++ struct tlv *tlv;
++ int i;
++
++ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld));
++ eht_mld = (struct sta_rec_eht_mld *)tlv;
++
++ for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++)
++ eht_mld->str_cap[i] = 0x7;
++ /* TODO:
++ eht_mld->nsep = ;
++ eht_mld->eml_cap = cpu_to_le16()
++ */
++}
++
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, unsigned long add)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ unsigned int link_id;
++
++ if (!sta->mlo)
++ return 0;
++
++ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
++ struct sk_buff *skb;
++ int ret;
++
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
++ &mlink->wcid,
++ MT7996_STA_UPDATE_MAX_SIZE);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++ /* starec mld setup */
++ mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta);
++ /* starec eht mld */
++ mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
++ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
++ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++ if (ret)
++ return ret;
++ }
++ return 0;
++}
++int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
++ struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink)
++{
++ struct sk_buff *skb;
++
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76,
++ &mconf->mt76,
++ &mlink->wcid,
++ MT7996_STA_UPDATE_MAX_SIZE);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_TEARDOWN, sizeof(struct tlv));
++
++ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++}
++
+ static int
+ mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
+ struct sk_buff *skb,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 814072e3a..ee36cf5ed 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -698,6 +698,35 @@ struct sta_rec_hdr_trans {
+ u8 mesh;
+ } __packed;
+
++struct sta_rec_mld_setup {
++ __le16 tag;
++ __le16 len;
++ u8 mld_addr[ETH_ALEN];
++ __le16 primary_id;
++ __le16 seconed_id;
++ __le16 setup_wcid;
++ u8 link_num;
++ u8 info;
++ u8 __rsv[2];
++ u8 link_info[];
++} __packed;
++
++struct mld_setup_link {
++ __le16 wcid;
++ u8 bss_idx;
++ u8 __rsv[1];
++} __packed;
++
++struct sta_rec_eht_mld {
++ __le16 tag;
++ __le16 len;
++ u8 nsep;
++ u8 __rsv1[2];
++ u8 str_cap[__MT_MAX_BAND];
++ __le16 eml_cap;
++ u8 __rsv2[4];
++} __packed;
++
+ struct hdr_trans_en {
+ __le16 tag;
+ __le16 len;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index ed8c82e8c..913aff173 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -329,6 +329,8 @@ struct mt7996_sta {
+ struct mt7996_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+
+ struct mt7996_vif *vif;
++ u8 pri_link;
++ u8 sec_link;
+ };
+
+ struct mt7996_bss_conf {
+@@ -856,6 +858,9 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_link_sta *mlink, bool enable, bool newly);
++int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
++ struct mt7996_bss_conf *mconf,
++ struct mt7996_link_sta *mlink);
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool add);
+@@ -879,6 +884,8 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ struct mt7996_bss_conf *mconf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_link_sta *mlink, bool changed);
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, unsigned long add);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0092-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0092-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch
new file mode 100644
index 0000000..8e8cfb9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0092-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch
@@ -0,0 +1,271 @@
+From 04384a0b4762de5741c6220aba3b0500e260d8a2 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 1 Dec 2023 16:01:53 +0800
+Subject: [PATCH 092/116] wifi: mt76: mt7996: introduce mt7996_band_phy() for
+ ch band and phy mapping
+
+For MLO devices, one ieee80211_hw can be mapped to several bands, and
+thus several mt76_phy. Add mt7996_band_phy() to temporarily do the
+mapping.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 149 +++++++++++++++++++++++++++++-------------------
+ mt7996/mt7996.h | 22 +++++++
+ 2 files changed, 112 insertions(+), 59 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index b193e638e..435815e9a 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -41,9 +41,8 @@ static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
+ int mt7996_run(struct ieee80211_hw *hw)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ bool running;
+- int ret;
++ int band, ret;
+
+ running = mt7996_dev_running(dev);
+ if (!running) {
+@@ -62,66 +61,76 @@ int mt7996_run(struct ieee80211_hw *hw)
+
+ mt7996_testmode_disable_all(dev);
+
+- mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
++ for (band = 0; band < NUM_NL80211_BANDS; band++) {
++ struct mt7996_phy *phy;
+
+- ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
+- if (ret)
+- goto out;
++ if (!hw->wiphy->bands[band])
++ continue;
+
+- ret = mt7996_mcu_set_radio_en(phy, true);
+- if (ret)
+- goto out;
++ phy = mt7996_band_phy(hw, band);
+
+- ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
+- if (ret)
+- goto out;
++ if (!phy || test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
++ continue;
+
+- /* set a parking channel */
+- ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
+- if (ret)
+- goto out;
++ mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
+
+- ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
+- if (ret)
+- goto out;
++ ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
++ if (ret)
++ goto out;
+
+- ret = mt7996_mcu_set_thermal_protect(phy, true);
+- if (ret)
+- goto out;
++ ret = mt7996_mcu_set_radio_en(phy, true);
++ if (ret)
++ goto out;
+
+- ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
+- if (ret)
+- goto out;
++ ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
++ if (ret)
++ goto out;
+
+-#ifdef CONFIG_MTK_DEBUG
+- phy->sr_enable = true;
+- phy->enhanced_sr_enable = true;
+- phy->thermal_protection_enable = true;
++ /* set a parking channel */
++ ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++ if (ret)
++ goto out;
+
+- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+- dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
++ ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
++ if (ret)
++ goto out;
++
++ ret = mt7996_mcu_set_thermal_protect(phy, true);
++ if (ret)
++ goto out;
++
++ ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
++ if (ret)
++ goto out;
+
+- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
+- dev->dbg.sku_disable ? 0 : phy->sku_path_en);
++#ifdef CONFIG_MTK_DEBUG
++ phy->sr_enable = true;
++ phy->enhanced_sr_enable = true;
++ phy->thermal_protection_enable = true;
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++ dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
++
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++ dev->dbg.sku_disable ? 0 : phy->sku_path_en);
+ #else
+- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+- phy->sku_limit_en);
+- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
+- phy->sku_path_en);
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++ phy->sku_limit_en);
++ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++ phy->sku_path_en);
+ #endif
+- if (ret)
+- goto out;
+-
+- set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++ if (ret)
++ goto out;
+
+- ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+- MT7996_WATCHDOG_TIME);
++ set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+- ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++ ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
++ MT7996_WATCHDOG_TIME);
+
+- if (!running)
+- mt7996_mac_reset_counters(phy);
++ if (!running)
++ mt7996_mac_reset_counters(phy);
++ }
+
++ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
+ out:
+ return ret;
+ }
+@@ -143,18 +152,29 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ static void mt7996_stop(struct ieee80211_hw *hw)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ int band;
+
+- cancel_delayed_work_sync(&phy->mt76->mac_work);
+ cancel_delayed_work_sync(&dev->scs_work);
+
+- mutex_lock(&dev->mt76.mutex);
++ for (band = 0; band < NUM_NL80211_BANDS; band++) {
++ struct mt7996_phy *phy;
+
+- mt7996_mcu_set_radio_en(phy, false);
++ if (!hw->wiphy->bands[band])
++ continue;
+
+- clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++ phy = mt7996_band_phy(hw, band);
+
+- mutex_unlock(&dev->mt76.mutex);
++ if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state) ||
++ (phy->chanctx && phy->chanctx->nbss_assigned))
++ continue;
++
++ cancel_delayed_work_sync(&phy->mt76->mac_work);
++
++ mutex_lock(&dev->mt76.mutex);
++ mt7996_mcu_set_radio_en(phy, false);
++ clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++ mutex_unlock(&dev->mt76.mutex);
++ }
+ }
+
+ static inline int get_free_idx(u32 mask, u8 start, u8 end)
+@@ -2057,7 +2077,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+ {
+ struct cfg80211_scan_request *req = &hw_req->req;
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_phy *phy = mt7996_band_phy(hw, req->channels[0]->band);
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ if (WARN_ON(phy->scan_req || phy->scan_chan)) {
+@@ -2079,19 +2099,30 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ static void
+ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ int band;
+
+- cancel_delayed_work_sync(&phy->scan_work);
++ for (band = 0; band < NUM_NL80211_BANDS; band++) {
++ struct mt7996_phy *phy;
+
+- mutex_lock(&phy->dev->mt76.mutex);
+- mt7996_scan_complete(phy, true);
+- mutex_unlock(&phy->dev->mt76.mutex);
++ if (!hw->wiphy->bands[band])
++ continue;
++
++ phy = mt7996_band_phy(hw, band);
++ if (!(test_bit(MT76_SCANNING, &phy->mt76->state)))
++ continue;
++
++ cancel_delayed_work_sync(&phy->scan_work);
++
++ mutex_lock(&phy->dev->mt76.mutex);
++ mt7996_scan_complete(phy, true);
++ mutex_unlock(&phy->dev->mt76.mutex);
++ }
+ }
+
+ static int
+ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ {
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_phy *phy = mt7996_band_phy(hw, conf->def.chan->band);
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ int ret;
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 913aff173..4c090badf 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -788,6 +788,28 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
+ return 1;
+ }
+
++static inline struct mt7996_phy *
++mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
++{
++ struct mt76_phy *phy = hw->priv;
++
++ if (!(hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO))
++ return phy->priv;
++
++ /* TODO: mlo: temporarily hardcode */
++ if (band == NL80211_BAND_6GHZ)
++ phy = phy->dev->phys[MT_BAND2];
++ else if (band == NL80211_BAND_5GHZ)
++ phy = phy->dev->phys[MT_BAND1];
++ else
++ phy = phy->dev->phys[MT_BAND0];
++
++ if (!phy)
++ phy = hw->priv;
++
++ return phy->priv;
++}
++
+ static inline struct mt7996_chanctx *
+ mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+ {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0093-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0093-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch
new file mode 100644
index 0000000..9c04e89
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0093-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch
@@ -0,0 +1,501 @@
+From 29127cd132bbeca563f105bb43fb6e5855cc3572 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 1 Dec 2023 17:26:43 +0800
+Subject: [PATCH 093/116] wifi: mt76: mt7996: rework ieee80211_ops callbacks
+ for link consideration
+
+Extend ieee80211 callback functions to support multi-link operation.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 313 ++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 204 insertions(+), 109 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 435815e9a..3bd97d4aa 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -568,7 +568,6 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_key_conf *key)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
+ &mvif->sta;
+@@ -578,70 +577,77 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ u8 *wcid_keyidx;
+ int idx = key->keyidx;
+ int err = 0;
++ unsigned long add;
++ unsigned int link_id;
+
+- /* The hardware does not support per-STA RX GTK, fallback
+- * to software mode for these.
+- */
+- if ((vif->type == NL80211_IFTYPE_ADHOC ||
+- vif->type == NL80211_IFTYPE_MESH_POINT) &&
+- (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
+- key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
+- !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+- return -EOPNOTSUPP;
++ if (key->link_id >= 0) {
++ add = BIT(key->link_id);
++ } else {
++ if (sta)
++ add = sta->valid_links ?: BIT(0);
++ else
++ add = vif->valid_links ?: BIT(0);
++ }
+
+ mutex_lock(&dev->mt76.mutex);
+- conf = link_conf_dereference_protected(vif, 0);
+- mconf = mconf_dereference_protected(mvif, 0);
+- mlink = mlink_dereference_protected(msta, 0);
+- wcid_keyidx = &mlink->wcid.hw_key_idx;
+-
+- /* fall back to sw encryption for unsupported ciphers */
+- switch (key->cipher) {
+- case WLAN_CIPHER_SUITE_TKIP:
+- case WLAN_CIPHER_SUITE_CCMP:
+- case WLAN_CIPHER_SUITE_CCMP_256:
+- case WLAN_CIPHER_SUITE_GCMP:
+- case WLAN_CIPHER_SUITE_GCMP_256:
+- case WLAN_CIPHER_SUITE_SMS4:
+- break;
+- case WLAN_CIPHER_SUITE_AES_CMAC:
+- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+- case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+- if (key->keyidx == 6 || key->keyidx == 7) {
+- wcid_keyidx = &wcid->hw_key_idx2;
+- key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
++
++ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++ conf = link_conf_dereference_protected(vif, link_id);
++ mconf = mconf_dereference_protected(mvif, link_id);
++ mlink = mlink_dereference_protected(msta, link_id);
++ wcid_keyidx = &mlink->wcid.hw_key_idx;
++
++ if (!conf || !mconf || !mlink)
++ continue;
++
++ /* fall back to sw encryption for unsupported ciphers */
++ switch (key->cipher) {
++ case WLAN_CIPHER_SUITE_TKIP:
++ case WLAN_CIPHER_SUITE_CCMP:
++ case WLAN_CIPHER_SUITE_CCMP_256:
++ case WLAN_CIPHER_SUITE_GCMP:
++ case WLAN_CIPHER_SUITE_GCMP_256:
++ case WLAN_CIPHER_SUITE_SMS4:
+ break;
++ case WLAN_CIPHER_SUITE_AES_CMAC:
++ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
++ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
++ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
++ if (key->keyidx == 6 || key->keyidx == 7) {
++ wcid_keyidx = &mlink->wcid.hw_key_idx2;
++ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
++ break;
++ }
++ fallthrough;
++ case WLAN_CIPHER_SUITE_WEP40:
++ case WLAN_CIPHER_SUITE_WEP104:
++ default:
++ mutex_unlock(&dev->mt76.mutex);
++ return -EOPNOTSUPP;
+ }
+- fallthrough;
+- case WLAN_CIPHER_SUITE_WEP40:
+- case WLAN_CIPHER_SUITE_WEP104:
+- default:
+- mutex_unlock(&dev->mt76.mutex);
+- return -EOPNOTSUPP;
+- }
+
+- if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
+- mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+- mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+- }
++ if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
++ mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
++ mt7996_mcu_add_bss_info(mconf->phy, conf, mconf, mlink, true);
++ }
+
+- if (cmd == SET_KEY) {
+- *wcid_keyidx = idx;
+- } else {
+- if (idx == *wcid_keyidx)
+- *wcid_keyidx = -1;
+- goto out;
+- }
++ if (cmd == SET_KEY) {
++ *wcid_keyidx = idx;
++ } else {
++ if (idx == *wcid_keyidx)
++ *wcid_keyidx = -1;
++ goto out;
++ }
+
+- mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
++ mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
+
+- if (key->keyidx == 6 || key->keyidx == 7)
+- err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
+- else
+- err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
+- MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+- &mlink->wcid, cmd);
++ if (key->keyidx == 6 || key->keyidx == 7)
++ err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
++ else
++ err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
++ MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
++ &mlink->wcid, cmd);
++ }
+ out:
+ mutex_unlock(&dev->mt76.mutex);
+
+@@ -697,7 +703,11 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ };
+
+ mutex_lock(&dev->mt76.mutex);
+- mconf = mconf_dereference_protected(mvif, 0);
++ mconf = mconf_dereference_protected(mvif, link_id);
++ if (!mconf) {
++ mutex_unlock(&dev->mt76.mutex);
++ return -EINVAL;
++ }
+
+ /* firmware uses access class index */
+ mconf->queue_params[mq_to_aci[queue]] = *params;
+@@ -840,19 +850,26 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u64 changed)
+ {
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
+- struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
+- struct mt7996_link_sta *mlink = mlink_dereference_protected(&mvif->sta, 0);
+-
+- mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+- mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
++ unsigned long valid_links = vif->valid_links ?: BIT(0);
++ unsigned int link_id;
++
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct ieee80211_bss_conf *conf =
++ link_conf_dereference_protected(vif, link_id);
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(&mvif->sta, link_id);
++
++ mt7996_mcu_add_bss_info(mconf->phy, conf, mconf, mlink, true);
++ mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
++ }
+ }
+
+ mutex_unlock(&dev->mt76.mutex);
+@@ -871,8 +888,13 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+
+ mutex_lock(&dev->mt76.mutex);
+
+- mconf = mconf_dereference_protected(mvif, 0);
+- mlink = mlink_dereference_protected(&mvif->sta, 0);
++ mconf = mconf_dereference_protected(mvif, info->link_id);
++ mlink = mlink_dereference_protected(&mvif->sta, info->link_id);
++ if (!mconf || !mlink)
++ goto out;
++
++ if (mconf->phy)
++ phy = mconf->phy;
+ /* station mode uses BSSID to map the wlan entry to a peer,
+ * and then peer references bss_info_rfch to set bandwidth cap.
+ */
+@@ -928,6 +950,7 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+ if (changed & BSS_CHANGED_MU_GROUPS)
+ mt7996_update_mu_group(hw, info, mconf);
+
++out:
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -938,13 +961,22 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_bss_conf *mconf;
+- struct ieee80211_bss_conf *conf;
++ struct mt7996_phy *phy = mt7996_band_phy(hw, chandef->chan->band);
++ unsigned long valid_links = vif->valid_links ?: BIT(0);
++ unsigned int link_id;
+
+ mutex_lock(&dev->mt76.mutex);
+- mconf = mconf_dereference_protected(mvif, 0);
+- conf = link_conf_dereference_protected(vif, 0);
+- mt7996_mcu_add_beacon(hw, conf, mconf, true);
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct ieee80211_bss_conf *conf =
++ link_conf_dereference_protected(vif, link_id);
++
++ if (!mconf || phy != mconf->phy)
++ continue;
++
++ mt7996_mcu_add_beacon(hw, conf, mconf, true);
++ }
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -1216,34 +1248,74 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ }
+
++static void
++mt7996_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
++{
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ unsigned long rem = sta->valid_links ?: BIT(0);
++ unsigned int link_id;
++
++ mutex_lock(&dev->mt76.mutex);
++ spin_lock_bh(&dev->mt76.status_lock);
++ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
++
++ rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
++ }
++ spin_unlock_bh(&dev->mt76.status_lock);
++ mutex_unlock(&dev->mt76.mutex);
++}
++
+ static void mt7996_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+ {
+- struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt76_phy *mphy = hw->priv;
++ struct mt76_phy *mphy;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_vif *vif = info->control.vif;
+- struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+- struct mt7996_link_sta *mlink;
++ struct mt76_wcid *wcid;
++ struct mt7996_vif *mvif;
++ struct mt7996_sta *msta;
+
+ if (control->sta) {
+- struct mt7996_sta *msta;
+-
+ msta = (struct mt7996_sta *)control->sta->drv_priv;
+- mlink = rcu_dereference(msta->link[0]);
+- wcid = &mlink->wcid;
++ mvif = msta->vif;
++ } else if (vif) {
++ mvif = (struct mt7996_vif *)vif->drv_priv;
++ msta = &mvif->sta;
+ }
+
+- if (vif && !control->sta) {
+- struct mt7996_vif *mvif;
++ rcu_read_lock();
++ if (mvif && msta) {
++ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
++
++ u8 link_id = u32_get_bits(info->control.flags,
++ IEEE80211_TX_CTRL_MLO_LINK);
+
+- mvif = (struct mt7996_vif *)vif->drv_priv;
+- mlink = rcu_dereference(mvif->sta.link[0]);
++ if (link_id >= IEEE80211_LINK_UNSPECIFIED)
++ link_id = mvif->master_link_id;
++
++ mconf = rcu_dereference(mvif->link[link_id]);
++ mlink = rcu_dereference(msta->link[link_id]);
++
++ if (!mconf || !mlink)
++ goto unlock;
++
++ mphy = mconf->phy->mt76;
+ wcid = &mlink->wcid;
++ } else {
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++
++ mphy = hw->priv;
++ wcid = &dev->mt76.global_wcid;
+ }
+
+ mt76_tx(mphy, control->sta, wcid, skb);
++ rcu_read_unlock();
+ }
+
+ static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
+@@ -1279,7 +1351,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mtxq = (struct mt76_txq *)txq->drv_priv;
+
+ mutex_lock(&dev->mt76.mutex);
+- mlink = mlink_dereference_protected(msta, 0);
++ mlink = mlink_dereference_protected(msta, msta->pri_link);
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ mt76_rx_aggr_start(&dev->mt76, &mlink->wcid, tid, ssn,
+@@ -1494,7 +1566,7 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+
+ /* TODO: support per-link rate report */
+ mutex_lock(&dev->mt76.mutex);
+- mlink = mlink_dereference_protected(msta, 0);
++ mlink = mlink_dereference_protected(msta, msta->pri_link);
+ if (!mlink)
+ goto out;
+
+@@ -1554,7 +1626,7 @@ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ u32 *changed = data;
+
+ rcu_read_lock();
+- mlink = rcu_dereference(msta->link[0]);
++ mlink = rcu_dereference(msta->link[msta->pri_link]);
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ mlink->changed |= *changed;
+@@ -1621,19 +1693,26 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_bss_conf *mconf;
+- struct mt7996_link_sta *mlink;
++ unsigned long valid_links = sta->valid_links ?: BIT(0);
++ unsigned int link_id;
+
+ mutex_lock(&dev->mt76.mutex);
+- mconf = mconf_dereference_protected(mvif, 0);
+- mlink = mlink_dereference_protected(msta, 0);
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
+
+- if (enabled)
+- set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+- else
+- clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
++ if (!mconf || !mlink)
++ continue;
++
++ if (enabled)
++ set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
++ else
++ clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+
+- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++ }
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -1645,19 +1724,26 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_bss_conf *mconf;
+- struct mt7996_link_sta *mlink;
++ unsigned long valid_links = sta->valid_links ?: BIT(0);
++ unsigned int link_id;
+
+ mutex_lock(&dev->mt76.mutex);
+- mconf = mconf_dereference_protected(mvif, 0);
+- mlink = mlink_dereference_protected(msta, 0);
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
+
+- if (enabled)
+- set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+- else
+- clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
++ if (!mconf || !mlink)
++ continue;
++
++ if (enabled)
++ set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
++ else
++ clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+
+- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++ }
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
+@@ -1790,9 +1876,13 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+ {
+ struct mt76_ethtool_worker_info *wi = wi_data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_link_sta *mlink = &msta->deflink;
++ struct mt7996_link_sta *mlink;
++ struct mt7996_bss_conf *mconf;
++
++ mlink = mlink_dereference_protected(msta, msta->pri_link);
++ mconf = mconf_dereference_protected(msta->vif, msta->pri_link);
+
+- if (msta->vif->deflink.mt76.idx != wi->idx)
++ if (mconf->mt76.idx != wi->idx)
+ return;
+
+ mt76_ethtool_worker(wi, &mlink->wcid.stats, true);
+@@ -2024,12 +2114,13 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ struct net_device_path *path)
+ {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_bss_conf *mconf = &mvif->deflink;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+- struct mt7996_link_sta *mlink = &msta->deflink;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
++ u8 link_id;
+
+ if (dev->hif2) {
+ switch (dev->option_type) {
+@@ -2049,6 +2140,10 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ if (!mtk_wed_device_active(wed))
+ return -ENODEV;
+
++ link_id = msta->pri_link;
++ mconf = rcu_dereference(mvif->link[link_id]);
++ mlink = rcu_dereference(msta->link[link_id]);
++
+ if (mlink->wcid.idx > MT7996_WTBL_STA)
+ return -EIO;
+
+@@ -2380,7 +2475,7 @@ const struct ieee80211_ops mt7996_ops = {
+ .vif_cfg_changed = mt7996_vif_cfg_changed,
+ .link_info_changed = mt7996_link_info_changed,
+ .sta_state = mt76_sta_state,
+- .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
++ .sta_pre_rcu_remove = mt7996_sta_pre_rcu_remove,
+ .sta_rc_update = mt7996_sta_rc_update,
+ .set_key = mt7996_set_key,
+ .ampdu_action = mt7996_ampdu_action,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0094-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0094-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch
new file mode 100644
index 0000000..0519b1b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0094-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch
@@ -0,0 +1,203 @@
+From dba16ea5f62b21ec911c67a7ca1539004c5e5f3b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 11:25:54 +0800
+Subject: [PATCH 094/116] wifi: mt76: mt7996: rework TXD for multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 88 +++++++++++++++++++++++++++++++++++--------------
+ mt7996/mt7996.h | 9 +++++
+ 2 files changed, 73 insertions(+), 24 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3cff43d6b..0fa3266cc 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -621,9 +621,8 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi,
+ u32 val;
+
+ if (wcid->sta) {
+- struct ieee80211_sta *sta;
++ struct ieee80211_sta *sta = wcid_to_sta(wcid);
+
+- sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
+ wmm = sta->wme;
+ }
+
+@@ -724,6 +723,10 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ txwi[3] |= cpu_to_le32(val);
+ txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
+ }
++
++ if (ieee80211_vif_is_mld(info->control.vif) &&
++ (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))))
++ txwi[5] |= cpu_to_le32(MT_TXD5_FL);
+ }
+
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+@@ -733,10 +736,12 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_vif *vif = info->control.vif;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
+ u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
+ u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0;
++ u8 link_id;
+ bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+- struct mt76_vif *mvif;
+ u16 tx_count = 15;
+ u32 val;
+ bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+@@ -744,11 +749,16 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ bool beacon = !!(changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc);
+
+- mvif = vif ? (struct mt76_vif *)vif->drv_priv : NULL;
+- if (mvif) {
+- omac_idx = mvif->omac_idx;
+- wmm_idx = mvif->wmm_idx;
+- band_idx = mvif->band_idx;
++ if (likely(wcid != &dev->mt76.global_wcid))
++ link_id = wcid->link_id;
++ else
++ link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
++
++ mconf = rcu_dereference(mvif->link[link_id]);
++ if (mconf) {
++ omac_idx = mconf->mt76.omac_idx;
++ wmm_idx = mconf->mt76.wmm_idx;
++ band_idx = mconf->mt76.band_idx;
+ }
+
+ if (inband_disc) {
+@@ -795,7 +805,10 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ val |= MT_TXD5_TX_STATUS_HOST;
+ txwi[5] = cpu_to_le32(val);
+
+- val = MT_TXD6_DIS_MAT | MT_TXD6_DAS;
++ val = MT_TXD6_DAS;
++ if ((q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))
++ val |= MT_TXD6_DIS_MAT;
++
+ if (is_mt7996(&dev->mt76))
+ val |= FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
+ else
+@@ -814,16 +827,18 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ is_multicast_ether_addr(hdr->addr1);
+ u8 idx = MT7996_BASIC_RATES_TBL;
+
+- if (mvif) {
+- if (mcast && mvif->mcast_rates_idx)
+- idx = mvif->mcast_rates_idx;
+- else if (beacon && mvif->beacon_rates_idx)
+- idx = mvif->beacon_rates_idx;
++ if (mconf) {
++ if (mcast && mconf->mt76.mcast_rates_idx)
++ idx = mconf->mt76.mcast_rates_idx;
++ else if (beacon && mconf->mt76.beacon_rates_idx)
++ idx = mconf->mt76.beacon_rates_idx;
+ else
+- idx = mvif->basic_rates_idx;
++ idx = mconf->mt76.basic_rates_idx;
+ }
+
+ val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW;
++ if (mcast)
++ val |= MT_TXD6_DIS_MAT;
+ txwi[6] |= cpu_to_le32(val);
+ txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+ }
+@@ -839,17 +854,48 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
+ struct ieee80211_key_conf *key = info->control.hw_key;
+ struct ieee80211_vif *vif = info->control.vif;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_sta *msta;
++ struct mt7996_bss_conf *mconf;
+ struct mt76_connac_txp_common *txp;
+ struct mt76_txwi_cache *t;
+ int id, i, pid, nbuf = tx_info->nbuf - 1;
+ bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ u8 *txwi = (u8 *)txwi_ptr;
++ u8 link_id;
+
+ if (unlikely(tx_info->skb->len <= ETH_HLEN))
+ return -EINVAL;
+
+- if (!wcid)
+- wcid = &dev->mt76.global_wcid;
++ if (WARN_ON(!wcid))
++ return -EINVAL;
++
++ msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
++ if (ieee80211_is_data_qos(hdr->frame_control) && sta->mlo) {
++ if (unlikely(tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE))) {
++ link_id = msta->pri_link;
++ } else {
++ u8 tid = tx_info->skb->priority & IEEE80211_QOS_CTL_TID_MASK;
++
++ link_id = (tid % 2) ? msta->sec_link : msta->pri_link;
++ }
++ } else {
++ link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
++
++ if (link_id == IEEE80211_LINK_UNSPECIFIED || (sta && !sta->mlo))
++ link_id = wcid->link_id;
++ }
++
++ if (link_id != wcid->link_id) {
++ struct mt7996_link_sta *mlink = rcu_dereference(msta->link[link_id]);
++
++ if (mlink)
++ wcid = &mlink->wcid;
++ }
++
++ mconf = rcu_dereference(mvif->link[wcid->link_id]);
++ if (!mconf)
++ return -ENOLINK;
+
+ t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
+ t->skb = tx_info->skb;
+@@ -894,13 +940,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control))
+ txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
+
+- if (vif) {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_bss_conf *mconf = &mvif->deflink;
+-
+- txp->fw.bss_idx = mconf->mt76.idx;
+- }
+-
++ txp->fw.bss_idx = mconf->mt76.idx;
+ txp->fw.token = cpu_to_le16(id);
+ txp->fw.rept_wds_wcid = cpu_to_le16(sta ? wcid->idx : 0xfff);
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 4c090badf..21a95c146 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -830,6 +830,15 @@ mlink_dereference_protected(struct mt7996_sta *msta, u8 link_id)
+ lockdep_is_held(&msta->vif->dev->mt76.mutex));
+ }
+
++static inline struct mt7996_link_sta *
++wcid_to_mlink(struct mt76_wcid *wcid)
++{
++ if (!wcid)
++ return NULL;
++
++ return container_of(wcid, struct mt7996_link_sta, wcid);
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0095-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0095-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch
new file mode 100644
index 0000000..6edeabe
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0095-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch
@@ -0,0 +1,120 @@
+From 1f35388fa6214a36e5f7a4770c248a187a543019 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 11:57:38 +0800
+Subject: [PATCH 095/116] wifi: mt76: mt7996: rework TXS for multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 9 +++++----
+ mt7996/main.c | 1 +
+ mt7996/mt7996.h | 28 ++++++++++++++++++++++++++++
+ 3 files changed, 34 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 0fa3266cc..65431c7d7 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1192,7 +1192,7 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ struct ieee80211_sta *sta;
+ u8 tid;
+
+- sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
++ sta = wcid_to_sta(wcid);
+ tid = FIELD_GET(MT_TXS0_TID, txs);
+ ieee80211_refresh_tx_agg_session_timer(sta, tid);
+ }
+@@ -1310,9 +1310,10 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ struct mt76_wcid *wcid;
+ __le32 *txs_data = data;
+ u16 wcidx;
+- u8 pid;
++ u8 band, pid;
+
+ wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
++ band = le32_get_bits(txs_data[2], MT_TXS2_BAND);
+ pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
+
+ if (pid < MT_PACKET_ID_NO_SKB)
+@@ -1323,7 +1324,7 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+
+ rcu_read_lock();
+
+- wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
++ wcid = mt7996_get_link_wcid(dev, wcidx, band);
+ if (!wcid)
+ goto out;
+
+@@ -1332,7 +1333,7 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ if (!wcid->sta)
+ goto out;
+
+- mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++ mlink = wcid_to_mlink(wcid);
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ if (list_empty(&mlink->wcid.poll_list))
+ list_add_tail(&mlink->wcid.poll_list, &dev->mt76.sta_poll_list);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 3bd97d4aa..aceb77ab2 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2317,6 +2317,7 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mconf = mconf_dereference_protected(mvif, link_id);
+ mconf->chanctx = ctx;
+ ctx->nbss_assigned++;
++ mvif->band_to_link[phy->mt76->band_idx] = link_id;
+
+ if (mt7996_hw_phy(hw) == phy)
+ mvif->master_link_id = link_id;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 21a95c146..c6ca00f1a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -357,6 +357,8 @@ struct mt7996_vif {
+ u8 master_link_id;
+ u8 group_mld_id;
+ u8 mld_remap_id;
++
++ u8 band_to_link[__MT_MAX_BAND];
+ };
+
+ /* crash-dump */
+@@ -839,6 +841,32 @@ wcid_to_mlink(struct mt76_wcid *wcid)
+ return container_of(wcid, struct mt7996_link_sta, wcid);
+ }
+
++static inline struct mt76_wcid *
++mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
++{
++ struct mt7996_link_sta *mlink;
++ struct mt76_wcid *wcid;
++ u8 link_id;
++
++ if (!idx || idx >= ARRAY_SIZE(dev->mt76.wcid))
++ return NULL;
++
++ wcid = rcu_dereference(dev->mt76.wcid[idx]);
++ if (!wcid)
++ return NULL;
++
++ if (wcid->phy_idx == band_idx)
++ return wcid;
++
++ mlink = wcid_to_mlink(wcid);
++ link_id = mlink->sta->vif->band_to_link[band_idx];
++ mlink = rcu_dereference(mlink->sta->link[link_id]);
++ if (!mlink)
++ return wcid;
++
++ return &mlink->wcid;
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0096-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0096-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch
new file mode 100644
index 0000000..426b18b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0096-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch
@@ -0,0 +1,64 @@
+From b332b97c6eadd80d210eafc4cd089163c910bf95 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 14:50:47 +0800
+Subject: [PATCH 096/116] wifi: mt76: mt7996: rework RXD for multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 27 ++-------------------------
+ 1 file changed, 2 insertions(+), 25 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 65431c7d7..a32b416c4 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -51,29 +51,6 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = {
+ },
+ };
+
+-static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
+- u16 idx, bool unicast)
+-{
+- struct mt7996_link_sta *mlink;
+- struct mt76_wcid *wcid;
+-
+- if (idx >= ARRAY_SIZE(dev->mt76.wcid))
+- return NULL;
+-
+- wcid = rcu_dereference(dev->mt76.wcid[idx]);
+- if (unicast || !wcid)
+- return wcid;
+-
+- if (!wcid->sta)
+- return NULL;
+-
+- mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+- if (!mlink->sta->vif)
+- return NULL;
+-
+- return &mlink->wcid;
+-}
+-
+ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
+ {
+ mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
+@@ -376,10 +353,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+
+ unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
+ idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
+- status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
++ status->wcid = mt7996_get_link_wcid(dev, idx, band_idx);
+
+ if (status->wcid) {
+- mlink = container_of(status->wcid, struct mt7996_link_sta, wcid);
++ mlink = wcid_to_mlink(status->wcid);
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ if (list_empty(&mlink->wcid.poll_list))
+ list_add_tail(&mlink->wcid.poll_list,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0097-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0097-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch
new file mode 100644
index 0000000..73838c5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0097-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch
@@ -0,0 +1,249 @@
+From 15d70608c54b7e8823589850714f396b76e39783 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 18:31:02 +0800
+Subject: [PATCH 097/116] wifi: mt76: mt7996: rework mac functions for
+ multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 91 +++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 65 insertions(+), 26 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index a32b416c4..31b916695 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -85,10 +85,11 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
+ {
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap);
+- struct mt7996_sta *msta = (struct mt7996_sta *)status->wcid;
++ struct mt7996_link_sta *mlink = (struct mt7996_link_sta *)status->wcid;
+ __le32 *rxd = (__le32 *)skb->data;
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
++ struct ieee80211_bss_conf *conf;
+ struct ieee80211_hdr hdr;
+ u16 frame_control;
+
+@@ -99,11 +100,14 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
+ if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4))
+ return -EINVAL;
+
+- if (!msta || !msta->vif)
++ if (!mlink->sta || !mlink->sta->vif)
+ return -EINVAL;
+
+- sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+- vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
++ sta = wcid_to_sta(status->wcid);
++ vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
++ conf = rcu_dereference(vif->link_conf[mlink->wcid.link_id]);
++ if (unlikely(!conf))
++ return -ENOLINK;
+
+ /* store the info from RXD and ethhdr to avoid being overridden */
+ frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL);
+@@ -116,7 +120,7 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
+ switch (frame_control & (IEEE80211_FCTL_TODS |
+ IEEE80211_FCTL_FROMDS)) {
+ case 0:
+- ether_addr_copy(hdr.addr3, vif->bss_conf.bssid);
++ ether_addr_copy(hdr.addr3, conf->bssid);
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ ether_addr_copy(hdr.addr3, eth_hdr->h_source);
+@@ -955,15 +959,21 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
+ }
+
+ static void
+-mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
++mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
++ struct mt76_wcid *wcid)
+ {
+ struct mt7996_sta *msta;
+ struct mt7996_link_sta *mlink;
++ struct ieee80211_link_sta *link_sta;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ u16 fc, tid;
+
+- if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
++ link_sta = rcu_dereference(sta->link[wcid->link_id]);
++ if (!link_sta)
++ return;
++
++ if (!sta->mlo && !(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
+ return;
+
+ tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+@@ -987,17 +997,17 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ return;
+
+ msta = (struct mt7996_sta *)sta->drv_priv;
+- mlink = rcu_dereference(msta->link[0]);
++ mlink = rcu_dereference(msta->link[msta->pri_link]);
+ if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
+ ieee80211_start_tx_ba_session(sta, tid, 0);
+ }
+
+ static void
+ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
+- struct ieee80211_sta *sta, struct list_head *free_list)
++ struct ieee80211_sta *sta, struct mt76_wcid *wcid,
++ struct list_head *free_list)
+ {
+ struct mt76_dev *mdev = &dev->mt76;
+- struct mt76_wcid *wcid;
+ __le32 *txwi;
+ u16 wcid_idx;
+
+@@ -1007,11 +1017,10 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
+
+ txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
+ if (sta) {
+- wcid = (struct mt76_wcid *)sta->drv_priv;
+ wcid_idx = wcid->idx;
+
+ if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
+- mt7996_tx_check_aggr(sta, t->skb);
++ mt7996_tx_check_aggr(sta, t->skb, wcid);
+ } else {
+ wcid_idx = le32_get_bits(txwi[9], MT_TXD9_WLAN_IDX);
+ }
+@@ -1066,7 +1075,9 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ */
+ info = le32_to_cpu(*cur_info);
+ if (info & MT_TXFREE_INFO_PAIR) {
+- struct mt7996_link_sta *mlink;
++ struct mt7996_sta *msta;
++ unsigned long valid_links;
++ unsigned int link_id;
+ u16 idx;
+
+ idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
+@@ -1075,11 +1086,17 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ if (!sta)
+ continue;
+
+- mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++ valid_links = sta->valid_links ?: BIT(0);
++ msta = (struct mt7996_sta *)sta->drv_priv;
++ /* for MLD STA, add all link's wcid to sta_poll_list */
+ spin_lock_bh(&mdev->sta_poll_lock);
+- if (list_empty(&mlink->wcid.poll_list))
+- list_add_tail(&mlink->wcid.poll_list,
+- &mdev->sta_poll_list);
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_link_sta *mlink =
++ rcu_dereference(msta->link[link_id]);
++
++ if (list_empty(&mlink->wcid.poll_list))
++ list_add_tail(&mlink->wcid.poll_list, &mdev->sta_poll_list);
++ }
+ spin_unlock_bh(&mdev->sta_poll_lock);
+ continue;
+ } else if (info & MT_TXFREE_INFO_HEADER) {
+@@ -1115,7 +1132,8 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ if (!txwi)
+ continue;
+
+- mt7996_txwi_free(dev, txwi, sta, &free_list);
++ mt7996_txwi_free(dev, txwi, sta, wcid, &free_list);
++ txwi->jiffies = 0;
+ }
+ }
+
+@@ -1537,19 +1555,29 @@ static void
+ mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+ struct ieee80211_hw *hw = priv;
+- struct ieee80211_bss_conf *conf = &vif->bss_conf;
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_bss_conf *mconf = &mvif->deflink;
++ unsigned long update = vif->valid_links ?: BIT(0);
++ unsigned int link_id;
+
++ mutex_lock(&dev->mt76.mutex);
+ switch (vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+- mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
++ for_each_set_bit(link_id, &update, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct ieee80211_bss_conf *conf =
++ link_conf_dereference_protected(vif, link_id);
++
++ mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
++ }
+ break;
+ default:
+ break;
+ }
++ mutex_unlock(&dev->mt76.mutex);
+ }
+
+ static void
+@@ -1585,7 +1613,7 @@ void mt7996_tx_token_put(struct mt7996_dev *dev)
+
+ spin_lock_bh(&dev->mt76.token_lock);
+ idr_for_each_entry(&dev->mt76.token, txwi, id) {
+- mt7996_txwi_free(dev, txwi, NULL, NULL);
++ mt7996_txwi_free(dev, txwi, NULL, NULL, NULL);
+ dev->mt76.token_count--;
+ }
+ spin_unlock_bh(&dev->mt76.token_lock);
+@@ -2266,21 +2294,31 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ u32 changed;
+ LIST_HEAD(list);
+
++ rcu_read_lock();
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ list_splice_init(&dev->sta_rc_list, &list);
+
+ while (!list_empty(&list)) {
++ u8 link_id;
++
+ mlink = list_first_entry(&list, struct mt7996_link_sta, rc_list);
++ link_id = mlink->wcid.link_id;
++
+ list_del_init(&mlink->rc_list);
+ changed = mlink->changed;
+ mlink->changed = 0;
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+- sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
+- link_sta = &sta->deflink;
+ vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
+- conf = &vif->bss_conf;
+- mconf = &mlink->sta->vif->deflink;
++ conf = rcu_dereference(vif->link_conf[link_id]);
++ mconf = rcu_dereference(mlink->sta->vif->link[link_id]);
++ sta = wcid_to_sta(&mlink->wcid);
++ link_sta = rcu_dereference(sta->link[link_id]);
++
++ if (unlikely(!conf || !mconf || !link_sta)) {
++ spin_lock_bh(&dev->mt76.sta_poll_lock);
++ continue;
++ }
+
+ if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ IEEE80211_RC_NSS_CHANGED |
+@@ -2295,6 +2333,7 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ }
+
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
++ rcu_read_unlock();
+ }
+
+ void mt7996_mac_work(struct work_struct *work)
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0098-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0098-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch
new file mode 100644
index 0000000..78653d9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0098-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch
@@ -0,0 +1,259 @@
+From 8b128999c458c20f3ad09d8a0dde31e38f23e38b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 7 Dec 2023 15:39:03 +0800
+Subject: [PATCH 098/116] wifi: mt76: connac: rework mcu functions for
+ multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7615/mcu.c | 4 +--
+ mt76_connac_mcu.c | 11 ++++----
+ mt76_connac_mcu.h | 2 +-
+ mt7915/mcu.c | 3 +-
+ mt7925/mcu.c | 3 +-
+ mt7996/mcu.c | 70 +++++++++++++++++++++++++++++++++--------------
+ 6 files changed, 63 insertions(+), 30 deletions(-)
+
+diff --git a/mt7615/mcu.c b/mt7615/mcu.c
+index 8f4f203ef..e72dd654c 100644
+--- a/mt7615/mcu.c
++++ b/mt7615/mcu.c
+@@ -862,8 +862,8 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ else
+ mvif->sta_added = true;
+ }
+- mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, vif, &sta->deflink,
+- enable, new_entry);
++ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, &vif->bss_conf,
++ &sta->deflink, enable, new_entry);
+ if (enable && sta)
+ mt76_connac_mcu_sta_tlv(phy->mt76, sskb, sta, vif, 0,
+ MT76_STA_INFO_STATE_ASSOC);
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index d83d31441..9ea74719a 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -370,10 +370,11 @@ void mt76_connac_mcu_bss_omac_tlv(struct sk_buff *skb,
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_omac_tlv);
+
+ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+- struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf,
+ struct ieee80211_link_sta *link_sta,
+ bool enable, bool newly)
+ {
++ struct ieee80211_vif *vif = conf->vif;
+ struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL;
+ struct sta_rec_basic *basic;
+ struct tlv *tlv;
+@@ -394,10 +395,9 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+
+ if (!sta) {
+ basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC);
+-
+ if (vif->type == NL80211_IFTYPE_STATION &&
+- !is_zero_ether_addr(vif->bss_conf.bssid)) {
+- memcpy(basic->peer_addr, vif->bss_conf.bssid, ETH_ALEN);
++ conf && !is_zero_ether_addr(conf->bssid)) {
++ memcpy(basic->peer_addr, conf->bssid, ETH_ALEN);
+ basic->aid = cpu_to_le16(vif->cfg.aid);
+ } else {
+ eth_broadcast_addr(basic->peer_addr);
+@@ -1056,7 +1056,8 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+ return PTR_ERR(skb);
+
+ if (info->sta || !info->offload_fw)
+- mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
++ mt76_connac_mcu_sta_basic_tlv(dev, skb, &info->vif->bss_conf,
++ &info->sta->deflink,
+ info->enable, info->newly);
+ if (info->sta && info->enable)
+ mt76_connac_mcu_sta_tlv(phy, skb, info->sta,
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index d45765beb..7f1562b8e 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1904,7 +1904,7 @@ mt76_connac_mcu_add_tlv(struct sk_buff *skb, int tag, int len)
+ int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy);
+ int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif);
+ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+- struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf,
+ struct ieee80211_link_sta *link_sta,
+ bool enable, bool newly);
+ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index 1d1b3cead..366f2bba2 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -1669,7 +1669,8 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ return PTR_ERR(skb);
+
+ /* starec basic */
+- mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, &sta->deflink, enable,
++ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf,
++ &sta->deflink, enable,
+ !rcu_access_pointer(dev->mt76.wcid[msta->wcid.idx]));
+ if (!enable)
+ goto out;
+diff --git a/mt7925/mcu.c b/mt7925/mcu.c
+index 1cb556302..e9cf442b8 100644
+--- a/mt7925/mcu.c
++++ b/mt7925/mcu.c
+@@ -1626,7 +1626,8 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
+ return PTR_ERR(skb);
+
+ if (info->sta || !info->offload_fw)
+- mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
++ mt76_connac_mcu_sta_basic_tlv(dev, skb, &info->vif->bss_conf,
++ &info->sta->deflink,
+ info->enable, info->newly);
+ if (info->sta && info->enable) {
+ mt7925_mcu_sta_phy_tlv(skb, info->vif, info->sta);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index ed854b473..4bdb1fcbc 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1169,10 +1169,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ sta = ieee80211_find_sta(vif, conf->bssid);
+ /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
+ if (sta) {
+- struct mt76_wcid *wcid;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_link_sta *mlink;
+
+- wcid = (struct mt76_wcid *)sta->drv_priv;
+- sta_wlan_idx = wcid->idx;
++ mlink = rcu_dereference(msta->link[conf->link_id]);
++ if (mlink)
++ sta_wlan_idx = mlink->wcid.idx;
+ }
+ rcu_read_unlock();
+ }
+@@ -1306,9 +1308,8 @@ int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf)
+ static int
+ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif *mvif,
+ struct ieee80211_ampdu_params *params,
+- bool enable, bool tx)
++ struct mt76_wcid *wcid, bool enable, bool tx)
+ {
+- struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv;
+ struct sta_rec_ba_uni *ba;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+@@ -1338,24 +1339,53 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool enable)
+ {
+- struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+- struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+- struct mt7996_link_sta *mlink = mlink_dereference_protected(msta, 0);
++ struct ieee80211_sta *sta = params->sta;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ unsigned long valid_links = sta->valid_links ?: BIT(0);
++ unsigned int link_id;
++
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(msta->vif, link_id);
++ int ret;
+
+- if (enable && !params->amsdu)
+- mlink->wcid.amsdu = false;
++ if (enable && !params->amsdu)
++ mlink->wcid.amsdu = false;
+
+- return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
++ ret = mt7996_mcu_sta_ba(dev, &mconf->mt76, params,
++ &mlink->wcid, enable, true);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
+ }
+
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool enable)
+ {
+- struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+- struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
++ struct ieee80211_sta *sta = params->sta;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ unsigned long valid_links = sta->valid_links ?: BIT(0);
++ unsigned int link_id;
+
+- return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, false);
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(msta->vif, link_id);
++ int ret;
++
++ ret = mt7996_mcu_sta_ba(dev, &mconf->mt76, params, &mlink->wcid,
++ enable, false);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
+ }
+
+ static void
+@@ -2379,7 +2409,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ return PTR_ERR(skb);
+
+ /* starec basic */
+- mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, link_sta, enable, newly);
++ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, conf, link_sta, enable, newly);
+
+ if (!enable)
+ goto out;
+@@ -2840,7 +2870,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ struct mt7996_bss_conf *mconf, int en)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_phy *phy = mconf->phy;
+ struct ieee80211_mutable_offsets offs;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb, *rskb;
+@@ -2856,7 +2886,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ if (IS_ERR(rskb))
+ return PTR_ERR(rskb);
+
+- skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, 0);
++ skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, conf->link_id);
+ if (!skb) {
+ dev_kfree_skb(rskb);
+ return -EINVAL;
+@@ -2894,9 +2924,9 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ {
+ #define OFFLOAD_TX_MODE_SU BIT(0)
+ #define OFFLOAD_TX_MODE_MU BIT(1)
+- struct ieee80211_hw *hw = mt76_hw(dev);
+ struct ieee80211_vif *vif = conf->vif;
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_phy *phy = mconf->phy;
+ struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
+ enum nl80211_band band = chandef->chan->band;
+ struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+@@ -5154,7 +5184,7 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
+
+ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+- phy = mlink->sta->vif->deflink.phy->mt76;
++ phy = dev->phys[wcid->phy_idx];
+ mlink->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
+ ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
+ } else {
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0099-wifi-mt76-connac-rework-connac-helpers.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0099-wifi-mt76-connac-rework-connac-helpers.patch
new file mode 100644
index 0000000..78704e7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0099-wifi-mt76-connac-rework-connac-helpers.patch
@@ -0,0 +1,174 @@
+From 458a75e252d6d8a7969108695b8b9c7cceb079e3 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 11 Dec 2023 18:45:00 +0800
+Subject: [PATCH 099/116] wifi: mt76: connac: rework connac helpers
+
+Rework connac helpers related to rate and phymode.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac.h | 2 +-
+ mt76_connac_mac.c | 17 ++++++++---------
+ mt76_connac_mcu.c | 6 +++---
+ mt76_connac_mcu.h | 2 +-
+ mt7925/main.c | 2 +-
+ mt7996/main.c | 2 +-
+ mt7996/mcu.c | 2 +-
+ 7 files changed, 16 insertions(+), 17 deletions(-)
+
+diff --git a/mt76_connac.h b/mt76_connac.h
+index 91987bdf2..0025add80 100644
+--- a/mt76_connac.h
++++ b/mt76_connac.h
+@@ -427,7 +427,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
+ struct ieee80211_key_conf *key, int pid,
+ enum mt76_txq_id qid, u32 changed);
+ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+- struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf,
+ bool beacon, bool mcast);
+ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ __le32 *txs_data);
+diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
+index b841bf628..c1e9ba0fc 100644
+--- a/mt76_connac_mac.c
++++ b/mt76_connac_mac.c
+@@ -291,12 +291,11 @@ EXPORT_SYMBOL_GPL(mt76_connac_init_tx_queues);
+ })
+
+ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+- struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *conf,
+ bool beacon, bool mcast)
+ {
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+- struct cfg80211_chan_def *chandef = mvif->ctx ?
+- &mvif->ctx->def : &mphy->chandef;
++ struct ieee80211_vif *vif = conf->vif;
++ struct cfg80211_chan_def *chandef = &mphy->chandef;
+ u8 nss = 0, mode = 0, band = chandef->chan->band;
+ int rateidx = 0, mcast_rate;
+
+@@ -304,14 +303,14 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+ goto legacy;
+
+ if (is_mt7921(mphy->dev)) {
+- rateidx = ffs(vif->bss_conf.basic_rates) - 1;
++ rateidx = ffs(conf->basic_rates) - 1;
+ goto legacy;
+ }
+
+ if (beacon) {
+ struct cfg80211_bitrate_mask *mask;
+
+- mask = &vif->bss_conf.beacon_tx_rate;
++ mask = &conf->beacon_tx_rate;
+
+ __bitrate_mask_check(he_mcs, HE_SU);
+ __bitrate_mask_check(vht_mcs, VHT);
+@@ -323,11 +322,11 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+ }
+ }
+
+- mcast_rate = vif->bss_conf.mcast_rate[band];
++ mcast_rate = conf->mcast_rate[band];
+ if (mcast && mcast_rate > 0)
+ rateidx = mcast_rate - 1;
+ else
+- rateidx = ffs(vif->bss_conf.basic_rates) - 1;
++ rateidx = ffs(conf->basic_rates) - 1;
+
+ legacy:
+ rateidx = mt76_calculate_default_rate(mphy, vif, rateidx);
+@@ -561,7 +560,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ bool multicast = ieee80211_is_data(hdr->frame_control) &&
+ is_multicast_ether_addr(hdr->addr1);
+- u16 rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon,
++ u16 rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon,
+ multicast);
+ u32 val = MT_TXD6_FIXED_BW;
+
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 9ea74719a..71f3d301c 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -1361,7 +1361,7 @@ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode);
+
+-u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
++u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf,
+ enum nl80211_band band)
+ {
+ const struct ieee80211_sta_eht_cap *eht_cap;
+@@ -1372,9 +1372,9 @@ u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ mode |= PHY_MODE_AX_6G;
+
+ sband = phy->hw->wiphy->bands[band];
+- eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type);
++ eht_cap = ieee80211_get_eht_iftype_cap(sband, conf->vif->type);
+
+- if (!eht_cap || !eht_cap->has_eht || !vif->bss_conf.eht_support)
++ if (!eht_cap || !eht_cap->has_eht || !conf->eht_support)
+ return mode;
+
+ switch (band) {
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 7f1562b8e..41e26b8a4 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -2016,7 +2016,7 @@ mt76_connac_get_eht_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
+ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ enum nl80211_band band,
+ struct ieee80211_link_sta *link_sta);
+-u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
++u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf,
+ enum nl80211_band band);
+
+ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+diff --git a/mt7925/main.c b/mt7925/main.c
+index 1f07ec5a2..6d7ec7708 100644
+--- a/mt7925/main.c
++++ b/mt7925/main.c
+@@ -675,7 +675,7 @@ mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u16 rate;
+ u8 i, idx, ht;
+
+- rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
++ rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon, mcast);
+ ht = FIELD_GET(MT_TX_RATE_MODE, rate) > MT_PHY_TYPE_OFDM;
+
+ if (beacon && ht) {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index aceb77ab2..bf3ae65e4 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -804,7 +804,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ u16 rate;
+ u8 i, idx;
+
+- rate = mt76_connac2_mac_tx_rate_val(mphy, conf->vif, beacon, mcast);
++ rate = mt76_connac2_mac_tx_rate_val(mphy, conf, beacon, mcast);
+
+ if (beacon) {
+ struct mt7996_phy *phy = mphy->priv;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 4bdb1fcbc..35e0578b5 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1219,7 +1219,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ bss->dtim_period = conf->dtim_period;
+ bss->phymode = mt76_connac_get_phy_mode(phy, vif,
+ chandef->chan->band, NULL);
+- bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, vif,
++ bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, conf,
+ chandef->chan->band);
+
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch
new file mode 100644
index 0000000..6351831
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch
@@ -0,0 +1,218 @@
+From 5cbd493fec6ab4c947bde92425b7af7754112e15 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 5 Dec 2023 13:56:51 +0800
+Subject: [PATCH 100/116] wifi: mt76: mt7996: handle mapping for hw and phy
+
+We've used mt7996_band_phy() to do mapping from ieee80211_hw to mt7996_phy,
+and this patch is a temporal workaround for opposite direction.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c | 11 ++++++++++-
+ mt76.h | 10 ++++++++++
+ mt7996/mac.c | 7 +++++--
+ mt7996/main.c | 41 ++++++++++++++++++++++++++++++-----------
+ 4 files changed, 55 insertions(+), 14 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 96fab2320..175f19169 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -823,9 +823,13 @@ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
+ struct mt76_channel_state *
+ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ {
++ struct mt76_phy *ori_phy = phy;
+ struct mt76_sband *msband;
+ int idx;
+
++ if (phy->main_phy)
++ phy = phy->main_phy;
++begin:
+ if (c->band == NL80211_BAND_2GHZ)
+ msband = &phy->sband_2g;
+ else if (c->band == NL80211_BAND_6GHZ)
+@@ -834,6 +838,11 @@ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ msband = &phy->sband_5g;
+
+ idx = c - &msband->sband.channels[0];
++ /* TODO: mlo: this is a temp solution, need to come up with a more clever one */
++ if (idx < 0 || idx >= msband->sband.n_channels) {
++ phy = ori_phy;
++ goto begin;
++ }
+ return &msband->chan[idx];
+ }
+ EXPORT_SYMBOL_GPL(mt76_channel_state);
+@@ -1072,7 +1081,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+ }
+
+ *sta = wcid_to_sta(mstat.wcid);
+- *hw = mt76_phy_hw(dev, mstat.phy_idx);
++ *hw = mt76_main_hw(dev->phys[mstat.phy_idx]);
+
+ if ((mstat.flag & RX_FLAG_8023) || ieee80211_is_data_qos(hdr->frame_control)) {
+ struct mt76_phy *phy = mt76_dev_phy(dev, mstat.phy_idx);
+diff --git a/mt76.h b/mt76.h
+index b60b5161b..3f9a0b8aa 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -831,6 +831,7 @@ struct mt76_vif {
+ struct mt76_phy {
+ struct ieee80211_hw *hw;
+ struct mt76_dev *dev;
++ struct mt76_phy *main_phy;
+ void *priv;
+
+ unsigned long state;
+@@ -1322,6 +1323,15 @@ mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx)
+ return mt76_dev_phy(dev, phy_idx)->hw;
+ }
+
++static inline struct ieee80211_hw *
++mt76_main_hw(struct mt76_phy *phy)
++{
++ if (phy->main_phy)
++ return mt76_dev_phy(phy->dev, phy->main_phy->band_idx)->hw;
++
++ return mt76_dev_phy(phy->dev, phy->band_idx)->hw;
++}
++
+ static inline u8 *
+ mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+ {
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 31b916695..cd25ea616 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2383,7 +2383,10 @@ void mt7996_mac_work(struct work_struct *work)
+
+ mt76_tx_status_check(mdev, false);
+
+- ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
++ if (mphy->main_phy && !test_bit(MT76_STATE_RUNNING, &mphy->main_phy->state))
++ return;
++
++ ieee80211_queue_delayed_work(mt76_main_hw(mphy), &mphy->mac_work,
+ MT7996_WATCHDOG_TIME);
+ }
+
+@@ -2802,7 +2805,7 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+
+ rcu_read_lock();
+- if (!ieee80211_tx_prepare_skb(hw, vif, skb,
++ if (!ieee80211_tx_prepare_skb(mt76_main_hw(phy->mt76), vif, skb,
+ phy->scan_chan->band,
+ NULL)) {
+ rcu_read_unlock();
+diff --git a/mt7996/main.c b/mt7996/main.c
+index bf3ae65e4..8b463b17d 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -173,6 +173,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ mutex_lock(&dev->mt76.mutex);
+ mt7996_mcu_set_radio_en(phy, false);
+ clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++ phy->mt76->main_phy = NULL;
+ mutex_unlock(&dev->mt76.mutex);
+ }
+ }
+@@ -541,9 +542,10 @@ out:
+ clear_bit(MT76_RESET, &phy->mt76->state);
+ mutex_unlock(&dev->mt76.mutex);
+
+- mt76_txq_schedule_all(phy->mt76);
++ if (phy->mt76 == phy->mt76->main_phy)
++ mt76_txq_schedule_all(phy->mt76);
+
+- ieee80211_queue_delayed_work(phy->mt76->hw,
++ ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76),
+ &phy->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
+
+@@ -554,11 +556,11 @@ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef
+ {
+ int ret;
+
+- ieee80211_stop_queues(phy->mt76->hw);
++ ieee80211_stop_queues(mt76_main_hw(phy->mt76));
+ ret = __mt7996_set_channel(phy, chandef);
+ if (ret)
+ return ret;
+- ieee80211_wake_queues(phy->mt76->hw);
++ ieee80211_wake_queues(mt76_main_hw(phy->mt76));
+
+ return 0;
+ }
+@@ -731,6 +733,7 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ MT_WF_RFCR1_DROP_CFEND |
+ MT_WF_RFCR1_DROP_CFACK;
+ u32 flags = 0;
++ u8 band;
+
+ #define MT76_FILTER(_flag, _hw) do { \
+ flags |= *total_flags & FIF_##_flag; \
+@@ -764,12 +767,26 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ MT_WF_RFCR_DROP_NDPA);
+
+ *total_flags = flags;
+- mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter);
+
+- if (*total_flags & FIF_CONTROL)
+- mt76_clear(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
+- else
+- mt76_set(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
++ /* configure rx filter to all affliated phy */
++ for (band = 0; band < __MT_MAX_BAND; band++) {
++ struct mt7996_phy *tmp;
++
++ if (!hw->wiphy->bands[band])
++ continue;
++
++ tmp = dev->mt76.phys[band]->priv;
++ if (tmp->mt76->main_phy != phy->mt76)
++ continue;
++
++ tmp->rxfilter = phy->rxfilter;
++ mt76_wr(dev, MT_WF_RFCR(tmp->mt76->band_idx), phy->rxfilter);
++
++ if (*total_flags & FIF_CONTROL)
++ mt76_clear(dev, MT_WF_RFCR1(tmp->mt76->band_idx), ctl_flags);
++ else
++ mt76_set(dev, MT_WF_RFCR1(tmp->mt76->band_idx), ctl_flags);
++ }
+
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -2186,7 +2203,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ phy->scan_chan_idx = 0;
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+- ieee80211_queue_delayed_work(hw, &phy->scan_work, 0);
++ ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
+
+ return 0;
+ }
+@@ -2203,7 +2220,8 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ continue;
+
+ phy = mt7996_band_phy(hw, band);
+- if (!(test_bit(MT76_SCANNING, &phy->mt76->state)))
++ if (!(test_bit(MT76_SCANNING, &phy->mt76->state) &&
++ phy->mt76->main_phy == hw->priv))
+ continue;
+
+ cancel_delayed_work_sync(&phy->scan_work);
+@@ -2224,6 +2242,7 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
+ mutex_lock(&phy->dev->mt76.mutex);
+
++ phy->mt76->main_phy = hw->priv;
+ if (ctx->assigned) {
+ mutex_unlock(&phy->dev->mt76.mutex);
+ return -ENOSPC;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0101-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0101-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch
new file mode 100644
index 0000000..d32cbf5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0101-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch
@@ -0,0 +1,222 @@
+From 239433584a6782a9b99d105698dbd4841b18cf9a Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 8 Dec 2023 18:08:13 +0800
+Subject: [PATCH 101/116] wifi: mt76: mt7996: handle mapping for hw and vif
+
+We have several temporal workarounds for ieee80211_hw and mt76_phy
+mappings. For legacy MBSS cases, we also need a method to do the
+hw and vif mappings.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ dma.c | 2 +-
+ mac80211.c | 2 +-
+ mt76.h | 14 ++++++++++++--
+ mt7996/mac.c | 24 +++++++++++++++++++++++-
+ mt7996/main.c | 1 +
+ mt7996/mcu.c | 4 ++--
+ mt7996/mmio.c | 1 +
+ mt7996/mt7996.h | 3 +++
+ tx.c | 4 ++--
+ 9 files changed, 46 insertions(+), 9 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 38701c71e..3f1fb6c2a 100644
+--- a/dma.c
++++ b/dma.c
+@@ -685,7 +685,7 @@ free:
+
+ free_skb:
+ status.skb = tx_info.skb;
+- hw = mt76_tx_status_get_hw(dev, tx_info.skb);
++ hw = mt76_tx_status_get_hw(dev, tx_info.skb, wcid);
+ spin_lock_bh(&dev->rx_lock);
+ ieee80211_tx_status_ext(hw, &status);
+ spin_unlock_bh(&dev->rx_lock);
+diff --git a/mac80211.c b/mac80211.c
+index 175f19169..dad659ecb 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1519,7 +1519,7 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
+ spin_unlock_bh(&phy->tx_lock);
+
+ while ((skb = __skb_dequeue(&list)) != NULL) {
+- hw = mt76_tx_status_get_hw(dev, skb);
++ hw = mt76_tx_status_get_hw(dev, skb, wcid);
+ ieee80211_free_txskb(hw, skb);
+ }
+ }
+diff --git a/mt76.h b/mt76.h
+index 3f9a0b8aa..64ea323d8 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -556,6 +556,9 @@ struct mt76_driver_ops {
+
+ void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
++
++ void (*get_hw)(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
++ struct ieee80211_hw **hw);
+ };
+
+ struct mt76_channel_state {
+@@ -1601,14 +1604,21 @@ static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
+
+ /* internal */
+ static inline struct ieee80211_hw *
+-mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
++mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb,
++ struct mt76_wcid *wcid)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
+- struct ieee80211_hw *hw = mt76_phy_hw(dev, phy_idx);
++ struct ieee80211_hw *hw;
+
+ info->hw_queue &= ~MT_TX_HW_QUEUE_PHY;
+
++ if (dev->drv->get_hw) {
++ dev->drv->get_hw(dev, wcid, phy_idx, &hw);
++ } else {
++ hw = mt76_phy_hw(dev, phy_idx);
++ }
++
+ return hw;
+ }
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index cd25ea616..6ca3e06e2 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2846,13 +2846,23 @@ static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
+ void mt7996_scan_work(struct work_struct *work)
+ {
+ struct mt7996_phy *phy = container_of(work, struct mt7996_phy, scan_work.work);
+- struct ieee80211_hw *hw = phy->mt76->hw;
++ struct ieee80211_vif *vif = phy->scan_vif;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct ieee80211_hw *hw = mvif->hw;
+ struct cfg80211_scan_request *req = phy->scan_req;
+ struct cfg80211_chan_def chandef = {};
+ int duration;
+ bool has_sta = false, active_scan = false;
+
+ mutex_lock(&phy->dev->mt76.mutex);
++ /* don't let non-MLD AP scan other bands */
++ if (vif->type != NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++ phy != mt7996_hw_phy(hw)) {
++ mt7996_scan_complete(phy, false);
++ mutex_unlock(&phy->dev->mt76.mutex);
++ return;
++ }
++
+ if (phy->scan_chan_idx >= req->n_channels) {
+ mt7996_scan_complete(phy, false);
+ mutex_unlock(&phy->dev->mt76.mutex);
+@@ -2912,3 +2922,15 @@ void mt7996_scan_work(struct work_struct *work)
+
+ ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
+ }
++
++void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
++ struct ieee80211_hw **hw)
++{
++ struct mt7996_link_sta *mlink = wcid_to_mlink(wcid);
++
++ if (mlink) {
++ *hw = mlink->sta->vif->hw;
++ } else {
++ *hw = mt76_phy_hw(dev, phy_idx);
++ }
++}
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8b463b17d..8bf520120 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -444,6 +444,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ phy->monitor_vif = vif;
+
+ mvif->dev = dev;
++ mvif->hw = hw;
+ mvif->sta.vif = mvif;
+
+ ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 35e0578b5..399e0ffd4 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2947,11 +2947,11 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+
+ if (changed & BSS_CHANGED_FILS_DISCOVERY) {
+ interval = conf->fils_discovery.max_interval;
+- skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
++ skb = ieee80211_get_fils_discovery_tmpl(mvif->hw, vif);
+ } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
+ conf->unsol_bcast_probe_resp_interval) {
+ interval = conf->unsol_bcast_probe_resp_interval;
+- skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
++ skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(mvif->hw, vif);
+ }
+
+ if (!skb) {
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 6abbcb68e..6bebdd81b 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -658,6 +658,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ .sta_assoc = mt7996_mac_sta_assoc,
+ .sta_remove = mt7996_mac_sta_remove,
+ .update_survey = mt7996_update_channel,
++ .get_hw = mt7996_get_hw,
+ };
+ struct mt7996_dev *dev;
+ struct mt76_dev *mdev;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c6ca00f1a..dc0a31d44 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -353,6 +353,7 @@ struct mt7996_vif {
+
+ struct mt7996_sta sta;
+ struct mt7996_dev *dev;
++ struct ieee80211_hw *hw;
+
+ u8 master_link_id;
+ u8 group_mld_id;
+@@ -897,6 +898,8 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
+ void mt7996_init_txpower(struct mt7996_phy *phy);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
+ int mt7996_get_chip_sku(struct mt7996_dev *dev);
++void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
++ struct ieee80211_hw **hw);
+ void mt7996_reset(struct mt7996_dev *dev);
+ void mt7996_coredump(struct mt7996_dev *dev, u8 state);
+ int mt7996_run(struct ieee80211_hw *hw);
+diff --git a/tx.c b/tx.c
+index e2795067c..6580833e9 100644
+--- a/tx.c
++++ b/tx.c
+@@ -76,7 +76,7 @@ mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
+ }
+ }
+
+- hw = mt76_tx_status_get_hw(dev, skb);
++ hw = mt76_tx_status_get_hw(dev, skb, wcid);
+ spin_lock_bh(&dev->rx_lock);
+ ieee80211_tx_status_ext(hw, &status);
+ spin_unlock_bh(&dev->rx_lock);
+@@ -272,7 +272,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
+ if (cb->pktid < MT_PACKET_ID_FIRST) {
+ struct ieee80211_rate_status rs = {};
+
+- hw = mt76_tx_status_get_hw(dev, skb);
++ hw = mt76_tx_status_get_hw(dev, skb, wcid);
+ status.sta = wcid_to_sta(wcid);
+ if (status.sta && (wcid->rate.flags || wcid->rate.legacy)) {
+ rs.rate_idx = wcid->rate;
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0102-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0102-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch
new file mode 100644
index 0000000..4aba3ff
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0102-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch
@@ -0,0 +1,153 @@
+From f60e775a0f4ca3389d4777b1b2200a5be06cab92 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 13 Dec 2023 16:58:31 +0800
+Subject: [PATCH 102/116] wifi: mt76: mt7996: rework scanning parts for MLD STA
+ support
+
+During the first scanning, the STA VIF is still a legacy interface,
+and at the moment it doesn't know if it's MLD or not. So do dome tricks
+here to let the legacy interface be abled to scan all bands.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 34 +++++++++++++++++++++++++---------
+ mt7996/main.c | 17 +++++++++++++++++
+ 2 files changed, 42 insertions(+), 9 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 6ca3e06e2..d6d1c0798 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2770,23 +2770,40 @@ static void
+ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ const u8 *dst)
+ {
+- struct ieee80211_hw *hw = phy->mt76->hw;
+ struct cfg80211_scan_request *req = phy->scan_req;
+ struct ieee80211_vif *vif = phy->scan_vif;
++ struct ieee80211_bss_conf *conf;
+ struct mt7996_vif *mvif;
+ struct mt7996_link_sta *mlink;
+ struct ieee80211_tx_info *info;
++ struct ieee80211_hw *hw;
+ struct sk_buff *skb;
++ unsigned long valid_links;
++ unsigned int link_id;
+
+ if (!req || !vif)
+ return;
+
++ valid_links = vif->valid_links ?: BIT(0);
+ mvif = (struct mt7996_vif *)vif->drv_priv;
++ hw = mvif->hw;
++
++ rcu_read_lock();
++
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ conf = rcu_dereference(vif->link_conf[link_id]);
++ mlink = rcu_dereference(mvif->sta.link[link_id]);
++ if (mlink->wcid.phy_idx != phy->mt76->band_idx)
++ continue;
++ }
+
+- skb = ieee80211_probereq_get(hw, vif->addr,
++ if (unlikely(!conf))
++ goto unlock;
++
++ skb = ieee80211_probereq_get(hw, conf->addr,
+ ssid->ssid, ssid->ssid_len, req->ie_len);
+ if (!skb)
+- return;
++ goto unlock;
+
+ if (is_unicast_ether_addr(dst)) {
+ struct ieee80211_hdr_3addr *hdr =
+@@ -2804,30 +2821,29 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+
+- rcu_read_lock();
+- if (!ieee80211_tx_prepare_skb(mt76_main_hw(phy->mt76), vif, skb,
+- phy->scan_chan->band,
+- NULL)) {
++ if (!ieee80211_tx_prepare_skb(hw, vif, skb,
++ phy->scan_chan->band, NULL)) {
+ rcu_read_unlock();
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+
+ local_bh_disable();
+- mlink = rcu_dereference(mvif->sta.link[0]);
+ mt76_tx(phy->mt76, NULL, &mlink->wcid, skb);
+ local_bh_enable();
+
++unlock:
+ rcu_read_unlock();
+ }
+
+ void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
+ {
++ struct mt7996_vif *mvif = (struct mt7996_vif *)phy->scan_vif->drv_priv;
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ };
+
+- ieee80211_scan_completed(phy->mt76->hw, &info);
++ ieee80211_scan_completed(mvif->hw, &info);
+ phy->scan_chan = NULL;
+ phy->scan_req = NULL;
+ phy->scan_vif = NULL;
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8bf520120..66972080f 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2191,6 +2191,8 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ {
+ struct cfg80211_scan_request *req = &hw_req->req;
+ struct mt7996_phy *phy = mt7996_band_phy(hw, req->channels[0]->band);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ int ret;
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ if (WARN_ON(phy->scan_req || phy->scan_chan)) {
+@@ -2202,6 +2204,17 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ phy->scan_req = req;
+ phy->scan_vif = vif;
+ phy->scan_chan_idx = 0;
++ if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++ (phy->mt76 != mvif->deflink.phy->mt76)) {
++ phy->mt76->main_phy = hw->priv;
++ mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
++
++ ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++ if (ret) {
++ mutex_unlock(&phy->dev->mt76.mutex);
++ return ret;
++ }
++ }
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
+@@ -2212,6 +2225,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ static void
+ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ int band;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+@@ -2229,6 +2243,9 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ mt7996_scan_complete(phy, true);
++ if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++ (phy->mt76 != mvif->deflink.phy->mt76))
++ phy->mt76->main_phy = NULL;
+ mutex_unlock(&phy->dev->mt76.mutex);
+ }
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0103-wifi-mt76-mt7996-implement-mld-address-translation.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0103-wifi-mt76-mt7996-implement-mld-address-translation.patch
new file mode 100644
index 0000000..13c3519
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0103-wifi-mt76-mt7996-implement-mld-address-translation.patch
@@ -0,0 +1,125 @@
+From 3ef17fdd49c8ded3d8aad5fea83fc1ffbf8972a0 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 7 Dec 2023 16:31:56 +0800
+Subject: [PATCH 103/116] wifi: mt76: mt7996: implement mld address translation
+
+Do the MLD to link address translation for EAPOL and management frames
+in driver.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Co-developed-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 20 ++++++++++++++++++++
+ mt7996/main.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 66 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index d6d1c0798..3141fe4e4 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -895,6 +895,26 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
+ pid, qid, 0);
+
++ /* translate addr3 of EAPOL by driver */
++ if (unlikely(tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE)) && sta->mlo) {
++ if (ether_addr_equal(vif->addr, hdr->addr3)) {
++ struct ieee80211_bss_conf *conf;
++
++ conf = rcu_dereference(vif->link_conf[wcid->link_id]);
++ if (unlikely(!conf))
++ return -ENOLINK;
++
++ memcpy(hdr->addr3, conf->addr, ETH_ALEN);
++ } else if (ether_addr_equal(sta->addr, hdr->addr3)) {
++ struct ieee80211_link_sta *link_sta;
++
++ link_sta = rcu_dereference(sta->link[wcid->link_id]);
++ memcpy(hdr->addr3, link_sta->addr, ETH_ALEN);
++ }
++
++ pr_info("EAPOL: a1=%pM, a2=%pM, a3=%pM\n", hdr->addr1, hdr->addr2, hdr->addr3);
++ }
++
+ txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
+ for (i = 0; i < nbuf; i++) {
+ u16 len;
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 66972080f..97431ae34 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1308,14 +1308,56 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+
+ rcu_read_lock();
+ if (mvif && msta) {
++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct mt7996_bss_conf *mconf;
+ struct mt7996_link_sta *mlink;
+-
+ u8 link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
++ struct ieee80211_sta *sta = ieee80211_find_sta(vif, hdr->addr1);
++
++ if (link_id >= IEEE80211_LINK_UNSPECIFIED) {
++ if (sta) {
++ struct mt7996_sta *peer;
++
++ peer = (struct mt7996_sta *)sta->drv_priv;
++ link_id = peer->pri_link;
++ } else {
++ link_id = mvif->master_link_id;
++ }
++ }
+
+- if (link_id >= IEEE80211_LINK_UNSPECIFIED)
+- link_id = mvif->master_link_id;
++ /* translate mld addr to link addr */
++ if (ieee80211_vif_is_mld(vif)) {
++ struct ieee80211_bss_conf *conf;
++ if (sta) {
++ struct ieee80211_link_sta *link_sta =
++ rcu_dereference(sta->link[link_id]);
++
++ if (!link_sta) {
++ mlo_dbg(mt7996_hw_phy(mvif->hw), "request TX on invalid link_id=%u, use primary link (id=%u) instead.\n",
++ link_id, msta->pri_link);
++ link_id = msta->pri_link;
++ link_sta = rcu_dereference(sta->link[link_id]);
++
++ if (!link_sta) {
++ mlo_dbg(mt7996_hw_phy(mvif->hw), "primary link became invalid, give up the TX\n");
++ goto unlock;
++ }
++ }
++
++ memcpy(hdr->addr1, link_sta->addr, ETH_ALEN);
++ if (ether_addr_equal(sta->addr, hdr->addr3))
++ memcpy(hdr->addr3, link_sta->addr, ETH_ALEN);
++ }
++
++ conf = rcu_dereference(vif->link_conf[link_id]);
++ if (unlikely(!conf))
++ goto unlock;
++
++ memcpy(hdr->addr2, conf->addr, ETH_ALEN);
++ if (ether_addr_equal(vif->addr, hdr->addr3))
++ memcpy(hdr->addr3, conf->addr, ETH_ALEN);
++ }
+
+ mconf = rcu_dereference(mvif->link[link_id]);
+ mlink = rcu_dereference(msta->link[link_id]);
+@@ -1333,6 +1375,7 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ }
+
+ mt76_tx(mphy, control->sta, wcid, skb);
++unlock:
+ rcu_read_unlock();
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0104-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0104-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch
new file mode 100644
index 0000000..97f1b55
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0104-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch
@@ -0,0 +1,106 @@
+From 463f4e447fe5f513e0b0f49b44152d359897c4c3 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 27 Feb 2024 18:07:11 +0800
+Subject: [PATCH 104/116] wifi: mt76: mt7996: use BSS_CHANGED_TXPOWER for
+ txpower setting
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 12 +++---------
+ mt7996/mcu.c | 5 +++--
+ mt7996/mt7996.h | 3 ++-
+ mt7996/testmode.c | 2 +-
+ 4 files changed, 9 insertions(+), 13 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 97431ae34..a38d77390 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -661,14 +661,6 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+- int ret;
+-
+- if (changed & (IEEE80211_CONF_CHANGE_POWER |
+- IEEE80211_CONF_CHANGE_CHANNEL)) {
+- ret = mt7996_mcu_set_txpower_sku(phy);
+- if (ret)
+- return ret;
+- }
+
+ mutex_lock(&dev->mt76.mutex);
+
+@@ -968,6 +960,9 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+ if (changed & BSS_CHANGED_MU_GROUPS)
+ mt7996_update_mu_group(hw, info, mconf);
+
++ if (changed & BSS_CHANGED_TXPOWER)
++ mt7996_mcu_set_txpower_sku(phy, info);
++
+ out:
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -1607,7 +1602,6 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ mt76_set_stream_caps(phy->mt76, true);
+ mt7996_set_stream_vht_txbf_caps(phy);
+ mt7996_set_stream_he_eht_caps(phy);
+- mt7996_mcu_set_txpower_sku(phy);
+
+ mutex_unlock(&dev->mt76.mutex);
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 399e0ffd4..39e353ab2 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5325,7 +5325,8 @@ mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
+ mphy->txpower_cur = e2p_power_limit;
+ }
+
+-int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
++int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
++ struct ieee80211_bss_conf *conf)
+ {
+ #define TX_POWER_LIMIT_TABLE_RATE 0
+ #define TX_POWER_LIMIT_TABLE_PATH 1
+@@ -5354,7 +5355,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+
+ if (hw->conf.power_level == INT_MIN)
+ hw->conf.power_level = 127;
+- txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
++ txpower_limit = mt7996_get_power_bound(phy, conf->txpower);
+
+ if (phy->sku_limit_en) {
+ txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index dc0a31d44..39aa3ee5b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -976,7 +976,8 @@ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
+ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
+ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
+ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
+-int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
++int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
++ struct ieee80211_bss_conf *conf);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ u8 rx_sel, u8 val);
+ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index ba17f947b..0565ebc94 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -1830,7 +1830,7 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ mt7996_tm_update_channel(phy);
+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
+- mt7996_mcu_set_txpower_sku(phy);
++ mt7996_mcu_set_txpower_sku(phy, &phy->monitor_vif->bss_conf);
+ }
+ if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
+ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0105-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0105-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch
new file mode 100644
index 0000000..841aaba
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0105-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch
@@ -0,0 +1,436 @@
+From 155e1bd8b7933deef463abc30f7d375761b93055 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 28 Mar 2024 18:50:04 +0800
+Subject: [PATCH 105/116] wifi: mt76: mt7996: temp support for single wiphy
+
+Add temporal single wiphy for simultaneously supporting MLD and legacy
+interfaces.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c | 11 +------
+ mt76.h | 11 +------
+ mt7996/eeprom.c | 6 ----
+ mt7996/init.c | 23 ++++++++++---
+ mt7996/mac.c | 5 +--
+ mt7996/main.c | 88 ++++++++++++++++++++++++++++++-------------------
+ mt7996/mt7996.h | 14 ++++----
+ 7 files changed, 81 insertions(+), 77 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index dad659ecb..20ab1db4d 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -823,13 +823,9 @@ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
+ struct mt76_channel_state *
+ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ {
+- struct mt76_phy *ori_phy = phy;
+ struct mt76_sband *msband;
+ int idx;
+
+- if (phy->main_phy)
+- phy = phy->main_phy;
+-begin:
+ if (c->band == NL80211_BAND_2GHZ)
+ msband = &phy->sband_2g;
+ else if (c->band == NL80211_BAND_6GHZ)
+@@ -838,11 +834,6 @@ begin:
+ msband = &phy->sband_5g;
+
+ idx = c - &msband->sband.channels[0];
+- /* TODO: mlo: this is a temp solution, need to come up with a more clever one */
+- if (idx < 0 || idx >= msband->sband.n_channels) {
+- phy = ori_phy;
+- goto begin;
+- }
+ return &msband->chan[idx];
+ }
+ EXPORT_SYMBOL_GPL(mt76_channel_state);
+@@ -1081,7 +1072,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+ }
+
+ *sta = wcid_to_sta(mstat.wcid);
+- *hw = mt76_main_hw(dev->phys[mstat.phy_idx]);
++ *hw = mt76_phy_hw(dev, mstat.phy_idx);
+
+ if ((mstat.flag & RX_FLAG_8023) || ieee80211_is_data_qos(hdr->frame_control)) {
+ struct mt76_phy *phy = mt76_dev_phy(dev, mstat.phy_idx);
+diff --git a/mt76.h b/mt76.h
+index 64ea323d8..b77a2f76b 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -833,8 +833,8 @@ struct mt76_vif {
+
+ struct mt76_phy {
+ struct ieee80211_hw *hw;
++ struct ieee80211_hw *ori_hw;
+ struct mt76_dev *dev;
+- struct mt76_phy *main_phy;
+ void *priv;
+
+ unsigned long state;
+@@ -1326,15 +1326,6 @@ mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx)
+ return mt76_dev_phy(dev, phy_idx)->hw;
+ }
+
+-static inline struct ieee80211_hw *
+-mt76_main_hw(struct mt76_phy *phy)
+-{
+- if (phy->main_phy)
+- return mt76_dev_phy(phy->dev, phy->main_phy->band_idx)->hw;
+-
+- return mt76_dev_phy(phy->dev, phy->band_idx)->hw;
+-}
+-
+ static inline u8 *
+ mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+ {
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 0393e93bf..51455d877 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -387,12 +387,6 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+ break;
+ }
+
+- /* TODO: for MLO, we enable all band capabilities */
+- phy->mt76->cap.has_2ghz = true;
+- phy->mt76->cap.has_5ghz = true;
+- if (is_mt7996(&phy->dev->mt76))
+- phy->mt76->cap.has_6ghz = true;
+-
+ return ret;
+ }
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index c6eb6a5c2..f374119f6 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -18,13 +18,13 @@ static const struct ieee80211_iface_limit if_limits[] = {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_ADHOC)
+ }, {
+- .max = 16,
++ .max = 16 * 3,
+ .types = BIT(NL80211_IFTYPE_AP)
+ #ifdef CONFIG_MAC80211_MESH
+ | BIT(NL80211_IFTYPE_MESH_POINT)
+ #endif
+ }, {
+- .max = MT7996_MAX_INTERFACES,
++ .max = MT7996_MAX_INTERFACES * 3,
+ .types = BIT(NL80211_IFTYPE_STATION)
+ }
+ };
+@@ -33,7 +33,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ {
+ .limits = if_limits,
+ .n_limits = ARRAY_SIZE(if_limits),
+- .max_interfaces = MT7996_MAX_INTERFACES,
++ .max_interfaces = MT7996_MAX_INTERFACES * 3,
+ .num_different_channels = 3,
+ .beacon_int_infra_match = true,
+ /*
+@@ -795,6 +795,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
+ }
+
++ /* TODO: FIXME: force to use single wiphy, need to rework init flow */
++ phy->mt76->ori_hw = mphy->hw;
++ mphy->hw = dev->phy.mt76->hw;
++
+ return 0;
+
+ error:
+@@ -811,6 +815,9 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ if (!phy)
+ return;
+
++ /* TODO: FIXME: temp for single wiphy support */
++ phy->mt76->hw = phy->mt76->ori_hw;
++
+ mt7996_unregister_thermal(phy);
+
+ mphy = phy->dev->mt76.phys[band];
+@@ -1679,6 +1686,12 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
++ hw->wiphy->bands[NL80211_BAND_2GHZ] = &dev->phy.mt76->sband_2g.sband;
++ if (mt7996_phy2(dev))
++ hw->wiphy->bands[NL80211_BAND_5GHZ] = &mt7996_phy2(dev)->mt76->sband_5g.sband;
++ if (mt7996_phy3(dev))
++ hw->wiphy->bands[NL80211_BAND_6GHZ] = &mt7996_phy3(dev)->mt76->sband_6g.sband;
++
+ ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
+
+ dev->recovery.hw_init_done = true;
+@@ -1708,11 +1721,11 @@ error:
+ void mt7996_unregister_device(struct mt7996_dev *dev)
+ {
+ cancel_work_sync(&dev->wed_rro.work);
+- mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
+- mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
+ mt7996_unregister_thermal(&dev->phy);
+ mt7996_coredump_unregister(dev);
+ mt76_unregister_device(&dev->mt76);
++ mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
++ mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
+ mt7996_wed_rro_free(dev);
+ mt7996_mcu_exit(dev);
+ mt7996_tx_token_put(dev);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3141fe4e4..e6db1765f 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2403,10 +2403,7 @@ void mt7996_mac_work(struct work_struct *work)
+
+ mt76_tx_status_check(mdev, false);
+
+- if (mphy->main_phy && !test_bit(MT76_STATE_RUNNING, &mphy->main_phy->state))
+- return;
+-
+- ieee80211_queue_delayed_work(mt76_main_hw(mphy), &mphy->mac_work,
++ ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+ MT7996_WATCHDOG_TIME);
+ }
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a38d77390..b9cc5a2e5 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -140,6 +140,10 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ int ret;
+
++ /* only allow settings from hw0 */
++ if (hw != dev->phy.mt76->hw)
++ return -1;
++
+ flush_work(&dev->init_work);
+
+ mutex_lock(&dev->mt76.mutex);
+@@ -154,6 +158,10 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ int band;
+
++ /* only allow settings from hw0 */
++ if (hw != dev->phy.mt76->hw)
++ return;
++
+ cancel_delayed_work_sync(&dev->scs_work);
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+@@ -173,7 +181,6 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ mutex_lock(&dev->mt76.mutex);
+ mt7996_mcu_set_radio_en(phy, false);
+ clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+- phy->mt76->main_phy = NULL;
+ mutex_unlock(&dev->mt76.mutex);
+ }
+ }
+@@ -446,8 +453,11 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ mvif->dev = dev;
+ mvif->hw = hw;
+ mvif->sta.vif = mvif;
++ /* TODO: temporaily set this to prevent some crashes */
++ mvif->deflink.phy = phy;
+
+- ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++ if (vif->type == NL80211_IFTYPE_STATION)
++ ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+@@ -543,10 +553,9 @@ out:
+ clear_bit(MT76_RESET, &phy->mt76->state);
+ mutex_unlock(&dev->mt76.mutex);
+
+- if (phy->mt76 == phy->mt76->main_phy)
+- mt76_txq_schedule_all(phy->mt76);
++ mt76_txq_schedule_all(phy->mt76);
+
+- ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76),
++ ieee80211_queue_delayed_work(phy->mt76->hw,
+ &phy->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
+
+@@ -557,11 +566,11 @@ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef
+ {
+ int ret;
+
+- ieee80211_stop_queues(mt76_main_hw(phy->mt76));
++ ieee80211_stop_queues(phy->mt76->hw);
+ ret = __mt7996_set_channel(phy, chandef);
+ if (ret)
+ return ret;
+- ieee80211_wake_queues(mt76_main_hw(phy->mt76));
++ ieee80211_wake_queues(phy->mt76->hw);
+
+ return 0;
+ }
+@@ -769,9 +778,6 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ continue;
+
+ tmp = dev->mt76.phys[band]->priv;
+- if (tmp->mt76->main_phy != phy->mt76)
+- continue;
+-
+ tmp->rxfilter = phy->rxfilter;
+ mt76_wr(dev, MT_WF_RFCR(tmp->mt76->band_idx), phy->rxfilter);
+
+@@ -1576,9 +1582,11 @@ static int
+ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ {
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
+- int max_nss = hweight8(hw->wiphy->available_antennas_tx);
+- u8 band_idx = phy->mt76->band_idx, shift = dev->chainshift[band_idx];
++ int band, max_nss = hweight8(hw->wiphy->available_antennas_tx);
++
++ /* only allow settings from hw0 */
++ if (hw != dev->phy.mt76->hw)
++ return 0;
+
+ if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
+ return -EINVAL;
+@@ -1588,20 +1596,34 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+
+ mutex_lock(&dev->mt76.mutex);
+
+- phy->mt76->antenna_mask = tx_ant;
++ for (band = 0; band < NUM_NL80211_BANDS; band++) {
++ struct mt7996_phy *phy;
++ u8 band_idx, shift;
++
++ if (!hw->wiphy->bands[band])
++ continue;
+
+- /* restore to the origin chainmask which might have auxiliary path */
+- if (hweight8(tx_ant) == max_nss && band_idx < MT_BAND2)
+- phy->mt76->chainmask = ((dev->chainmask >> shift) &
+- (BIT(dev->chainshift[band_idx + 1] - shift) - 1)) << shift;
+- else if (hweight8(tx_ant) == max_nss)
+- phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
+- else
+- phy->mt76->chainmask = tx_ant << shift;
++ phy = mt7996_band_phy(hw, band);
++ if (!phy)
++ continue;
+
+- mt76_set_stream_caps(phy->mt76, true);
+- mt7996_set_stream_vht_txbf_caps(phy);
+- mt7996_set_stream_he_eht_caps(phy);
++ phy->mt76->antenna_mask = tx_ant;
++ band_idx = phy->mt76->band_idx;
++ shift = dev->chainshift[band_idx];
++
++ /* restore to the origin chainmask which might have auxiliary path */
++ if (hweight8(tx_ant) == max_nss && band_idx < MT_BAND2)
++ phy->mt76->chainmask = ((dev->chainmask >> shift) &
++ (BIT(dev->chainshift[band_idx + 1] - shift) - 1)) << shift;
++ else if (hweight8(tx_ant) == max_nss)
++ phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
++ else
++ phy->mt76->chainmask = tx_ant << shift;
++
++ mt76_set_stream_caps(phy->mt76, true);
++ mt7996_set_stream_vht_txbf_caps(phy);
++ mt7996_set_stream_he_eht_caps(phy);
++ }
+
+ mutex_unlock(&dev->mt76.mutex);
+
+@@ -2243,7 +2265,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ phy->scan_chan_idx = 0;
+ if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
+ (phy->mt76 != mvif->deflink.phy->mt76)) {
+- phy->mt76->main_phy = hw->priv;
++ // phy->mt76->main_phy = hw->priv;
+ mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
+
+ ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+@@ -2254,7 +2276,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ }
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+- ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
++ ieee80211_queue_delayed_work(phy->mt76->hw, &phy->scan_work, 0);
+
+ return 0;
+ }
+@@ -2262,7 +2284,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ static void
+ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
+- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ // struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ int band;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+@@ -2272,17 +2294,16 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ continue;
+
+ phy = mt7996_band_phy(hw, band);
+- if (!(test_bit(MT76_SCANNING, &phy->mt76->state) &&
+- phy->mt76->main_phy == hw->priv))
++ if (!test_bit(MT76_SCANNING, &phy->mt76->state))
+ continue;
+
+ cancel_delayed_work_sync(&phy->scan_work);
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ mt7996_scan_complete(phy, true);
+- if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
+- (phy->mt76 != mvif->deflink.phy->mt76))
+- phy->mt76->main_phy = NULL;
++ // if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++ // (phy->mt76 != mvif->deflink.phy->mt76))
++ // phy->mt76->main_phy = NULL;
+ mutex_unlock(&phy->dev->mt76.mutex);
+ }
+ }
+@@ -2297,7 +2318,6 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
+ mutex_lock(&phy->dev->mt76.mutex);
+
+- phy->mt76->main_phy = hw->priv;
+ if (ctx->assigned) {
+ mutex_unlock(&phy->dev->mt76.mutex);
+ return -ENOSPC;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 39aa3ee5b..0444ae58e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -794,21 +794,19 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
+ static inline struct mt7996_phy *
+ mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
+ {
+- struct mt76_phy *phy = hw->priv;
+-
+- if (!(hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO))
+- return phy->priv;
++ struct mt76_dev *dev = hw->priv;
++ struct mt76_phy *phy;
+
+ /* TODO: mlo: temporarily hardcode */
+ if (band == NL80211_BAND_6GHZ)
+- phy = phy->dev->phys[MT_BAND2];
++ phy = dev->phys[MT_BAND2];
+ else if (band == NL80211_BAND_5GHZ)
+- phy = phy->dev->phys[MT_BAND1];
++ phy = dev->phys[MT_BAND1];
+ else
+- phy = phy->dev->phys[MT_BAND0];
++ phy = dev->phys[MT_BAND0];
+
+ if (!phy)
+- phy = hw->priv;
++ return NULL;
+
+ return phy->priv;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0106-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0106-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch
new file mode 100644
index 0000000..53a5557
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0106-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch
@@ -0,0 +1,157 @@
+From d17884fc654153615c5d6f6ac4192eed3ec34525 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 29 Dec 2023 18:37:41 +0800
+Subject: [PATCH 106/116] wifi: mt76: mt7996: implement ieee80211_ops for link
+ debugfs
+
+Add .link_sta_add_debugfs and .link_add_debugfs.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/debugfs.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mac.c | 4 +--
+ mt7996/main.c | 2 ++
+ mt7996/mcu.c | 6 +++--
+ mt7996/mt7996.h | 5 ++++
+ 5 files changed, 78 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 56e219231..26927eda9 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -1307,4 +1307,69 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
+ }
+
++static int
++mt7996_link_sta_info_show(struct seq_file *file, void *data)
++{
++ struct ieee80211_link_sta *link_sta = file->private;
++ struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
++ struct mt7996_link_sta *mlink;
++ struct mt7996_dev *dev = msta->vif->dev;
++ struct rate_info *r;
++
++ mutex_lock(&dev->mt76.mutex);
++
++ mlink = mlink_dereference_protected(msta, link_sta->link_id);
++ r = &mlink->wcid.rate;
++ seq_printf(file, "tx rate: flags=0x%x,legacy=%u,mcs=%u,nss=%u,bw=%u,he_gi=%u,he_dcm=%u,he_ru_alloc=%u,eht_gi=%u,eht_ru_alloc=%u\n",
++ r->flags, r->legacy, r->mcs, r->nss, r->bw, r->he_gi, r->he_dcm, r->he_ru_alloc, r->eht_gi, r->eht_ru_alloc);
++ seq_printf(file, "tx_bytes=%llu\n", mlink->wcid.stats.tx_bytes);
++ seq_printf(file, "rx_bytes=%llu\n", mlink->wcid.stats.rx_bytes);
++ seq_printf(file, "tx_airtime=%llu\n", mlink->wcid.stats.tx_airtime);
++ seq_printf(file, "rx_airtime=%llu\n", mlink->wcid.stats.rx_airtime);
++
++ mutex_unlock(&dev->mt76.mutex);
++
++ return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_link_sta_info);
++
++void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_link_sta *link_sta,
++ struct dentry *dir)
++{
++ debugfs_create_file("link_sta_info", 0600, dir, link_sta,
++ &mt7996_link_sta_info_fops);
++}
++
++static int
++mt7996_link_info_show(struct seq_file *file, void *data)
++{
++ struct ieee80211_bss_conf *conf = file->private;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
++ struct mt7996_sta *msta = &mvif->sta;
++ struct mt7996_bss_conf *mconf;
++ struct mt7996_link_sta *mlink;
++ struct mt7996_dev *dev = mvif->dev;
++ struct rate_info *r;
++
++ mutex_lock(&dev->mt76.mutex);
++
++ mconf = mconf_dereference_protected(mvif, conf->link_id);
++ mlink = mlink_dereference_protected(msta, conf->link_id);
++ r = &mlink->wcid.rate;
++ seq_printf(file, "band mapping=%u\n", mconf->phy->mt76->band_idx);
++ seq_printf(file, "tx rate: flags=0x%x,legacy=%u,mcs=%u,nss=%u,bw=%u,he_gi=%u,he_dcm=%u,he_ru_alloc=%u,eht_gi=%u,eht_ru_alloc=%u\n",
++ r->flags, r->legacy, r->mcs, r->nss, r->bw, r->he_gi, r->he_dcm, r->he_ru_alloc, r->eht_gi, r->eht_ru_alloc);
++
++ mutex_unlock(&dev->mt76.mutex);
++
++ return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_link_info);
++
++void mt7996_link_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf, struct dentry *dir)
++{
++ debugfs_create_file("link_info", 0600, dir, link_conf, &mt7996_link_info_fops);
++}
+ #endif
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index e6db1765f..5967b6aeb 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2379,10 +2379,10 @@ void mt7996_mac_work(struct work_struct *work)
+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
+ mt7996_mcu_get_rssi(mdev);
+- if (mtk_wed_device_active(&mdev->mmio.wed)) {
++ // if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
+- }
++ // }
+
+ if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
+ BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
+diff --git a/mt7996/main.c b/mt7996/main.c
+index b9cc5a2e5..64797f177 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2602,6 +2602,8 @@ const struct ieee80211_ops mt7996_ops = {
+ CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ .sta_add_debugfs = mt7996_sta_add_debugfs,
++ .link_sta_add_debugfs = mt7996_link_sta_add_debugfs,
++ // .link_add_debugfs = mt7996_link_add_debugfs,
+ #endif
+ .set_radar_background = mt7996_set_radar_background,
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 39e353ab2..24489ac27 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -616,8 +616,10 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ wcid->stats.tx_packets += tx_packets;
+ wcid->stats.rx_packets += rx_packets;
+
+- __mt7996_stat_to_netdev(mphy, wcid, 0, 0,
+- tx_packets, rx_packets);
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
++ __mt7996_stat_to_netdev(mphy, wcid, 0, 0,
++ tx_packets, rx_packets);
++ }
+ break;
+ case UNI_ALL_STA_TXRX_AIRTIME:
+ wlan_idx = le16_to_cpu(res->airtime[i].wlan_idx);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 0444ae58e..dc3cacc3a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1138,6 +1138,11 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct dentry *dir);
++void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_link_sta *link_sta,
++ struct dentry *dir);
++void mt7996_link_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf, struct dentry *dir);
+ #endif
+ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ bool hif2, int *irq);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch
new file mode 100644
index 0000000..c7e57c9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch
@@ -0,0 +1,169 @@
+From 7ece9b1ea9a8960f2ff94ac84754ddddf645b3a1 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:04:03 +0800
+Subject: [PATCH 107/116] mtk: wifi: mt76: mt7996: support multi-link channel
+ switch
+
+mtk: wifi: mt76: mt7996: remove the limitation of radar detect width for mlo
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: mt7996: start and finalize channel switch on link basis
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: mt7996: fix DFS RDD init issue
+
+1. Add radar enabled flag in mt76_phy since hw->conf.radar_enabled
+is only used for non-chanctx driver.
+2. Add IEEE80211_CHANCTX_CHANGE_RADAR flag in change_chanctx for RDD
+DFS state update.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+mtk: wifi: mt76: mt7996: fix background radar using wrong phy for mld ap
+
+mt7996_hw_phy will be phy0 for 3 link mld ap
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mac80211.c | 2 +-
+ mt76.h | 1 +
+ mt7996/init.c | 2 --
+ mt7996/main.c | 12 +++++++++---
+ mt7996/mcu.c | 14 +++++++++++---
+ 5 files changed, 22 insertions(+), 9 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 20ab1db4d..6512dee61 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1797,7 +1797,7 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
+ test_bit(MT76_SCANNING, &phy->state))
+ return MT_DFS_STATE_DISABLED;
+
+- if (!hw->conf.radar_enabled) {
++ if (!phy->radar_enabled) {
+ if ((hw->conf.flags & IEEE80211_CONF_MONITOR) &&
+ (phy->chandef.chan->flags & IEEE80211_CHAN_RADAR))
+ return MT_DFS_STATE_ACTIVE;
+diff --git a/mt76.h b/mt76.h
+index b77a2f76b..7919d9c99 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -849,6 +849,7 @@ struct mt76_phy {
+
+ struct mt76_channel_state *chan_state;
+ enum mt76_dfs_state dfs_state;
++ bool radar_enabled;
+ ktime_t survey_time;
+
+ u32 aggr_stats[32];
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f374119f6..0dee6596f 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -36,13 +36,11 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ .max_interfaces = MT7996_MAX_INTERFACES * 3,
+ .num_different_channels = 3,
+ .beacon_int_infra_match = true,
+- /*
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80) |
+ BIT(NL80211_CHAN_WIDTH_160),
+- */
+ }
+ };
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 64797f177..05efec469 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2133,7 +2133,7 @@ static int
+ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ struct cfg80211_chan_def *chandef)
+ {
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mt7996_phy *phy = mt7996_band_phy(hw, NL80211_BAND_5GHZ);
+ struct mt7996_dev *dev = phy->dev;
+ int ret = -EINVAL;
+ bool running;
+@@ -2332,6 +2332,7 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ }
+
+ phy->chanctx = ctx;
++ phy->mt76->radar_enabled = conf->radar_enabled;
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
+@@ -2359,8 +2360,10 @@ mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ ctx->assigned = false;
+- if (ctx == phy->chanctx)
++ if (ctx == phy->chanctx) {
+ phy->chanctx = NULL;
++ phy->mt76->radar_enabled = false;
++ }
+ mutex_unlock(&phy->dev->mt76.mutex);
+ }
+
+@@ -2372,8 +2375,10 @@ mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
+ struct mt7996_phy *phy = ctx->phy;
+
+ wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
+- if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
++ if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH ||
++ changed & IEEE80211_CHANCTX_CHANGE_RADAR) {
+ ctx->chandef = conf->def;
++ phy->mt76->radar_enabled = conf->radar_enabled;
+
+ mt7996_set_channel(phy, &ctx->chandef);
+ }
+@@ -2471,6 +2476,7 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ mutex_lock(&phy->dev->mt76.mutex);
+
+ phy->chanctx = new_ctx;
++ phy->mt76->radar_enabled = vifs->new_ctx->radar_enabled;
+ new_ctx->assigned = true;
+ new_ctx->chandef = vifs->new_ctx->def;
+ new_ctx->phy = phy;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 24489ac27..dc719ecbe 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -357,10 +357,18 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
+ static void
+ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+- if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
++ struct mt76_phy *mphy = (struct mt76_phy *)priv;
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct ieee80211_bss_conf *link_conf;
++ int link_id, band_idx = mphy->band_idx;
++
++ link_id = mvif->band_to_link[band_idx];
++ link_conf = rcu_dereference(vif->link_conf[link_id]);
++
++ if (!link_conf || !link_conf->csa_active || vif->type == NL80211_IFTYPE_STATION)
+ return;
+
+- ieee80211_csa_finish(vif, 0);
++ ieee80211_csa_finish(vif, link_id);
+ }
+
+ static void
+@@ -475,7 +483,7 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+ case UNI_EVENT_IE_COUNTDOWN_CSA:
+ ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+- mt7996_mcu_csa_finish, mphy->hw);
++ mt7996_mcu_csa_finish, mphy);
+ break;
+ case UNI_EVENT_IE_COUNTDOWN_BCC:
+ ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0108-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0108-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch
new file mode 100644
index 0000000..d62352f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0108-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch
@@ -0,0 +1,41 @@
+From 469ce653f972aa5b6cf213418cfdcecffe0af924 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 22 Feb 2024 11:09:10 +0800
+Subject: [PATCH 108/116] mtk: mt76: mt7996: hw_scan: ACS channel time too long
+ on duty channel
+
+This problem happens in SW scan and was already fixed.
+(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/mt76/+/8312969)
+
+This commit applys same solution for HW scan.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 05efec469..6943b28ba 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -497,6 +497,7 @@ static void ___mt7996_set_channel(struct mt7996_phy *phy,
+ struct mt76_phy *mphy = phy->mt76;
+ bool offchannel = phy->scan_chan != NULL;
+ int timeout = HZ / 5;
++ unsigned long was_scanning = ieee80211_get_scanning(mphy->hw);
+
+ wait_event_timeout(mdev->tx_wait, !mt76_has_tx_pending(mphy), timeout);
+ mt76_update_survey(mphy);
+@@ -511,7 +512,7 @@ static void ___mt7996_set_channel(struct mt7996_phy *phy,
+ if (!offchannel)
+ mphy->main_chan = chandef->chan;
+
+- if (chandef->chan != mphy->main_chan)
++ if (chandef->chan != mphy->main_chan || was_scanning)
+ memset(mphy->chan_state, 0, sizeof(*mphy->chan_state));
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0109-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0109-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch
new file mode 100644
index 0000000..b7c620f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0109-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch
@@ -0,0 +1,203 @@
+From 184ecc9c9c16bbc9b93a69c994194d8bf1e41e72 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 20 Feb 2024 10:08:01 +0800
+Subject: [PATCH 109/116] wifi: mt76: mt7996: add beacon monitoring in driver
+ for mlo
+
+Add beacon monitoring in driver since mac80211 does not
+support connect monitoring if WIPHY_FLAG_SUPPORTS_MLO is set.
+(IEEE80211_HW_CONNECTION_MONITOR should be set)
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: I52211987abd6309bbb23dc648af3f377adf8982a
+---
+ mt7996/mac.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/main.c | 50 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mt7996.h | 7 +++++++
+ 3 files changed, 113 insertions(+)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 5967b6aeb..2a45fc03d 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -565,6 +565,21 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ */
+ if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
+ *qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
++ } else if (ieee80211_is_beacon(fc)) {
++ struct ieee80211_hw *hw = phy->mt76->hw;
++ struct ieee80211_sta *sta;
++ struct mt7996_sta *msta;
++ unsigned int link_id;
++
++ sta = ieee80211_find_sta_by_link_addrs(hw, hdr->addr2, NULL, &link_id);
++ if (!sta)
++ sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL);
++
++ if (sta) {
++ msta = (struct mt7996_sta *)sta->drv_priv;
++ if (msta && msta->vif)
++ msta->vif->beacon_received_time[band_idx] = jiffies;
++ }
+ }
+ #ifdef CONFIG_MTK_VENDOR
+ if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
+@@ -2956,6 +2971,47 @@ void mt7996_scan_work(struct work_struct *work)
+ ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
+ }
+
++void mt7996_beacon_mon_work(struct work_struct *work)
++{
++ struct mt7996_vif *mvif = container_of(work, struct mt7996_vif, beacon_mon_work.work);
++ struct ieee80211_vif *vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
++ struct ieee80211_hw *hw = mvif->hw;
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ unsigned long next_time = ULONG_MAX, valid_links = vif->valid_links ?: BIT(0);
++ unsigned int link_id;
++
++ mutex_lock(&dev->mt76.mutex);
++
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct ieee80211_bss_conf *conf;
++ struct mt7996_bss_conf *mconf;
++ struct mt7996_phy *phy;
++ unsigned long timeout, loss_duration;
++
++ conf = link_conf_dereference_protected(vif, link_id);
++ mconf = mconf_dereference_protected(mvif, link_id);
++ if (!conf || !mconf)
++ continue;
++
++ phy = mconf->phy;
++ loss_duration = msecs_to_jiffies(MT7996_MAX_BEACON_LOSS * conf->beacon_int);
++ timeout = mvif->beacon_received_time[phy->mt76->band_idx] + loss_duration;
++ if (time_after_eq(jiffies, timeout)) {
++ mutex_unlock(&dev->mt76.mutex);
++ wiphy_info(hw->wiphy,
++ "link %d: detected beacon loss, start disconnecting\n",
++ link_id);
++ /* TODO: disconnect single link & handle link reconfiguration for MLD */
++ ieee80211_connection_loss(vif);
++ return;
++ }
++ next_time = min(next_time, timeout - jiffies);
++ }
++ mutex_unlock(&dev->mt76.mutex);
++
++ ieee80211_queue_delayed_work(hw, &mvif->beacon_mon_work, next_time);
++}
++
+ void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
+ struct ieee80211_hw **hw)
+ {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 6943b28ba..e37f0c013 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -450,6 +450,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ is_zero_ether_addr(vif->addr))
+ phy->monitor_vif = vif;
+
++ INIT_DELAYED_WORK(&mvif->beacon_mon_work, mt7996_beacon_mon_work);
+ mvif->dev = dev;
+ mvif->hw = hw;
+ mvif->sta.vif = mvif;
+@@ -2565,6 +2566,54 @@ out:
+ return ret;
+ }
+
++static void
++mt7996_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ const struct ieee80211_event *event)
++{
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++
++ mutex_lock(&dev->mt76.mutex);
++
++ switch (event->type) {
++ case MLME_EVENT:
++ if (event->u.mlme.data == ASSOC_EVENT &&
++ event->u.mlme.status == MLME_SUCCESS) {
++ struct ieee80211_bss_conf *conf;
++ struct mt7996_bss_conf *mconf;
++ struct mt7996_phy *phy;
++ unsigned long cur, valid_links = vif->valid_links ?: BIT(0);
++ unsigned int link_id;
++ int next_time = INT_MAX;
++
++ cur = jiffies;
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ conf = link_conf_dereference_protected(vif, link_id);
++ mconf = mconf_dereference_protected(mvif, link_id);
++ if (conf && mconf) {
++ phy = mconf->phy;
++ mvif->beacon_received_time[phy->mt76->band_idx] = cur;
++ next_time = min(next_time,
++ MT7996_MAX_BEACON_LOSS *
++ conf->beacon_int);
++ }
++ }
++
++ ieee80211_queue_delayed_work(hw, &mvif->beacon_mon_work,
++ msecs_to_jiffies(next_time));
++ break;
++ }
++
++ cancel_delayed_work_sync(&mvif->beacon_mon_work);
++ break;
++ default:
++ break;
++ }
++
++ mutex_unlock(&dev->mt76.mutex);
++ return;
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ .tx = mt7996_tx,
+ .start = mt7996_start,
+@@ -2617,6 +2666,7 @@ const struct ieee80211_ops mt7996_ops = {
+ .net_fill_forward_path = mt7996_net_fill_forward_path,
+ .net_setup_tc = mt76_wed_net_setup_tc,
+ #endif
++ .event_callback = mt7996_event_callback,
+ .add_chanctx = mt7996_add_chanctx,
+ .remove_chanctx = mt7996_remove_chanctx,
+ .change_chanctx = mt7996_change_chanctx,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index dc3cacc3a..0412d7375 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -128,6 +128,8 @@
+
+ #define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
+
++#define MT7996_MAX_BEACON_LOSS 50
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -360,6 +362,10 @@ struct mt7996_vif {
+ u8 mld_remap_id;
+
+ u8 band_to_link[__MT_MAX_BAND];
++
++ /* for beacon monitoring */
++ struct delayed_work beacon_mon_work;
++ unsigned long beacon_received_time[__MT_MAX_BAND];
+ };
+
+ /* crash-dump */
+@@ -1113,6 +1119,7 @@ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+ void mt7996_stats_work(struct work_struct *work);
+ void mt7996_scan_work(struct work_struct *work);
+ void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
++void mt7996_beacon_mon_work(struct work_struct *work);
+ int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
+ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
+ void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch
new file mode 100644
index 0000000..c07333f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch
@@ -0,0 +1,180 @@
+From e9b712d9fa71f982a52ee20eb86929d8938ec7ca Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 4 Mar 2024 16:21:16 +0800
+Subject: [PATCH 110/116] mtk: wifi: mt76: mt7996: support band_idx option for
+ set_mu/get_mu vendor command
+
+The vendor command set_mu and get_mu should be executed with band_idx.
+With band_idx, driver can access the corrsponding phy by band_idx.
+
+CR-Id: WCNCR00240772
+Change-Id: Id33d5efd3752e767fc11e852836d9939e4d6a088
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mcu.c | 26 +++++++++++++++++++-------
+ mt7996/mcu.h | 1 +
+ mt7996/vendor.c | 40 +++++++++++++++++++++++++++++++++++-----
+ mt7996/vendor.h | 1 +
+ 4 files changed, 56 insertions(+), 12 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index dc719ecbe..cd49f7058 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5853,12 +5853,23 @@ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+- u8 mode, val;
++ u8 mode, val, band_idx;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+- struct mt7996_phy *phy = mvif->deflink.phy;
++ struct mt7996_phy *phy;
++ struct mt76_phy *mphy;
+
+ mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
+ val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
++ band_idx = FIELD_GET(RATE_CFG_BAND_IDX, *((u32 *)data));
++
++ if (!mt7996_band_valid(mvif->dev, band_idx))
++ goto error;
++
++ mphy = mvif->dev->mt76.phys[band_idx];
++ if (!mphy)
++ goto error;
++
++ phy = (struct mt7996_phy *)mphy->priv;
+
+ switch (mode) {
+ case RATE_PARAM_FIXED_OFDMA:
+@@ -5874,13 +5885,14 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ phy->muru_onoff = MUMIMO_UL;
+ break;
+ case RATE_PARAM_AUTO_MU:
+- if (val < 0 || val > 15) {
+- printk("Wrong value! The value is between 0-15.\n");
+- break;
+- }
+- phy->muru_onoff = val;
++ phy->muru_onoff = val & GENMASK(3, 0);
+ break;
+ }
++
++ return;
++error:
++ dev_err(mvif->dev->mt76.dev, "Invalid band_idx to config\n");
++ return;
+ }
+
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index ee36cf5ed..3d5a0c3c2 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -888,6 +888,7 @@ enum {
+ #endif
+ };
+
++#define RATE_CFG_BAND_IDX GENMASK(17, 16)
+ #define RATE_CFG_MODE GENMASK(15, 8)
+ #define RATE_CFG_VAL GENMASK(7, 0)
+
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 31688c373..64ef5515c 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -16,6 +16,7 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
+ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
+ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
+ [MTK_VENDOR_ATTR_MU_CTRL_STRUCT] = {.type = NLA_BINARY },
++ [MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX] = {.type = NLA_U8 },
+ };
+
+ static const struct nla_policy
+@@ -135,7 +136,7 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_muru *muru;
+ int err;
+- u8 val8;
++ u8 val8, band_idx;
+ u32 val32 = 0;
+
+ err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
+@@ -143,10 +144,13 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ if (err)
+ return err;
+
+- if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
++ if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] &&
++ tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]) {
+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
++ band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]);
+ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
+- FIELD_PREP(RATE_CFG_VAL, val8);
++ FIELD_PREP(RATE_CFG_VAL, val8) |
++ FIELD_PREP(RATE_CFG_BAND_IDX, band_idx);
+ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_set_wireless_vif, &val32);
+ } else if (tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT]) {
+@@ -168,18 +172,44 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ unsigned long *storage)
+ {
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+- struct mt7996_phy *phy = mt7996_hw_phy(hw);
+- int len = 0;
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_phy *phy;
++ struct mt76_phy *mphy;
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
++ int len = 0, err;
++ u8 band_idx;
+
+ if (*storage == 1)
+ return -ENOENT;
+ *storage = 1;
+
++ err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
++ mu_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (!tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX])
++ return -EINVAL;
++
++ band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]);
++ if (!mt7996_band_valid(dev, band_idx))
++ goto error;
++
++ mphy = dev->mt76.phys[band_idx];
++ if (!mphy)
++ goto error;
++
++ phy = (struct mt7996_phy *)mphy->priv;
++
+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
+ return -ENOMEM;
+ len += 1;
+
+ return len;
++
++error:
++ dev_err(dev->mt76.dev, "Invalid band idx to dump\n");
++ return -EINVAL;
+ }
+
+ void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 0d1ef3228..323467756 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -68,6 +68,7 @@ enum mtk_vendor_attr_mu_ctrl {
+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
+ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++ MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch
new file mode 100644
index 0000000..e8dd3e2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch
@@ -0,0 +1,59 @@
+From bc25ea014bff5720ae8b2276dab96b7cc317635b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 20 Mar 2024 22:56:44 +0800
+Subject: [PATCH 111/116] mtk: wifi: mt76: mt7996: tmp disable VOW
+
+FW will return failed when legacy 5G station connects after legacy 2G
+station, need to check.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mcu.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index cd49f7058..1fd844c27 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2369,6 +2369,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, link_sta, mlink);
+ }
+
++#if 0
+ static int
+ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
+ struct mt7996_link_sta *mlink)
+@@ -2402,6 +2403,7 @@ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
+
+ return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_ALL);
+ }
++#endif
+
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf,
+@@ -2410,7 +2412,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ {
+ struct ieee80211_vif *vif = conf->vif;
+ struct sk_buff *skb;
+- int ret;
++ // int ret;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ &mlink->wcid,
+@@ -2455,11 +2457,13 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, link_sta);
+ }
+
++#if 0
+ ret = mt7996_mcu_sta_init_vow(mconf, mlink);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+ }
++#endif
+ out:
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0112-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0112-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch
new file mode 100644
index 0000000..b2a0fda
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0112-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch
@@ -0,0 +1,188 @@
+From d707638e405334b74dd27428876edb8c7703eafd Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 1 Apr 2024 17:00:21 +0800
+Subject: [PATCH 112/116] mtk: wifi: mt76: mt7996: enable ampdu limit to avoid
+ BA bound issue
+
+[Description]
+When the station is MTK device and the peak is higher than 15G, the PPS
+would exceed HW-RRO's bandwidth and lead to Rx fifo full and PER. When
+a link occurs PER, it may occupy SSN and the other two bands are not
+able to transmit.
+
+Limit AMPDU to 512 when satisify all of the following conditions
+1. BA winsize is 1024.
+2. At least one link use BW320 and its spatial stream is larger
+than 3.
+3. At least one link use BW160 and its spatial stream is larger
+than 3.
+
+By limiting AMPDU to 512, it can solve this issue.
+1. Reduce PPS so we can avoid Rx fifo full due to HW-RRO.
+2. If a bind occupy SSN, the other two bands can use the SSN
+between 512 to 1024.
+
+[Release-log]
+N/A
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I64ea1d5df012c1eb9462391e5e9c20658ed7f4fe
+---
+ mt76_connac_mcu.h | 1 +
+ mt7996/mcu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h | 8 +++++
+ 3 files changed, 95 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 41e26b8a4..b592890ca 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -821,6 +821,7 @@ enum {
+ STA_REC_KEY_V3 = 0x27,
+ STA_REC_HDRT = 0x28,
+ STA_REC_HDR_TRANS = 0x2B,
++ STA_REC_TX_CAP = 0x2f,
+ STA_REC_MAX_NUM
+ };
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1fd844c27..db6aa24ed 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1344,6 +1344,85 @@ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif *mvif,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+
++static int
++mt7996_mcu_sta_tx_cap(struct mt7996_dev *dev, struct mt76_vif *mvif,
++ struct mt76_wcid *wcid)
++{
++ struct sta_rec_tx_cap *tx_cap;
++ struct sk_buff *skb;
++ struct tlv *tlv;
++
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mvif, wcid,
++ MT7996_STA_UPDATE_MAX_SIZE);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_TX_CAP, sizeof(*tx_cap));
++
++ tx_cap = (struct sta_rec_tx_cap *)tlv;
++ tx_cap->ampdu_limit_en = true;
++
++ dev_info(dev->mt76.dev, "%s: limit wcid %d ampdu to 512\n", __func__, wcid->idx);
++
++ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++}
++
++static bool mt7996_check_limit_ampdu_en(struct ieee80211_ampdu_params *params) {
++ struct ieee80211_sta *sta = params->sta;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ unsigned long valid_links = sta->valid_links ?: BIT(0);
++ unsigned int link_id;
++ bool BW320 = false, BW160 = false;
++
++ if (params->buf_size < 1024)
++ return false;
++
++ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct ieee80211_link_sta __rcu *link =
++ link_sta_dereference_protected(sta, link_id);
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(msta->vif, link_id);
++ struct mt76_phy *phy = mconf->phy->mt76;
++ struct ieee80211_eht_mcs_nss_supp_bw *ss = NULL;
++ u8 sta_bw, ap_nss, sta_nss;
++
++ switch (phy->chandef.width) {
++ case NL80211_CHAN_WIDTH_160:
++ if (link->bandwidth >= IEEE80211_STA_RX_BW_160) {
++ ss = &link->eht_cap.eht_mcs_nss_supp.bw._160;
++ sta_bw = NL80211_CHAN_WIDTH_160;
++ }
++ break;
++ case NL80211_CHAN_WIDTH_320:
++ if (link->bandwidth == IEEE80211_STA_RX_BW_320) {
++ ss = &link->eht_cap.eht_mcs_nss_supp.bw._320;
++ sta_bw = NL80211_CHAN_WIDTH_320;
++ }
++ break;
++ default:
++ break;
++ }
++
++ if (!ss)
++ continue;
++
++ ap_nss = hweight8(phy->antenna_mask);
++ sta_nss = max(u8_get_bits(ss->rx_tx_mcs11_max_nss, IEEE80211_EHT_MCS_NSS_RX),
++ u8_get_bits(ss->rx_tx_mcs13_max_nss, IEEE80211_EHT_MCS_NSS_RX));
++
++ if (min(ap_nss, sta_nss) <= 2)
++ continue;
++
++ if (sta_bw == NL80211_CHAN_WIDTH_160)
++ BW160 = true;
++ else if (sta_bw == NL80211_CHAN_WIDTH_320)
++ BW320 = true;
++ }
++
++ return BW320 && BW160;
++}
++
+ /** starec & wtbl **/
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+@@ -1353,6 +1432,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned long valid_links = sta->valid_links ?: BIT(0);
+ unsigned int link_id;
++ bool limit_ampdu_en = mt7996_check_limit_ampdu_en(params);
+
+ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_link_sta *mlink =
+@@ -1368,6 +1448,12 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ &mlink->wcid, enable, true);
+ if (ret)
+ return ret;
++
++ if (limit_ampdu_en) {
++ ret = mt7996_mcu_sta_tx_cap(dev, &mconf->mt76, &mlink->wcid);
++ if (ret)
++ return ret;
++ }
+ }
+
+ return 0;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 3d5a0c3c2..a1ac18f72 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -579,6 +579,13 @@ struct sta_rec_ba_uni {
+ u8 __rsv[3];
+ } __packed;
+
++struct sta_rec_tx_cap {
++ __le16 tag;
++ __le16 len;
++ u8 ampdu_limit_en;
++ u8 rsv[3];
++} __packed;
++
+ struct sta_rec_eht {
+ __le16 tag;
+ __le16 len;
+@@ -945,6 +952,7 @@ enum {
+ sizeof(struct sta_rec_eht) + \
+ sizeof(struct sta_rec_hdrt) + \
+ sizeof(struct sta_rec_hdr_trans) + \
++ sizeof(struct sta_rec_tx_cap) + \
+ sizeof(struct tlv))
+
+ #define MT7996_MAX_BEACON_SIZE 1338
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0113-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0113-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch
new file mode 100644
index 0000000..7b9c302
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0113-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch
@@ -0,0 +1,66 @@
+From 4449a5a89e46ab7ce56ca6313fda11c6767dde8d Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Mon, 8 Apr 2024 16:56:09 +0800
+Subject: [PATCH 113/116] wifi: mt76: mt7996: Fix get_txpower wrong result in
+ single wiphy and legacy mode
+
+Fix get_txpower wrong result in single wiphy and legacy mode.
+ieee80211_hw is get from wiphy0, so we need to get correct phy from vif.
+
+Temporarily use link 0 bss due to mac80211 didn't pass link id here.
+
+CR-Id: WCNCR00259302
+Change-Id: I306897ded276b0e5aee191339f18f0f25ca322f2
+---
+ mt7996/main.c | 28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index e37f0c013..7abee0c57 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -975,6 +975,32 @@ out:
+ mutex_unlock(&dev->mt76.mutex);
+ }
+
++int mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ int *dbm)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_bss_conf *mconf;
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt76_phy *mphy;
++ int delta;
++
++ mutex_lock(&dev->mt76.mutex);
++ mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
++ if (!mconf || !mconf->phy) {
++ *dbm = 0;
++ goto out;
++ }
++
++ mphy = mconf->phy->mt76;
++
++ delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
++
++ *dbm = DIV_ROUND_UP(mphy->txpower_cur + delta, 2);
++out:
++ mutex_unlock(&dev->mt76.mutex);
++ return 0;
++}
++
+ static void
+ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+@@ -2635,7 +2661,7 @@ const struct ieee80211_ops mt7996_ops = {
+ .hw_scan = mt7996_hw_scan,
+ .cancel_hw_scan = mt7996_cancel_hw_scan,
+ .release_buffered_frames = mt76_release_buffered_frames,
+- .get_txpower = mt76_get_txpower,
++ .get_txpower = mt7996_get_txpower,
+ .channel_switch_beacon = mt7996_channel_switch_beacon,
+ .get_stats = mt7996_get_stats,
+ .get_et_sset_count = mt7996_get_et_sset_count,
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch
new file mode 100644
index 0000000..c997806
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch
@@ -0,0 +1,36 @@
+From 8da22299c32a6f07912f8d4333b2c5a904d03a0a Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 19 Apr 2024 11:01:21 +0800
+Subject: [PATCH 114/116] mtk: wifi: mt76: mt7996: add beacon_int_min_gcd to
+ support different beacon interval
+
+When beacon_int_min_gcd is zero, all beacon intervals for different
+interfaces should be same. If beacon_int_min_gcd is larger than zero,
+all beacon intervals for different interfaces should be larger or
+equal than beacon_int_min_gcd.
+
+Without this patch, set beacon fail when different interfaces use
+different beacon interval.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: Ic92582c86f6bf41b58ac1dd03175289a16be32c8
+---
+ mt7996/init.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 0dee6596f..3af9d02fe 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -41,6 +41,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80) |
+ BIT(NL80211_CHAN_WIDTH_160),
++ .beacon_int_min_gcd = 100,
+ }
+ };
+
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch
new file mode 100644
index 0000000..68acfd9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch
@@ -0,0 +1,1091 @@
+From 4e663529b04108914d4d684a50ab4c3accb9f232 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Sat, 20 Jan 2024 12:03:24 +0800
+Subject: [PATCH 115/116] mtk: wifi: mt76: mt7996: Add connac3 csi feature.
+
+1. format align to wifi6.
+2. add bw320 support.
+3. add active mode.
+
+CR-Id: WCNCR00364748
+Change-Id: If37ac6de4781c3673671707ee3ee243dda8163f8
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ mt76_connac_mcu.h | 2 +
+ mt7996/init.c | 22 +++
+ mt7996/main.c | 3 +
+ mt7996/mcu.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h | 105 +++++++++++
+ mt7996/mt7996.h | 55 ++++++
+ mt7996/vendor.c | 208 +++++++++++++++++++++
+ mt7996/vendor.h | 50 +++++
+ 8 files changed, 910 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index b592890ca..680c08ed7 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1052,6 +1052,7 @@ enum {
+ MCU_UNI_EVENT_THERMAL = 0x35,
+ MCU_UNI_EVENT_NIC_CAPAB = 0x43,
+ MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
++ MCU_UNI_EVENT_CSI_REPORT = 0x4A,
+ MCU_UNI_EVENT_WED_RRO = 0x57,
+ MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
+@@ -1288,6 +1289,7 @@ enum {
+ MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ MCU_UNI_CMD_PRECAL_RESULT = 0x47,
++ MCU_UNI_CMD_CSI_CTRL = 0x4A,
+ MCU_UNI_CMD_THERMAL_CAL = 0x4c,
+ MCU_UNI_CMD_RRO = 0x57,
+ MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 3af9d02fe..ff72aabed 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -806,6 +806,24 @@ error:
+ return ret;
+ }
+
++#ifdef CONFIG_MTK_VENDOR
++static int mt7996_unregister_csi(struct mt7996_phy *phy)
++{
++ struct csi_data *c, *tmp_c;
++
++ spin_lock_bh(&phy->csi.lock);
++ phy->csi.enable = 0;
++
++ list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
++ list_del(&c->node);
++ kfree(c);
++ }
++ spin_unlock_bh(&phy->csi.lock);
++
++ return 0;
++}
++#endif
++
+ static void
+ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ {
+@@ -817,6 +835,10 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ /* TODO: FIXME: temp for single wiphy support */
+ phy->mt76->hw = phy->mt76->ori_hw;
+
++#ifdef CONFIG_MTK_VENDOR
++ mt7996_unregister_csi(phy);
++#endif
++
+ mt7996_unregister_thermal(phy);
+
+ mphy = phy->dev->mt76.phys[band];
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 7abee0c57..8dca88ac2 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1292,6 +1292,9 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ unsigned long rem = sta->valid_links ?: BIT(0);
+
++#ifdef CONFIG_MTK_VENDOR
++ mt7996_mcu_set_csi(&dev->phy, 2, 8, 1, 0, sta->addr);
++#endif
+ mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ }
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index db6aa24ed..d8e3181b1 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -653,6 +653,263 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ }
+ }
+
++static int
++csi_integrate_segment_data(struct mt7996_phy *phy, struct csi_data *csi)
++{
++ struct csi_data *csi_temp = NULL;
++
++ if (csi->segment_num == 0 && csi->remain_last == 0)
++ return CSI_CHAIN_COMPLETE;
++ else if (csi->segment_num == 0 && csi->remain_last == 1) {
++ memcpy(&phy->csi.buffered_csi,
++ csi, sizeof(struct csi_data));
++
++ return CSI_CHAIN_SEGMENT_FIRST;
++ } else if (csi->segment_num != 0) {
++ csi_temp = &phy->csi.buffered_csi;
++ if (csi->chain_info != csi_temp->chain_info ||
++ csi->segment_num != (csi_temp->segment_num + 1))
++ return CSI_CHAIN_SEGMENT_ERR;
++
++ memcpy(&csi_temp->data_i[csi_temp->data_num],
++ csi->data_i, csi->data_num * sizeof(s16));
++
++ memcpy(&csi_temp->data_q[csi_temp->data_num],
++ csi->data_q, csi->data_num * sizeof(s16));
++
++ csi_temp->data_num += csi->data_num;
++ csi_temp->segment_num = csi->segment_num;
++ csi_temp->remain_last = csi->remain_last;
++
++ if (csi->remain_last == 0)
++ return CSI_CHAIN_SEGMENT_LAST;
++ else if (csi->remain_last == 1)
++ return CSI_CHAIN_SEGMENT_MIDDLE;
++ }
++
++ return CSI_CHAIN_ERR;
++}
++
++static int
++mt7996_mcu_csi_report_data(struct mt7996_phy *phy, u8 *tlv_buf, u32 len)
++{
++ int ret, i;
++ struct csi_data *current_csi;
++ struct csi_data *target_csi;
++ struct csi_tlv *tlv_data;
++ u8 *buf_tmp;
++ u32 rx_info, tx_rx_idx;
++ u32 buf_len_last, offset;
++
++ buf_tmp = tlv_buf;
++ buf_len_last = len;
++ offset = sizeof(((struct csi_tlv *)0)->basic);
++
++ current_csi = kzalloc(sizeof(*current_csi), GFP_KERNEL);
++ if (!current_csi)
++ return -ENOMEM;
++
++ while (buf_len_last >= offset) {
++ u32 tag, len;
++ s16 *data_tmp = NULL;
++
++ tlv_data = (struct csi_tlv *)buf_tmp;
++ tag = le32_to_cpu(tlv_data->basic.tag);
++ len = le32_to_cpu(tlv_data->basic.len);
++
++ switch (tag) {
++ case CSI_EVENT_FW_VER:
++ current_csi->fw_ver = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_CBW:
++ current_csi->ch_bw = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_RSSI:
++ current_csi->rssi = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_SNR:
++ current_csi->snr = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_BAND:
++ current_csi->band = le32_to_cpu(tlv_data->info);
++
++ if (current_csi->band != phy->mt76->band_idx) {
++ kfree(current_csi);
++ return -EINVAL;
++ }
++
++ break;
++ case CSI_EVENT_CSI_NUM:
++ current_csi->data_num = le32_to_cpu(tlv_data->info);
++
++ if (current_csi->data_num > CSI_BW80_DATA_COUNT) {
++ kfree(current_csi);
++ return -EINVAL;
++ }
++
++ break;
++ case CSI_EVENT_CSI_I_DATA:
++ if (len != sizeof(s16) * current_csi->data_num) {
++ kfree(current_csi);
++ return -EINVAL;
++ }
++
++ data_tmp = tlv_data->data;
++ for (i = 0; i < current_csi->data_num; i++)
++ current_csi->data_i[i] = le16_to_cpu(*(data_tmp + i));
++ break;
++ case CSI_EVENT_CSI_Q_DATA:
++ if (len != sizeof(s16) * current_csi->data_num) {
++ kfree(current_csi);
++ return -EINVAL;
++ }
++
++ data_tmp = tlv_data->data;
++ for (i = 0; i < current_csi->data_num; i++)
++ current_csi->data_q[i] = le16_to_cpu(*(data_tmp + i));
++ break;
++ case CSI_EVENT_DBW:
++ current_csi->data_bw = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_CH_IDX:
++ current_csi->pri_ch_idx = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_TA:
++ memcpy(current_csi->ta, tlv_data->mac, ETH_ALEN);
++ break;
++ case CSI_EVENT_EXTRA_INFO:
++ current_csi->ext_info = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_RX_MODE:
++ rx_info = le32_to_cpu(tlv_data->info);
++ current_csi->rx_mode = u32_get_bits(rx_info, GENMASK(15, 0));
++ current_csi->rx_rate = u32_get_bits(rx_info, GENMASK(31, 16));
++ break;
++ case CSI_EVENT_H_IDX:
++ current_csi->chain_info = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_TX_RX_IDX:
++ tx_rx_idx = le32_to_cpu(tlv_data->info);
++ current_csi->tx_idx = u32_get_bits(tx_rx_idx, GENMASK(31, 16));
++ current_csi->rx_idx = u32_get_bits(tx_rx_idx, GENMASK(15, 0));
++ break;
++ case CSI_EVENT_TS:
++ current_csi->ts = le32_to_cpu(tlv_data->info);
++
++ if (phy->csi.interval &&
++ current_csi->ts < phy->csi.last_record + phy->csi.interval) {
++ kfree(current_csi);
++ return 0;
++ }
++
++ break;
++ case CSI_EVENT_PKT_SN:
++ current_csi->pkt_sn = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_BW_SEG:
++ current_csi->segment_num = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_REMAIN_LAST:
++ current_csi->remain_last = le32_to_cpu(tlv_data->info);
++ break;
++ case CSI_EVENT_TR_STREAM:
++ current_csi->tr_stream = le32_to_cpu(tlv_data->info);
++ break;
++ default:
++ break;
++ };
++
++ buf_len_last -= (offset + len);
++
++ if (buf_len_last >= offset)
++ buf_tmp += (offset + len);
++ }
++
++ /* integret the bw80 segment */
++ if (current_csi->ch_bw >= CSI_BW80) {
++ ret = csi_integrate_segment_data(phy, current_csi);
++
++ switch (ret) {
++ case CSI_CHAIN_ERR:
++ case CSI_CHAIN_SEGMENT_ERR:
++ kfree(current_csi);
++ return -EINVAL;
++ break;
++ case CSI_CHAIN_SEGMENT_FIRST:
++ case CSI_CHAIN_SEGMENT_MIDDLE:
++ kfree(current_csi);
++ return 0;
++ break;
++ case CSI_CHAIN_COMPLETE:
++ target_csi = current_csi;
++ break;
++ case CSI_CHAIN_SEGMENT_LAST:
++ target_csi = current_csi;
++ memcpy(target_csi, &phy->csi.buffered_csi, sizeof(struct csi_data));
++ memset(&phy->csi.buffered_csi, 0, sizeof(struct csi_data));
++ break;
++ default:
++ break;
++ }
++ } else {
++ target_csi = current_csi;
++ }
++
++ /* put the csi data into list */
++ INIT_LIST_HEAD(&target_csi->node);
++ spin_lock_bh(&phy->csi.lock);
++
++ if (!phy->csi.enable) {
++ kfree(target_csi);
++ goto out;
++ }
++
++ list_add_tail(&target_csi->node, &phy->csi.list);
++ phy->csi.count++;
++
++ if (phy->csi.count > CSI_MAX_BUF_NUM) {
++ struct csi_data *old;
++
++ old = list_first_entry(&phy->csi.list,
++ struct csi_data, node);
++
++ list_del(&old->node);
++ kfree(old);
++ phy->csi.count--;
++ }
++
++ if (target_csi->chain_info & BIT(15)) /* last chain */
++ phy->csi.last_record = target_csi->ts;
++
++out:
++ spin_unlock_bh(&phy->csi.lock);
++ return 0;
++}
++
++void
++mt7996_mcu_csi_report_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++ struct mt7996_mcu_csi_event *event;
++ struct mt76_phy *mphy;
++ struct mt7996_phy *phy;
++
++ event = (struct mt7996_mcu_csi_event *)skb->data;
++
++ mphy = dev->mt76.phys[event->band_idx];
++ if (!mphy)
++ return;
++
++ phy = mphy->priv;
++
++ switch (le16_to_cpu(event->tag)) {
++ case UNI_EVENT_CSI_DATA:
++ mt7996_mcu_csi_report_data(phy, event->tlv_buf, le16_to_cpu(event->len) - 4);
++ break;
++ default:
++ break;
++ }
++}
++
+ static void
+ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -896,6 +1153,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ case MCU_UNI_EVENT_BF:
+ mt7996_mcu_rx_bf_event(dev, skb);
+ break;
++#endif
++#ifdef CONFIG_MTK_VENDOR
++ case MCU_UNI_EVENT_CSI_REPORT:
++ mt7996_mcu_csi_report_event(dev, skb);
++ break;
+ #endif
+ default:
+ break;
+@@ -5995,4 +6257,207 @@ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+
+ mt7996_mcu_add_beacon(hw, &vif->bss_conf, &mvif->deflink, val);
+ }
++
++static int mt7996_mcu_set_csi_enable(struct mt7996_phy *phy, u16 tag)
++{
++ struct {
++ u8 band;
++ u8 rsv1[3];
++
++ __le16 tag;
++ __le16 len;
++ } __packed req = {
++ .band = phy->mt76->band_idx,
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ };
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++ sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_frame_type(struct mt7996_phy *phy, u16 tag, u8 type_idx, u32 type)
++{
++ struct {
++ u8 band;
++ u8 rsv1[3];
++
++ __le16 tag;
++ __le16 len;
++ u8 frame_type_idx;
++ u8 frame_type;
++ u8 rsv2[2];
++ } __packed req = {
++ .band = phy->mt76->band_idx,
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .frame_type_idx = type_idx,
++ .frame_type = type,
++ };
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++ sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_chain_filter(struct mt7996_phy *phy, u16 tag, u8 func, u32 value)
++{
++ struct {
++ u8 band;
++ u8 rsv1[3];
++
++ __le16 tag;
++ __le16 len;
++ u8 function;
++ u8 chain_value;
++ u8 rsv2[2];
++ } __packed req = {
++ .band = phy->mt76->band_idx,
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .function = func,
++ .chain_value = value,
++ };
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++ sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_sta_filter(struct mt7996_phy *phy, u16 tag, u32 op, u8 *sta_mac)
++{
++ struct {
++ u8 band;
++ u8 rsv1[3];
++
++ __le16 tag;
++ __le16 len;
++ u8 operation;
++ u8 rsv2[1];
++ u8 mac[6];
++ } __packed req = {
++ .band = phy->mt76->band_idx,
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .operation = op,
++ };
++
++ memcpy(req.mac, sta_mac, ETH_ALEN);
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++ sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_active_mode(struct mt7996_phy *phy, u16 tag,
++ u32 interval, u8 frame_idx, u8 subframe_idx, u32 bitmap)
++{
++ struct {
++ u8 band;
++ u8 rsv1[3];
++
++ __le16 tag;
++ __le16 len;
++ __le16 interval; /* uint: ms */
++ u8 frame_type_idx;
++ u8 subframe_type_idx;
++ __le32 bitmap; /* sta wcid bitmap */
++ u8 rsv2[4];
++ } __packed req = {
++ .band = phy->mt76->band_idx,
++ .tag = cpu_to_le16(tag),
++ .len = cpu_to_le16(sizeof(req) - 4),
++ .interval = cpu_to_le16(interval),
++ .frame_type_idx = frame_idx,
++ .subframe_type_idx = subframe_idx,
++ .bitmap = cpu_to_le32(bitmap),
++ };
++
++ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++ sizeof(req), false);
++}
++
++void mt7996_csi_wcid_bitmap_update(void *data, struct ieee80211_sta *sta)
++{
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_phy *phy = msta->vif->deflink.phy;
++ struct csi_bitmap_info_update *sta_info = (struct csi_bitmap_info_update *)data;
++ u16 wcid = 0;
++
++#define CSI_ACTIVE_MODE_ADD 1
++#define CSI_ACTIVE_MODE_REMOVE 0
++
++ if (!memcmp(sta_info->addr, sta->addr, ETH_ALEN)) {
++ wcid = msta->deflink.wcid.idx;
++
++ /* active mode: only support station with wcid less than 32 */
++ if (wcid > 32)
++ return;
++
++ if (sta_info->action == CSI_ACTIVE_MODE_ADD)
++ phy->csi.active_bitmap |= BIT(wcid);
++ else if (sta_info->action == CSI_ACTIVE_MODE_REMOVE)
++ phy->csi.active_bitmap &= ~(BIT(wcid));
++ }
++}
++
++int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
++ u8 cfg, u8 v1, u32 v2, u8 *mac_addr)
++{
++ switch (mode) {
++ case CSI_CONTROL_MODE_STOP:
++ return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_STOP);
++ case CSI_CONTROL_MODE_START:
++ return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_START);
++ case CSI_CONTROL_MODE_SET:
++ switch (cfg) {
++ case CSI_CONFIG_FRAME_TYPE:
++ if (v2 > 255)
++ return -EINVAL;
++
++ return mt7996_mcu_set_csi_frame_type(phy,
++ UNI_CMD_CSI_SET_FRAME_TYPE, v1, v2);
++ case CSI_CONFIG_CHAIN_FILTER:
++ if (v2 > 255)
++ return -EINVAL;
++
++ return mt7996_mcu_set_csi_chain_filter(phy,
++ UNI_CMD_CSI_SET_CHAIN_FILTER, v1, v2);
++ case CSI_CONFIG_STA_FILTER:
++ if (!is_valid_ether_addr(mac_addr))
++ return -EINVAL;
++
++ if (v2 > 255)
++ return -EINVAL;
++
++ return mt7996_mcu_set_csi_sta_filter(phy,
++ UNI_CMD_CSI_SET_STA_FILTER, v2, mac_addr);
++ case CSI_CONFIG_ACTIVE_MODE:
++ if (is_valid_ether_addr(mac_addr)) {
++ struct csi_bitmap_info_update sta_info;
++
++ if (v2 > 255)
++ return -EINVAL;
++
++ memcpy(sta_info.addr, mac_addr, ETH_ALEN);
++ sta_info.action = v2;
++
++ ieee80211_iterate_stations_atomic(phy->mt76->hw,
++ mt7996_csi_wcid_bitmap_update, &sta_info);
++ return 0;
++ } else {
++ u8 frame_type = v1 & 0x3;
++ u8 frame_subtype = (v1 & 0x3c) >> 2;
++
++ /* active mode: max interval is 3000ms */
++ if (v2 > 3000)
++ return -EINVAL;
++
++ return mt7996_mcu_set_csi_active_mode(phy, UNI_CMD_CSI_SET_ACTIVE_MODE,
++ v2, frame_type, frame_subtype, phy->csi.active_bitmap);
++ }
++ default:
++ return -EINVAL;
++ }
++ default:
++ return -EINVAL;
++ }
++}
+ #endif
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index a1ac18f72..dc93fab25 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1159,4 +1159,109 @@ struct fixed_rate_table_ctrl {
+ u8 _rsv2;
+ } __packed;
+
++#ifdef CONFIG_MTK_VENDOR
++struct mt7996_mcu_csi_event {
++ struct mt7996_mcu_rxd rxd;
++
++ u8 band_idx;
++ u8 _rsv[3];
++
++ __le16 tag;
++ __le16 len;
++ u8 tlv_buf[0];
++};
++
++enum UNI_EVENT_CSI_TAG_T {
++ UNI_EVENT_CSI_DATA = 0,
++ UNI_EVENT_CSI_MAX_NUM
++};
++
++struct csi_tlv {
++ struct {
++ __le32 tag;
++ __le32 len;
++ } basic;
++ union {
++ u8 mac[ETH_ALEN];
++ __le32 info;
++ s16 data[0];
++ };
++} __packed;
++
++struct csi_bitmap_info_update {
++ u8 action;
++ u8 addr[ETH_ALEN];
++};
++
++#define CSI_MAX_BUF_NUM 3000
++
++enum CSI_EVENT_TLV_TAG {
++ CSI_EVENT_FW_VER,
++ CSI_EVENT_CBW,
++ CSI_EVENT_RSSI,
++ CSI_EVENT_SNR,
++ CSI_EVENT_BAND,
++ CSI_EVENT_CSI_NUM,
++ CSI_EVENT_CSI_I_DATA,
++ CSI_EVENT_CSI_Q_DATA,
++ CSI_EVENT_DBW,
++ CSI_EVENT_CH_IDX,
++ CSI_EVENT_TA,
++ CSI_EVENT_EXTRA_INFO,
++ CSI_EVENT_RX_MODE,
++ CSI_EVENT_RSVD1,
++ CSI_EVENT_RSVD2,
++ CSI_EVENT_RSVD3,
++ CSI_EVENT_RSVD4,
++ CSI_EVENT_H_IDX,
++ CSI_EVENT_TX_RX_IDX,
++ CSI_EVENT_TS,
++ CSI_EVENT_PKT_SN,
++ CSI_EVENT_BW_SEG,
++ CSI_EVENT_REMAIN_LAST,
++ CSI_EVENT_TR_STREAM,
++ CSI_EVENT_TLV_TAG_NUM,
++};
++
++enum CSI_CHAIN_TYPE {
++ CSI_CHAIN_ERR,
++ CSI_CHAIN_COMPLETE,
++ CSI_CHAIN_SEGMENT_FIRST,
++ CSI_CHAIN_SEGMENT_MIDDLE,
++ CSI_CHAIN_SEGMENT_LAST,
++ CSI_CHAIN_SEGMENT_ERR,
++};
++
++enum CSI_CONTROL_MODE_T {
++ CSI_CONTROL_MODE_STOP,
++ CSI_CONTROL_MODE_START,
++ CSI_CONTROL_MODE_SET,
++ CSI_CONTROL_MODE_NUM
++};
++
++enum CSI_CONFIG_ITEM_T {
++ CSI_CONFIG_RSVD1,
++ CSI_CONFIG_WF,
++ CSI_CONFIG_RSVD2,
++ CSI_CONFIG_FRAME_TYPE,
++ CSI_CONFIG_TX_PATH,
++ CSI_CONFIG_OUTPUT_FORMAT,
++ CSI_CONFIG_INFO,
++ CSI_CONFIG_CHAIN_FILTER,
++ CSI_CONFIG_STA_FILTER,
++ CSI_CONFIG_ACTIVE_MODE,
++ CSI_CONFIG_ITEM_NUM
++};
++
++/* CSI config Tag */
++enum UNI_CMD_CSI_TAG_T {
++ UNI_CMD_CSI_STOP = 0,
++ UNI_CMD_CSI_START = 1,
++ UNI_CMD_CSI_SET_FRAME_TYPE = 2,
++ UNI_CMD_CSI_SET_CHAIN_FILTER = 3,
++ UNI_CMD_CSI_SET_STA_FILTER = 4,
++ UNI_CMD_CSI_SET_ACTIVE_MODE = 5,
++};
++#endif
++
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 0412d7375..0d537fb9b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -438,6 +438,47 @@ struct mt7996_air_monitor_ctrl {
+ struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
+ struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
+ };
++
++enum {
++ CSI_BW20,
++ CSI_BW40,
++ CSI_BW80,
++ CSI_BW160,
++ CSI_BW320
++};
++
++#define CSI_BW20_DATA_COUNT 64
++#define CSI_BW40_DATA_COUNT 128
++#define CSI_BW80_DATA_COUNT 256
++#define CSI_BW160_DATA_COUNT 512
++#define CSI_BW320_DATA_COUNT 1024
++
++struct csi_data {
++ u8 fw_ver;
++ u8 ch_bw;
++ u16 data_num;
++ s16 data_i[CSI_BW320_DATA_COUNT];
++ s16 data_q[CSI_BW320_DATA_COUNT];
++ u8 band;
++ s8 rssi;
++ u8 snr;
++ u32 ts;
++ u8 data_bw;
++ u8 pri_ch_idx;
++ u8 ta[ETH_ALEN];
++ u32 ext_info;
++ u16 rx_mode;
++ u16 rx_rate;
++ u32 chain_info;
++ u16 tx_idx;
++ u16 rx_idx;
++ u32 segment_num;
++ u8 remain_last;
++ u16 pkt_sn;
++ u8 tr_stream;
++
++ struct list_head node;
++};
+ #endif
+
+ struct mt7996_rro_ba_session {
+@@ -534,6 +575,18 @@ struct mt7996_phy {
+ u8 rts_bw_sig;
+ spinlock_t amnt_lock;
+ struct mt7996_air_monitor_ctrl amnt_ctrl;
++
++ struct {
++ struct list_head list;
++ spinlock_t lock;
++ u32 count;
++ bool enable;
++
++ struct csi_data buffered_csi;
++ u32 active_bitmap;
++ u32 interval;
++ u32 last_record;
++ } csi;
+ #endif
+ #ifdef CONFIG_MTK_DEBUG
+ bool sr_enable:1;
+@@ -1166,6 +1219,8 @@ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
+ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
+ int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
++int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
++ u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
+ #endif
+
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 64ef5515c..07c0c367f 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -119,6 +119,18 @@ beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
+ [MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
+ };
+
++static const struct nla_policy
++csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG] = {.type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++};
++
+ struct mt7996_amnt_data {
+ u8 idx;
+ u8 addr[ETH_ALEN];
+@@ -1000,7 +1012,188 @@ static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
+
+ return 0;
+ }
++static int mt7996_vendor_csi_ctrl(struct wiphy *wiphy,
++ struct wireless_dev *wdev,
++ const void *data,
++ int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
++ int err;
++
++ err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
++ csi_ctrl_policy, NULL);
++ if (err)
++ return err;
++
++ if (tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG]) {
++ u8 mode = 0, type = 0, v1 = 0;
++ u32 v2 = 0;
++ u8 mac_addr[ETH_ALEN] = {};
++ struct nlattr *cur;
++ int rem;
++
++ nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG], rem) {
++ switch (nla_type(cur)) {
++ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE:
++ mode = nla_get_u8(cur);
++ break;
++ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE:
++ type = nla_get_u8(cur);
++ break;
++ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1:
++ v1 = nla_get_u8(cur);
++ break;
++ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2:
++ v2 = nla_get_u32(cur);
++ break;
++ default:
++ return -EINVAL;
++ };
++ }
++
++ if (tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR]) {
++ u8 idx = 0;
++
++ nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR], rem) {
++ mac_addr[idx++] = nla_get_u8(cur);
++ }
++ }
++
++ err = mt7996_mcu_set_csi(phy, mode, type, v1, v2, mac_addr);
++ if (err < 0)
++ return err;
++
++ spin_lock_bh(&phy->csi.lock);
+
++ phy->csi.enable = !!mode;
++
++ /* clean up old csi stats */
++ if ((mode == CSI_CONTROL_MODE_STOP || mode == CSI_CONTROL_MODE_SET)
++ && !list_empty(&phy->csi.list)) {
++ struct csi_data *c, *tmp_c;
++
++ list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
++ list_del(&c->node);
++ kfree(c);
++ phy->csi.count--;
++ }
++ } else if (mode == CSI_CONTROL_MODE_START) {
++ phy->csi.last_record = 0;
++ }
++
++ spin_unlock_bh(&phy->csi.lock);
++
++ if (mode == CSI_CONTROL_MODE_SET && type == CSI_CONFIG_STA_FILTER && v1 == 2)
++ phy->csi.interval = v2;
++ }
++
++ return 0;
++}
++
++static int
++mt7996_vendor_csi_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ struct sk_buff *skb, const void *data, int data_len,
++ unsigned long *storage)
++{
++#define RESERVED_SET BIT(31)
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {0};
++ int err = 0;
++
++ if (*storage & RESERVED_SET) {
++ if ((*storage & GENMASK(15, 0)) == 0)
++ return -ENOENT;
++ (*storage)--;
++ }
++
++ if (data) {
++ err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
++ csi_ctrl_policy, NULL);
++ if (err)
++ return err;
++ }
++
++ if (!(*storage & RESERVED_SET) && tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]) {
++ *storage = nla_get_u16(tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]);
++ *storage |= RESERVED_SET;
++ }
++
++ spin_lock_bh(&phy->csi.lock);
++
++ if (!list_empty(&phy->csi.list)) {
++ struct csi_data *csi;
++ void *a, *b;
++ int i;
++
++ csi = list_first_entry(&phy->csi.list, struct csi_data, node);
++
++ a = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_CTRL_DATA);
++ if (!a)
++ goto out;
++
++ if (nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_VER, 1) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_RSSI, csi->rssi) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_SNR, csi->snr) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_BW, csi->data_bw) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_CH_IDX, csi->pri_ch_idx) ||
++ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_MODE, csi->rx_mode))
++ goto out;
++
++ if (nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_TX_ANT, csi->tx_idx) ||
++ nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_RX_ANT, csi->rx_idx))
++ goto out;
++
++ if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_INFO, csi->ext_info) ||
++ nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO, csi->chain_info) ||
++ nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_TS, csi->ts))
++ goto out;
++
++ b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_TA);
++ if (!b)
++ goto out;
++
++ for (i = 0; i < ARRAY_SIZE(csi->ta); i++)
++ if (nla_put_u8(skb, i, csi->ta[i]))
++ goto out;
++ nla_nest_end(skb, b);
++
++ if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_NUM, csi->data_num))
++ goto out;
++
++ b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_I);
++ if (!b)
++ goto out;
++
++ for (i = 0; i < csi->data_num; i++)
++ if (nla_put_u16(skb, i, csi->data_i[i]))
++ goto out;
++ nla_nest_end(skb, b);
++
++ b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_Q);
++ if (!b)
++ goto out;
++
++ for (i = 0; i < csi->data_num; i++)
++ if (nla_put_u16(skb, i, csi->data_q[i]))
++ goto out;
++ nla_nest_end(skb, b);
++
++ nla_nest_end(skb, a);
++
++ list_del(&csi->node);
++ kfree(csi);
++ phy->csi.count--;
++
++ err = phy->csi.count;
++ }
++out:
++ spin_unlock_bh(&phy->csi.lock);
++
++ return err;
++}
+
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ {
+@@ -1129,6 +1322,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ .policy = beacon_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
+ },
++ {
++ .info = {
++ .vendor_id = MTK_NL80211_VENDOR_ID,
++ .subcmd = MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL,
++ },
++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++ WIPHY_VENDOR_CMD_NEED_RUNNING,
++ .doit = mt7996_vendor_csi_ctrl,
++ .dumpit = mt7996_vendor_csi_ctrl_dump,
++ .policy = csi_ctrl_policy,
++ .maxattr = MTK_VENDOR_ATTR_CSI_CTRL_MAX,
++ },
+ };
+
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+@@ -1136,6 +1341,9 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
+ phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
+ phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
+
++ INIT_LIST_HEAD(&phy->csi.list);
++ spin_lock_init(&phy->csi.lock);
++
+ spin_lock_init(&phy->amnt_lock);
+ }
+ #endif
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 323467756..abe3fdeab 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -7,6 +7,7 @@
+
+ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
+ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
+ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+@@ -240,6 +241,55 @@ enum mtk_vendor_attr_beacon_ctrl {
+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+
++enum mtk_vendor_attr_csi_ctrl {
++ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++ MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++ MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++ MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++ MTK_VENDOR_ATTR_CSI_DATA_VER,
++ MTK_VENDOR_ATTR_CSI_DATA_TS,
++ MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++ MTK_VENDOR_ATTR_CSI_DATA_SNR,
++ MTK_VENDOR_ATTR_CSI_DATA_BW,
++ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++ MTK_VENDOR_ATTR_CSI_DATA_TA,
++ MTK_VENDOR_ATTR_CSI_DATA_NUM,
++ MTK_VENDOR_ATTR_CSI_DATA_I,
++ MTK_VENDOR_ATTR_CSI_DATA_Q,
++ MTK_VENDOR_ATTR_CSI_DATA_INFO,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++ MTK_VENDOR_ATTR_CSI_DATA_MODE,
++ MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++ MTK_VENDOR_ATTR_CSI_DATA_MAX =
++ NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
++};
+ #endif
+
+ #endif
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch
new file mode 100644
index 0000000..5cc3522
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch
@@ -0,0 +1,2140 @@
+From 42e52bb5cc78f560047d602325a38709eed0d0ca Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 22 Apr 2024 11:06:48 +0800
+Subject: [PATCH 116/116] mtk: wifi: mt76: mt7996: add more debug info for MLO
+
+---
+ mt76_connac_mcu.c | 1 +
+ mt76_connac_mcu.h | 1 +
+ mt7996/debugfs.c | 34 +-
+ mt7996/mac.c | 15 +
+ mt7996/main.c | 14 +
+ mt7996/mcu.c | 18 +
+ mt7996/mcu.h | 8 +
+ mt7996/mt7996.h | 20 +
+ mt7996/mtk_debug.h | 660 +++++++++++++++++++++++++
+ mt7996/mtk_debugfs.c | 1085 ++++++++++++++++++++++++++++++++++++++++++
+ 10 files changed, 1847 insertions(+), 9 deletions(-)
+
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 71f3d301c..a4924c4e4 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -433,6 +433,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ }
+
+ memcpy(basic->peer_addr, link_sta->addr, ETH_ALEN);
++ pr_info("%s: link %u addr [%pM]\n", __func__, link_sta->link_id, basic->peer_addr);
+ basic->qos = sta->wme;
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv);
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 680c08ed7..6fde64bac 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1285,6 +1285,7 @@ enum {
+ MCU_UNI_CMD_THERMAL = 0x35,
+ MCU_UNI_CMD_VOW = 0x37,
+ MCU_UNI_CMD_PP = 0x38,
++ MCU_UNI_CMD_MEC = 0x3a,
+ MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 26927eda9..77349c263 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -314,15 +314,17 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ DEBUG_CMD_RPT_TRIG,
+ DEBUG_SPL,
+ DEBUG_RPT_RX,
+- DEBUG_RPT_RA = 68,
+ DEBUG_IDS_SND = 84,
++ DEBUG_IDS_BSRP,
++ DEBUG_IDS_TPUT_MON,
+ DEBUG_IDS_PP = 93,
+- DEBUG_IDS_RA = 94,
+- DEBUG_IDS_BF = 95,
+- DEBUG_IDS_SR = 96,
+- DEBUG_IDS_RU = 97,
+- DEBUG_IDS_MUMIMO = 98,
+- DEBUG_IDS_ERR_LOG = 101,
++ DEBUG_IDS_RA,
++ DEBUG_IDS_BF,
++ DEBUG_IDS_SR,
++ DEBUG_IDS_RU,
++ DEBUG_IDS_MUMIMO,
++ DEBUG_IDS_MLO = 100,
++ DEBUG_IDS_ERR_LOG,
+ };
+ u8 debug_category[] = {
+ DEBUG_TXCMD,
+@@ -330,14 +332,16 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ DEBUG_CMD_RPT_TRIG,
+ DEBUG_SPL,
+ DEBUG_RPT_RX,
+- DEBUG_RPT_RA,
+ DEBUG_IDS_SND,
++ DEBUG_IDS_BSRP,
++ DEBUG_IDS_TPUT_MON,
+ DEBUG_IDS_PP,
+ DEBUG_IDS_RA,
+ DEBUG_IDS_BF,
+ DEBUG_IDS_SR,
+ DEBUG_IDS_RU,
+ DEBUG_IDS_MUMIMO,
++ DEBUG_IDS_MLO,
+ DEBUG_IDS_ERR_LOG,
+ };
+ bool tx, rx, en;
+@@ -372,7 +376,8 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ if (ret)
+ return ret;
+
+- if (debug_category[i] == DEBUG_IDS_SND && en) {
++ if ((debug_category[i] == DEBUG_TXCMD ||
++ debug_category[i] == DEBUG_IDS_SND) && en) {
+ ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], 2);
+ if (ret)
+ return ret;
+@@ -506,6 +511,14 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
+ if (ret)
+ return ret;
+
++#ifdef CONFIG_MTK_DEBUG
++ dev->dbg.dump_mcu_pkt = val & BIT(4) ? true : false;
++ dev->dbg.dump_txd = val & BIT(5) ? true : false;
++ dev->dbg.dump_tx_pkt = val & BIT(6) ? true : false;
++ dev->dbg.dump_rx_pkt = val & BIT(7) ? true : false;
++ dev->dbg.dump_rx_raw = val & BIT(8) ? true : false;
++#endif
++
+ return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
+ }
+
+@@ -1208,6 +1221,9 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
+ {
+ bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
++#ifdef CONFIG_MTK_DEBUG
++ is_fwlog |= get_unaligned_le32(data) == PKT_BIN_DEBUG_MAGIC;
++#endif
+
+ if (is_fwlog) {
+ if (dev->relay_fwlog)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 2a45fc03d..a8cf24c3c 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -331,6 +331,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ u8 hw_aggr = false;
+ struct mt7996_link_sta *mlink = NULL;
+
++#ifdef CONFIG_MTK_DEBUG
++ if (dev->dbg.dump_rx_raw)
++ mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_RX_RAW, 0);
++#endif
+ hw_aggr = status->aggr;
+ memset(status, 0, sizeof(*status));
+
+@@ -511,6 +515,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ }
+
+ hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad;
++#ifdef CONFIG_MTK_DEBUG
++ if (dev->dbg.dump_rx_pkt)
++ mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_RX, hdr_gap);
++#endif
+ if (hdr_trans && ieee80211_has_morefrags(fc)) {
+ if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap))
+ return -EINVAL;
+@@ -967,6 +975,13 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ tx_info->buf[1].skip_unmap = true;
+ tx_info->nbuf = MT_CT_DMA_BUF_NUM;
+
++#ifdef CONFIG_MTK_DEBUG
++ if (dev->dbg.dump_txd)
++ mt7996_packet_log_to_host(dev, txwi, MT_TXD_SIZE, PKT_BIN_DEBUG_TXD, 0);
++ if (dev->dbg.dump_tx_pkt)
++ mt7996_packet_log_to_host(dev, t->skb->data, t->skb->len, PKT_BIN_DEBUG_TX, 0);
++#endif
++
+ return 0;
+ }
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8dca88ac2..74b874758 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -431,6 +431,9 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ rcu_assign_pointer(mvif->link[link_id], mconf);
+ rcu_assign_pointer(mvif->sta.link[link_id], mlink);
+
++ mlo_dbg(phy, "bss_idx=%u, link_id=%u, wcid=%u\n",
++ mconf->mt76.idx, mconf->link_id, mlink->wcid.idx);
++
+ return 0;
+ error:
+ mt7996_remove_bss_conf(vif, conf, mconf);
+@@ -603,6 +606,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ add = vif->valid_links ?: BIT(0);
+ }
+
++ mlo_dbg(mt7996_hw_phy(hw), "cipher = 0x%x, icv_len = %u, iv_len = %u, hw_key_idx = %u, keyidx = %d, flags = 0x%x, link_id = %d, keylen = %u\n",
++ key->cipher, key->icv_len, key->iv_len, key->hw_key_idx, key->keyidx, key->flags, key->link_id, key->keylen);
++ // print_hex_dump(KERN_INFO , "", DUMP_PREFIX_OFFSET, 16, 1, key->key, key->keylen, false);
++ mlo_dbg(mt7996_hw_phy(hw), "add=%lx, valid_links=%x, active_links=%x\n", add, vif->valid_links, vif->active_links);
++
+ mutex_lock(&dev->mt76.mutex);
+
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+@@ -1133,6 +1141,8 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
+ mt76_wcid_mask_set(dev->mt76.wcid_phy_mask, idx);
+ rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
+ mt76_wcid_init(&mlink->wcid);
++
++ mlo_dbg(mconf->phy, "wcid=%u, link_id=%u, link_addr=%pM, pri_link=%u, sec_link=%u\n", mlink->wcid.idx, link_id, link_sta->addr, msta->pri_link, msta->sec_link);
+ }
+
+ if (!assoc)
+@@ -1167,6 +1177,7 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned int link_id;
+
++ mlo_dbg(mt7996_hw_phy(mvif->hw), "rem=%lu\n", rem);
+ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_bss_conf *mconf =
+ mconf_dereference_protected(mvif, link_id);
+@@ -1192,6 +1203,7 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ unsigned int link_id;
+ int i, ret;
+
++ mlo_dbg(mt7996_hw_phy(mvif->hw), "add=%lu, assoc=%d\n", add, assoc);
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_bss_conf *mconf =
+ mconf_dereference_protected(mvif, link_id);
+@@ -2530,6 +2542,7 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ unsigned int link_id;
+ int ret = 0;
+
++ mlo_dbg(phy, "old=%u, new=%u\n", old_links, new_links);
+ if (old_links == new_links)
+ return 0;
+
+@@ -2578,6 +2591,7 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ unsigned long rem = old_links & ~new_links;
+ int ret = 0;
+
++ mlo_dbg(mt7996_hw_phy(hw), "old=%u, new=%u\n", old_links, new_links);
+ mutex_lock(&dev->mt76.mutex);
+
+ if (rem)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index d8e3181b1..38305d810 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -333,6 +333,10 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ mcu_txd->s2d_index = MCU_S2D_H2N;
+
+ exit:
++#ifdef CONFIG_MTK_DEBUG
++ if (dev->dbg.dump_mcu_pkt)
++ mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_MCU, 0);
++#endif
+ if (wait_seq)
+ *wait_seq = seq;
+
+@@ -1336,6 +1340,8 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+
+ mld->own_mld_id = mconf->own_mld_id;
++ pr_info("%s: group_mld_id=%d own_mld_id=%d remap_idx=%d mld->addr[%pM]\n",
++ __func__, mld->group_mld_id, mld->own_mld_id, mld->remap_idx, mld->mac_addr);
+ }
+
+ static void
+@@ -1487,6 +1493,10 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ }
+
+ memcpy(bss->bssid, conf->bssid, ETH_ALEN);
++
++ mlo_dbg(mconf->phy, "omac_idx=%d band_idx=%d wmm_idx=%d bss->bssid=%pM enable=%d\n",
++ bss->omac_idx, bss->band_idx, bss->wmm_idx, bss->bssid, enable);
++
+ bss->bcn_interval = cpu_to_le16(conf->beacon_int);
+ bss->dtim_period = conf->dtim_period;
+ bss->phymode = mt76_connac_get_phy_mode(phy, vif,
+@@ -2770,6 +2780,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+
+ /* starec basic */
+ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, conf, link_sta, enable, newly);
++ mlo_dbg(mconf->phy, "link=%u, newly=%d, en=%d\n",
++ mlink->wcid.link_id, newly, enable);
+
+ if (!enable)
+ goto out;
+@@ -2852,12 +2864,16 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ mld_setup->link_num = hweight16(sta->valid_links);
+
+ mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
++ mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "pri_link(%u) primary_id(%d) seconed_id(%d) wcid(%d), link_num(%d), mld_addr[%pM]\n",
++ msta->pri_link, mld_setup->primary_id, mld_setup->seconed_id, mld_setup->setup_wcid, mld_setup->link_num, mld_setup->mld_addr);
+ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ mlink = mlink_dereference_protected(msta, link_id);
+ mconf = mconf_dereference_protected(msta->vif, link_id);
+
+ mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
+ mld_setup_link->bss_idx = mconf->mt76.idx;
++ mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "link_id(%d) wcid(%d) bss_idx(%d)\n",
++ link_id, mld_setup_link->wcid, mld_setup_link->bss_idx);
+ mld_setup_link++;
+ }
+ }
+@@ -3121,6 +3137,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ return mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
+
+ memcpy(data.tlv.omac_addr, conf->addr, ETH_ALEN);
++ mlo_dbg(phy, "omac=%u, band=%u, addr=%pM, en=%d\n",
++ data.hdr.omac_idx,data.hdr.band_idx, data.tlv.omac_addr, enable);
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
+ &data, sizeof(data), true);
+ }
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index dc93fab25..84d961d11 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1035,6 +1035,14 @@ enum {
+ UNI_RRO_SET_FLUSH_TIMEOUT
+ };
+
++enum {
++ UNI_MEC_READ_INFO = 0,
++ UNI_MEC_AMSDU_ALGO_EN_STA,
++ UNI_MEC_AMSDU_PARA_STA,
++ UNI_MEC_AMSDU_ALGO_THRESHOLD,
++ UNI_MEC_IFAC_SPEED,
++};
++
+ enum{
+ UNI_CMD_SR_ENABLE = 0x1,
+ UNI_CMD_SR_ENABLE_SD,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 0d537fb9b..de7cf1ecc 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -730,6 +730,13 @@ struct mt7996_dev {
+ u8 fw_dbg_lv;
+ u32 bcn_total_cnt[__MT_MAX_BAND];
+ u32 sid;
++
++ bool dump_mcu_pkt:1;
++ bool dump_txd:1;
++ bool dump_tx_pkt:1;
++ bool dump_rx_pkt:1;
++ bool dump_rx_raw:1;
++ u32 token_idx;
+ } dbg;
+ const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
+@@ -925,6 +932,8 @@ mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
+ return &mlink->wcid;
+ }
+
++#define mlo_dbg(phy, fmt, ...) wiphy_info(phy->mt76->hw->wiphy, "%s: " fmt, __func__, ##__VA_ARGS__)
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+@@ -1256,6 +1265,17 @@ void mt7996_tm_update_channel(struct mt7996_phy *phy);
+
+ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
+ int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action);
++
++#define PKT_BIN_DEBUG_MAGIC 0xc8763123
++enum {
++ PKT_BIN_DEBUG_MCU,
++ PKT_BIN_DEBUG_TXD,
++ PKT_BIN_DEBUG_TX,
++ PKT_BIN_DEBUG_RX,
++ PKT_BIN_DEBUG_RX_RAW,
++};
++
++void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len, int type, int des_len);
+ #endif
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
+index da2a60723..829902398 100644
+--- a/mt7996/mtk_debug.h
++++ b/mt7996/mtk_debug.h
+@@ -2288,4 +2288,664 @@ enum cipher_suit {
+ #define WTBL_RATE_STBC_OFFSET 14
+ #endif
+
++struct bin_debug_hdr {
++ __le32 magic_num;
++ __le16 serial_id;
++ __le16 msg_type;
++ __le16 len;
++ __le16 des_len; /* descriptor len for rxd */
++} __packed;
++
++enum umac_port {
++ ENUM_UMAC_HIF_PORT_0 = 0,
++ ENUM_UMAC_CPU_PORT_1 = 1,
++ ENUM_UMAC_LMAC_PORT_2 = 2,
++ ENUM_PLE_CTRL_PSE_PORT_3 = 3,
++ ENUM_UMAC_PSE_PLE_PORT_TOTAL_NUM = 4
++};
++
++/* N9 MCU QUEUE LIST */
++enum umac_cpu_port_queue_idx {
++ ENUM_UMAC_CTX_Q_0 = 0,
++ ENUM_UMAC_CTX_Q_1 = 1,
++ ENUM_UMAC_CTX_Q_2 = 2,
++ ENUM_UMAC_CTX_Q_3 = 3,
++ ENUM_UMAC_CRX = 0,
++ ENUM_UMAC_CIF_QUEUE_TOTAL_NUM = 4
++};
++
++/* LMAC PLE For PSE Control P3 */
++enum umac_ple_ctrl_port3_queue_idx {
++ ENUM_UMAC_PLE_CTRL_P3_Q_0X1E = 0x1e,
++ ENUM_UMAC_PLE_CTRL_P3_Q_0X1F = 0x1f,
++ ENUM_UMAC_PLE_CTRL_P3_TOTAL_NUM = 2
++};
++
++/* PSE PLE QUEUE */
++#define CR_NUM_OF_AC_MT7996 34
++#define CR_NUM_OF_AC_MT7992 17
++struct bmac_queue_info {
++ char *QueueName;
++ u32 Portid;
++ u32 Queueid;
++ u32 tgid;
++};
++
++struct bmac_queue_info_t {
++ char *QueueName;
++ u32 Portid;
++ u32 Queueid;
++};
++
++#define WF_DRR_TOP_BASE 0x820c8800
++#define WF_DRR_TOP_SBRR_ADDR (WF_DRR_TOP_BASE + 0x00E0) // 88E0
++#define WF_DRR_TOP_TWT_STA_MAP00_ADDR (WF_DRR_TOP_BASE + 0x0100) // 8900
++#define WF_DRR_TOP_TWT_STA_MAP_EXT_00_ADDR (WF_DRR_TOP_BASE + 0x0180) // 8980
++#define WF_DRR_TOP_AC0_STATION_PAUSE00_ADDR (WF_DRR_TOP_BASE + 0x0200) // 8A00
++#define WF_DRR_TOP_AC0_STATION_PAUSE_EXT_00_ADDR (WF_DRR_TOP_BASE + 0x0280) // 8A80
++#define WF_DRR_TOP_AC1_STATION_PAUSE00_ADDR (WF_DRR_TOP_BASE + 0x0300) // 8B00
++#define WF_DRR_TOP_AC1_STATION_PAUSE_EXT_00_ADDR (WF_DRR_TOP_BASE + 0x0380) // 8B80
++#define WF_DRR_TOP_AC2_STATION_PAUSE00_ADDR (WF_DRR_TOP_BASE + 0x0400) // 8C00
++#define WF_DRR_TOP_AC2_STATION_PAUSE_EXT_00_ADDR (WF_DRR_TOP_BASE + 0x0480) // 8C80
++#define WF_DRR_TOP_AC3_STATION_PAUSE00_ADDR (WF_DRR_TOP_BASE + 0x0500) // 8D00
++#define WF_DRR_TOP_AC3_STATION_PAUSE_EXT_00_ADDR (WF_DRR_TOP_BASE + 0x0580) // 8D80
++
++#define WF_DRR_TOP_SBRR_TARGET_BAND_MASK 0x00000003 // TARGET_BAND[1..0]
++/* PLE AMSDU */
++#define WF_PLE_TOP_BASE 0x820c0000
++
++#define WF_PLE_TOP_AMSDU_PACK_1_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10e0) // 10E0
++#define WF_PLE_TOP_AMSDU_PACK_2_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10e4) // 10E4
++#define WF_PLE_TOP_AMSDU_PACK_3_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10e8) // 10E8
++#define WF_PLE_TOP_AMSDU_PACK_4_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10ec) // 10EC
++#define WF_PLE_TOP_AMSDU_PACK_5_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10f0) // 10F0
++#define WF_PLE_TOP_AMSDU_PACK_6_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10f4) // 10F4
++#define WF_PLE_TOP_AMSDU_PACK_7_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10f8) // 10F8
++#define WF_PLE_TOP_AMSDU_PACK_8_MSDU_CNT_ADDR (WF_PLE_TOP_BASE + 0x10fc) // 10FC
++#define WF_PLE_TOP_AMSDU_PACK_NUM 8
++
++/* PLE */
++#define WF_PLE_TOP_PBUF_CTRL_ADDR (WF_PLE_TOP_BASE + 0x04) // 0004
++
++#define WF_PLE_TOP_PG_HIF_GROUP_ADDR (WF_PLE_TOP_BASE + 0x0c) // 000C
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR (WF_PLE_TOP_BASE + 0x10) // 0010
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR (WF_PLE_TOP_BASE + 0x14) // 0014
++#define WF_PLE_TOP_PG_CPU_GROUP_ADDR (WF_PLE_TOP_BASE + 0x18) // 0018
++#define WF_PLE_TOP_QUEUE_EMPTY_ADDR (WF_PLE_TOP_BASE + 0x360) // 0360
++
++#define WF_PLE_TOP_DIS_STA_MAP0_ADDR (WF_PLE_TOP_BASE + 0x100) // 0100
++#define WF_PLE_TOP_DIS_STA_MAP1_ADDR (WF_PLE_TOP_BASE + 0x104) // 0104
++#define WF_PLE_TOP_DIS_STA_MAP2_ADDR (WF_PLE_TOP_BASE + 0x108) // 0108
++#define WF_PLE_TOP_DIS_STA_MAP3_ADDR (WF_PLE_TOP_BASE + 0x10c) // 010C
++#define WF_PLE_TOP_DIS_STA_MAP4_ADDR (WF_PLE_TOP_BASE + 0x110) // 0110
++#define WF_PLE_TOP_DIS_STA_MAP5_ADDR (WF_PLE_TOP_BASE + 0x114) // 0114
++#define WF_PLE_TOP_DIS_STA_MAP6_ADDR (WF_PLE_TOP_BASE + 0x118) // 0118
++#define WF_PLE_TOP_DIS_STA_MAP7_ADDR (WF_PLE_TOP_BASE + 0x11c) // 011C
++#define WF_PLE_TOP_DIS_STA_MAP8_ADDR (WF_PLE_TOP_BASE + 0x120) // 0120
++
++#define WF_PLE_TOP_TXCMD_QUEUE_EMPTY_ADDR (WF_PLE_TOP_BASE + 0x378) // 0378
++#define WF_PLE_TOP_NATIVE_TXCMD_QUEUE_EMPTY_ADDR (WF_PLE_TOP_BASE + 0x37c) // 037C
++#define WF_PLE_TOP_BN1_TXCMD_QUEUE_EMPTY_ADDR (WF_PLE_TOP_BASE + 0x388) // 0388
++#define WF_PLE_TOP_BN1_NATIVE_TXCMD_QUEUE_EMPTY_ADDR (WF_PLE_TOP_BASE + 0x38c) // 038C
++#define WF_PLE_TOP_BN2_TXCMD_QUEUE_EMPTY_ADDR (WF_PLE_TOP_BASE + 0x398) // 0398
++#define WF_PLE_TOP_BN2_NATIVE_TXCMD_QUEUE_EMPTY_ADDR (WF_PLE_TOP_BASE + 0x39c) // 039C
++
++#define WF_PLE_TOP_FREEPG_CNT_ADDR (WF_PLE_TOP_BASE + 0x3a0) // 03A0
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR (WF_PLE_TOP_BASE + 0x3a4) // 03A4
++#define WF_PLE_TOP_HIF_PG_INFO_ADDR (WF_PLE_TOP_BASE + 0x3a8) // 03A8
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR (WF_PLE_TOP_BASE + 0x3ac) // 03AC
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR (WF_PLE_TOP_BASE + 0x3b0) // 03B0
++#define WF_PLE_TOP_CPU_PG_INFO_ADDR (WF_PLE_TOP_BASE + 0x3b4) // 03B4
++
++#define WF_PLE_TOP_FL_QUE_CTRL_0_ADDR (WF_PLE_TOP_BASE + 0x3e0) // 03E0
++#define WF_PLE_TOP_FL_QUE_CTRL_1_ADDR (WF_PLE_TOP_BASE + 0x3e4) // 03E4
++#define WF_PLE_TOP_FL_QUE_CTRL_2_ADDR (WF_PLE_TOP_BASE + 0x3e8) // 03E8
++#define WF_PLE_TOP_FL_QUE_CTRL_3_ADDR (WF_PLE_TOP_BASE + 0x3ec) // 03EC
++
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY0_ADDR (WF_PLE_TOP_BASE + 0x600) // 0600
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY1_ADDR (WF_PLE_TOP_BASE + 0x604) // 0604
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY2_ADDR (WF_PLE_TOP_BASE + 0x608) // 0608
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY3_ADDR (WF_PLE_TOP_BASE + 0x60c) // 060C
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY4_ADDR (WF_PLE_TOP_BASE + 0x610) // 0610
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY5_ADDR (WF_PLE_TOP_BASE + 0x614) // 0614
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY6_ADDR (WF_PLE_TOP_BASE + 0x618) // 0618
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY7_ADDR (WF_PLE_TOP_BASE + 0x61c) // 061C
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY8_ADDR (WF_PLE_TOP_BASE + 0x620) // 0620
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY_EXT0_ADDR (WF_PLE_TOP_BASE + 0x680) // 0680
++
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY0_ADDR (WF_PLE_TOP_BASE + 0x700) // 0700
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY1_ADDR (WF_PLE_TOP_BASE + 0x704) // 0704
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY2_ADDR (WF_PLE_TOP_BASE + 0x708) // 0708
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY3_ADDR (WF_PLE_TOP_BASE + 0x70c) // 070C
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY4_ADDR (WF_PLE_TOP_BASE + 0x710) // 0710
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY5_ADDR (WF_PLE_TOP_BASE + 0x714) // 0714
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY6_ADDR (WF_PLE_TOP_BASE + 0x718) // 0718
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY7_ADDR (WF_PLE_TOP_BASE + 0x71c) // 071C
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY8_ADDR (WF_PLE_TOP_BASE + 0x720) // 0720
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY_EXT0_ADDR (WF_PLE_TOP_BASE + 0x780) // 0780
++
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY0_ADDR (WF_PLE_TOP_BASE + 0x800) // 0800
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY1_ADDR (WF_PLE_TOP_BASE + 0x804) // 0804
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY2_ADDR (WF_PLE_TOP_BASE + 0x808) // 0808
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY3_ADDR (WF_PLE_TOP_BASE + 0x80c) // 080C
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY4_ADDR (WF_PLE_TOP_BASE + 0x810) // 0810
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY5_ADDR (WF_PLE_TOP_BASE + 0x814) // 0814
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY6_ADDR (WF_PLE_TOP_BASE + 0x818) // 0818
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY7_ADDR (WF_PLE_TOP_BASE + 0x81c) // 081C
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY8_ADDR (WF_PLE_TOP_BASE + 0x820) // 0820
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY_EXT0_ADDR (WF_PLE_TOP_BASE + 0x880) // 0880
++
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY0_ADDR (WF_PLE_TOP_BASE + 0x900) // 0900
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY1_ADDR (WF_PLE_TOP_BASE + 0x904) // 0904
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY2_ADDR (WF_PLE_TOP_BASE + 0x908) // 0908
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY3_ADDR (WF_PLE_TOP_BASE + 0x90c) // 090C
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY4_ADDR (WF_PLE_TOP_BASE + 0x910) // 0910
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY5_ADDR (WF_PLE_TOP_BASE + 0x914) // 0914
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY6_ADDR (WF_PLE_TOP_BASE + 0x918) // 0918
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY7_ADDR (WF_PLE_TOP_BASE + 0x91c) // 091C
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY8_ADDR (WF_PLE_TOP_BASE + 0x920) // 0920
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY_EXT0_ADDR (WF_PLE_TOP_BASE + 0x980) // 0980
++
++#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_ADDR WF_PLE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_MASK 0x01000000 // ALL_AC_EMPTY[24]
++#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_SHFT 24
++
++#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_ADDR WF_PLE_TOP_PBUF_CTRL_ADDR
++#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK 0x80000000 // PAGE_SIZE_CFG[31]
++#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT 31
++#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_ADDR WF_PLE_TOP_PBUF_CTRL_ADDR
++#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK 0x07FE0000 // PBUF_OFFSET[26..17]
++#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT 17
++#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_ADDR WF_PLE_TOP_PBUF_CTRL_ADDR
++#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK 0x00001FFF // TOTAL_PAGE_NUM[12..0]
++#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT 0
++
++#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_ADDR WF_PLE_TOP_FREEPG_CNT_ADDR
++#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_MASK 0x1FFF0000 // FFA_CNT[28..16]
++#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_SHFT 16
++#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_ADDR WF_PLE_TOP_FREEPG_CNT_ADDR
++#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_MASK 0x00001FFF // FREEPG_CNT[12..0]
++#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT 0
++
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_ADDR WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK 0x1FFF0000 // FREEPG_TAIL[28..16]
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT 16
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_ADDR WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK 0x00001FFF // FREEPG_HEAD[12..0]
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT 0
++
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_ADDR WF_PLE_TOP_PG_HIF_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_MASK 0x1FFF0000 // HIF_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_SHFT 16
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_ADDR WF_PLE_TOP_PG_HIF_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_MASK 0x00001FFF // HIF_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_SHFT 0
++
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_ADDR WF_PLE_TOP_HIF_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_MASK 0x1FFF0000 // HIF_SRC_CNT[28..16]
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_SHFT 16
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_ADDR WF_PLE_TOP_HIF_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_MASK 0x00001FFF // HIF_RSV_CNT[12..0]
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_SHFT 0
++
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_ADDR WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_MASK 0x1FFF0000 // HIF_WMTXD_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_SHFT 16
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_ADDR WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_MASK 0x00001FFF // HIF_WMTXD_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_SHFT 0
++
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_ADDR WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_MASK 0x1FFF0000 // HIF_WMTXD_SRC_CNT[28..16]
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_SHFT 16
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_ADDR WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_MASK 0x00001FFF // HIF_WMTXD_RSV_CNT[12..0]
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_SHFT 0
++
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_ADDR WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_MASK 0x1FFF0000 // HIF_TXCMD_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_SHFT 16
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_ADDR WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_MASK 0x00001FFF // HIF_TXCMD_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_SHFT 0
++
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_ADDR WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_MASK 0x1FFF0000 // HIF_TXCMD_SRC_CNT[28..16]
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_SHFT 16
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_ADDR WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_MASK 0x00001FFF // HIF_TXCMD_RSV_CNT[12..0]
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_SHFT 0
++
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_ADDR WF_PLE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK 0x1FFF0000 // CPU_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT 16
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_ADDR WF_PLE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK 0x00001FFF // CPU_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT 0
++
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_ADDR WF_PLE_TOP_CPU_PG_INFO_ADDR
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK 0x1FFF0000 // CPU_SRC_CNT[28..16]
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT 16
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_ADDR WF_PLE_TOP_CPU_PG_INFO_ADDR
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK 0x00001FFF // CPU_RSV_CNT[12..0]
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT 0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_ADDR WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK 0x80000000 // EXECUTE[31]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_SHFT 31
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_ADDR WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK 0x7F000000 // Q_BUF_QID[30..24]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT 24
++#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_ADDR WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_MASK 0x00FFF000 // FL_BUFFER_ADDR[23..12]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_SHFT 12
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_ADDR WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_MASK 0x00000FFF // Q_BUF_WLANID[11..0]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_SHFT 0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_ADDR WF_PLE_TOP_FL_QUE_CTRL_1_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_MASK 0xC0000000 // Q_BUF_TGID[31..30]
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_SHFT 30
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_ADDR WF_PLE_TOP_FL_QUE_CTRL_1_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_MASK 0x30000000 // Q_BUF_PID[29..28]
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_SHFT 28
++
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_ADDR WF_PLE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK 0x1FFF0000 // QUEUE_TAIL_FID[28..16]
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT 16
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_ADDR WF_PLE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK 0x00001FFF // QUEUE_HEAD_FID[12..0]
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT 0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_ADDR WF_PLE_TOP_FL_QUE_CTRL_3_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK 0x00001FFF // QUEUE_PKT_NUM[12..0]
++#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT 0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_ADDR WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_MASK 0x00300000 // Q_BUF_TGID[21..20]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_SHFT 20
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_ADDR WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_MASK 0x00030000 // Q_BUF_PID[17..16]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT 16
++/* PSE */
++#define WF_PSE_TOP_BASE 0x820c8000
++
++#define WF_PSE_TOP_PBUF_CTRL_ADDR (WF_PSE_TOP_BASE + 0x04) // 8004
++#define WF_PSE_TOP_QUEUE_EMPTY_ADDR (WF_PSE_TOP_BASE + 0xB0) // 80B0
++#define WF_PSE_TOP_QUEUE_EMPTY_1_ADDR (WF_PSE_TOP_BASE + 0xBC) // 80BC
++#define WF_PSE_TOP_PG_HIF0_GROUP_ADDR (WF_PSE_TOP_BASE + 0x110) // 8110
++#define WF_PSE_TOP_PG_HIF1_GROUP_ADDR (WF_PSE_TOP_BASE + 0x114) // 8114
++#define WF_PSE_TOP_PG_CPU_GROUP_ADDR (WF_PSE_TOP_BASE + 0x118) // 8118
++#define WF_PSE_TOP_PG_PLE_GROUP_ADDR (WF_PSE_TOP_BASE + 0x11C) // 811C
++#define WF_PSE_TOP_PG_PLE1_GROUP_ADDR (WF_PSE_TOP_BASE + 0x120) // 8120
++#define WF_PSE_TOP_PG_LMAC0_GROUP_ADDR (WF_PSE_TOP_BASE + 0x124) // 8124
++#define WF_PSE_TOP_PG_LMAC1_GROUP_ADDR (WF_PSE_TOP_BASE + 0x128) // 8128
++#define WF_PSE_TOP_PG_LMAC2_GROUP_ADDR (WF_PSE_TOP_BASE + 0x12C) // 812C
++#define WF_PSE_TOP_PG_LMAC3_GROUP_ADDR (WF_PSE_TOP_BASE + 0x130) // 8130
++#define WF_PSE_TOP_PG_MDP_GROUP_ADDR (WF_PSE_TOP_BASE + 0x134) // 8134
++#define WF_PSE_TOP_PG_MDP2_GROUP_ADDR (WF_PSE_TOP_BASE + 0x13C) // 813C
++#define WF_PSE_TOP_PG_HIF2_GROUP_ADDR (WF_PSE_TOP_BASE + 0x140) // 8140
++#define WF_PSE_TOP_PG_MDP3_GROUP_ADDR (WF_PSE_TOP_BASE + 0x144) // 8144
++#define WF_PSE_TOP_HIF0_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x150) // 8150
++#define WF_PSE_TOP_HIF1_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x154) // 8154
++#define WF_PSE_TOP_CPU_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x158) // 8158
++#define WF_PSE_TOP_PLE_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x15C) // 815C
++#define WF_PSE_TOP_PLE1_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x160) // 8160
++#define WF_PSE_TOP_LMAC0_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x164) // 8164
++#define WF_PSE_TOP_LMAC1_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x168) // 8168
++#define WF_PSE_TOP_LMAC2_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x16C) // 816C
++#define WF_PSE_TOP_LMAC3_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x170) // 8170
++#define WF_PSE_TOP_MDP_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x174) // 8174
++#define WF_PSE_TOP_MDP2_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x17C) // 817C
++#define WF_PSE_TOP_HIF2_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x180) // 8180
++#define WF_PSE_TOP_MDP3_PG_INFO_ADDR (WF_PSE_TOP_BASE + 0x184) // 8184
++#define WF_PSE_TOP_FL_QUE_CTRL_0_ADDR (WF_PSE_TOP_BASE + 0x1B0) // 81B0
++#define WF_PSE_TOP_FL_QUE_CTRL_1_ADDR (WF_PSE_TOP_BASE + 0x1B4) // 81B4
++#define WF_PSE_TOP_FL_QUE_CTRL_2_ADDR (WF_PSE_TOP_BASE + 0x1B8) // 81B8
++#define WF_PSE_TOP_FL_QUE_CTRL_3_ADDR (WF_PSE_TOP_BASE + 0x1BC) // 81BC
++#define WF_PSE_TOP_FREEPG_CNT_ADDR (WF_PSE_TOP_BASE + 0x3A0) // 83A0
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR (WF_PSE_TOP_BASE + 0x3A4) // 83A4
++
++#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_ADDR WF_PSE_TOP_PBUF_CTRL_ADDR
++#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK 0x80000000 // PAGE_SIZE_CFG[31]
++#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT 31
++#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_ADDR WF_PSE_TOP_PBUF_CTRL_ADDR
++#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK 0x07FE0000 // PBUF_OFFSET[26..17]
++#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT 17
++#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_ADDR WF_PSE_TOP_PBUF_CTRL_ADDR
++#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK 0x00001FFF // TOTAL_PAGE_NUM[12..0]
++#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT 0
++
++#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_MASK 0x80000000 // RLS_Q_EMTPY[31]
++#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_SHFT 31
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_MASK 0x10000000 // CPU_Q4_EMPTY[28]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_SHFT 28
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_MASK 0x08000000 // MDP_RXIOC1_QUEUE_EMPTY[27]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_SHFT 27
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_MASK 0x04000000 // MDP_TXIOC1_QUEUE_EMPTY[26]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_SHFT 26
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_MASK 0x02000000 // SEC_TX1_QUEUE_EMPTY[25]
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_SHFT 25
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_MASK 0x01000000 // MDP_TX1_QUEUE_EMPTY[24]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_SHFT 24
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_MASK 0x00800000 // MDP_RXIOC_QUEUE_EMPTY[23]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_SHFT 23
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_MASK 0x00400000 // MDP_TXIOC_QUEUE_EMPTY[22]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_SHFT 22
++#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_MASK 0x00200000 // SFD_PARK_QUEUE_EMPTY[21]
++#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_SHFT 21
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_MASK 0x00100000 // SEC_RX_QUEUE_EMPTY[20]
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_SHFT 20
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_MASK 0x00080000 // SEC_TX_QUEUE_EMPTY[19]
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_SHFT 19
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_MASK 0x00040000 // MDP_RX_QUEUE_EMPTY[18]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_SHFT 18
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_MASK 0x00020000 // MDP_TX_QUEUE_EMPTY[17]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_SHFT 17
++#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_MASK 0x00010000 // LMAC_TX_QUEUE_EMPTY[16]
++#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_SHFT 16
++
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_MASK 0x00000008 // CPU_Q3_EMPTY[3]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_SHFT 3
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_MASK 0x00000004 // CPU_Q2_EMPTY[2]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_SHFT 2
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_MASK 0x00000002 // CPU_Q1_EMPTY[1]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_SHFT 1
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_MASK 0x00000001 // CPU_Q0_EMPTY[0]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_SHFT 0
++
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_MASK 0x20000000 // HIF_13_EMPTY[29]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_SHFT 29
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_MASK 0x10000000 // HIF_12_EMPTY[28]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_SHFT 28
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_MASK 0x08000000 // HIF_11_EMPTY[27]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_SHFT 27
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_MASK 0x04000000 // HIF_10_EMPTY[26]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_SHFT 26
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_MASK 0x02000000 // HIF_9_EMPTY[25]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_SHFT 25
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_MASK 0x01000000 // HIF_8_EMPTY[24]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_SHFT 24
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_MASK 0x00800000 // HIF_7_EMPTY[23]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_SHFT 23
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_MASK 0x00400000 // HIF_6_EMPTY[22]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_SHFT 22
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_MASK 0x00200000 // HIF_5_EMPTY[21]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_SHFT 21
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_MASK 0x00100000 // HIF_4_EMPTY[20]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_SHFT 20
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_MASK 0x00080000 // HIF_3_EMPTY[19]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_SHFT 19
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_MASK 0x00040000 // HIF_2_EMPTY[18]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_SHFT 18
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_MASK 0x00020000 // HIF_1_EMPTY[17]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_SHFT 17
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_MASK 0x00010000 // HIF_0_EMPTY[16]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_SHFT 16
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_MASK 0x00008000 // MDP_RXIOC3_QUEUE_EMPTY[15]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_SHFT 15
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_MASK 0x00000800 // MDP_RXIOC2_QUEUE_EMPTY[11]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_SHFT 11
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_MASK 0x00000400 // MDP_TXIOC2_QUEUE_EMPTY[10]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_SHFT 10
++#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_MASK 0x00000200 // SEC_TX2_QUEUE_EMPTY[9]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_SHFT 9
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_ADDR WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_MASK 0x00000100 // MDP_TX2_QUEUE_EMPTY[8]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_SHFT 8
++
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_ADDR WF_PSE_TOP_PG_HIF0_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_MASK 0x1FFF0000 // HIF0_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_ADDR WF_PSE_TOP_PG_HIF0_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_MASK 0x00001FFF // HIF0_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_SHFT 0
++
++
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_ADDR WF_PSE_TOP_PG_HIF1_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_MASK 0x1FFF0000 // HIF1_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_ADDR WF_PSE_TOP_PG_HIF1_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_MASK 0x00001FFF // HIF1_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_ADDR WF_PSE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK 0x1FFF0000 // CPU_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_ADDR WF_PSE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK 0x00001FFF // CPU_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_ADDR WF_PSE_TOP_PG_PLE_GROUP_ADDR
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK 0x1FFF0000 // PLE_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_ADDR WF_PSE_TOP_PG_PLE_GROUP_ADDR
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK 0x00001FFF // PLE_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_ADDR WF_PSE_TOP_PG_LMAC0_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_MASK 0x1FFF0000 // LMAC0_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_ADDR WF_PSE_TOP_PG_LMAC0_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_MASK 0x00001FFF // LMAC0_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_ADDR WF_PSE_TOP_PG_LMAC1_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_MASK 0x1FFF0000 // LMAC1_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_ADDR WF_PSE_TOP_PG_LMAC1_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_MASK 0x00001FFF // LMAC1_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_ADDR WF_PSE_TOP_PG_LMAC2_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_MASK 0x1FFF0000 // LMAC2_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_ADDR WF_PSE_TOP_PG_LMAC2_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_MASK 0x00001FFF // LMAC2_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_ADDR WF_PSE_TOP_PG_LMAC3_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_MASK 0x1FFF0000 // LMAC3_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_ADDR WF_PSE_TOP_PG_LMAC3_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_MASK 0x00001FFF // LMAC3_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_ADDR WF_PSE_TOP_PG_MDP_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_MASK 0x1FFF0000 // MDP_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_ADDR WF_PSE_TOP_PG_MDP_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_MASK 0x00001FFF // MDP_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_ADDR WF_PSE_TOP_PG_MDP2_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_MASK 0x1FFF0000 // MDP2_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_ADDR WF_PSE_TOP_PG_MDP2_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_MASK 0x00001FFF // MDP2_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_ADDR WF_PSE_TOP_PG_HIF2_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_MASK 0x1FFF0000 // HIF2_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_ADDR WF_PSE_TOP_PG_HIF2_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_MASK 0x00001FFF // HIF2_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_ADDR WF_PSE_TOP_PG_MDP3_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_MASK 0x1FFF0000 // MDP3_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_SHFT 16
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_ADDR WF_PSE_TOP_PG_MDP3_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_MASK 0x00001FFF // MDP3_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_SHFT 0
++
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_ADDR WF_PSE_TOP_HIF0_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_MASK 0x1FFF0000 // HIF0_SRC_CNT[28..16]
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_ADDR WF_PSE_TOP_HIF0_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_MASK 0x00001FFF // HIF0_RSV_CNT[12..0]
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_ADDR WF_PSE_TOP_HIF1_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_MASK 0x1FFF0000 // HIF1_SRC_CNT[28..16]
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_ADDR WF_PSE_TOP_HIF1_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_MASK 0x00001FFF // HIF1_RSV_CNT[12..0]
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_ADDR WF_PSE_TOP_CPU_PG_INFO_ADDR
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK 0x1FFF0000 // CPU_SRC_CNT[28..16]
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_ADDR WF_PSE_TOP_CPU_PG_INFO_ADDR
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK 0x00001FFF // CPU_RSV_CNT[12..0]
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_ADDR WF_PSE_TOP_PLE_PG_INFO_ADDR
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK 0x1FFF0000 // PLE_SRC_CNT[28..16]
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_ADDR WF_PSE_TOP_PLE_PG_INFO_ADDR
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK 0x00001FFF // PLE_RSV_CNT[12..0]
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_ADDR WF_PSE_TOP_LMAC0_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_MASK 0x1FFF0000 // LMAC0_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_ADDR WF_PSE_TOP_LMAC0_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_MASK 0x00001FFF // LMAC0_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_ADDR WF_PSE_TOP_LMAC1_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_MASK 0x1FFF0000 // LMAC1_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_ADDR WF_PSE_TOP_LMAC1_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_MASK 0x00001FFF // LMAC1_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_ADDR WF_PSE_TOP_LMAC2_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_MASK 0x1FFF0000 // LMAC2_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_ADDR WF_PSE_TOP_LMAC2_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_MASK 0x00001FFF // LMAC2_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_ADDR WF_PSE_TOP_LMAC3_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_MASK 0x1FFF0000 // LMAC3_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_ADDR WF_PSE_TOP_LMAC3_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_MASK 0x00001FFF // LMAC3_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_ADDR WF_PSE_TOP_MDP_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_MASK 0x1FFF0000 // MDP_SRC_CNT[28..16]
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_ADDR WF_PSE_TOP_MDP_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_MASK 0x00001FFF // MDP_RSV_CNT[12..0]
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_ADDR WF_PSE_TOP_MDP2_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_MASK 0x1FFF0000 // MDP2_SRC_CNT[28..16]
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_ADDR WF_PSE_TOP_MDP2_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_MASK 0x00001FFF // MDP2_RSV_CNT[12..0]
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_ADDR WF_PSE_TOP_HIF2_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_MASK 0x1FFF0000 // HIF2_SRC_CNT[28..16]
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_ADDR WF_PSE_TOP_HIF2_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_MASK 0x00001FFF // HIF2_RSV_CNT[12..0]
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_ADDR WF_PSE_TOP_MDP3_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_MASK 0x1FFF0000 // MDP3_SRC_CNT[28..16]
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_SHFT 16
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_ADDR WF_PSE_TOP_MDP3_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_MASK 0x00001FFF // MDP3_RSV_CNT[12..0]
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_SHFT 0
++
++#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_ADDR WF_PSE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK 0x80000000 // EXECUTE[31]
++#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_SHFT 31
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_ADDR WF_PSE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK 0x7F000000 // Q_BUF_QID[30..24]
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT 24
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT 16
++
++#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_ADDR WF_PSE_TOP_FL_QUE_CTRL_1_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_MASK 0x30000000 // Q_BUF_PID[29..28]
++#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_SHFT 28
++
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_ADDR WF_PSE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK 0x1FFF0000 // QUEUE_TAIL_FID[28..16]
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT 16
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_ADDR WF_PSE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK 0x00001FFF // QUEUE_HEAD_FID[12..0]
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT 0
++
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_ADDR WF_PSE_TOP_FL_QUE_CTRL_3_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_MASK 0x00FFF000 // QUEUE_PAGE_NUM[23..12]
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_SHFT 12
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_ADDR WF_PSE_TOP_FL_QUE_CTRL_3_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK 0x00001FFF // QUEUE_PKT_NUM[12..0]
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT 0
++
++#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_ADDR WF_PSE_TOP_FREEPG_CNT_ADDR
++#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_MASK 0x1FFF0000 // FFA_CNT[28..16]
++#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_SHFT 16
++#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_ADDR WF_PSE_TOP_FREEPG_CNT_ADDR
++#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_MASK 0x00001FFF // FREEPG_CNT[12..0]
++#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT 0
++
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_ADDR WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK 0x1FFF0000 // FREEPG_TAIL[28..16]
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT 16
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_ADDR WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK 0x00001FFF // FREEPG_HEAD[12..0]
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT 0
++
++/* RXD */
++enum {
++ BMAC_GROUP_VLD_1 = 0x01,
++ BMAC_GROUP_VLD_2 = 0x02,
++ BMAC_GROUP_VLD_3 = 0x04,
++ BMAC_GROUP_VLD_4 = 0x08,
++ BMAC_GROUP_VLD_5 = 0x10,
++};
++
+ #endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index ddb4a7ca2..d20c40a5f 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3228,6 +3228,1072 @@ mt7996_thermal_recal_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_recal, NULL,
+ mt7996_thermal_recal_set, "%llu\n");
+
++static int
++mt7996_reset_counter(void *data, u64 val)
++{
++ struct mt7996_dev *dev = data;
++ struct mt76_dev *mdev = &dev->mt76;
++ struct mt76_wcid *wcid;
++ int ret;
++
++ /* Reset read-clear counters in FW and WTBL. */
++ ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
++ if (ret)
++ return ret;
++
++ ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
++ if (ret)
++ return ret;
++
++ ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
++ if (ret)
++ return ret;
++
++ ret = mt7996_mcu_get_per_sta_info(mdev, UNI_PER_STA_TX_CNT, 1, &dev->wlan_idx);
++ if (ret)
++ return ret;
++
++ /* Reset counters in MT76. */
++ rcu_read_lock();
++ wcid = rcu_dereference(dev->mt76.wcid[dev->wlan_idx]);
++ if (wcid)
++ memset(&wcid->stats, 0, sizeof(struct mt76_sta_stats));
++ else
++ ret = -EINVAL;
++ rcu_read_unlock();
++
++ return ret;
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_reset_counter, NULL, mt7996_reset_counter, "%llu\n");
++
++static int
++mt7996_per_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u16 wlan_idx = dev->wlan_idx;
++ struct mt76_wcid *wcid;
++ u32 total, failed;
++ int ret;
++
++ ret = mt7996_mcu_get_per_sta_info(&dev->mt76, UNI_PER_STA_TX_CNT, 1, &wlan_idx);
++ if (ret)
++ return ret;
++
++ rcu_read_lock();
++ wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
++ if (wcid) {
++ total = wcid->stats.tx_total_mpdu_cnt;
++ failed = wcid->stats.tx_failed_mpdu_cnt;
++ seq_printf(s, "WCID %hu\tTxTotalMpduCount: %u\tTxFailedMpduCount: %u\tPER: %u.%u%%\n",
++ wlan_idx, total, failed,
++ total ? failed * 1000 / total / 10 : 0,
++ total ? failed * 1000 / total % 10 : 0);
++ } else {
++ ret = -EINVAL;
++ }
++ rcu_read_unlock();
++
++ return ret;
++}
++
++void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len, int type, int des_len)
++{
++ struct bin_debug_hdr *hdr;
++ char *buf;
++
++ if (len > 1500 - sizeof(*hdr))
++ len = 1500 - sizeof(*hdr);
++
++ buf = kzalloc(sizeof(*hdr) + len, GFP_KERNEL);
++ if (!buf)
++ return;
++
++ hdr = (struct bin_debug_hdr *)buf;
++ hdr->magic_num = cpu_to_le32(PKT_BIN_DEBUG_MAGIC);
++ hdr->serial_id = cpu_to_le16(dev->fw_debug_seq++);
++ hdr->msg_type = cpu_to_le16(type);
++ hdr->len = cpu_to_le16(len);
++ hdr->des_len = cpu_to_le16(des_len);
++
++ memcpy(buf + sizeof(*hdr), data, len);
++
++ mt7996_debugfs_rx_log(dev, buf, sizeof(*hdr) + len);
++ kfree(buf);
++}
++
++static int mt7996_rx_token_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ int id, count = 0;
++ struct mt76_rxwi_cache *r;
++
++ seq_printf(s, "Rx cut through token:\n");
++ spin_lock_bh(&dev->mt76.rx_token_lock);
++ idr_for_each_entry(&dev->mt76.rx_token, r, id) {
++ count++;
++ }
++ seq_printf(s, "\ttotal:%8d used:%8d\n",
++ dev->mt76.rx_token_size, count);
++ spin_unlock_bh(&dev->mt76.rx_token_lock);
++
++ return 0;
++}
++
++/* AMSDU SETTING */
++static ssize_t mt7996_amsdu_algo_write(struct file *file,
++ const char __user *user_buf,
++ size_t count,
++ loff_t *ppos)
++{
++ struct mt7996_dev *dev = file->private_data;
++ char buf[100];
++ int ret;
++ struct {
++ u8 _rsv[4];
++
++ u16 tag;
++ u16 len;
++
++ u16 wlan_idx;
++ u8 algo_en;
++ u8 rsv[1];
++ } __packed data = {
++ .tag = cpu_to_le16(UNI_MEC_AMSDU_ALGO_EN_STA),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ };
++
++ if (count >= sizeof(buf))
++ return -EINVAL;
++
++ if (copy_from_user(buf, user_buf, count))
++ return -EFAULT;
++
++ if (count && buf[count - 1] == '\n')
++ buf[count - 1] = '\0';
++ else
++ buf[count] = '\0';
++
++ if (sscanf(buf, "%hu %hhu", &data.wlan_idx, &data.algo_en) != 2)
++ return -EINVAL;
++
++ if (data.wlan_idx >= mt7996_wtbl_size(dev))
++ return -EINVAL;
++
++ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MEC), &data,
++ sizeof(data), true);
++ if (ret)
++ return -EINVAL;
++
++ return count;
++}
++static const struct file_operations fops_amsdu_algo = {
++ .write = mt7996_amsdu_algo_write,
++ .read = NULL,
++ .open = simple_open,
++ .llseek = default_llseek,
++};
++
++static ssize_t mt7996_amsdu_para_write(struct file *file,
++ const char __user *user_buf,
++ size_t count,
++ loff_t *ppos)
++{
++ struct mt7996_dev *dev = file->private_data;
++ char buf[100];
++ int ret;
++ struct {
++ u8 _rsv[4];
++
++ u16 tag;
++ u16 len;
++
++ u16 wlan_idx;
++ u8 amsdu_en;
++ u8 num;
++ u16 lenth;
++ u8 rsv[2];
++ } __packed data = {
++ .tag = cpu_to_le16(UNI_MEC_AMSDU_PARA_STA),
++ .len = cpu_to_le16(sizeof(data) - 4),
++ };
++
++ if (count >= sizeof(buf))
++ return -EINVAL;
++
++ if (copy_from_user(buf, user_buf, count))
++ return -EFAULT;
++
++ if (count && buf[count - 1] == '\n')
++ buf[count - 1] = '\0';
++ else
++ buf[count] = '\0';
++
++ if (sscanf(buf, "%hu %hhu %hhu %hu", &data.wlan_idx, &data.amsdu_en, &data.num, &data.lenth) != 4)
++ return -EINVAL;
++
++ if (data.wlan_idx >= mt7996_wtbl_size(dev))
++ return -EINVAL;
++
++ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MEC), &data,
++ sizeof(data), true);
++ if (ret)
++ return -EINVAL;
++
++ return count;
++}
++static const struct file_operations fops_amsdu_para = {
++ .write = mt7996_amsdu_para_write,
++ .read = NULL,
++ .open = simple_open,
++ .llseek = default_llseek,
++};
++
++static int mt7996_amsdu_info_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u32 amsdu_cnt[WF_PLE_TOP_AMSDU_PACK_NUM] = {0}, total_cnt;
++ u8 i;
++
++ seq_printf(s, "HW A-MSDU Information:\n");
++
++ for (total_cnt = 0, i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
++ amsdu_cnt[i] = mt76_rr(dev, WF_PLE_TOP_AMSDU_PACK_1_MSDU_CNT_ADDR + i * 4);
++ total_cnt += amsdu_cnt[i];
++ }
++
++ for (i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
++ seq_printf(s, "# of HW A-MSDU containing %hhu MSDU: 0x%x",
++ i + 1, amsdu_cnt[i]);
++ seq_printf(s, "\t(%u.%u%%)\n",
++ total_cnt ? amsdu_cnt[i] * 1000 / total_cnt / 10 : 0,
++ total_cnt ? amsdu_cnt[i] * 1000 / total_cnt % 10 : 0);
++ }
++
++ return 0;
++}
++
++/* PSE INFO */
++static struct bmac_queue_info_t pse_queue_empty_info[] = {
++ {"CPU Q0", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_0},
++ {"CPU Q1", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_1},
++ {"CPU Q2", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_2},
++ {"CPU Q3", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_3},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, /* 4~7 not defined */
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {NULL, 0, 0}, {NULL, 0, 0}, /* 14~15 not defined */
++ {"LMAC Q", ENUM_UMAC_LMAC_PORT_2, 0},
++ {"MDP TX Q0", ENUM_UMAC_LMAC_PORT_2, 1},
++ {"MDP RX Q", ENUM_UMAC_LMAC_PORT_2, 2},
++ {"SEC TX Q0", ENUM_UMAC_LMAC_PORT_2, 3},
++ {"SEC RX Q", ENUM_UMAC_LMAC_PORT_2, 4},
++ {"SFD_PARK Q", ENUM_UMAC_LMAC_PORT_2, 5},
++ {"MDP_TXIOC Q0", ENUM_UMAC_LMAC_PORT_2, 6},
++ {"MDP_RXIOC Q0", ENUM_UMAC_LMAC_PORT_2, 7},
++ {"MDP TX Q1", ENUM_UMAC_LMAC_PORT_2, 0x11},
++ {"SEC TX Q1", ENUM_UMAC_LMAC_PORT_2, 0x13},
++ {"MDP_TXIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x16},
++ {"MDP_RXIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x17},
++ {"CPU Q3", ENUM_UMAC_CPU_PORT_1, 4},
++ {NULL, 0, 0}, {NULL, 0, 0},
++ {"RLS Q", ENUM_PLE_CTRL_PSE_PORT_3, ENUM_UMAC_PLE_CTRL_P3_Q_0X1F}
++};
++
++static struct bmac_queue_info_t pse_queue_empty2_info[] = {
++ {"MDP_TDPIOC Q0", ENUM_UMAC_LMAC_PORT_2, 0x8},
++ {"MDP_RDPIOC Q0", ENUM_UMAC_LMAC_PORT_2, 0x9},
++ {"MDP_TDPIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x18},
++ {"MDP_RDPIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x19},
++ {"MDP_TDPIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x28},
++ {"MDP_RDPIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x29},
++ {NULL, 0, 0},
++ {"MDP_RDPIOC Q3", ENUM_UMAC_LMAC_PORT_2, 0x39},
++ {"MDP TX Q2", ENUM_UMAC_LMAC_PORT_2, 0x21},
++ {"SEC TX Q2", ENUM_UMAC_LMAC_PORT_2, 0x23},
++ {"MDP_TXIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x26},
++ {"MDP_RXIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x27},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {"MDP_RXIOC Q3", ENUM_UMAC_LMAC_PORT_2, 0x37},
++ {"HIF Q0", ENUM_UMAC_HIF_PORT_0, 0},
++ {"HIF Q1", ENUM_UMAC_HIF_PORT_0, 1},
++ {"HIF Q2", ENUM_UMAC_HIF_PORT_0, 2},
++ {"HIF Q3", ENUM_UMAC_HIF_PORT_0, 3},
++ {"HIF Q4", ENUM_UMAC_HIF_PORT_0, 4},
++ {"HIF Q5", ENUM_UMAC_HIF_PORT_0, 5},
++ {"HIF Q6", ENUM_UMAC_HIF_PORT_0, 6},
++ {"HIF Q7", ENUM_UMAC_HIF_PORT_0, 7},
++ {"HIF Q8", ENUM_UMAC_HIF_PORT_0, 8},
++ {"HIF Q9", ENUM_UMAC_HIF_PORT_0, 9},
++ {"HIF Q10", ENUM_UMAC_HIF_PORT_0, 10},
++ {"HIF Q11", ENUM_UMAC_HIF_PORT_0, 11},
++ {"HIF Q12", ENUM_UMAC_HIF_PORT_0, 12},
++ {"HIF Q13", ENUM_UMAC_HIF_PORT_0, 13},
++ {NULL, 0, 0}, {NULL, 0, 0}
++};
++
++static int
++mt7996_pseinfo_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u32 pse_buf_ctrl, pg_sz, pg_num;
++ u32 pse_stat[2], pg_flow_ctrl[28] = {0};
++ u32 fpg_cnt, ffa_cnt, fpg_head, fpg_tail;
++ u32 max_q, min_q, rsv_pg, used_pg;
++ int i;
++
++ pse_buf_ctrl = mt76_rr(dev, WF_PSE_TOP_PBUF_CTRL_ADDR);
++ pse_stat[0] = mt76_rr(dev, WF_PSE_TOP_QUEUE_EMPTY_ADDR);
++ pse_stat[1] = mt76_rr(dev, WF_PSE_TOP_QUEUE_EMPTY_1_ADDR);
++ pg_flow_ctrl[0] = mt76_rr(dev, WF_PSE_TOP_FREEPG_CNT_ADDR);
++ pg_flow_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR);
++ pg_flow_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_PG_HIF0_GROUP_ADDR);
++ pg_flow_ctrl[3] = mt76_rr(dev, WF_PSE_TOP_HIF0_PG_INFO_ADDR);
++ pg_flow_ctrl[4] = mt76_rr(dev, WF_PSE_TOP_PG_HIF1_GROUP_ADDR);
++ pg_flow_ctrl[5] = mt76_rr(dev, WF_PSE_TOP_HIF1_PG_INFO_ADDR);
++ pg_flow_ctrl[6] = mt76_rr(dev, WF_PSE_TOP_PG_CPU_GROUP_ADDR);
++ pg_flow_ctrl[7] = mt76_rr(dev, WF_PSE_TOP_CPU_PG_INFO_ADDR);
++ pg_flow_ctrl[8] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC0_GROUP_ADDR);
++ pg_flow_ctrl[9] = mt76_rr(dev, WF_PSE_TOP_LMAC0_PG_INFO_ADDR);
++ pg_flow_ctrl[10] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC1_GROUP_ADDR);
++ pg_flow_ctrl[11] = mt76_rr(dev, WF_PSE_TOP_LMAC1_PG_INFO_ADDR);
++ pg_flow_ctrl[12] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC2_GROUP_ADDR);
++ pg_flow_ctrl[13] = mt76_rr(dev, WF_PSE_TOP_LMAC2_PG_INFO_ADDR);
++ pg_flow_ctrl[14] = mt76_rr(dev, WF_PSE_TOP_PG_PLE_GROUP_ADDR);
++ pg_flow_ctrl[15] = mt76_rr(dev, WF_PSE_TOP_PLE_PG_INFO_ADDR);
++ pg_flow_ctrl[16] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC3_GROUP_ADDR);
++ pg_flow_ctrl[17] = mt76_rr(dev, WF_PSE_TOP_LMAC3_PG_INFO_ADDR);
++ pg_flow_ctrl[18] = mt76_rr(dev, WF_PSE_TOP_PG_MDP_GROUP_ADDR);
++ pg_flow_ctrl[19] = mt76_rr(dev, WF_PSE_TOP_MDP_PG_INFO_ADDR);
++ pg_flow_ctrl[20] = mt76_rr(dev, WF_PSE_TOP_PG_PLE1_GROUP_ADDR);
++ pg_flow_ctrl[21] = mt76_rr(dev, WF_PSE_TOP_PLE1_PG_INFO_ADDR);
++ pg_flow_ctrl[22] = mt76_rr(dev, WF_PSE_TOP_PG_MDP2_GROUP_ADDR);
++ pg_flow_ctrl[23] = mt76_rr(dev, WF_PSE_TOP_MDP2_PG_INFO_ADDR);
++ if (mt7996_band_valid(dev, MT_BAND2)) {
++ pg_flow_ctrl[24] = mt76_rr(dev, WF_PSE_TOP_PG_MDP3_GROUP_ADDR);
++ pg_flow_ctrl[25] = mt76_rr(dev, WF_PSE_TOP_MDP3_PG_INFO_ADDR);
++ }
++ pg_flow_ctrl[26] = mt76_rr(dev, WF_PSE_TOP_PG_HIF2_GROUP_ADDR);
++ pg_flow_ctrl[27] = mt76_rr(dev, WF_PSE_TOP_HIF2_PG_INFO_ADDR);
++ /* Configuration Info */
++ seq_printf(s, "PSE Configuration Info:\n");
++ seq_printf(s, "\tPacket Buffer Control: 0x%08x\n", pse_buf_ctrl);
++ pg_sz = (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK) >> WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT;
++ seq_printf(s, "\t\tPage Size=%d(%d bytes per page)\n", pg_sz, (pg_sz == 1 ? 256 : 128));
++ seq_printf(s, "\t\tPage Offset=%d(in unit of 64KB)\n",
++ (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK) >> WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT);
++ pg_num = (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK) >> WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT;
++ seq_printf(s, "\t\tTotal page numbers=%d pages\n", pg_num);
++ /* Page Flow Control */
++ seq_printf(s, "PSE Page Flow Control:\n");
++ seq_printf(s, "\tFree page counter: 0x%08x\n", pg_flow_ctrl[0]);
++ fpg_cnt = (pg_flow_ctrl[0] & WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_MASK) >> WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT;
++ seq_printf(s, "\t\tThe toal page number of free=0x%03x\n", fpg_cnt);
++ ffa_cnt = (pg_flow_ctrl[0] & WF_PSE_TOP_FREEPG_CNT_FFA_CNT_MASK) >> WF_PSE_TOP_FREEPG_CNT_FFA_CNT_SHFT;
++ seq_printf(s, "\t\tThe free page numbers of free for all=0x%03x\n", ffa_cnt);
++ seq_printf(s, "\tFree page head and tail: 0x%08x\n", pg_flow_ctrl[1]);
++ fpg_head = (pg_flow_ctrl[1] & WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK) >> WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT;
++ fpg_tail = (pg_flow_ctrl[1] & WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK) >> WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT;
++ seq_printf(s, "\t\tThe tail/head page of free page list=0x%03x/0x%03x\n", fpg_tail, fpg_head);
++ seq_printf(s, "\tReserved page counter of HIF0 group: 0x%08x\n", pg_flow_ctrl[2]);
++ seq_printf(s, "\tHIF0 group page status: 0x%08x\n", pg_flow_ctrl[3]);
++ min_q = (pg_flow_ctrl[2] & WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[2] & WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of HIF0 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[3] & WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_MASK) >> WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[3] & WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_MASK) >> WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of HIF0 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ seq_printf(s, "\tReserved page counter of HIF1 group: 0x%08x\n", pg_flow_ctrl[4]);
++ seq_printf(s, "\tHIF1 group page status: 0x%08x\n", pg_flow_ctrl[5]);
++ min_q = (pg_flow_ctrl[4] & WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[4] & WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of HIF1 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[5] & WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_MASK) >> WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[5] & WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_MASK) >> WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of HIF1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ seq_printf(s, "\tReserved page counter of HIF2 group: 0x%08x\n", pg_flow_ctrl[26]);
++ seq_printf(s, "\tHIF2 group page status: 0x%08x\n", pg_flow_ctrl[27]);
++ min_q = (pg_flow_ctrl[26] & WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[26] & WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of HIF2 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[27] & WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_MASK) >> WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[27] & WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_MASK) >> WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of HIF2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ seq_printf(s, "\tReserved page counter of CPU group: 0x%08x\n", pg_flow_ctrl[6]);
++ seq_printf(s, "\tCPU group page status: 0x%08x\n", pg_flow_ctrl[7]);
++ min_q = (pg_flow_ctrl[6] & WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[6] & WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of CPU group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[7] & WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK) >> WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[7] & WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK) >> WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of CPU group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ seq_printf(s, "\tReserved page counter of LMAC0 group: 0x%08x\n", pg_flow_ctrl[8]);
++ seq_printf(s, "\tLMAC0 group page status: 0x%08x\n", pg_flow_ctrl[9]);
++ min_q = (pg_flow_ctrl[8] & WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[8] & WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of LMAC0 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[9] & WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[9] & WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of LMAC0 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ seq_printf(s, "\tReserved page counter of LMAC1 group: 0x%08x\n", pg_flow_ctrl[10]);
++ seq_printf(s, "\tLMAC1 group page status: 0x%08x\n", pg_flow_ctrl[11]);
++ min_q = (pg_flow_ctrl[10] & WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[10] & WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of LMAC1 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[11] & WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[11] & WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of LMAC1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ seq_printf(s, "\tReserved page counter of LMAC2 group: 0x%08x\n", pg_flow_ctrl[11]);
++ seq_printf(s, "\tLMAC2 group page status: 0x%08x\n", pg_flow_ctrl[12]);
++ min_q = (pg_flow_ctrl[12] & WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[12] & WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of LMAC2 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[13] & WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[13] & WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of LMAC2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++ seq_printf(s, "\tReserved page counter of LMAC3 group: 0x%08x\n", pg_flow_ctrl[16]);
++ seq_printf(s, "\tLMAC3 group page status: 0x%08x\n", pg_flow_ctrl[17]);
++ min_q = (pg_flow_ctrl[16] & WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[16] & WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of LMAC3 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[17] & WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[17] & WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of LMAC3 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++ seq_printf(s, "\tReserved page counter of PLE group: 0x%08x\n", pg_flow_ctrl[14]);
++ seq_printf(s, "\tPLE group page status: 0x%08x\n", pg_flow_ctrl[15]);
++ min_q = (pg_flow_ctrl[14] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[14] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of PLE group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[15] & WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[15] & WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of PLE group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++ seq_printf(s, "\tReserved page counter of PLE1 group: 0x%08x\n", pg_flow_ctrl[14]);
++ seq_printf(s, "\tPLE1 group page status: 0x%08x\n", pg_flow_ctrl[15]);
++ min_q = (pg_flow_ctrl[20] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[20] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of PLE1 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[21] & WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[21] & WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of PLE1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++ seq_printf(s, "\tReserved page counter of MDP group: 0x%08x\n", pg_flow_ctrl[18]);
++ seq_printf(s, "\tMDP group page status: 0x%08x\n", pg_flow_ctrl[19]);
++ min_q = (pg_flow_ctrl[18] & WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[18] & WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of MDP group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[19] & WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_MASK) >> WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[19] & WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_MASK) >> WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of MDP group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ seq_printf(s, "\tReserved page counter of MDP2 group: 0x%08x\n", pg_flow_ctrl[22]);
++ seq_printf(s, "\tMDP2 group page status: 0x%08x\n", pg_flow_ctrl[23]);
++ min_q = (pg_flow_ctrl[22] & WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[22] & WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of MDP2 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[23] & WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_MASK) >> WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[23] & WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_MASK) >> WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of MDP2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ if (mt7996_band_valid(dev, MT_BAND2)) {
++ seq_printf(s, "\tReserved page counter of MDP3 group: 0x%08x\n", pg_flow_ctrl[24]);
++ seq_printf(s, "\tMDP3 group page status: 0x%08x\n", pg_flow_ctrl[25]);
++ min_q = (pg_flow_ctrl[24] & WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_SHFT;
++ max_q = (pg_flow_ctrl[24] & WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_SHFT;
++ seq_printf(s, "\t\tThe max/min quota pages of MDP3 group=0x%03x/0x%03x\n", max_q, min_q);
++ rsv_pg = (pg_flow_ctrl[25] & WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_MASK) >> WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_SHFT;
++ used_pg = (pg_flow_ctrl[25] & WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_MASK) >> WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_SHFT;
++ seq_printf(s, "\t\tThe used/reserved pages of MDP3 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++ }
++ /* Queue Empty Status */
++ seq_printf(s, "PSE Queue Empty Status:\n");
++ seq_printf(s, "\tQUEUE_EMPTY: 0x%08x, QUEUE_EMPTY2: 0x%08x\n", pse_stat[0], pse_stat[1]);
++ seq_printf(s, "\t\tCPU Q0/1/2/3/4 empty=%d/%d/%d/%d/%d\n",
++ (pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_SHFT,
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_SHFT));
++ seq_printf(s, "\t\tHIF Q0/1/2/3/4/5/6/7/8/9/10/11/12/13 empty=%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d\n",
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_SHFT));
++ seq_printf(s, "\t\tLMAC TX Q empty=%d\n",
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_SHFT));
++ seq_printf(s, "\t\tMDP TX Q0/Q1/Q2/RX Q empty=%d/%d/%d/%d\n",
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_SHFT));
++ seq_printf(s, "\t\tSEC TX Q0/Q1/Q2/RX Q empty=%d/%d/%d/%d\n",
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_SHFT));
++ seq_printf(s, "\t\tSFD PARK Q empty=%d\n",
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_SHFT));
++ seq_printf(s, "\t\tMDP TXIOC Q0/Q1/Q2 empty=%d/%d/%d\n",
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_SHFT));
++ seq_printf(s, "\t\tMDP RXIOC Q0/Q1/Q2/Q3 empty=%d/%d/%d/%d\n",
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_SHFT),
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_SHFT),
++ ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_SHFT));
++ seq_printf(s, "\t\tRLS Q empty=%d\n",
++ ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_SHFT));
++ seq_printf(s, "Nonempty Q info:\n");
++
++ for (i = 0; i < 31; i++) {
++ if (((pse_stat[0] & (0x1 << i)) >> i) == 0) {
++ u32 hfid, tfid, pktcnt, fl_que_ctrl[3] = {0};
++
++ if (pse_queue_empty_info[i].QueueName != NULL) {
++ seq_printf(s, "\t%s: ", pse_queue_empty_info[i].QueueName);
++ fl_que_ctrl[0] |= WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK;
++ fl_que_ctrl[0] |= (pse_queue_empty_info[i].Portid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT);
++ fl_que_ctrl[0] |= (pse_queue_empty_info[i].Queueid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT);
++ } else
++ continue;
++
++ fl_que_ctrl[0] |= (0x1 << 31);
++ mt76_wr(dev, WF_PSE_TOP_FL_QUE_CTRL_0_ADDR, fl_que_ctrl[0]);
++ fl_que_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_2_ADDR);
++ fl_que_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_3_ADDR);
++ hfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT;
++ tfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT;
++ pktcnt = (fl_que_ctrl[2] & WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT;
++ seq_printf(s, "tail/head fid = 0x%03x/0x%03x, pkt cnt = 0x%03x\n",
++ tfid, hfid, pktcnt);
++ }
++ }
++
++ for (i = 0; i < 31; i++) {
++ if (((pse_stat[1] & (0x1 << i)) >> i) == 0) {
++ u32 hfid, tfid, pktcnt, fl_que_ctrl[3] = {0};
++
++ if (pse_queue_empty2_info[i].QueueName != NULL) {
++ seq_printf(s, "\t%s: ", pse_queue_empty2_info[i].QueueName);
++ fl_que_ctrl[0] |= WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK;
++ fl_que_ctrl[0] |= (pse_queue_empty2_info[i].Portid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT);
++ fl_que_ctrl[0] |= (pse_queue_empty2_info[i].Queueid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT);
++ } else
++ continue;
++
++ fl_que_ctrl[0] |= (0x1 << 31);
++ mt76_wr(dev, WF_PSE_TOP_FL_QUE_CTRL_0_ADDR, fl_que_ctrl[0]);
++ fl_que_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_2_ADDR);
++ fl_que_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_3_ADDR);
++ hfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT;
++ tfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT;
++ pktcnt = (fl_que_ctrl[2] & WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT;
++ seq_printf(s, "tail/head fid = 0x%03x/0x%03x, pkt cnt = 0x%03x\n",
++ tfid, hfid, pktcnt);
++ }
++ }
++
++ return 0;
++}
++
++/* PLE INFO */
++static char *sta_ctrl_reg[] = {"ENABLE", "DISABLE", "PAUSE", "TWT_PAUSE"};
++static struct bmac_queue_info ple_queue_empty_info[] = {
++ {"CPU Q0", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_0, 0},
++ {"CPU Q1", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_1, 0},
++ {"CPU Q2", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_2, 0},
++ {"CPU Q3", ENUM_UMAC_CPU_PORT_1, ENUM_UMAC_CTX_Q_3, 0},
++ {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x10, 0},
++ {"BMC Q0", ENUM_UMAC_LMAC_PORT_2, 0x11, 0},
++ {"BCN Q0", ENUM_UMAC_LMAC_PORT_2, 0x12, 0},
++ {"PSMP Q0", ENUM_UMAC_LMAC_PORT_2, 0x13, 0},
++ {"ALTX Q1", ENUM_UMAC_LMAC_PORT_2, 0x10, 1},
++ {"BMC Q1", ENUM_UMAC_LMAC_PORT_2, 0x11, 1},
++ {"BCN Q1", ENUM_UMAC_LMAC_PORT_2, 0x12, 1},
++ {"PSMP Q1", ENUM_UMAC_LMAC_PORT_2, 0x13, 1},
++ {"ALTX Q2", ENUM_UMAC_LMAC_PORT_2, 0x10, 2},
++ {"BMC Q2", ENUM_UMAC_LMAC_PORT_2, 0x11, 2},
++ {"BCN Q2", ENUM_UMAC_LMAC_PORT_2, 0x12, 2},
++ {"PSMP Q2", ENUM_UMAC_LMAC_PORT_2, 0x13, 2},
++ {"NAF Q", ENUM_UMAC_LMAC_PORT_2, 0x18, 0},
++ {"NBCN Q", ENUM_UMAC_LMAC_PORT_2, 0x19, 0},
++ {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, /* 18, 19 not defined */
++ {"FIXFID Q", ENUM_UMAC_LMAC_PORT_2, 0x1a, 0},
++ {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0},
++ {NULL, 0, 0, 0}, {NULL, 0, 0, 0},
++ {"RLS4 Q", ENUM_PLE_CTRL_PSE_PORT_3, 0x7c, 0},
++ {"RLS3 Q", ENUM_PLE_CTRL_PSE_PORT_3, 0x7d, 0},
++ {"RLS2 Q", ENUM_PLE_CTRL_PSE_PORT_3, 0x7e, 0},
++ {"RLS Q", ENUM_PLE_CTRL_PSE_PORT_3, 0x7f, 0}
++};
++
++static struct bmac_queue_info_t ple_txcmd_queue_empty_info[__MT_MAX_BAND][32] = {
++ {{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x40},
++ {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x41},
++ {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x42},
++ {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x43},
++ {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x44},
++ {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x45},
++ {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x46},
++ {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x47},
++ {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x48},
++ {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x49},
++ {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x4a},
++ {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x4b},
++ {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x4c},
++ {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x4d},
++ {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x4e},
++ {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x4f},
++ {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x70},
++ {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x71},
++ {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x72},
++ {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x73},
++ {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x74},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
++
++ {{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x50},
++ {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x51},
++ {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x52},
++ {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x53},
++ {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x54},
++ {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x55},
++ {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x56},
++ {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x57},
++ {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x58},
++ {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x59},
++ {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x5a},
++ {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x5b},
++ {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x5c},
++ {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x5d},
++ {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x5e},
++ {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x5f},
++ {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x75},
++ {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x76},
++ {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x77},
++ {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x78},
++ {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x79},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
++
++ {{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x60},
++ {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x61},
++ {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x62},
++ {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x63},
++ {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x64},
++ {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x65},
++ {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x66},
++ {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x67},
++ {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x68},
++ {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x69},
++ {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x6a},
++ {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x6b},
++ {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x6c},
++ {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x6d},
++ {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x6e},
++ {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x6f},
++ {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x7a},
++ {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x7b},
++ {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x7c},
++ {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x7d},
++ {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x7e},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++ {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}}
++};
++
++static size_t
++ple_cr_num_of_ac(struct mt76_dev *dev)
++{
++ switch (mt76_chip(dev)) {
++ case 0x7990:
++ return CR_NUM_OF_AC_MT7996;
++ case 0x7992:
++ default:
++ return CR_NUM_OF_AC_MT7992;
++ }
++}
++
++static void
++mt7996_show_ple_pg_info(struct mt7996_dev *dev, struct seq_file *s)
++{
++ u32 val[2];
++
++ seq_printf(s, "PLE Configuration Info:\n");
++
++ val[0] = mt76_rr(dev, WF_PLE_TOP_PBUF_CTRL_ADDR);
++ seq_printf(s, "\tPacket Buffer Control: 0x%08x\n", val[0]);
++ seq_printf(s, "\t\tPage size: %u bytes\n",
++ u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK) ? 128 : 64);
++ seq_printf(s, "\t\tPacket buffer offset: %u (unit: 2KB)\n",
++ u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK));
++ seq_printf(s, "\t\tTotal number of pages: %u pages\n",
++ u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK));
++
++ seq_printf(s, "PLE Page Flow Control:\n");
++
++ val[0] = mt76_rr(dev, WF_PLE_TOP_FREEPG_CNT_ADDR);
++ val[1] = mt76_rr(dev, WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR);
++ seq_printf(s, "\tFree Page Counter: 0x%08x\n", val[0]);
++ seq_printf(s, "\tFree Page Head and Tail: 0x%08x\n", val[1]);
++ seq_printf(s, "\t\tNumber of free pages: 0x%04x\n",
++ u32_get_bits(val[0], WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_MASK));
++ seq_printf(s, "\t\tNumber of unassigned pages: 0x%04x\n",
++ u32_get_bits(val[0], WF_PLE_TOP_FREEPG_CNT_FFA_CNT_MASK));
++ seq_printf(s, "\t\tFID of tail/head free page: 0x%04x/0x%04x\n",
++ u32_get_bits(val[1], WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK),
++ u32_get_bits(val[1], WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK));
++
++ val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_GROUP_ADDR);
++ val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_PG_INFO_ADDR);
++ seq_printf(s, "\tReserved Page Counter of HIF Group: 0x%08x\n", val[0]);
++ seq_printf(s, "\tHIF Group Page Status: 0x%08x\n", val[1]);
++ seq_printf(s, "\t\tMax/min page quota for HIF group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_MASK),
++ u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_MASK));
++ seq_printf(s, "\t\tUsed/free page count for HIF group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[1], WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_MASK),
++ u32_get_bits(val[1], WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_MASK));
++
++ val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR);
++ val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR);
++ seq_printf(s, "\tReserved Page Counter of HIF WMCPU TXD Group: 0x%08x\n", val[0]);
++ seq_printf(s, "\tHIF WMCPU TXD Group Page Status: 0x%08x\n", val[1]);
++ seq_printf(s, "\t\tMax/min page quota for HIF WMCPU TXD group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_MASK),
++ u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_MASK));
++ seq_printf(s, "\t\tUsed/free page count for HIF WMCPU TXD group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[1], WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_MASK),
++ u32_get_bits(val[1], WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_MASK));
++
++ val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR);
++ val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR);
++ seq_printf(s, "\tReserved Page Counter of HIF TXCMD Group: 0x%08x\n", val[0]);
++ seq_printf(s, "\tHIF TXCMD Group Page Status: 0x%08x\n", val[1]);
++ seq_printf(s, "\t\tMax/min page quota for HIF TXCMD group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_MASK),
++ u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_MASK));
++ seq_printf(s, "\t\tUsed/free page count for HIF TXCMD group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[1], WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_MASK),
++ u32_get_bits(val[1], WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_MASK));
++
++ val[0] = mt76_rr(dev, WF_PLE_TOP_PG_CPU_GROUP_ADDR);
++ val[1] = mt76_rr(dev, WF_PLE_TOP_CPU_PG_INFO_ADDR);
++ seq_printf(s, "\tReserved Page Counter of CPU Group: 0x%08x\n", val[0]);
++ seq_printf(s, "\tCPU Group Page Status: 0x%08x\n", val[1]);
++ seq_printf(s, "\t\tMax/min page quota for CPU group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[0], WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK),
++ u32_get_bits(val[0], WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK));
++ seq_printf(s, "\t\tUsed/free page count for CPU group: 0x%04x/0x%04x\n",
++ u32_get_bits(val[1], WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK),
++ u32_get_bits(val[1], WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK));
++}
++
++static void
++mt7996_get_ple_acq_stat(struct mt7996_dev *dev, unsigned long *ple_stat)
++{
++ u32 i, addr;
++ size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++
++ ple_stat[0] = mt76_rr(dev, WF_PLE_TOP_QUEUE_EMPTY_ADDR);
++
++ /* Legacy */
++ addr = WF_PLE_TOP_AC0_QUEUE_EMPTY0_ADDR;
++ for (i = 1; i <= cr_num_of_ac; i++, addr += 4) {
++ if (i == cr_num_of_ac && is_mt7992(&dev->mt76))
++ ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC0_QUEUE_EMPTY_EXT0_ADDR);
++ else
++ ple_stat[i] = mt76_rr(dev, addr);
++ }
++
++ addr = WF_PLE_TOP_AC1_QUEUE_EMPTY0_ADDR;
++ for (; i <= cr_num_of_ac * 2; i++, addr += 4) {
++ if (i == cr_num_of_ac * 2 && is_mt7992(&dev->mt76))
++ ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC1_QUEUE_EMPTY_EXT0_ADDR);
++ else
++ ple_stat[i] = mt76_rr(dev, addr);
++ }
++
++ addr = WF_PLE_TOP_AC2_QUEUE_EMPTY0_ADDR;
++ for (; i <= cr_num_of_ac * 3; i++, addr += 4) {
++ if (i == cr_num_of_ac * 3 && is_mt7992(&dev->mt76))
++ ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC2_QUEUE_EMPTY_EXT0_ADDR);
++ else
++ ple_stat[i] = mt76_rr(dev, addr);
++ }
++
++ addr = WF_PLE_TOP_AC3_QUEUE_EMPTY0_ADDR;
++ for (; i <= cr_num_of_ac * 4; i++, addr += 4) {
++ if (i == cr_num_of_ac * 4 && is_mt7992(&dev->mt76))
++ ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC3_QUEUE_EMPTY_EXT0_ADDR);
++ else
++ ple_stat[i] = mt76_rr(dev, addr);
++ }
++}
++
++static void
++mt7996_get_sta_pause(struct mt7996_dev *dev, u8 band, u32 *sta_pause, u32 *twt_pause)
++{
++ u32 i, addr;
++ size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++
++ /* switch to target band */
++ mt76_wr(dev, WF_DRR_TOP_SBRR_ADDR, u32_encode_bits(band, WF_DRR_TOP_SBRR_TARGET_BAND_MASK));
++
++ /* Legacy */
++ addr = WF_DRR_TOP_AC0_STATION_PAUSE00_ADDR;
++ for (i = 0; i < cr_num_of_ac; i++, addr += 4) {
++ if (i == cr_num_of_ac - 1 && is_mt7992(&dev->mt76))
++ sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC0_STATION_PAUSE_EXT_00_ADDR);
++ else
++ sta_pause[i] = mt76_rr(dev, addr);
++ }
++
++ addr = WF_DRR_TOP_AC1_STATION_PAUSE00_ADDR;
++ for (; i < cr_num_of_ac * 2; i++, addr += 4) {
++ if (i == cr_num_of_ac * 2 - 1 && is_mt7992(&dev->mt76))
++ sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC1_STATION_PAUSE_EXT_00_ADDR);
++ else
++ sta_pause[i] = mt76_rr(dev, addr);
++ }
++
++ addr = WF_DRR_TOP_AC2_STATION_PAUSE00_ADDR;
++ for (; i < cr_num_of_ac * 3; i++, addr += 4) {
++ if (i == cr_num_of_ac * 3 - 1 && is_mt7992(&dev->mt76))
++ sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC2_STATION_PAUSE_EXT_00_ADDR);
++ else
++ sta_pause[i] = mt76_rr(dev, addr);
++ }
++
++ addr = WF_DRR_TOP_AC3_STATION_PAUSE00_ADDR;
++ for (; i < cr_num_of_ac * 4; i++, addr += 4) {
++ if (i == cr_num_of_ac * 4 - 1 && is_mt7992(&dev->mt76))
++ sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC3_STATION_PAUSE_EXT_00_ADDR);
++ else
++ sta_pause[i] = mt76_rr(dev, addr);
++ }
++
++ /* TWT */
++ addr = WF_DRR_TOP_TWT_STA_MAP00_ADDR;
++ for (i = 0; i < cr_num_of_ac; i++, addr += 4) {
++ if (i == cr_num_of_ac - 1 && is_mt7992(&dev->mt76))
++ twt_pause[i] = mt76_rr(dev, WF_DRR_TOP_TWT_STA_MAP_EXT_00_ADDR);
++ else
++ twt_pause[i] = mt76_rr(dev, addr);
++ }
++}
++
++static void
++mt7996_get_ple_queue_info(struct mt7996_dev *dev, u32 pid, u32 qid, u32 tgid,
++ u16 wlan_idx, u16 *hfid, u16 *tfid, u16 *pktcnt)
++{
++ u32 val = WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK |
++ u32_encode_bits(pid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_MASK) |
++ u32_encode_bits(tgid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_MASK) |
++ u32_encode_bits(qid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK) |
++ u32_encode_bits(wlan_idx, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_MASK);
++ mt76_wr(dev, WF_PLE_TOP_FL_QUE_CTRL_0_ADDR, val);
++
++ val = mt76_rr(dev, WF_PLE_TOP_FL_QUE_CTRL_2_ADDR);
++ *hfid = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK);
++ *tfid = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK);
++
++ val = mt76_rr(dev, WF_PLE_TOP_FL_QUE_CTRL_3_ADDR);
++ *pktcnt = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK);
++}
++
++static void
++mt7996_show_sta_acq_info(struct seq_file *s, unsigned long *ple_stat,
++ u32 *sta_pause, u32 *twt_sta_pause)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++ size_t cr_num_of_all_ac = cr_num_of_ac * IEEE80211_NUM_ACS;
++ int i, j;
++
++ for (j = 0; j < cr_num_of_all_ac; j++) { /* show AC Q info */
++ for (i = 0; i < 32; i++) {
++ if (!test_bit(i, &ple_stat[j + 1])) {
++ u16 hfid, tfid, pktcnt, wlan_idx = i + (j % cr_num_of_ac) * 32;
++ u8 wmmidx, ctrl = 0, acq_idx = j / cr_num_of_ac;
++ struct mt7996_link_sta *mlink;
++ struct mt76_wcid *wcid;
++ size_t idx;
++
++ if (wlan_idx >= MT76_N_WCIDS) {
++ seq_printf(s, "Error: WCID %hu exceeded threshold.\n", wlan_idx);
++ continue;
++ }
++ wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
++ if (!wcid) {
++ seq_printf(s, "Error: STA %hu does not exist.\n", wlan_idx);
++ continue;
++ }
++ mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++ wmmidx = mlink->sta->vif->deflink.mt76.wmm_idx;
++
++ seq_printf(s, "\tSTA%hu AC%hhu: ", wlan_idx, acq_idx);
++ mt7996_get_ple_queue_info(dev, ENUM_UMAC_LMAC_PORT_2, acq_idx,
++ 0, wlan_idx, &hfid, &tfid, &pktcnt);
++ seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x",
++ tfid, hfid, pktcnt);
++
++ idx = wcid->phy_idx * cr_num_of_all_ac + j;
++ if (sta_pause[idx] & BIT(i))
++ ctrl = 2;
++
++ idx = wcid->phy_idx * cr_num_of_ac + j % cr_num_of_ac;
++ if (twt_sta_pause[idx] & BIT(i))
++ ctrl = 3;
++
++ seq_printf(s, ", ctrl = %s (wmmidx=%hhu, band=%hhu)\n",
++ sta_ctrl_reg[ctrl], wmmidx, wcid->phy_idx);
++ }
++ }
++ }
++}
++
++static void
++mt7996_show_txcmdq_info(struct seq_file *s)
++{
++ const u32 txcmd_queue_empty_addr[__MT_MAX_BAND][2] = {
++ [MT_BAND0] = {WF_PLE_TOP_TXCMD_QUEUE_EMPTY_ADDR,
++ WF_PLE_TOP_NATIVE_TXCMD_QUEUE_EMPTY_ADDR},
++ [MT_BAND1] = {WF_PLE_TOP_BN1_TXCMD_QUEUE_EMPTY_ADDR,
++ WF_PLE_TOP_BN1_NATIVE_TXCMD_QUEUE_EMPTY_ADDR},
++ [MT_BAND2] = {WF_PLE_TOP_BN2_TXCMD_QUEUE_EMPTY_ADDR,
++ WF_PLE_TOP_BN2_NATIVE_TXCMD_QUEUE_EMPTY_ADDR}
++ };
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ u8 band;
++
++ for (band = MT_BAND0; band < __MT_MAX_BAND; ++band) {
++ unsigned long txcmdq_stat, native_txcmdq_stat;
++ int i;
++
++ if (!dev->mt76.phys[band])
++ continue;
++
++ txcmdq_stat = mt76_rr(dev, txcmd_queue_empty_addr[band][0]);
++ native_txcmdq_stat = mt76_rr(dev, txcmd_queue_empty_addr[band][1]);
++
++ seq_printf(s, "Band%hhu Non-native/native TXCMD Queue Empty: 0x%08lx/0x%08lx\n",
++ band, txcmdq_stat, native_txcmdq_stat);
++
++ for (i = 0; i < 32 ; i++) {
++ if (!test_bit(i, &native_txcmdq_stat)) {
++ struct bmac_queue_info_t *queue = &ple_txcmd_queue_empty_info[band][i];
++ u16 hfid, tfid, pktcnt;
++
++ if (!queue->QueueName)
++ continue;
++
++ seq_printf(s, "\t%s: ", queue->QueueName);
++ mt7996_get_ple_queue_info(dev, queue->Portid, queue->Queueid,
++ 0, 0, &hfid, &tfid, &pktcnt);
++ seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x\n",
++ tfid, hfid, pktcnt);
++ }
++ }
++ }
++}
++
++static int
++mt7996_pleinfo_read(struct seq_file *s, void *data)
++{
++ struct mt7996_dev *dev = dev_get_drvdata(s->private);
++ size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++ size_t cr_num_of_all_ac = cr_num_of_ac * IEEE80211_NUM_ACS;
++ u32 *sta_pause, *twt_sta_pause;
++ unsigned long *ple_stat;
++ int i, j, ret = 0;
++
++ ple_stat = kzalloc((cr_num_of_all_ac + 1) * sizeof(unsigned long), GFP_KERNEL);
++ if (!ple_stat)
++ return -ENOMEM;
++
++ sta_pause = kzalloc(__MT_MAX_BAND * cr_num_of_all_ac * sizeof(u32), GFP_KERNEL);
++ if (!sta_pause) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ twt_sta_pause = kzalloc(__MT_MAX_BAND * cr_num_of_ac * sizeof(u32), GFP_KERNEL);
++ if (!twt_sta_pause) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ mt7996_show_ple_pg_info(dev, s);
++ mt7996_get_ple_acq_stat(dev, ple_stat);
++
++ for (i = MT_BAND0; i < __MT_MAX_BAND; i++) {
++ if (dev->mt76.phys[i])
++ mt7996_get_sta_pause(dev, i,
++ sta_pause + i * cr_num_of_all_ac,
++ twt_sta_pause + i * cr_num_of_ac);
++ }
++
++ if ((ple_stat[0] & WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_MASK) == 0) {
++ for (j = 0; j < cr_num_of_all_ac; j++) {
++ if (j % cr_num_of_ac == 0)
++ seq_printf(s, "\n\tSTA in nonempty AC%ld TXD queue: ", j / cr_num_of_ac);
++
++ for (i = 0; i < 32; i++) {
++ if (!test_bit(i, &ple_stat[j + 1]))
++ seq_printf(s, "%lu ", i + (j % cr_num_of_ac) * 32);
++ }
++ }
++ seq_printf(s, "\n");
++ }
++
++ seq_printf(s, "Nonempty TXD Queue Info:\n");
++
++ for (i = 0; i < 32; i++) {
++ if (!test_bit(i, &ple_stat[0])) {
++ struct bmac_queue_info *queue = &ple_queue_empty_info[i];
++ u16 hfid, tfid, pktcnt;
++
++ if (!queue->QueueName)
++ continue;
++
++ seq_printf(s, "\t%s: ", queue->QueueName);
++ mt7996_get_ple_queue_info(dev, queue->Portid, queue->Queueid,
++ queue->tgid, 0, &hfid, &tfid, &pktcnt);
++ seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x\n",
++ tfid, hfid, pktcnt);
++ }
++ }
++
++ mt7996_show_sta_acq_info(s, ple_stat, sta_pause, twt_sta_pause);
++ mt7996_show_txcmdq_info(s);
++
++ kfree(twt_sta_pause);
++out:
++ kfree(sta_pause);
++ kfree(ple_stat);
++ return ret;
++}
++
++/* DRR */
++static int
++mt7996_drr_info(struct seq_file *s, void *data)
++{
++ /* TODO: Wait MIB counter API implement complete */
++ return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ struct mt7996_dev *dev = phy->dev;
+@@ -3337,6 +4403,25 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+
+ debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
+ debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
++ debugfs_create_file("reset_counter", 0200, dir, dev, &fops_reset_counter);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "per", dir, mt7996_per_read);
++
++ debugfs_create_devm_seqfile(dev->mt76.dev, "drr_info", dir,
++ mt7996_drr_info);
++
++ debugfs_create_u32("token_idx", 0600, dir, &dev->dbg.token_idx);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "rx_token", dir,
++ mt7996_rx_token_read);
++
++ debugfs_create_devm_seqfile(dev->mt76.dev, "ple_info", dir,
++ mt7996_pleinfo_read);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "pse_info", dir,
++ mt7996_pseinfo_read);
++ /* amsdu */
++ debugfs_create_file("amsdu_algo", 0600, dir, dev, &fops_amsdu_algo);
++ debugfs_create_file("amsdu_para", 0600, dir, dev, &fops_amsdu_para);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "amsdu_info", dir,
++ mt7996_amsdu_info_read);
+
+ return 0;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0999-mtk-wifi-mt76-mt7996-for-build-pass.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0999-mtk-wifi-mt76-mt7996-for-build-pass.patch
deleted file mode 100644
index 9c72243..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0999-mtk-wifi-mt76-mt7996-for-build-pass.patch
+++ /dev/null
@@ -1,158 +0,0 @@
-From 1df5a85005f59c81ddc50c6378562e4c9bc31056 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 3 Nov 2022 00:27:17 +0800
-Subject: [PATCH 0999/1044] mtk: wifi: mt76: mt7996: for build pass
-
----
- debugfs.c | 3 +++
- dma.c | 2 +-
- eeprom.c | 8 +++++++-
- mcu.c | 1 +
- mt7615/mcu.c | 1 +
- mt76_connac_mcu.c | 1 +
- mt7915/mcu.c | 1 +
- mt7996/dma.c | 4 ++--
- mt7996/eeprom.c | 1 +
- mt7996/mcu.c | 1 +
- 10 files changed, 19 insertions(+), 4 deletions(-)
-
-diff --git a/debugfs.c b/debugfs.c
-index c4649ba0..ac5207e5 100644
---- a/debugfs.c
-+++ b/debugfs.c
-@@ -33,8 +33,11 @@ mt76_napi_threaded_set(void *data, u64 val)
- if (!mt76_is_mmio(dev))
- return -EOPNOTSUPP;
-
-+#if 0
-+ /* need to backport patch from networking stack */
- if (dev->napi_dev.threaded != val)
- return dev_set_threaded(&dev->napi_dev, val);
-+#endif
-
- return 0;
- }
-diff --git a/dma.c b/dma.c
-index f4f88c44..56044639 100644
---- a/dma.c
-+++ b/dma.c
-@@ -883,7 +883,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- !(dev->drv->rx_check(dev, data, len)))
- goto free_frag;
-
-- skb = napi_build_skb(data, q->buf_size);
-+ skb = build_skb(data, q->buf_size);
- if (!skb)
- goto free_frag;
-
-diff --git a/eeprom.c b/eeprom.c
-index 0bc66cc1..7d5cf28f 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -163,9 +163,15 @@ void
- mt76_eeprom_override(struct mt76_phy *phy)
- {
- struct mt76_dev *dev = phy->dev;
-+#ifdef CONFIG_OF
- struct device_node *np = dev->dev->of_node;
-+ const u8 *mac = NULL;
-
-- of_get_mac_address(np, phy->macaddr);
-+ if (np)
-+ mac = of_get_mac_address(np);
-+ if (!IS_ERR_OR_NULL(mac))
-+ ether_addr_copy(phy->macaddr, mac);
-+#endif
-
- if (!is_valid_ether_addr(phy->macaddr)) {
- eth_random_addr(phy->macaddr);
-diff --git a/mcu.c b/mcu.c
-index a8cafa39..fa4b0544 100644
---- a/mcu.c
-+++ b/mcu.c
-@@ -4,6 +4,7 @@
- */
-
- #include "mt76.h"
-+#include <linux/moduleparam.h>
-
- struct sk_buff *
- __mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data,
-diff --git a/mt7615/mcu.c b/mt7615/mcu.c
-index ae34d019..c9444c6d 100644
---- a/mt7615/mcu.c
-+++ b/mt7615/mcu.c
-@@ -10,6 +10,7 @@
- #include "mcu.h"
- #include "mac.h"
- #include "eeprom.h"
-+#include <linux/moduleparam.h>
-
- static bool prefer_offload_fw = true;
- module_param(prefer_offload_fw, bool, 0644);
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index fec158ec..42f12672 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -4,6 +4,7 @@
- #include <linux/firmware.h>
- #include "mt76_connac2_mac.h"
- #include "mt76_connac_mcu.h"
-+#include <linux/module.h>
-
- int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option)
- {
-diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index fe54a2f4..35d57989 100644
---- a/mt7915/mcu.c
-+++ b/mt7915/mcu.c
-@@ -6,6 +6,7 @@
- #include "mcu.h"
- #include "mac.h"
- #include "eeprom.h"
-+#include <linux/moduleparam.h>
-
- #define fw_name(_dev, name, ...) ({ \
- char *_fw; \
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 73e633d0..759a58e8 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -641,8 +641,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- if (ret < 0)
- return ret;
-
-- netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
-- mt7996_poll_tx);
-+ netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
-+ mt7996_poll_tx, NAPI_POLL_WEIGHT);
- napi_enable(&dev->mt76.tx_napi);
-
- mt7996_dma_enable(dev, false);
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 3260d1fe..121a3c95 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -138,6 +138,7 @@ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
- if (ret)
- return ret;
-
-+ cap = 0x4b249248; /* internal hardcode */
- if (cap) {
- dev->has_eht = !(cap & MODE_HE_ONLY);
- dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index b9939e0c..e92a3e53 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5,6 +5,7 @@
-
- #include <linux/firmware.h>
- #include <linux/fs.h>
-+#include <linux/moduleparam.h>
- #include "mt7996.h"
- #include "mcu.h"
- #include "mac.h"
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1000-mtk-wifi-mt76-mt7996-add-debug-tool.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1000-mtk-wifi-mt76-mt7996-add-debug-tool.patch
deleted file mode 100644
index bfbb09d..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1000-mtk-wifi-mt76-mt7996-add-debug-tool.patch
+++ /dev/null
@@ -1,5237 +0,0 @@
-From 57b3bc3bdbb3fb05897ff92ff59e970598fbf223 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 24 Mar 2023 14:02:32 +0800
-Subject: [PATCH 1000/1044] mtk: wifi: mt76: mt7996: add debug tool
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add PSM bit in sta_info
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-
-Remove the duplicate function in mtk_debugfs.c & mtk_debug_i.c
-Only enable mt7996_mcu_fw_log_2_host function in mcu.c
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
-
-Support more ids category NDPA/NDP TXD/FBK and debug log recommended by
-CTD members.
-
-This commit equals to run the follwoing commands on Logan driver:
-command:
-1. iwpriv ra0 set fw_dbg=1:84
-2. iwpriv ra0 set fw_dbg=2:84
-3. iwpriv ra0 set fw_dbg=1:101
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add wtbl_info support for mt7992
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add mt7992 & mt7996 CR debug offset revision
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7992: refactor code for FW log
-
-Refactor code for FW log.
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt76.h | 2 +
- mt7996/Makefile | 4 +
- mt7996/coredump.c | 10 +-
- mt7996/coredump.h | 7 +
- mt7996/debugfs.c | 64 +-
- mt7996/mac.c | 3 +
- mt7996/mt7996.h | 11 +
- mt7996/mtk_debug.h | 2286 ++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_debugfs.c | 2484 ++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.c | 18 +
- mt7996/mtk_mcu.h | 16 +
- tools/fwlog.c | 25 +-
- 12 files changed, 4905 insertions(+), 25 deletions(-)
- create mode 100644 mt7996/mtk_debug.h
- create mode 100644 mt7996/mtk_debugfs.c
- create mode 100644 mt7996/mtk_mcu.c
- create mode 100644 mt7996/mtk_mcu.h
-
-diff --git a/mt76.h b/mt76.h
-index 294e379a..8cf21f98 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -394,6 +394,8 @@ struct mt76_txwi_cache {
- struct sk_buff *skb;
- void *ptr;
- };
-+
-+ unsigned long jiffies;
- };
-
- struct mt76_rx_tid {
-diff --git a/mt7996/Makefile b/mt7996/Makefile
-index 07c8b555..a056b40e 100644
---- a/mt7996/Makefile
-+++ b/mt7996/Makefile
-@@ -1,4 +1,6 @@
- # SPDX-License-Identifier: ISC
-+EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
-+EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
-
- obj-$(CONFIG_MT7996E) += mt7996e.o
-
-@@ -6,3 +8,5 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
- debugfs.o mmio.o
-
- mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
-+
-+mt7996e-y += mtk_debugfs.o mtk_mcu.o
-diff --git a/mt7996/coredump.c b/mt7996/coredump.c
-index 60b88085..a7f91b56 100644
---- a/mt7996/coredump.c
-+++ b/mt7996/coredump.c
-@@ -195,7 +195,7 @@ mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump
- }
- }
-
--static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
-+struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
- {
- struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
- struct mt7996_coredump *dump;
-@@ -206,7 +206,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
-
- len = hdr_len;
-
-- if (coredump_memdump && crash_data->memdump_buf_len)
-+ if (full_dump && coredump_memdump && crash_data->memdump_buf_len)
- len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
-
- sofar += hdr_len;
-@@ -248,6 +248,9 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
- mt7996_coredump_fw_state(dev, type, dump, &exception);
- mt7996_coredump_fw_stack(dev, type, dump, exception);
-
-+ if (!full_dump)
-+ goto skip_dump_mem;
-+
- /* gather memory content */
- dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
- dump_mem->len = crash_data->memdump_buf_len;
-@@ -255,6 +258,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
- memcpy(dump_mem->data, crash_data->memdump_buf,
- crash_data->memdump_buf_len);
-
-+skip_dump_mem:
- mutex_unlock(&dev->dump_mutex);
-
- return dump;
-@@ -264,7 +268,7 @@ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- {
- struct mt7996_coredump *dump;
-
-- dump = mt7996_coredump_build(dev, type);
-+ dump = mt7996_coredump_build(dev, type, true);
- if (!dump) {
- dev_warn(dev->mt76.dev, "no crash dump data found\n");
- return -ENODATA;
-diff --git a/mt7996/coredump.h b/mt7996/coredump.h
-index 01ed3731..93cd84a0 100644
---- a/mt7996/coredump.h
-+++ b/mt7996/coredump.h
-@@ -75,6 +75,7 @@ struct mt7996_mem_region {
- const struct mt7996_mem_region *
- mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
- struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
-+struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump);
- int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
- int mt7996_coredump_register(struct mt7996_dev *dev);
- void mt7996_coredump_unregister(struct mt7996_dev *dev);
-@@ -92,6 +93,12 @@ static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- return 0;
- }
-
-+static inline struct
-+mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
-+{
-+ return NULL;
-+}
-+
- static inline struct
- mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
- {
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 9bd95358..1637b39d 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -290,11 +290,39 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
- DEBUG_SPL,
- DEBUG_RPT_RX,
- DEBUG_RPT_RA = 68,
-- } debug;
-+ DEBUG_IDS_SND = 84,
-+ DEBUG_IDS_PP = 93,
-+ DEBUG_IDS_RA = 94,
-+ DEBUG_IDS_BF = 95,
-+ DEBUG_IDS_SR = 96,
-+ DEBUG_IDS_RU = 97,
-+ DEBUG_IDS_MUMIMO = 98,
-+ DEBUG_IDS_ERR_LOG = 101,
-+ };
-+ u8 debug_category[] = {
-+ DEBUG_TXCMD,
-+ DEBUG_CMD_RPT_TX,
-+ DEBUG_CMD_RPT_TRIG,
-+ DEBUG_SPL,
-+ DEBUG_RPT_RX,
-+ DEBUG_RPT_RA,
-+ DEBUG_IDS_SND,
-+ DEBUG_IDS_PP,
-+ DEBUG_IDS_RA,
-+ DEBUG_IDS_BF,
-+ DEBUG_IDS_SR,
-+ DEBUG_IDS_RU,
-+ DEBUG_IDS_MUMIMO,
-+ DEBUG_IDS_ERR_LOG,
-+ };
- bool tx, rx, en;
- int ret;
-+ u8 i;
-
- dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0;
-+#ifdef CONFIG_MTK_DEBUG
-+ dev->fw_debug_wm = val;
-+#endif
-
- if (dev->fw_debug_bin)
- val = MCU_FW_LOG_RELAY;
-@@ -309,18 +337,21 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
- if (ret)
- return ret;
-
-- for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) {
-- if (debug == 67)
-- continue;
--
-- if (debug == DEBUG_RPT_RX)
-+ for (i = 0; i < ARRAY_SIZE(debug_category); i++) {
-+ if (debug_category[i] == DEBUG_RPT_RX)
- val = en && rx;
- else
- val = en && tx;
-
-- ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val);
-+ ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], val);
- if (ret)
- return ret;
-+
-+ if (debug_category[i] == DEBUG_IDS_SND && en) {
-+ ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], 2);
-+ if (ret)
-+ return ret;
-+ }
- }
-
- return 0;
-@@ -401,11 +432,12 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
- };
- struct mt7996_dev *dev = data;
-
-- if (!dev->relay_fwlog)
-+ if (!dev->relay_fwlog) {
- dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir,
- 1500, 512, &relay_cb, NULL);
-- if (!dev->relay_fwlog)
-- return -ENOMEM;
-+ if (!dev->relay_fwlog)
-+ return -ENOMEM;
-+ }
-
- dev->fw_debug_bin = val;
-
-@@ -819,6 +851,11 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- if (phy == &dev->phy)
- dev->debugfs_dir = dir;
-
-+#ifdef CONFIG_MTK_DEBUG
-+ debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
-+ mt7996_mtk_init_debugfs(phy, dir);
-+#endif
-+
- return 0;
- }
-
-@@ -830,7 +867,11 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
- unsigned long flags;
- void *dest;
-
-+ if (!dev->relay_fwlog)
-+ return;
-+
- spin_lock_irqsave(&lock, flags);
-+
- dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
- if (dest) {
- *(u32 *)dest = hdrlen + len;
-@@ -863,9 +904,6 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
- .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR),
- };
-
-- if (!dev->relay_fwlog)
-- return;
--
- hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++);
- hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0)));
- hdr.len = *(__le16 *)data;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index d88bbfb2..1f53d230 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -936,6 +936,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- id = mt76_token_consume(mdev, &t);
- if (id < 0)
- return id;
-+#ifdef CONFIG_MTK_DEBUG
-+ t->jiffies = jiffies;
-+#endif
-
- pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
- memset(txwi_ptr, 0, MT_TXD_SIZE);
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 4176e51a..34159f97 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -370,6 +370,17 @@ struct mt7996_dev {
- spinlock_t reg_lock;
-
- u8 wtbl_size_group;
-+
-+#ifdef CONFIG_MTK_DEBUG
-+ u16 wlan_idx;
-+ struct {
-+ u8 sku_disable;
-+ u32 fw_dbg_module;
-+ u8 fw_dbg_lv;
-+ u32 bcn_total_cnt[__MT_MAX_BAND];
-+ } dbg;
-+ const struct mt7996_dbg_reg_desc *dbg_reg;
-+#endif
- };
-
- enum {
-diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
-new file mode 100644
-index 00000000..27d8f1cb
---- /dev/null
-+++ b/mt7996/mtk_debug.h
-@@ -0,0 +1,2286 @@
-+#ifndef __MTK_DEBUG_H
-+#define __MTK_DEBUG_H
-+
-+#ifdef CONFIG_MTK_DEBUG
-+#define NO_SHIFT_DEFINE 0xFFFFFFFF
-+#define BITS(m, n) (~(BIT(m)-1) & ((BIT(n) - 1) | BIT(n)))
-+
-+#define GET_FIELD(_field, _reg) \
-+ ({ \
-+ (((_reg) & (_field##_MASK)) >> (_field##_SHIFT)); \
-+ })
-+
-+#define __DBG_OFFS(id) (dev->dbg_reg->offs_rev[(id)])
-+
-+enum dbg_offs_rev {
-+ AGG_AALCR2,
-+ AGG_AALCR3,
-+ AGG_AALCR4,
-+ AGG_AALCR5,
-+ AGG_AALCR6,
-+ AGG_AALCR7,
-+ MIB_TDRCR0,
-+ MIB_TDRCR1,
-+ MIB_TDRCR2,
-+ MIB_TDRCR3,
-+ MIB_TDRCR4,
-+ MIB_RSCR26,
-+ MIB_TSCR18,
-+ MIB_TRDR0,
-+ MIB_TRDR2,
-+ MIB_TRDR3,
-+ MIB_TRDR4,
-+ MIB_TRDR5,
-+ MIB_TRDR6,
-+ MIB_TRDR7,
-+ MIB_TRDR8,
-+ MIB_TRDR9,
-+ MIB_TRDR10,
-+ MIB_TRDR11,
-+ MIB_TRDR12,
-+ MIB_TRDR13,
-+ MIB_TRDR14,
-+ MIB_TRDR15,
-+ MIB_MSR0,
-+ MIB_MSR1,
-+ MIB_MSR2,
-+ MIB_MCTR5,
-+ MIB_MCTR6,
-+ __MT_DBG_OFFS_REV_MAX,
-+};
-+
-+static const u32 mt7996_dbg_offs[] = {
-+ [AGG_AALCR2] = 0x128,
-+ [AGG_AALCR3] = 0x12c,
-+ [AGG_AALCR4] = 0x130,
-+ [AGG_AALCR5] = 0x134,
-+ [AGG_AALCR6] = 0x138,
-+ [AGG_AALCR7] = 0x13c,
-+ [MIB_TDRCR0] = 0x728,
-+ [MIB_TDRCR1] = 0x72c,
-+ [MIB_TDRCR2] = 0x730,
-+ [MIB_TDRCR3] = 0x734,
-+ [MIB_TDRCR4] = 0x738,
-+ [MIB_RSCR26] = 0x950,
-+ [MIB_TSCR18] = 0xa1c,
-+ [MIB_TRDR0] = 0xa24,
-+ [MIB_TRDR2] = 0xa2c,
-+ [MIB_TRDR3] = 0xa30,
-+ [MIB_TRDR4] = 0xa34,
-+ [MIB_TRDR5] = 0xa38,
-+ [MIB_TRDR6] = 0xa3c,
-+ [MIB_TRDR7] = 0xa40,
-+ [MIB_TRDR8] = 0xa44,
-+ [MIB_TRDR9] = 0xa48,
-+ [MIB_TRDR10] = 0xa4c,
-+ [MIB_TRDR11] = 0xa50,
-+ [MIB_TRDR12] = 0xa54,
-+ [MIB_TRDR13] = 0xa58,
-+ [MIB_TRDR14] = 0xa5c,
-+ [MIB_TRDR15] = 0xa60,
-+ [MIB_MSR0] = 0xa64,
-+ [MIB_MSR1] = 0xa68,
-+ [MIB_MSR2] = 0xa6c,
-+ [MIB_MCTR5] = 0xa70,
-+ [MIB_MCTR6] = 0xa74,
-+};
-+
-+static const u32 mt7992_dbg_offs[] = {
-+ [AGG_AALCR2] = 0x12c,
-+ [AGG_AALCR3] = 0x130,
-+ [AGG_AALCR4] = 0x134,
-+ [AGG_AALCR5] = 0x138,
-+ [AGG_AALCR6] = 0x13c,
-+ [AGG_AALCR7] = 0x140,
-+ [MIB_TDRCR0] = 0x768,
-+ [MIB_TDRCR1] = 0x76c,
-+ [MIB_TDRCR2] = 0x770,
-+ [MIB_TDRCR3] = 0x774,
-+ [MIB_TDRCR4] = 0x778,
-+ [MIB_RSCR26] = 0x994,
-+ [MIB_TSCR18] = 0xb18,
-+ [MIB_TRDR0] = 0xb20,
-+ [MIB_TRDR2] = 0xb28,
-+ [MIB_TRDR3] = 0xb2c,
-+ [MIB_TRDR4] = 0xb30,
-+ [MIB_TRDR5] = 0xb34,
-+ [MIB_TRDR6] = 0xb38,
-+ [MIB_TRDR7] = 0xb3c,
-+ [MIB_TRDR8] = 0xb40,
-+ [MIB_TRDR9] = 0xb44,
-+ [MIB_TRDR10] = 0xb48,
-+ [MIB_TRDR11] = 0xb4c,
-+ [MIB_TRDR12] = 0xb50,
-+ [MIB_TRDR13] = 0xb54,
-+ [MIB_TRDR14] = 0xb58,
-+ [MIB_TRDR15] = 0xb5c,
-+ [MIB_MSR0] = 0xb60,
-+ [MIB_MSR1] = 0xb64,
-+ [MIB_MSR2] = 0xb68,
-+ [MIB_MCTR5] = 0xb6c,
-+ [MIB_MCTR6] = 0xb70,
-+};
-+
-+/* used to differentiate between generations */
-+struct mt7996_dbg_reg_desc {
-+ const u32 id;
-+ const u32 *offs_rev;
-+};
-+
-+/* AGG */
-+#define BN0_WF_AGG_TOP_BASE 0x820e2000
-+#define BN1_WF_AGG_TOP_BASE 0x820f2000
-+#define IP1_BN0_WF_AGG_TOP_BASE 0x830e2000
-+
-+#define BN0_WF_AGG_TOP_SCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x0) // 2000
-+#define BN0_WF_AGG_TOP_SCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x4) // 2004
-+#define BN0_WF_AGG_TOP_SCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x8) // 2008
-+#define BN0_WF_AGG_TOP_BCR_ADDR (BN0_WF_AGG_TOP_BASE + 0xc) // 200C
-+#define BN0_WF_AGG_TOP_BWCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x10) // 2010
-+#define BN0_WF_AGG_TOP_ARCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x14) // 2014
-+#define BN0_WF_AGG_TOP_ARUCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x18) // 2018
-+#define BN0_WF_AGG_TOP_ARDCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x1c) // 201C
-+#define BN0_WF_AGG_TOP_AALCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x20) // 2020
-+#define BN0_WF_AGG_TOP_AALCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x24) // 2024
-+#define BN0_WF_AGG_TOP_PCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x28) // 2028
-+#define BN0_WF_AGG_TOP_PCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2c) // 202C
-+#define BN0_WF_AGG_TOP_TTCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x30) // 2030
-+#define BN0_WF_AGG_TOP_TTCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x34) // 2034
-+#define BN0_WF_AGG_TOP_ACR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x38) // 2038
-+#define BN0_WF_AGG_TOP_ACR4_ADDR (BN0_WF_AGG_TOP_BASE + 0x3c) // 203C
-+#define BN0_WF_AGG_TOP_ACR5_ADDR (BN0_WF_AGG_TOP_BASE + 0x40) // 2040
-+#define BN0_WF_AGG_TOP_ACR6_ADDR (BN0_WF_AGG_TOP_BASE + 0x44) // 2044
-+#define BN0_WF_AGG_TOP_ACR8_ADDR (BN0_WF_AGG_TOP_BASE + 0x4c) // 204C
-+#define BN0_WF_AGG_TOP_MRCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x50) // 2050
-+#define BN0_WF_AGG_TOP_MMPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x54) // 2054
-+#define BN0_WF_AGG_TOP_GFPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x58) // 2058
-+#define BN0_WF_AGG_TOP_VHTPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x5c) // 205C
-+#define BN0_WF_AGG_TOP_HEPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x60) // 2060
-+#define BN0_WF_AGG_TOP_CTCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x64) // 2064
-+#define BN0_WF_AGG_TOP_ATCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0x68) // 2068
-+#define BN0_WF_AGG_TOP_SRCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x6c) // 206C
-+#define BN0_WF_AGG_TOP_VBCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x70) // 2070
-+#define BN0_WF_AGG_TOP_TCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x74) // 2074
-+#define BN0_WF_AGG_TOP_SRHS_ADDR (BN0_WF_AGG_TOP_BASE + 0x78) // 2078
-+#define BN0_WF_AGG_TOP_DBRCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x7c) // 207C
-+#define BN0_WF_AGG_TOP_DBRCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x80) // 2080
-+#define BN0_WF_AGG_TOP_CTETCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x84) // 2084
-+#define BN0_WF_AGG_TOP_WPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x88) // 2088
-+#define BN0_WF_AGG_TOP_PLRPDR_ADDR (BN0_WF_AGG_TOP_BASE + 0x8c) // 208C
-+#define BN0_WF_AGG_TOP_CECR_ADDR (BN0_WF_AGG_TOP_BASE + 0x90) // 2090
-+#define BN0_WF_AGG_TOP_OMRCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x94) // 2094
-+#define BN0_WF_AGG_TOP_OMRCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x98) // 2098
-+#define BN0_WF_AGG_TOP_OMRCR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x9c) // 209C
-+#define BN0_WF_AGG_TOP_OMRCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0xa0) // 20A0
-+#define BN0_WF_AGG_TOP_TMCR_ADDR (BN0_WF_AGG_TOP_BASE + 0xa4) // 20A4
-+#define BN0_WF_AGG_TOP_TWTCR_ADDR (BN0_WF_AGG_TOP_BASE + 0xa8) // 20A8
-+#define BN0_WF_AGG_TOP_TWTSTACR_ADDR (BN0_WF_AGG_TOP_BASE + 0xac) // 20AC
-+#define BN0_WF_AGG_TOP_TWTE0TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xb0) // 20B0
-+#define BN0_WF_AGG_TOP_TWTE1TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xb4) // 20B4
-+#define BN0_WF_AGG_TOP_TWTE2TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xb8) // 20B8
-+#define BN0_WF_AGG_TOP_TWTE3TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xbc) // 20BC
-+#define BN0_WF_AGG_TOP_TWTE4TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xc0) // 20C0
-+#define BN0_WF_AGG_TOP_TWTE5TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xc4) // 20C4
-+#define BN0_WF_AGG_TOP_TWTE6TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xc8) // 20C8
-+#define BN0_WF_AGG_TOP_TWTE7TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xcc) // 20CC
-+#define BN0_WF_AGG_TOP_TWTE8TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xd0) // 20D0
-+#define BN0_WF_AGG_TOP_TWTE9TB_ADDR (BN0_WF_AGG_TOP_BASE + 0xd4) // 20D4
-+#define BN0_WF_AGG_TOP_TWTEATB_ADDR (BN0_WF_AGG_TOP_BASE + 0xd8) // 20D8
-+#define BN0_WF_AGG_TOP_TWTEBTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xdc) // 20DC
-+#define BN0_WF_AGG_TOP_TWTECTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xe0) // 20E0
-+#define BN0_WF_AGG_TOP_TWTEDTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xe4) // 20E4
-+#define BN0_WF_AGG_TOP_TWTEETB_ADDR (BN0_WF_AGG_TOP_BASE + 0xe8) // 20E8
-+#define BN0_WF_AGG_TOP_TWTEFTB_ADDR (BN0_WF_AGG_TOP_BASE + 0xec) // 20EC
-+#define BN0_WF_AGG_TOP_ATCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x108) // 2108
-+#define BN0_WF_AGG_TOP_ATCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x10c) // 210C
-+#define BN0_WF_AGG_TOP_TCCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x110) // 2110
-+#define BN0_WF_AGG_TOP_TFCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x114) // 2114
-+#define BN0_WF_AGG_TOP_MUCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x118) // 2118
-+#define BN0_WF_AGG_TOP_MUCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x11c) // 211C
-+#define BN0_WF_AGG_TOP_AALCR2_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR2))
-+#define BN0_WF_AGG_TOP_AALCR3_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR3))
-+#define BN0_WF_AGG_TOP_AALCR4_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR4))
-+#define BN0_WF_AGG_TOP_AALCR5_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR5))
-+#define BN0_WF_AGG_TOP_AALCR6_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR6))
-+#define BN0_WF_AGG_TOP_AALCR7_ADDR (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR7))
-+#define BN0_WF_AGG_TOP_CSDCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x150) // 2150
-+#define BN0_WF_AGG_TOP_CSDCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x154) // 2154
-+#define BN0_WF_AGG_TOP_CSDCR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x158) // 2158
-+#define BN0_WF_AGG_TOP_CSDCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0x15c) // 215C
-+#define BN0_WF_AGG_TOP_CSDCR4_ADDR (BN0_WF_AGG_TOP_BASE + 0x160) // 2160
-+#define BN0_WF_AGG_TOP_DYNSCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x178) // 2178
-+#define BN0_WF_AGG_TOP_DYNSSCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x198) // 2198
-+#define BN0_WF_AGG_TOP_TCDCNT0_ADDR (BN0_WF_AGG_TOP_BASE + 0x2c8) // 22C8
-+#define BN0_WF_AGG_TOP_TCDCNT1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2cc) // 22CC
-+#define BN0_WF_AGG_TOP_TCSR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x2d0) // 22D0
-+#define BN0_WF_AGG_TOP_TCSR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2d4) // 22D4
-+#define BN0_WF_AGG_TOP_TCSR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x2d8) // 22D8
-+#define BN0_WF_AGG_TOP_DCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x2e4) // 22E4
-+#define BN0_WF_AGG_TOP_SMDCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x2e8) // 22E8
-+#define BN0_WF_AGG_TOP_TXCMDSMCR_ADDR (BN0_WF_AGG_TOP_BASE + 0x2ec) // 22EC
-+#define BN0_WF_AGG_TOP_SMCR0_ADDR (BN0_WF_AGG_TOP_BASE + 0x2f0) // 22F0
-+#define BN0_WF_AGG_TOP_SMCR1_ADDR (BN0_WF_AGG_TOP_BASE + 0x2f4) // 22F4
-+#define BN0_WF_AGG_TOP_SMCR2_ADDR (BN0_WF_AGG_TOP_BASE + 0x2f8) // 22F8
-+#define BN0_WF_AGG_TOP_SMCR3_ADDR (BN0_WF_AGG_TOP_BASE + 0x2fc) // 22FC
-+
-+#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR0_ADDR
-+#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK 0x03FF0000 // AC01_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR0_ADDR
-+#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK 0x000003FF // AC00_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT 0
-+
-+#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR1_ADDR
-+#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK 0x03FF0000 // AC03_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR1_ADDR
-+#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK 0x000003FF // AC02_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT 0
-+
-+#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR2_ADDR
-+#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK 0x03FF0000 // AC11_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR2_ADDR
-+#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK 0x000003FF // AC10_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT 0
-+
-+#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR3_ADDR
-+#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK 0x03FF0000 // AC13_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR3_ADDR
-+#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK 0x000003FF // AC12_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT 0
-+
-+#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR4_ADDR
-+#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK 0x03FF0000 // AC21_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR4_ADDR
-+#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK 0x000003FF // AC20_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT 0
-+
-+#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR5_ADDR
-+#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK 0x03FF0000 // AC23_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR5_ADDR
-+#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK 0x000003FF // AC22_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT 0
-+
-+#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR6_ADDR
-+#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK 0x03FF0000 // AC31_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR6_ADDR
-+#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK 0x000003FF // AC30_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT 0
-+#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR7_ADDR
-+#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK 0x03FF0000 // AC33_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT 16
-+#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_ADDR BN0_WF_AGG_TOP_AALCR7_ADDR
-+#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK 0x000003FF // AC32_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT 0
-+
-+/* DMA */
-+struct queue_desc {
-+ u32 hw_desc_base;
-+ u16 ring_size;
-+ char *const ring_info;
-+};
-+
-+// HOST DMA
-+#define WF_WFDMA_HOST_DMA0_BASE 0xd4000
-+
-+#define WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x200) /* 4200 */
-+#define WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0X204) /* 4204 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x208) /* 4208 */
-+
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR \
-+ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK \
-+ 0x00000008 /* RX_DMA_BUSY[3] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR \
-+ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK \
-+ 0x00000004 /* RX_DMA_EN[2] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR \
-+ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK \
-+ 0x00000002 /* TX_DMA_BUSY[1] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR \
-+ WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK \
-+ 0x00000001 /* TX_DMA_EN[0] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
-+
-+
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x300) /* 4300 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x304) /* 4304 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x308) /* 4308 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x30c) /* 430C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x310) /* 4310 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x314) /* 4314 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x318) /* 4318 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x31c) /* 431C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x320) /* 4320 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x324) /* 4324 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x328) /* 4328 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x32c) /* 432C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x330) /* 4330 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x334) /* 4334 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x338) /* 4338 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x33c) /* 433C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x340) /* 4340 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x344) /* 4344 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x348) /* 4348 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x34c) /* 434C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x350) /* 4350 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x354) /* 4354 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x358) /* 4358 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x35c) /* 435C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x360) /* 4360 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x364) /* 4364 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x368) /* 4368 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x36c) /* 436C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x400) /* 4400 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x404) /* 4404 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x408) /* 4408 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x40c) /* 440C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x410) /* 4410 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x414) /* 4414 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x418) /* 4418 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x41c) /* 441C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x420) /* 4420 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x424) /* 4424 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x428) /* 4428 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x42c) /* 442C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x430) /* 4430 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x434) /* 4434 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x438) /* 4438 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x43c) /* 443C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x440) /* 4440 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x444) /* 4444 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x448) /* 4448 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x44c) /* 444C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x450) /* 4450 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x454) /* 4454 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x458) /* 4458 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x45c) /* 445c */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x460) // 4460
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x464) // 4464
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x468) // 4468
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x46c) // 446C
-+
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x500) /* 4500 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x504) /* 4504 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x508) /* 4508 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x50c) /* 450C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x510) /* 4510 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x514) /* 4514 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x518) /* 4518 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x51c) /* 451C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x520) /* 4520 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x524) /* 4524 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x528) /* 4528 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x52C) /* 452C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x530) /* 4530 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x534) /* 4534 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x538) /* 4538 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x53C) /* 453C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x540) /* 4540 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x544) /* 4544 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x548) /* 4548 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x54c) /* 454C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x550) /* 4550 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x554) /* 4554 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x558) /* 4558 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x55c) /* 455C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x560) /* 4560 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x564) /* 4564 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x568) /* 4568 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x56c) /* 456C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x570) /* 4570 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x574) /* 4574 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x578) /* 4578 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x57c) /* 457C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x580) /* 4580 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x584) /* 4584 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x588) /* 4588 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x58c) /* 458C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x590) /* 4590 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL1_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x594) /* 4594 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL2_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x598) /* 4598 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL3_ADDR \
-+ (WF_WFDMA_HOST_DMA0_BASE + 0x59c) /* 459C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5a0) // 45A0
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5a4) // 45A4
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5a8) // 45A8
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5ac) // 45AC
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5b0) // 45B0
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5b4) // 45B4
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5b8) // 45B8
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5bc) // 45BC
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5C0) // 45C0
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5C4) // 45C4
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5C8) // 45C8
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_BASE + 0x5CC) // 45CC
-+
-+// HOST PCIE1 DMA
-+#define WF_WFDMA_HOST_DMA0_PCIE1_BASE 0xd8000
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x200) // 8200
-+#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0X204) // 8204
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x208) // 8208
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_PDMA_BT_SIZE_SHFT 4
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK 0x00000008
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK 0x00000004
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK 0x00000002
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK 0x00000001
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x450) // 8450
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x454) // 8454
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x458) // 8458
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x45c) // 845C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x460) // 8460
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x464) // 8464
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x468) // 8468
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x46c) // 846C
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x530) // 8530
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x534) // 8534
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x538) // 8538
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x53C) // 853C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x550) // 8550
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x554) // 8554
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x558) // 8558
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x55c) // 855C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x560) // 8560
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x564) // 8564
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x568) // 8568
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x56c) // 856C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x570) // 8570
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
-+//MCU DMA
-+//#define WF_WFDMA_MCU_DMA0_BASE 0x02000
-+#define WF_WFDMA_MCU_DMA0_BASE 0x54000000
-+
-+#define WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x200) // 0200
-+#define WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0X204) // 0204
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x208) // 0208
-+
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK 0x00000008 // RX_DMA_BUSY[3]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK 0x00000004 // RX_DMA_EN[2]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK 0x00000002 // TX_DMA_BUSY[1]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK 0x00000001 // TX_DMA_EN[0]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
-+
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x300) // 0300
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x304) // 0304
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x308) // 0308
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x30c) // 030C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x310) // 0310
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x314) // 0314
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x318) // 0318
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x31c) // 031C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x320) // 0320
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x324) // 0324
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x328) // 0328
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x32c) // 032C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x330) // 0330
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x334) // 0334
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x338) // 0338
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x33c) // 033C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x340) // 0340
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x344) // 0344
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x348) // 0348
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x34c) // 034C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x350) // 0350
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x354) // 0354
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x358) // 0358
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x35c) // 035C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x360) // 0360
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x364) // 0364
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x368) // 0368
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x36c) // 036C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x370) // 0370
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x374) // 0374
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x378) // 0378
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x37c) // 037C
-+
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x500) // 0500
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x504) // 0504
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x508) // 0508
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x50c) // 050C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x510) // 0510
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x514) // 0514
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x518) // 0518
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x51c) // 051C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x520) // 0520
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x524) // 0524
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x528) // 0528
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x52C) // 052C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x530) // 0530
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x534) // 0534
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x538) // 0538
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x53C) // 053C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x540) // 0540
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x544) // 0544
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x548) // 0548
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x54C) // 054C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x550) // 0550
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x554) // 0554
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x558) // 0558
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x55C) // 055C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x560) // 0560
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x564) // 0564
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x568) // 0568
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x56c) // 056C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x570) // 0570
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x574) // 0574
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x578) // 0578
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x57c) // 057C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x580) // 0580
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x584) // 0584
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x588) // 0588
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x58c) // 058C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x590) // 0590
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x594) // 0594
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x598) // 0598
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x59c) // 059C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5A0) // 05A0
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL1_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5A4) // 05A4
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL2_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5A8) // 05A8
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL3_ADDR (WF_WFDMA_MCU_DMA0_BASE + 0x5Ac) // 05AC
-+
-+// MEM DMA
-+#define WF_WFDMA_MEM_DMA_BASE 0x58000000
-+
-+#define WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x200) // 0200
-+#define WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR (WF_WFDMA_MEM_DMA_BASE + 0X204) // 0204
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x208) // 0208
-+
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK 0x00000008 // RX_DMA_BUSY[3]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK 0x00000004 // RX_DMA_EN[2]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK 0x00000002 // TX_DMA_BUSY[1]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_ADDR WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK 0x00000001 // TX_DMA_EN[0]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
-+
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x300) // 0300
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x304) // 0304
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x308) // 0308
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x30c) // 030C
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x310) // 0310
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x314) // 0314
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x318) // 0318
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x31c) // 031C
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x320) // 0320
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x324) // 0324
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x328) // 0328
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x32c) // 032C
-+
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x500) // 0500
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x504) // 0504
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x508) // 0508
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x50c) // 050C
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x510) // 0510
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x514) // 0514
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x518) // 0518
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x51c) // 051C
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL0_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x520) // 0520
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL1_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x524) // 0524
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL2_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x528) // 0528
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL3_ADDR (WF_WFDMA_MEM_DMA_BASE + 0x52C) // 052C
-+
-+/* MIB */
-+#define WF_UMIB_TOP_BASE 0x820cd000
-+#define BN0_WF_MIB_TOP_BASE 0x820ed000
-+#define BN1_WF_MIB_TOP_BASE 0x820fd000
-+#define IP1_BN0_WF_MIB_TOP_BASE 0x830ed000
-+
-+#define WF_UMIB_TOP_B0BROCR_ADDR (WF_UMIB_TOP_BASE + 0x484) // D484
-+#define WF_UMIB_TOP_B0BRBCR_ADDR (WF_UMIB_TOP_BASE + 0x4D4) // D4D4
-+#define WF_UMIB_TOP_B0BRDCR_ADDR (WF_UMIB_TOP_BASE + 0x524) // D524
-+#define WF_UMIB_TOP_B1BROCR_ADDR (WF_UMIB_TOP_BASE + 0x5E8) // D5E8
-+#define WF_UMIB_TOP_B2BROCR_ADDR (WF_UMIB_TOP_BASE + 0x74C) // D74C
-+
-+#define BN0_WF_MIB_TOP_M0SCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x000) // D000
-+#define BN0_WF_MIB_TOP_M0SDR6_ADDR (BN0_WF_MIB_TOP_BASE + 0x020) // D020
-+#define BN0_WF_MIB_TOP_M0SDR9_ADDR (BN0_WF_MIB_TOP_BASE + 0x024) // D024
-+#define BN0_WF_MIB_TOP_M0SDR18_ADDR (BN0_WF_MIB_TOP_BASE + 0x030) // D030
-+#define BN0_WF_MIB_TOP_BTOCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x400) // D400
-+#define BN0_WF_MIB_TOP_BTBCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x450) // D450
-+#define BN0_WF_MIB_TOP_BTDCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x590) // D590
-+#define BN0_WF_MIB_TOP_BTCR_ADDR (BN0_WF_MIB_TOP_BASE + 0x5A0) // D5A0
-+#define BN0_WF_MIB_TOP_RVSR0_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RVSR0))
-+
-+#define BN0_WF_MIB_TOP_TSCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x6B0) // D6B0
-+#define BN0_WF_MIB_TOP_TSCR3_ADDR (BN0_WF_MIB_TOP_BASE + 0x6BC) // D6BC
-+#define BN0_WF_MIB_TOP_TSCR4_ADDR (BN0_WF_MIB_TOP_BASE + 0x6C0) // D6C0
-+#define BN0_WF_MIB_TOP_TSCR5_ADDR (BN0_WF_MIB_TOP_BASE + 0x6C4) // D6C4
-+#define BN0_WF_MIB_TOP_TSCR6_ADDR (BN0_WF_MIB_TOP_BASE + 0x6C8) // D6C8
-+#define BN0_WF_MIB_TOP_TSCR7_ADDR (BN0_WF_MIB_TOP_BASE + 0x6D0) // D6D0
-+#define BN0_WF_MIB_TOP_TSCR8_ADDR (BN0_WF_MIB_TOP_BASE + 0x6CC) // D6CC
-+
-+#define BN0_WF_MIB_TOP_TBCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x6EC) // D6EC
-+#define BN0_WF_MIB_TOP_TBCR1_ADDR (BN0_WF_MIB_TOP_BASE + 0x6F0) // D6F0
-+#define BN0_WF_MIB_TOP_TBCR2_ADDR (BN0_WF_MIB_TOP_BASE + 0x6F4) // D6F4
-+#define BN0_WF_MIB_TOP_TBCR3_ADDR (BN0_WF_MIB_TOP_BASE + 0x6F8) // D6F8
-+#define BN0_WF_MIB_TOP_TBCR4_ADDR (BN0_WF_MIB_TOP_BASE + 0x6FC) // D6FC
-+
-+#define BN0_WF_MIB_TOP_TDRCR0_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR0))
-+#define BN0_WF_MIB_TOP_TDRCR1_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR1))
-+#define BN0_WF_MIB_TOP_TDRCR2_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR2))
-+#define BN0_WF_MIB_TOP_TDRCR3_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR3))
-+#define BN0_WF_MIB_TOP_TDRCR4_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR4))
-+
-+#define BN0_WF_MIB_TOP_BTSCR0_ADDR (BN0_WF_MIB_TOP_BASE + 0x5E0) // D5E0
-+#define BN0_WF_MIB_TOP_BTSCR1_ADDR (BN0_WF_MIB_TOP_BASE + 0x5F0) // D5F0
-+#define BN0_WF_MIB_TOP_BTSCR2_ADDR (BN0_WF_MIB_TOP_BASE + 0x600) // D600
-+#define BN0_WF_MIB_TOP_BTSCR3_ADDR (BN0_WF_MIB_TOP_BASE + 0x610) // D610
-+#define BN0_WF_MIB_TOP_BTSCR4_ADDR (BN0_WF_MIB_TOP_BASE + 0x620) // D620
-+#define BN0_WF_MIB_TOP_BTSCR5_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR5))
-+#define BN0_WF_MIB_TOP_BTSCR6_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR6))
-+
-+#define BN0_WF_MIB_TOP_RSCR1_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR1))
-+#define BN0_WF_MIB_TOP_BSCR2_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BSCR2))
-+#define BN0_WF_MIB_TOP_TSCR18_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TSCR18))
-+
-+#define BN0_WF_MIB_TOP_MSR0_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR0))
-+#define BN0_WF_MIB_TOP_MSR1_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR1))
-+#define BN0_WF_MIB_TOP_MSR2_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR2))
-+#define BN0_WF_MIB_TOP_MCTR5_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR5))
-+#define BN0_WF_MIB_TOP_MCTR6_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR6))
-+
-+#define BN0_WF_MIB_TOP_RSCR26_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_RSCR26))
-+#define BN0_WF_MIB_TOP_RSCR27_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR27))
-+#define BN0_WF_MIB_TOP_RSCR28_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR28))
-+#define BN0_WF_MIB_TOP_RSCR31_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR31))
-+#define BN0_WF_MIB_TOP_RSCR33_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR33))
-+#define BN0_WF_MIB_TOP_RSCR35_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR35))
-+#define BN0_WF_MIB_TOP_RSCR36_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR36))
-+
-+#define BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK 0xFFFFFFFF // AMPDU_MPDU_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK 0xFFFFFFFF // AMPDU_ACKED_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK 0x0000FFFF // CHANNEL_IDLE_COUNT[15..0]
-+#define BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK 0x00FFFFFF // CCA_NAV_TX_TIME[23..0]
-+#define BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK 0xFFFFFFFF // RX_MDRDY_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK 0xFFFFFFFF // CCK_MDRDY_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK 0xFFFFFFFF // OFDM_LG_MIXED_VHT_MDRDY_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK 0xFFFFFFFF // OFDM_GREEN_MDRDY_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK 0xFFFFFFFF // P_CCA_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK 0xFFFFFFFF // S_CCA_TIME[31..0]
-+#define BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK 0x00FFFFFF // P_ED_TIME[23..0]
-+#define BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK 0xFFFFFFFF // BEACONTXCOUNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK 0xFFFFFFFF // TX_20MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK 0xFFFFFFFF // TX_40MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK 0xFFFFFFFF // TX_80MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK 0xFFFFFFFF // TX_160MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK 0xFFFFFFFF // TX_320MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK 0xFFFFFFFF // MUBF_TX_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK 0xFFFFFFFF // VEC_MISS_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK 0xFFFFFFFF // DELIMITER_FAIL_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK 0xFFFFFFFF // RX_FCS_ERROR_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK 0xFFFFFFFF // RX_FIFO_FULL_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK 0xFFFFFFFF // RX_LEN_MISMATCH[31..0]
-+#define BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK 0xFFFFFFFF // RX_MPDU_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK 0xFFFFFFFF // RTSTXCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK 0xFFFFFFFF // RTSRETRYCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK 0xFFFFFFFF // BAMISSCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK 0xFFFFFFFF // ACKFAILCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK 0xFFFFFFFF // FRAMERETRYCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK 0xFFFFFFFF // FRAMERETRY2COUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK 0xFFFFFFFF // FRAMERETRY3COUNTn[31..0]
-+#define BN0_WF_MIB_TOP_TRARC0_ADDR (BN0_WF_MIB_TOP_BASE + 0x0B0) // D0B0
-+#define BN0_WF_MIB_TOP_TRARC1_ADDR (BN0_WF_MIB_TOP_BASE + 0x0B4) // D0B4
-+#define BN0_WF_MIB_TOP_TRARC2_ADDR (BN0_WF_MIB_TOP_BASE + 0x0B8) // D0B8
-+#define BN0_WF_MIB_TOP_TRARC3_ADDR (BN0_WF_MIB_TOP_BASE + 0x0BC) // D0BC
-+#define BN0_WF_MIB_TOP_TRARC4_ADDR (BN0_WF_MIB_TOP_BASE + 0x0C0) // D0C0
-+#define BN0_WF_MIB_TOP_TRARC5_ADDR (BN0_WF_MIB_TOP_BASE + 0x0C4) // D0C4
-+#define BN0_WF_MIB_TOP_TRARC6_ADDR (BN0_WF_MIB_TOP_BASE + 0x0C8) // D0C8
-+#define BN0_WF_MIB_TOP_TRARC7_ADDR (BN0_WF_MIB_TOP_BASE + 0x0CC) // D0CC
-+
-+#define BN0_WF_MIB_TOP_TRDR0_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR0))
-+#define BN0_WF_MIB_TOP_TRDR1_ADDR (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_TRDR1))
-+#define BN0_WF_MIB_TOP_TRDR2_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR2))
-+#define BN0_WF_MIB_TOP_TRDR3_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR3))
-+#define BN0_WF_MIB_TOP_TRDR4_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR4))
-+#define BN0_WF_MIB_TOP_TRDR5_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR5))
-+#define BN0_WF_MIB_TOP_TRDR6_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR6))
-+#define BN0_WF_MIB_TOP_TRDR7_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR7))
-+#define BN0_WF_MIB_TOP_TRDR8_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR8))
-+#define BN0_WF_MIB_TOP_TRDR9_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR9))
-+#define BN0_WF_MIB_TOP_TRDR10_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR10))
-+#define BN0_WF_MIB_TOP_TRDR11_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR11))
-+#define BN0_WF_MIB_TOP_TRDR12_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR12))
-+#define BN0_WF_MIB_TOP_TRDR13_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR13))
-+#define BN0_WF_MIB_TOP_TRDR14_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR14))
-+#define BN0_WF_MIB_TOP_TRDR15_ADDR (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR15))
-+
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_ADDR BN0_WF_MIB_TOP_TRARC0_ADDR
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK 0x03FF0000 // AGG_RANG_SEL_1[25..16]
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT 16
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_ADDR BN0_WF_MIB_TOP_TRARC0_ADDR
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK 0x000003FF // AGG_RANG_SEL_0[9..0]
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT 0
-+
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_ADDR BN0_WF_MIB_TOP_TRARC1_ADDR
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK 0x03FF0000 // AGG_RANG_SEL_3[25..16]
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT 16
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_ADDR BN0_WF_MIB_TOP_TRARC1_ADDR
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK 0x000003FF // AGG_RANG_SEL_2[9..0]
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT 0
-+
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_ADDR BN0_WF_MIB_TOP_TRARC2_ADDR
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK 0x03FF0000 // AGG_RANG_SEL_5[25..16]
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT 16
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_ADDR BN0_WF_MIB_TOP_TRARC2_ADDR
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK 0x000003FF // AGG_RANG_SEL_4[9..0]
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT 0
-+
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_ADDR BN0_WF_MIB_TOP_TRARC3_ADDR
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK 0x03FF0000 // AGG_RANG_SEL_7[25..16]
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT 16
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_ADDR BN0_WF_MIB_TOP_TRARC3_ADDR
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK 0x000003FF // AGG_RANG_SEL_6[9..0]
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT 0
-+
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_ADDR BN0_WF_MIB_TOP_TRARC4_ADDR
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK 0x03FF0000 // AGG_RANG_SEL_9[25..16]
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT 16
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_ADDR BN0_WF_MIB_TOP_TRARC4_ADDR
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK 0x000003FF // AGG_RANG_SEL_8[9..0]
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT 0
-+
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_ADDR BN0_WF_MIB_TOP_TRARC5_ADDR
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK 0x03FF0000 // AGG_RANG_SEL_11[25..16]
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT 16
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_ADDR BN0_WF_MIB_TOP_TRARC5_ADDR
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK 0x000003FF // AGG_RANG_SEL_10[9..0]
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT 0
-+
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_ADDR BN0_WF_MIB_TOP_TRARC6_ADDR
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK 0x03FF0000 // AGG_RANG_SEL_13[25..16]
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT 16
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_ADDR BN0_WF_MIB_TOP_TRARC6_ADDR
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK 0x000003FF // AGG_RANG_SEL_12[9..0]
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT 0
-+
-+#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_ADDR BN0_WF_MIB_TOP_TRARC7_ADDR
-+#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK 0x000003FF // AGG_RANG_SEL_14[9..0]
-+#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT 0
-+
-+/* RRO TOP */
-+#define WF_RRO_TOP_BASE 0xA000 /*0x820C2000 */
-+#define WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR (WF_RRO_TOP_BASE + 0x40) // 2040
-+ //
-+/* WTBL */
-+enum mt7996_wtbl_type {
-+ WTBL_TYPE_LMAC, /* WTBL in LMAC */
-+ WTBL_TYPE_UMAC, /* WTBL in UMAC */
-+ WTBL_TYPE_KEY, /* Key Table */
-+ MAX_NUM_WTBL_TYPE
-+};
-+
-+struct berse_wtbl_parse {
-+ u8 *name;
-+ u32 mask;
-+ u32 shift;
-+ u8 new_line;
-+};
-+
-+enum muar_idx {
-+ MUAR_INDEX_OWN_MAC_ADDR_0 = 0,
-+ MUAR_INDEX_OWN_MAC_ADDR_1,
-+ MUAR_INDEX_OWN_MAC_ADDR_2,
-+ MUAR_INDEX_OWN_MAC_ADDR_3,
-+ MUAR_INDEX_OWN_MAC_ADDR_4,
-+ MUAR_INDEX_OWN_MAC_ADDR_BC_MC = 0xE,
-+ MUAR_INDEX_UNMATCHED = 0xF,
-+ MUAR_INDEX_OWN_MAC_ADDR_11 = 0x11,
-+ MUAR_INDEX_OWN_MAC_ADDR_12,
-+ MUAR_INDEX_OWN_MAC_ADDR_13,
-+ MUAR_INDEX_OWN_MAC_ADDR_14,
-+ MUAR_INDEX_OWN_MAC_ADDR_15,
-+ MUAR_INDEX_OWN_MAC_ADDR_16,
-+ MUAR_INDEX_OWN_MAC_ADDR_17,
-+ MUAR_INDEX_OWN_MAC_ADDR_18,
-+ MUAR_INDEX_OWN_MAC_ADDR_19,
-+ MUAR_INDEX_OWN_MAC_ADDR_1A,
-+ MUAR_INDEX_OWN_MAC_ADDR_1B,
-+ MUAR_INDEX_OWN_MAC_ADDR_1C,
-+ MUAR_INDEX_OWN_MAC_ADDR_1D,
-+ MUAR_INDEX_OWN_MAC_ADDR_1E,
-+ MUAR_INDEX_OWN_MAC_ADDR_1F,
-+ MUAR_INDEX_OWN_MAC_ADDR_20,
-+ MUAR_INDEX_OWN_MAC_ADDR_21,
-+ MUAR_INDEX_OWN_MAC_ADDR_22,
-+ MUAR_INDEX_OWN_MAC_ADDR_23,
-+ MUAR_INDEX_OWN_MAC_ADDR_24,
-+ MUAR_INDEX_OWN_MAC_ADDR_25,
-+ MUAR_INDEX_OWN_MAC_ADDR_26,
-+ MUAR_INDEX_OWN_MAC_ADDR_27,
-+ MUAR_INDEX_OWN_MAC_ADDR_28,
-+ MUAR_INDEX_OWN_MAC_ADDR_29,
-+ MUAR_INDEX_OWN_MAC_ADDR_2A,
-+ MUAR_INDEX_OWN_MAC_ADDR_2B,
-+ MUAR_INDEX_OWN_MAC_ADDR_2C,
-+ MUAR_INDEX_OWN_MAC_ADDR_2D,
-+ MUAR_INDEX_OWN_MAC_ADDR_2E,
-+ MUAR_INDEX_OWN_MAC_ADDR_2F
-+};
-+
-+enum cipher_suit {
-+ IGTK_CIPHER_SUIT_NONE = 0,
-+ IGTK_CIPHER_SUIT_BIP,
-+ IGTK_CIPHER_SUIT_BIP_256
-+};
-+
-+#define LWTBL_LEN_IN_DW 36
-+#define UWTBL_LEN_IN_DW 16
-+
-+#define MT_DBG_WTBL_BASE 0x820D8000
-+
-+#define MT_DBG_WTBLON_TOP_BASE 0x820d4000
-+#define MT_DBG_WTBLON_TOP_WDUCR_ADDR (MT_DBG_WTBLON_TOP_BASE + 0x0370) // 4370
-+#define MT_DBG_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0)
-+
-+#define MT_DBG_UWTBL_TOP_BASE 0x820c4000
-+#define MT_DBG_UWTBL_TOP_WDUCR_ADDR (MT_DBG_UWTBL_TOP_BASE + 0x0104) // 4104
-+#define MT_DBG_UWTBL_TOP_WDUCR_GROUP GENMASK(5, 0)
-+#define MT_DBG_UWTBL_TOP_WDUCR_TARGET BIT(31)
-+
-+#define LWTBL_IDX2BASE_ID GENMASK(14, 8)
-+#define LWTBL_IDX2BASE_DW GENMASK(7, 2)
-+#define LWTBL_IDX2BASE(_id, _dw) (MT_DBG_WTBL_BASE | \
-+ FIELD_PREP(LWTBL_IDX2BASE_ID, _id) | \
-+ FIELD_PREP(LWTBL_IDX2BASE_DW, _dw))
-+
-+#define UWTBL_IDX2BASE_ID GENMASK(12, 6)
-+#define UWTBL_IDX2BASE_DW GENMASK(5, 2)
-+#define UWTBL_IDX2BASE(_id, _dw) (MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
-+ FIELD_PREP(UWTBL_IDX2BASE_ID, _id) | \
-+ FIELD_PREP(UWTBL_IDX2BASE_DW, _dw))
-+
-+#define KEYTBL_IDX2BASE_KEY GENMASK(12, 6)
-+#define KEYTBL_IDX2BASE_DW GENMASK(5, 2)
-+#define KEYTBL_IDX2BASE(_key, _dw) (MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
-+ FIELD_PREP(KEYTBL_IDX2BASE_KEY, _key) | \
-+ FIELD_PREP(KEYTBL_IDX2BASE_DW, _dw))
-+
-+// UMAC WTBL
-+// DW0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__DW 0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__ADDR 0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__MASK 0x0000ffff // 15- 0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__SHIFT 0
-+#define WF_UWTBL_OWN_MLD_ID_DW 0
-+#define WF_UWTBL_OWN_MLD_ID_ADDR 0
-+#define WF_UWTBL_OWN_MLD_ID_MASK 0x003f0000 // 21-16
-+#define WF_UWTBL_OWN_MLD_ID_SHIFT 16
-+// DW1
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__DW 1
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__ADDR 4
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__MASK 0xffffffff // 31- 0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__SHIFT 0
-+// DW2
-+#define WF_UWTBL_PN_31_0__DW 2
-+#define WF_UWTBL_PN_31_0__ADDR 8
-+#define WF_UWTBL_PN_31_0__MASK 0xffffffff // 31- 0
-+#define WF_UWTBL_PN_31_0__SHIFT 0
-+// DW3
-+#define WF_UWTBL_PN_47_32__DW 3
-+#define WF_UWTBL_PN_47_32__ADDR 12
-+#define WF_UWTBL_PN_47_32__MASK 0x0000ffff // 15- 0
-+#define WF_UWTBL_PN_47_32__SHIFT 0
-+#define WF_UWTBL_COM_SN_DW 3
-+#define WF_UWTBL_COM_SN_ADDR 12
-+#define WF_UWTBL_COM_SN_MASK 0x0fff0000 // 27-16
-+#define WF_UWTBL_COM_SN_SHIFT 16
-+// DW4
-+#define WF_UWTBL_TID0_SN_DW 4
-+#define WF_UWTBL_TID0_SN_ADDR 16
-+#define WF_UWTBL_TID0_SN_MASK 0x00000fff // 11- 0
-+#define WF_UWTBL_TID0_SN_SHIFT 0
-+#define WF_UWTBL_RX_BIPN_31_0__DW 4
-+#define WF_UWTBL_RX_BIPN_31_0__ADDR 16
-+#define WF_UWTBL_RX_BIPN_31_0__MASK 0xffffffff // 31- 0
-+#define WF_UWTBL_RX_BIPN_31_0__SHIFT 0
-+#define WF_UWTBL_TID1_SN_DW 4
-+#define WF_UWTBL_TID1_SN_ADDR 16
-+#define WF_UWTBL_TID1_SN_MASK 0x00fff000 // 23-12
-+#define WF_UWTBL_TID1_SN_SHIFT 12
-+#define WF_UWTBL_TID2_SN_7_0__DW 4
-+#define WF_UWTBL_TID2_SN_7_0__ADDR 16
-+#define WF_UWTBL_TID2_SN_7_0__MASK 0xff000000 // 31-24
-+#define WF_UWTBL_TID2_SN_7_0__SHIFT 24
-+// DW5
-+#define WF_UWTBL_TID2_SN_11_8__DW 5
-+#define WF_UWTBL_TID2_SN_11_8__ADDR 20
-+#define WF_UWTBL_TID2_SN_11_8__MASK 0x0000000f // 3- 0
-+#define WF_UWTBL_TID2_SN_11_8__SHIFT 0
-+#define WF_UWTBL_RX_BIPN_47_32__DW 5
-+#define WF_UWTBL_RX_BIPN_47_32__ADDR 20
-+#define WF_UWTBL_RX_BIPN_47_32__MASK 0x0000ffff // 15- 0
-+#define WF_UWTBL_RX_BIPN_47_32__SHIFT 0
-+#define WF_UWTBL_TID3_SN_DW 5
-+#define WF_UWTBL_TID3_SN_ADDR 20
-+#define WF_UWTBL_TID3_SN_MASK 0x0000fff0 // 15- 4
-+#define WF_UWTBL_TID3_SN_SHIFT 4
-+#define WF_UWTBL_TID4_SN_DW 5
-+#define WF_UWTBL_TID4_SN_ADDR 20
-+#define WF_UWTBL_TID4_SN_MASK 0x0fff0000 // 27-16
-+#define WF_UWTBL_TID4_SN_SHIFT 16
-+#define WF_UWTBL_TID5_SN_3_0__DW 5
-+#define WF_UWTBL_TID5_SN_3_0__ADDR 20
-+#define WF_UWTBL_TID5_SN_3_0__MASK 0xf0000000 // 31-28
-+#define WF_UWTBL_TID5_SN_3_0__SHIFT 28
-+// DW6
-+#define WF_UWTBL_TID5_SN_11_4__DW 6
-+#define WF_UWTBL_TID5_SN_11_4__ADDR 24
-+#define WF_UWTBL_TID5_SN_11_4__MASK 0x000000ff // 7- 0
-+#define WF_UWTBL_TID5_SN_11_4__SHIFT 0
-+#define WF_UWTBL_KEY_LOC2_DW 6
-+#define WF_UWTBL_KEY_LOC2_ADDR 24
-+#define WF_UWTBL_KEY_LOC2_MASK 0x00001fff // 12- 0
-+#define WF_UWTBL_KEY_LOC2_SHIFT 0
-+#define WF_UWTBL_TID6_SN_DW 6
-+#define WF_UWTBL_TID6_SN_ADDR 24
-+#define WF_UWTBL_TID6_SN_MASK 0x000fff00 // 19- 8
-+#define WF_UWTBL_TID6_SN_SHIFT 8
-+#define WF_UWTBL_TID7_SN_DW 6
-+#define WF_UWTBL_TID7_SN_ADDR 24
-+#define WF_UWTBL_TID7_SN_MASK 0xfff00000 // 31-20
-+#define WF_UWTBL_TID7_SN_SHIFT 20
-+// DW7
-+#define WF_UWTBL_KEY_LOC0_DW 7
-+#define WF_UWTBL_KEY_LOC0_ADDR 28
-+#define WF_UWTBL_KEY_LOC0_MASK 0x00001fff // 12- 0
-+#define WF_UWTBL_KEY_LOC0_SHIFT 0
-+#define WF_UWTBL_KEY_LOC1_DW 7
-+#define WF_UWTBL_KEY_LOC1_ADDR 28
-+#define WF_UWTBL_KEY_LOC1_MASK 0x1fff0000 // 28-16
-+#define WF_UWTBL_KEY_LOC1_SHIFT 16
-+// DW8
-+#define WF_UWTBL_AMSDU_CFG_DW 8
-+#define WF_UWTBL_AMSDU_CFG_ADDR 32
-+#define WF_UWTBL_AMSDU_CFG_MASK 0x00000fff // 11- 0
-+#define WF_UWTBL_AMSDU_CFG_SHIFT 0
-+#define WF_UWTBL_SEC_ADDR_MODE_DW 8
-+#define WF_UWTBL_SEC_ADDR_MODE_ADDR 32
-+#define WF_UWTBL_SEC_ADDR_MODE_MASK 0x00300000 // 21-20
-+#define WF_UWTBL_SEC_ADDR_MODE_SHIFT 20
-+#define WF_UWTBL_WMM_Q_DW 8
-+#define WF_UWTBL_WMM_Q_ADDR 32
-+#define WF_UWTBL_WMM_Q_MASK 0x06000000 // 26-25
-+#define WF_UWTBL_WMM_Q_SHIFT 25
-+#define WF_UWTBL_QOS_DW 8
-+#define WF_UWTBL_QOS_ADDR 32
-+#define WF_UWTBL_QOS_MASK 0x08000000 // 27-27
-+#define WF_UWTBL_QOS_SHIFT 27
-+#define WF_UWTBL_HT_DW 8
-+#define WF_UWTBL_HT_ADDR 32
-+#define WF_UWTBL_HT_MASK 0x10000000 // 28-28
-+#define WF_UWTBL_HT_SHIFT 28
-+#define WF_UWTBL_HDRT_MODE_DW 8
-+#define WF_UWTBL_HDRT_MODE_ADDR 32
-+#define WF_UWTBL_HDRT_MODE_MASK 0x20000000 // 29-29
-+#define WF_UWTBL_HDRT_MODE_SHIFT 29
-+// DW9
-+#define WF_UWTBL_RELATED_IDX0_DW 9
-+#define WF_UWTBL_RELATED_IDX0_ADDR 36
-+#define WF_UWTBL_RELATED_IDX0_MASK 0x00000fff // 11- 0
-+#define WF_UWTBL_RELATED_IDX0_SHIFT 0
-+#define WF_UWTBL_RELATED_BAND0_DW 9
-+#define WF_UWTBL_RELATED_BAND0_ADDR 36
-+#define WF_UWTBL_RELATED_BAND0_MASK 0x00003000 // 13-12
-+#define WF_UWTBL_RELATED_BAND0_SHIFT 12
-+#define WF_UWTBL_PRIMARY_MLD_BAND_DW 9
-+#define WF_UWTBL_PRIMARY_MLD_BAND_ADDR 36
-+#define WF_UWTBL_PRIMARY_MLD_BAND_MASK 0x0000c000 // 15-14
-+#define WF_UWTBL_PRIMARY_MLD_BAND_SHIFT 14
-+#define WF_UWTBL_RELATED_IDX1_DW 9
-+#define WF_UWTBL_RELATED_IDX1_ADDR 36
-+#define WF_UWTBL_RELATED_IDX1_MASK 0x0fff0000 // 27-16
-+#define WF_UWTBL_RELATED_IDX1_SHIFT 16
-+#define WF_UWTBL_RELATED_BAND1_DW 9
-+#define WF_UWTBL_RELATED_BAND1_ADDR 36
-+#define WF_UWTBL_RELATED_BAND1_MASK 0x30000000 // 29-28
-+#define WF_UWTBL_RELATED_BAND1_SHIFT 28
-+#define WF_UWTBL_SECONDARY_MLD_BAND_DW 9
-+#define WF_UWTBL_SECONDARY_MLD_BAND_ADDR 36
-+#define WF_UWTBL_SECONDARY_MLD_BAND_MASK 0xc0000000 // 31-30
-+#define WF_UWTBL_SECONDARY_MLD_BAND_SHIFT 30
-+
-+/* LMAC WTBL */
-+// DW0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__DW 0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__ADDR 0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__MASK \
-+ 0x0000ffff // 15- 0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__SHIFT 0
-+#define WF_LWTBL_MUAR_DW 0
-+#define WF_LWTBL_MUAR_ADDR 0
-+#define WF_LWTBL_MUAR_MASK \
-+ 0x003f0000 // 21-16
-+#define WF_LWTBL_MUAR_SHIFT 16
-+#define WF_LWTBL_RCA1_DW 0
-+#define WF_LWTBL_RCA1_ADDR 0
-+#define WF_LWTBL_RCA1_MASK \
-+ 0x00400000 // 22-22
-+#define WF_LWTBL_RCA1_SHIFT 22
-+#define WF_LWTBL_KID_DW 0
-+#define WF_LWTBL_KID_ADDR 0
-+#define WF_LWTBL_KID_MASK \
-+ 0x01800000 // 24-23
-+#define WF_LWTBL_KID_SHIFT 23
-+#define WF_LWTBL_RCID_DW 0
-+#define WF_LWTBL_RCID_ADDR 0
-+#define WF_LWTBL_RCID_MASK \
-+ 0x02000000 // 25-25
-+#define WF_LWTBL_RCID_SHIFT 25
-+#define WF_LWTBL_BAND_DW 0
-+#define WF_LWTBL_BAND_ADDR 0
-+#define WF_LWTBL_BAND_MASK \
-+ 0x0c000000 // 27-26
-+#define WF_LWTBL_BAND_SHIFT 26
-+#define WF_LWTBL_RV_DW 0
-+#define WF_LWTBL_RV_ADDR 0
-+#define WF_LWTBL_RV_MASK \
-+ 0x10000000 // 28-28
-+#define WF_LWTBL_RV_SHIFT 28
-+#define WF_LWTBL_RCA2_DW 0
-+#define WF_LWTBL_RCA2_ADDR 0
-+#define WF_LWTBL_RCA2_MASK \
-+ 0x20000000 // 29-29
-+#define WF_LWTBL_RCA2_SHIFT 29
-+#define WF_LWTBL_WPI_FLAG_DW 0
-+#define WF_LWTBL_WPI_FLAG_ADDR 0
-+#define WF_LWTBL_WPI_FLAG_MASK \
-+ 0x40000000 // 30-30
-+#define WF_LWTBL_WPI_FLAG_SHIFT 30
-+// DW1
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__DW 1
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__ADDR 4
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__MASK \
-+ 0xffffffff // 31- 0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__SHIFT 0
-+// DW2
-+#define WF_LWTBL_AID_DW 2
-+#define WF_LWTBL_AID_ADDR 8
-+#define WF_LWTBL_AID_MASK \
-+ 0x00000fff // 11- 0
-+#define WF_LWTBL_AID_SHIFT 0
-+#define WF_LWTBL_GID_SU_DW 2
-+#define WF_LWTBL_GID_SU_ADDR 8
-+#define WF_LWTBL_GID_SU_MASK \
-+ 0x00001000 // 12-12
-+#define WF_LWTBL_GID_SU_SHIFT 12
-+#define WF_LWTBL_SPP_EN_DW 2
-+#define WF_LWTBL_SPP_EN_ADDR 8
-+#define WF_LWTBL_SPP_EN_MASK \
-+ 0x00002000 // 13-13
-+#define WF_LWTBL_SPP_EN_SHIFT 13
-+#define WF_LWTBL_WPI_EVEN_DW 2
-+#define WF_LWTBL_WPI_EVEN_ADDR 8
-+#define WF_LWTBL_WPI_EVEN_MASK \
-+ 0x00004000 // 14-14
-+#define WF_LWTBL_WPI_EVEN_SHIFT 14
-+#define WF_LWTBL_AAD_OM_DW 2
-+#define WF_LWTBL_AAD_OM_ADDR 8
-+#define WF_LWTBL_AAD_OM_MASK \
-+ 0x00008000 // 15-15
-+#define WF_LWTBL_AAD_OM_SHIFT 15
-+/* kite DW2 field bit 13-14 */
-+#define WF_LWTBL_DUAL_PTEC_EN_DW 2
-+#define WF_LWTBL_DUAL_PTEC_EN_ADDR 8
-+#define WF_LWTBL_DUAL_PTEC_EN_MASK \
-+ 0x00002000 // 13-13
-+#define WF_LWTBL_DUAL_PTEC_EN_SHIFT 13
-+#define WF_LWTBL_DUAL_CTS_CAP_DW 2
-+#define WF_LWTBL_DUAL_CTS_CAP_ADDR 8
-+#define WF_LWTBL_DUAL_CTS_CAP_MASK \
-+ 0x00004000 // 14-14
-+#define WF_LWTBL_DUAL_CTS_CAP_SHIFT 14
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_DW 2
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_ADDR 8
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_MASK \
-+ 0x001f0000 // 20-16
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT 16
-+#define WF_LWTBL_FD_DW 2
-+#define WF_LWTBL_FD_ADDR 8
-+#define WF_LWTBL_FD_MASK \
-+ 0x00200000 // 21-21
-+#define WF_LWTBL_FD_SHIFT 21
-+#define WF_LWTBL_TD_DW 2
-+#define WF_LWTBL_TD_ADDR 8
-+#define WF_LWTBL_TD_MASK \
-+ 0x00400000 // 22-22
-+#define WF_LWTBL_TD_SHIFT 22
-+#define WF_LWTBL_SW_DW 2
-+#define WF_LWTBL_SW_ADDR 8
-+#define WF_LWTBL_SW_MASK \
-+ 0x00800000 // 23-23
-+#define WF_LWTBL_SW_SHIFT 23
-+#define WF_LWTBL_UL_DW 2
-+#define WF_LWTBL_UL_ADDR 8
-+#define WF_LWTBL_UL_MASK \
-+ 0x01000000 // 24-24
-+#define WF_LWTBL_UL_SHIFT 24
-+#define WF_LWTBL_TX_PS_DW 2
-+#define WF_LWTBL_TX_PS_ADDR 8
-+#define WF_LWTBL_TX_PS_MASK \
-+ 0x02000000 // 25-25
-+#define WF_LWTBL_TX_PS_SHIFT 25
-+#define WF_LWTBL_QOS_DW 2
-+#define WF_LWTBL_QOS_ADDR 8
-+#define WF_LWTBL_QOS_MASK \
-+ 0x04000000 // 26-26
-+#define WF_LWTBL_QOS_SHIFT 26
-+#define WF_LWTBL_HT_DW 2
-+#define WF_LWTBL_HT_ADDR 8
-+#define WF_LWTBL_HT_MASK \
-+ 0x08000000 // 27-27
-+#define WF_LWTBL_HT_SHIFT 27
-+#define WF_LWTBL_VHT_DW 2
-+#define WF_LWTBL_VHT_ADDR 8
-+#define WF_LWTBL_VHT_MASK \
-+ 0x10000000 // 28-28
-+#define WF_LWTBL_VHT_SHIFT 28
-+#define WF_LWTBL_HE_DW 2
-+#define WF_LWTBL_HE_ADDR 8
-+#define WF_LWTBL_HE_MASK \
-+ 0x20000000 // 29-29
-+#define WF_LWTBL_HE_SHIFT 29
-+#define WF_LWTBL_EHT_DW 2
-+#define WF_LWTBL_EHT_ADDR 8
-+#define WF_LWTBL_EHT_MASK \
-+ 0x40000000 // 30-30
-+#define WF_LWTBL_EHT_SHIFT 30
-+#define WF_LWTBL_MESH_DW 2
-+#define WF_LWTBL_MESH_ADDR 8
-+#define WF_LWTBL_MESH_MASK \
-+ 0x80000000 // 31-31
-+#define WF_LWTBL_MESH_SHIFT 31
-+// DW3
-+#define WF_LWTBL_WMM_Q_DW 3
-+#define WF_LWTBL_WMM_Q_ADDR 12
-+#define WF_LWTBL_WMM_Q_MASK \
-+ 0x00000003 // 1- 0
-+#define WF_LWTBL_WMM_Q_SHIFT 0
-+#define WF_LWTBL_EHT_SIG_MCS_DW 3
-+#define WF_LWTBL_EHT_SIG_MCS_ADDR 12
-+#define WF_LWTBL_EHT_SIG_MCS_MASK \
-+ 0x0000000c // 3- 2
-+#define WF_LWTBL_EHT_SIG_MCS_SHIFT 2
-+#define WF_LWTBL_HDRT_MODE_DW 3
-+#define WF_LWTBL_HDRT_MODE_ADDR 12
-+#define WF_LWTBL_HDRT_MODE_MASK \
-+ 0x00000010 // 4- 4
-+#define WF_LWTBL_HDRT_MODE_SHIFT 4
-+#define WF_LWTBL_BEAM_CHG_DW 3
-+#define WF_LWTBL_BEAM_CHG_ADDR 12
-+#define WF_LWTBL_BEAM_CHG_MASK \
-+ 0x00000020 // 5- 5
-+#define WF_LWTBL_BEAM_CHG_SHIFT 5
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_DW 3
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_ADDR 12
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK \
-+ 0x000000c0 // 7- 6
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT 6
-+#define WF_LWTBL_PFMU_IDX_DW 3
-+#define WF_LWTBL_PFMU_IDX_ADDR 12
-+#define WF_LWTBL_PFMU_IDX_MASK \
-+ 0x0000ff00 // 15- 8
-+#define WF_LWTBL_PFMU_IDX_SHIFT 8
-+#define WF_LWTBL_ULPF_IDX_DW 3
-+#define WF_LWTBL_ULPF_IDX_ADDR 12
-+#define WF_LWTBL_ULPF_IDX_MASK \
-+ 0x00ff0000 // 23-16
-+#define WF_LWTBL_ULPF_IDX_SHIFT 16
-+#define WF_LWTBL_RIBF_DW 3
-+#define WF_LWTBL_RIBF_ADDR 12
-+#define WF_LWTBL_RIBF_MASK \
-+ 0x01000000 // 24-24
-+#define WF_LWTBL_RIBF_SHIFT 24
-+#define WF_LWTBL_ULPF_DW 3
-+#define WF_LWTBL_ULPF_ADDR 12
-+#define WF_LWTBL_ULPF_MASK \
-+ 0x02000000 // 25-25
-+#define WF_LWTBL_ULPF_SHIFT 25
-+#define WF_LWTBL_BYPASS_TXSMM_DW 3
-+#define WF_LWTBL_BYPASS_TXSMM_ADDR 12
-+#define WF_LWTBL_BYPASS_TXSMM_MASK \
-+ 0x04000000 // 26-26
-+#define WF_LWTBL_BYPASS_TXSMM_SHIFT 26
-+#define WF_LWTBL_TBF_HT_DW 3
-+#define WF_LWTBL_TBF_HT_ADDR 12
-+#define WF_LWTBL_TBF_HT_MASK \
-+ 0x08000000 // 27-27
-+#define WF_LWTBL_TBF_HT_SHIFT 27
-+#define WF_LWTBL_TBF_VHT_DW 3
-+#define WF_LWTBL_TBF_VHT_ADDR 12
-+#define WF_LWTBL_TBF_VHT_MASK \
-+ 0x10000000 // 28-28
-+#define WF_LWTBL_TBF_VHT_SHIFT 28
-+#define WF_LWTBL_TBF_HE_DW 3
-+#define WF_LWTBL_TBF_HE_ADDR 12
-+#define WF_LWTBL_TBF_HE_MASK \
-+ 0x20000000 // 29-29
-+#define WF_LWTBL_TBF_HE_SHIFT 29
-+#define WF_LWTBL_TBF_EHT_DW 3
-+#define WF_LWTBL_TBF_EHT_ADDR 12
-+#define WF_LWTBL_TBF_EHT_MASK \
-+ 0x40000000 // 30-30
-+#define WF_LWTBL_TBF_EHT_SHIFT 30
-+#define WF_LWTBL_IGN_FBK_DW 3
-+#define WF_LWTBL_IGN_FBK_ADDR 12
-+#define WF_LWTBL_IGN_FBK_MASK \
-+ 0x80000000 // 31-31
-+#define WF_LWTBL_IGN_FBK_SHIFT 31
-+// DW4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_MASK \
-+ 0x00000007 // 2- 0
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT 0
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_MASK \
-+ 0x00000038 // 5- 3
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT 3
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_MASK \
-+ 0x000001c0 // 8- 6
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT 6
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_MASK \
-+ 0x00000e00 // 11- 9
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT 9
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_MASK \
-+ 0x00007000 // 14-12
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT 12
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_MASK \
-+ 0x00038000 // 17-15
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT 15
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_MASK \
-+ 0x001c0000 // 20-18
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT 18
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_DW 4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_ADDR 16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_MASK \
-+ 0x00e00000 // 23-21
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT 21
-+#define WF_LWTBL_PE_DW 4
-+#define WF_LWTBL_PE_ADDR 16
-+#define WF_LWTBL_PE_MASK \
-+ 0x03000000 // 25-24
-+#define WF_LWTBL_PE_SHIFT 24
-+#define WF_LWTBL_DIS_RHTR_DW 4
-+#define WF_LWTBL_DIS_RHTR_ADDR 16
-+#define WF_LWTBL_DIS_RHTR_MASK \
-+ 0x04000000 // 26-26
-+#define WF_LWTBL_DIS_RHTR_SHIFT 26
-+#define WF_LWTBL_LDPC_HT_DW 4
-+#define WF_LWTBL_LDPC_HT_ADDR 16
-+#define WF_LWTBL_LDPC_HT_MASK \
-+ 0x08000000 // 27-27
-+#define WF_LWTBL_LDPC_HT_SHIFT 27
-+#define WF_LWTBL_LDPC_VHT_DW 4
-+#define WF_LWTBL_LDPC_VHT_ADDR 16
-+#define WF_LWTBL_LDPC_VHT_MASK \
-+ 0x10000000 // 28-28
-+#define WF_LWTBL_LDPC_VHT_SHIFT 28
-+#define WF_LWTBL_LDPC_HE_DW 4
-+#define WF_LWTBL_LDPC_HE_ADDR 16
-+#define WF_LWTBL_LDPC_HE_MASK \
-+ 0x20000000 // 29-29
-+#define WF_LWTBL_LDPC_HE_SHIFT 29
-+#define WF_LWTBL_LDPC_EHT_DW 4
-+#define WF_LWTBL_LDPC_EHT_ADDR 16
-+#define WF_LWTBL_LDPC_EHT_MASK \
-+ 0x40000000 // 30-30
-+#define WF_LWTBL_LDPC_EHT_SHIFT 30
-+#define WF_LWTBL_BA_MODE_DW 4
-+#define WF_LWTBL_BA_MODE_ADDR 16
-+#define WF_LWTBL_BA_MODE_MASK \
-+ 0x80000000 // 31-31
-+#define WF_LWTBL_BA_MODE_SHIFT 31
-+// DW5
-+#define WF_LWTBL_AF_DW 5
-+#define WF_LWTBL_AF_ADDR 20
-+#define WF_LWTBL_AF_MASK \
-+ 0x00000007 // 2- 0
-+#define WF_LWTBL_AF_MASK_7992 \
-+ 0x0000000f // 3- 0
-+#define WF_LWTBL_AF_SHIFT 0
-+#define WF_LWTBL_AF_HE_DW 5
-+#define WF_LWTBL_AF_HE_ADDR 20
-+#define WF_LWTBL_AF_HE_MASK \
-+ 0x00000018 // 4- 3
-+#define WF_LWTBL_AF_HE_SHIFT 3
-+#define WF_LWTBL_RTS_DW 5
-+#define WF_LWTBL_RTS_ADDR 20
-+#define WF_LWTBL_RTS_MASK \
-+ 0x00000020 // 5- 5
-+#define WF_LWTBL_RTS_SHIFT 5
-+#define WF_LWTBL_SMPS_DW 5
-+#define WF_LWTBL_SMPS_ADDR 20
-+#define WF_LWTBL_SMPS_MASK \
-+ 0x00000040 // 6- 6
-+#define WF_LWTBL_SMPS_SHIFT 6
-+#define WF_LWTBL_DYN_BW_DW 5
-+#define WF_LWTBL_DYN_BW_ADDR 20
-+#define WF_LWTBL_DYN_BW_MASK \
-+ 0x00000080 // 7- 7
-+#define WF_LWTBL_DYN_BW_SHIFT 7
-+#define WF_LWTBL_MMSS_DW 5
-+#define WF_LWTBL_MMSS_ADDR 20
-+#define WF_LWTBL_MMSS_MASK \
-+ 0x00000700 // 10- 8
-+#define WF_LWTBL_MMSS_SHIFT 8
-+#define WF_LWTBL_USR_DW 5
-+#define WF_LWTBL_USR_ADDR 20
-+#define WF_LWTBL_USR_MASK \
-+ 0x00000800 // 11-11
-+#define WF_LWTBL_USR_SHIFT 11
-+#define WF_LWTBL_SR_R_DW 5
-+#define WF_LWTBL_SR_R_ADDR 20
-+#define WF_LWTBL_SR_R_MASK \
-+ 0x00007000 // 14-12
-+#define WF_LWTBL_SR_R_SHIFT 12
-+#define WF_LWTBL_SR_ABORT_DW 5
-+#define WF_LWTBL_SR_ABORT_ADDR 20
-+#define WF_LWTBL_SR_ABORT_MASK \
-+ 0x00008000 // 15-15
-+#define WF_LWTBL_SR_ABORT_SHIFT 15
-+#define WF_LWTBL_TX_POWER_OFFSET_DW 5
-+#define WF_LWTBL_TX_POWER_OFFSET_ADDR 20
-+#define WF_LWTBL_TX_POWER_OFFSET_MASK \
-+ 0x003f0000 // 21-16
-+#define WF_LWTBL_TX_POWER_OFFSET_SHIFT 16
-+#define WF_LWTBL_LTF_EHT_DW 5
-+#define WF_LWTBL_LTF_EHT_ADDR 20
-+#define WF_LWTBL_LTF_EHT_MASK \
-+ 0x00c00000 // 23-22
-+#define WF_LWTBL_LTF_EHT_SHIFT 22
-+#define WF_LWTBL_GI_EHT_DW 5
-+#define WF_LWTBL_GI_EHT_ADDR 20
-+#define WF_LWTBL_GI_EHT_MASK \
-+ 0x03000000 // 25-24
-+#define WF_LWTBL_GI_EHT_SHIFT 24
-+#define WF_LWTBL_DOPPL_DW 5
-+#define WF_LWTBL_DOPPL_ADDR 20
-+#define WF_LWTBL_DOPPL_MASK \
-+ 0x04000000 // 26-26
-+#define WF_LWTBL_DOPPL_SHIFT 26
-+#define WF_LWTBL_TXOP_PS_CAP_DW 5
-+#define WF_LWTBL_TXOP_PS_CAP_ADDR 20
-+#define WF_LWTBL_TXOP_PS_CAP_MASK \
-+ 0x08000000 // 27-27
-+#define WF_LWTBL_TXOP_PS_CAP_SHIFT 27
-+#define WF_LWTBL_DU_I_PSM_DW 5
-+#define WF_LWTBL_DU_I_PSM_ADDR 20
-+#define WF_LWTBL_DU_I_PSM_MASK \
-+ 0x10000000 // 28-28
-+#define WF_LWTBL_DU_I_PSM_SHIFT 28
-+#define WF_LWTBL_I_PSM_DW 5
-+#define WF_LWTBL_I_PSM_ADDR 20
-+#define WF_LWTBL_I_PSM_MASK \
-+ 0x20000000 // 29-29
-+#define WF_LWTBL_I_PSM_SHIFT 29
-+#define WF_LWTBL_PSM_DW 5
-+#define WF_LWTBL_PSM_ADDR 20
-+#define WF_LWTBL_PSM_MASK \
-+ 0x40000000 // 30-30
-+#define WF_LWTBL_PSM_SHIFT 30
-+#define WF_LWTBL_SKIP_TX_DW 5
-+#define WF_LWTBL_SKIP_TX_ADDR 20
-+#define WF_LWTBL_SKIP_TX_MASK \
-+ 0x80000000 // 31-31
-+#define WF_LWTBL_SKIP_TX_SHIFT 31
-+// DW6
-+#define WF_LWTBL_CBRN_DW 6
-+#define WF_LWTBL_CBRN_ADDR 24
-+#define WF_LWTBL_CBRN_MASK \
-+ 0x00000007 // 2- 0
-+#define WF_LWTBL_CBRN_SHIFT 0
-+#define WF_LWTBL_DBNSS_EN_DW 6
-+#define WF_LWTBL_DBNSS_EN_ADDR 24
-+#define WF_LWTBL_DBNSS_EN_MASK \
-+ 0x00000008 // 3- 3
-+#define WF_LWTBL_DBNSS_EN_SHIFT 3
-+#define WF_LWTBL_BAF_EN_DW 6
-+#define WF_LWTBL_BAF_EN_ADDR 24
-+#define WF_LWTBL_BAF_EN_MASK \
-+ 0x00000010 // 4- 4
-+#define WF_LWTBL_BAF_EN_SHIFT 4
-+#define WF_LWTBL_RDGBA_DW 6
-+#define WF_LWTBL_RDGBA_ADDR 24
-+#define WF_LWTBL_RDGBA_MASK \
-+ 0x00000020 // 5- 5
-+#define WF_LWTBL_RDGBA_SHIFT 5
-+#define WF_LWTBL_R_DW 6
-+#define WF_LWTBL_R_ADDR 24
-+#define WF_LWTBL_R_MASK \
-+ 0x00000040 // 6- 6
-+#define WF_LWTBL_R_SHIFT 6
-+#define WF_LWTBL_SPE_IDX_DW 6
-+#define WF_LWTBL_SPE_IDX_ADDR 24
-+#define WF_LWTBL_SPE_IDX_MASK \
-+ 0x00000f80 // 11- 7
-+#define WF_LWTBL_SPE_IDX_SHIFT 7
-+#define WF_LWTBL_G2_DW 6
-+#define WF_LWTBL_G2_ADDR 24
-+#define WF_LWTBL_G2_MASK \
-+ 0x00001000 // 12-12
-+#define WF_LWTBL_G2_SHIFT 12
-+#define WF_LWTBL_G4_DW 6
-+#define WF_LWTBL_G4_ADDR 24
-+#define WF_LWTBL_G4_MASK \
-+ 0x00002000 // 13-13
-+#define WF_LWTBL_G4_SHIFT 13
-+#define WF_LWTBL_G8_DW 6
-+#define WF_LWTBL_G8_ADDR 24
-+#define WF_LWTBL_G8_MASK \
-+ 0x00004000 // 14-14
-+#define WF_LWTBL_G8_SHIFT 14
-+#define WF_LWTBL_G16_DW 6
-+#define WF_LWTBL_G16_ADDR 24
-+#define WF_LWTBL_G16_MASK \
-+ 0x00008000 // 15-15
-+#define WF_LWTBL_G16_SHIFT 15
-+#define WF_LWTBL_G2_LTF_DW 6
-+#define WF_LWTBL_G2_LTF_ADDR 24
-+#define WF_LWTBL_G2_LTF_MASK \
-+ 0x00030000 // 17-16
-+#define WF_LWTBL_G2_LTF_SHIFT 16
-+#define WF_LWTBL_G4_LTF_DW 6
-+#define WF_LWTBL_G4_LTF_ADDR 24
-+#define WF_LWTBL_G4_LTF_MASK \
-+ 0x000c0000 // 19-18
-+#define WF_LWTBL_G4_LTF_SHIFT 18
-+#define WF_LWTBL_G8_LTF_DW 6
-+#define WF_LWTBL_G8_LTF_ADDR 24
-+#define WF_LWTBL_G8_LTF_MASK \
-+ 0x00300000 // 21-20
-+#define WF_LWTBL_G8_LTF_SHIFT 20
-+#define WF_LWTBL_G16_LTF_DW 6
-+#define WF_LWTBL_G16_LTF_ADDR 24
-+#define WF_LWTBL_G16_LTF_MASK \
-+ 0x00c00000 // 23-22
-+#define WF_LWTBL_G16_LTF_SHIFT 22
-+#define WF_LWTBL_G2_HE_DW 6
-+#define WF_LWTBL_G2_HE_ADDR 24
-+#define WF_LWTBL_G2_HE_MASK \
-+ 0x03000000 // 25-24
-+#define WF_LWTBL_G2_HE_SHIFT 24
-+#define WF_LWTBL_G4_HE_DW 6
-+#define WF_LWTBL_G4_HE_ADDR 24
-+#define WF_LWTBL_G4_HE_MASK \
-+ 0x0c000000 // 27-26
-+#define WF_LWTBL_G4_HE_SHIFT 26
-+#define WF_LWTBL_G8_HE_DW 6
-+#define WF_LWTBL_G8_HE_ADDR 24
-+#define WF_LWTBL_G8_HE_MASK \
-+ 0x30000000 // 29-28
-+#define WF_LWTBL_G8_HE_SHIFT 28
-+#define WF_LWTBL_G16_HE_DW 6
-+#define WF_LWTBL_G16_HE_ADDR 24
-+#define WF_LWTBL_G16_HE_MASK \
-+ 0xc0000000 // 31-30
-+#define WF_LWTBL_G16_HE_SHIFT 30
-+// DW7
-+#define WF_LWTBL_BA_WIN_SIZE0_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE0_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE0_MASK \
-+ 0x0000000f // 3- 0
-+#define WF_LWTBL_BA_WIN_SIZE0_SHIFT 0
-+#define WF_LWTBL_BA_WIN_SIZE1_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE1_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE1_MASK \
-+ 0x000000f0 // 7- 4
-+#define WF_LWTBL_BA_WIN_SIZE1_SHIFT 4
-+#define WF_LWTBL_BA_WIN_SIZE2_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE2_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE2_MASK \
-+ 0x00000f00 // 11- 8
-+#define WF_LWTBL_BA_WIN_SIZE2_SHIFT 8
-+#define WF_LWTBL_BA_WIN_SIZE3_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE3_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE3_MASK \
-+ 0x0000f000 // 15-12
-+#define WF_LWTBL_BA_WIN_SIZE3_SHIFT 12
-+#define WF_LWTBL_BA_WIN_SIZE4_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE4_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE4_MASK \
-+ 0x000f0000 // 19-16
-+#define WF_LWTBL_BA_WIN_SIZE4_SHIFT 16
-+#define WF_LWTBL_BA_WIN_SIZE5_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE5_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE5_MASK \
-+ 0x00f00000 // 23-20
-+#define WF_LWTBL_BA_WIN_SIZE5_SHIFT 20
-+#define WF_LWTBL_BA_WIN_SIZE6_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE6_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE6_MASK \
-+ 0x0f000000 // 27-24
-+#define WF_LWTBL_BA_WIN_SIZE6_SHIFT 24
-+#define WF_LWTBL_BA_WIN_SIZE7_DW 7
-+#define WF_LWTBL_BA_WIN_SIZE7_ADDR 28
-+#define WF_LWTBL_BA_WIN_SIZE7_MASK \
-+ 0xf0000000 // 31-28
-+#define WF_LWTBL_BA_WIN_SIZE7_SHIFT 28
-+// DW8
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_DW 8
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_ADDR 32
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_MASK \
-+ 0x0000001f // 4- 0
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT 0
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_DW 8
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_ADDR 32
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_MASK \
-+ 0x000003e0 // 9- 5
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT 5
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_DW 8
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_ADDR 32
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_MASK \
-+ 0x00007c00 // 14-10
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT 10
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_DW 8
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_ADDR 32
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_MASK \
-+ 0x000f8000 // 19-15
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT 15
-+#define WF_LWTBL_PARTIAL_AID_DW 8
-+#define WF_LWTBL_PARTIAL_AID_ADDR 32
-+#define WF_LWTBL_PARTIAL_AID_MASK \
-+ 0x1ff00000 // 28-20
-+#define WF_LWTBL_PARTIAL_AID_SHIFT 20
-+#define WF_LWTBL_CHK_PER_DW 8
-+#define WF_LWTBL_CHK_PER_ADDR 32
-+#define WF_LWTBL_CHK_PER_MASK \
-+ 0x80000000 // 31-31
-+#define WF_LWTBL_CHK_PER_SHIFT 31
-+// DW9
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_DW 9
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_ADDR 36
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_MASK \
-+ 0x00003fff // 13- 0
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT 0
-+#define WF_LWTBL_PRITX_SW_MODE_DW 9
-+#define WF_LWTBL_PRITX_SW_MODE_ADDR 36
-+#define WF_LWTBL_PRITX_SW_MODE_MASK \
-+ 0x00008000 // 15-15
-+#define WF_LWTBL_PRITX_SW_MODE_SHIFT 15
-+#define WF_LWTBL_PRITX_SW_MODE_MASK_7992 \
-+ 0x00004000 // 14-14
-+#define WF_LWTBL_PRITX_SW_MODE_SHIFT_7992 14
-+#define WF_LWTBL_PRITX_ERSU_DW 9
-+#define WF_LWTBL_PRITX_ERSU_ADDR 36
-+#define WF_LWTBL_PRITX_ERSU_MASK \
-+ 0x00010000 // 16-16
-+#define WF_LWTBL_PRITX_ERSU_SHIFT 16
-+#define WF_LWTBL_PRITX_ERSU_MASK_7992 \
-+ 0x00008000 // 15-15
-+#define WF_LWTBL_PRITX_ERSU_SHIFT_7992 15
-+#define WF_LWTBL_PRITX_PLR_DW 9
-+#define WF_LWTBL_PRITX_PLR_ADDR 36
-+#define WF_LWTBL_PRITX_PLR_MASK \
-+ 0x00020000 // 17-17
-+#define WF_LWTBL_PRITX_PLR_SHIFT 17
-+#define WF_LWTBL_PRITX_PLR_MASK_7992 \
-+ 0x00030000 // 17-16
-+#define WF_LWTBL_PRITX_PLR_SHIFT_7992 16
-+#define WF_LWTBL_PRITX_DCM_DW 9
-+#define WF_LWTBL_PRITX_DCM_ADDR 36
-+#define WF_LWTBL_PRITX_DCM_MASK \
-+ 0x00040000 // 18-18
-+#define WF_LWTBL_PRITX_DCM_SHIFT 18
-+#define WF_LWTBL_PRITX_ER106T_DW 9
-+#define WF_LWTBL_PRITX_ER106T_ADDR 36
-+#define WF_LWTBL_PRITX_ER106T_MASK \
-+ 0x00080000 // 19-19
-+#define WF_LWTBL_PRITX_ER106T_SHIFT 19
-+#define WF_LWTBL_FCAP_DW 9
-+#define WF_LWTBL_FCAP_ADDR 36
-+#define WF_LWTBL_FCAP_MASK \
-+ 0x00700000 // 22-20
-+#define WF_LWTBL_FCAP_SHIFT 20
-+#define WF_LWTBL_MPDU_FAIL_CNT_DW 9
-+#define WF_LWTBL_MPDU_FAIL_CNT_ADDR 36
-+#define WF_LWTBL_MPDU_FAIL_CNT_MASK \
-+ 0x03800000 // 25-23
-+#define WF_LWTBL_MPDU_FAIL_CNT_SHIFT 23
-+#define WF_LWTBL_MPDU_OK_CNT_DW 9
-+#define WF_LWTBL_MPDU_OK_CNT_ADDR 36
-+#define WF_LWTBL_MPDU_OK_CNT_MASK \
-+ 0x1c000000 // 28-26
-+#define WF_LWTBL_MPDU_OK_CNT_SHIFT 26
-+#define WF_LWTBL_RATE_IDX_DW 9
-+#define WF_LWTBL_RATE_IDX_ADDR 36
-+#define WF_LWTBL_RATE_IDX_MASK \
-+ 0xe0000000 // 31-29
-+#define WF_LWTBL_RATE_IDX_SHIFT 29
-+// DW10
-+#define WF_LWTBL_RATE1_DW 10
-+#define WF_LWTBL_RATE1_ADDR 40
-+#define WF_LWTBL_RATE1_MASK \
-+ 0x00007fff // 14- 0
-+#define WF_LWTBL_RATE1_SHIFT 0
-+#define WF_LWTBL_RATE2_DW 10
-+#define WF_LWTBL_RATE2_ADDR 40
-+#define WF_LWTBL_RATE2_MASK \
-+ 0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE2_SHIFT 16
-+// DW11
-+#define WF_LWTBL_RATE3_DW 11
-+#define WF_LWTBL_RATE3_ADDR 44
-+#define WF_LWTBL_RATE3_MASK \
-+ 0x00007fff // 14- 0
-+#define WF_LWTBL_RATE3_SHIFT 0
-+#define WF_LWTBL_RATE4_DW 11
-+#define WF_LWTBL_RATE4_ADDR 44
-+#define WF_LWTBL_RATE4_MASK \
-+ 0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE4_SHIFT 16
-+// DW12
-+#define WF_LWTBL_RATE5_DW 12
-+#define WF_LWTBL_RATE5_ADDR 48
-+#define WF_LWTBL_RATE5_MASK \
-+ 0x00007fff // 14- 0
-+#define WF_LWTBL_RATE5_SHIFT 0
-+#define WF_LWTBL_RATE6_DW 12
-+#define WF_LWTBL_RATE6_ADDR 48
-+#define WF_LWTBL_RATE6_MASK \
-+ 0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE6_SHIFT 16
-+// DW13
-+#define WF_LWTBL_RATE7_DW 13
-+#define WF_LWTBL_RATE7_ADDR 52
-+#define WF_LWTBL_RATE7_MASK \
-+ 0x00007fff // 14- 0
-+#define WF_LWTBL_RATE7_SHIFT 0
-+#define WF_LWTBL_RATE8_DW 13
-+#define WF_LWTBL_RATE8_ADDR 52
-+#define WF_LWTBL_RATE8_MASK \
-+ 0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE8_SHIFT 16
-+// DW14
-+#define WF_LWTBL_RATE1_TX_CNT_DW 14
-+#define WF_LWTBL_RATE1_TX_CNT_ADDR 56
-+#define WF_LWTBL_RATE1_TX_CNT_MASK \
-+ 0x0000ffff // 15- 0
-+#define WF_LWTBL_RATE1_TX_CNT_SHIFT 0
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_DW 14
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_ADDR 56
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_MASK \
-+ 0x00003000 // 13-12
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT 12
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_DW 14
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_ADDR 56
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_MASK \
-+ 0x0000c000 // 15-14
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT 14
-+#define WF_LWTBL_RATE1_FAIL_CNT_DW 14
-+#define WF_LWTBL_RATE1_FAIL_CNT_ADDR 56
-+#define WF_LWTBL_RATE1_FAIL_CNT_MASK \
-+ 0xffff0000 // 31-16
-+#define WF_LWTBL_RATE1_FAIL_CNT_SHIFT 16
-+// DW15
-+#define WF_LWTBL_RATE2_OK_CNT_DW 15
-+#define WF_LWTBL_RATE2_OK_CNT_ADDR 60
-+#define WF_LWTBL_RATE2_OK_CNT_MASK \
-+ 0x0000ffff // 15- 0
-+#define WF_LWTBL_RATE2_OK_CNT_SHIFT 0
-+#define WF_LWTBL_RATE3_OK_CNT_DW 15
-+#define WF_LWTBL_RATE3_OK_CNT_ADDR 60
-+#define WF_LWTBL_RATE3_OK_CNT_MASK \
-+ 0xffff0000 // 31-16
-+#define WF_LWTBL_RATE3_OK_CNT_SHIFT 16
-+// DW16
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_DW 16
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_ADDR 64
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_MASK \
-+ 0x0000ffff // 15- 0
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_SHIFT 0
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_DW 16
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_ADDR 64
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_MASK \
-+ 0xffff0000 // 31-16
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_SHIFT 16
-+// DW17
-+#define WF_LWTBL_OTHER_BW_TX_CNT_DW 17
-+#define WF_LWTBL_OTHER_BW_TX_CNT_ADDR 68
-+#define WF_LWTBL_OTHER_BW_TX_CNT_MASK \
-+ 0x0000ffff // 15- 0
-+#define WF_LWTBL_OTHER_BW_TX_CNT_SHIFT 0
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_DW 17
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_ADDR 68
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_MASK \
-+ 0xffff0000 // 31-16
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_SHIFT 16
-+// DW18
-+#define WF_LWTBL_RTS_OK_CNT_DW 18
-+#define WF_LWTBL_RTS_OK_CNT_ADDR 72
-+#define WF_LWTBL_RTS_OK_CNT_MASK \
-+ 0x0000ffff // 15- 0
-+#define WF_LWTBL_RTS_OK_CNT_SHIFT 0
-+#define WF_LWTBL_RTS_FAIL_CNT_DW 18
-+#define WF_LWTBL_RTS_FAIL_CNT_ADDR 72
-+#define WF_LWTBL_RTS_FAIL_CNT_MASK \
-+ 0xffff0000 // 31-16
-+#define WF_LWTBL_RTS_FAIL_CNT_SHIFT 16
-+// DW19
-+#define WF_LWTBL_DATA_RETRY_CNT_DW 19
-+#define WF_LWTBL_DATA_RETRY_CNT_ADDR 76
-+#define WF_LWTBL_DATA_RETRY_CNT_MASK \
-+ 0x0000ffff // 15- 0
-+#define WF_LWTBL_DATA_RETRY_CNT_SHIFT 0
-+#define WF_LWTBL_MGNT_RETRY_CNT_DW 19
-+#define WF_LWTBL_MGNT_RETRY_CNT_ADDR 76
-+#define WF_LWTBL_MGNT_RETRY_CNT_MASK \
-+ 0xffff0000 // 31-16
-+#define WF_LWTBL_MGNT_RETRY_CNT_SHIFT 16
-+// DW20
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_DW 20
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_ADDR 80
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_MASK \
-+ 0xffffffff // 31- 0
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_SHIFT 0
-+// DW21
-+// DO NOT process repeat field(adm[0])
-+// DW22
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_DW 22
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_ADDR 88
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_MASK \
-+ 0xffffffff // 31- 0
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_SHIFT 0
-+// DW23
-+// DO NOT process repeat field(adm[1])
-+// DW24
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_DW 24
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_ADDR 96
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_MASK \
-+ 0xffffffff // 31- 0
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_SHIFT 0
-+// DW25
-+// DO NOT process repeat field(adm[2])
-+// DW26
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_DW 26
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_ADDR 104
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_MASK \
-+ 0xffffffff // 31- 0
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_SHIFT 0
-+// DW27
-+// DO NOT process repeat field(adm[3])
-+// DW28
-+#define WF_LWTBL_RELATED_IDX0_DW 28
-+#define WF_LWTBL_RELATED_IDX0_ADDR 112
-+#define WF_LWTBL_RELATED_IDX0_MASK \
-+ 0x00000fff // 11- 0
-+#define WF_LWTBL_RELATED_IDX0_SHIFT 0
-+#define WF_LWTBL_RELATED_BAND0_DW 28
-+#define WF_LWTBL_RELATED_BAND0_ADDR 112
-+#define WF_LWTBL_RELATED_BAND0_MASK \
-+ 0x00003000 // 13-12
-+#define WF_LWTBL_RELATED_BAND0_SHIFT 12
-+#define WF_LWTBL_PRIMARY_MLD_BAND_DW 28
-+#define WF_LWTBL_PRIMARY_MLD_BAND_ADDR 112
-+#define WF_LWTBL_PRIMARY_MLD_BAND_MASK \
-+ 0x0000c000 // 15-14
-+#define WF_LWTBL_PRIMARY_MLD_BAND_SHIFT 14
-+#define WF_LWTBL_RELATED_IDX1_DW 28
-+#define WF_LWTBL_RELATED_IDX1_ADDR 112
-+#define WF_LWTBL_RELATED_IDX1_MASK \
-+ 0x0fff0000 // 27-16
-+#define WF_LWTBL_RELATED_IDX1_SHIFT 16
-+#define WF_LWTBL_RELATED_BAND1_DW 28
-+#define WF_LWTBL_RELATED_BAND1_ADDR 112
-+#define WF_LWTBL_RELATED_BAND1_MASK \
-+ 0x30000000 // 29-28
-+#define WF_LWTBL_RELATED_BAND1_SHIFT 28
-+#define WF_LWTBL_SECONDARY_MLD_BAND_DW 28
-+#define WF_LWTBL_SECONDARY_MLD_BAND_ADDR 112
-+#define WF_LWTBL_SECONDARY_MLD_BAND_MASK \
-+ 0xc0000000 // 31-30
-+#define WF_LWTBL_SECONDARY_MLD_BAND_SHIFT 30
-+// DW29
-+#define WF_LWTBL_DISPATCH_POLICY0_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY0_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY0_MASK \
-+ 0x00000003 // 1- 0
-+#define WF_LWTBL_DISPATCH_POLICY0_SHIFT 0
-+#define WF_LWTBL_DISPATCH_POLICY1_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY1_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY1_MASK \
-+ 0x0000000c // 3- 2
-+#define WF_LWTBL_DISPATCH_POLICY1_SHIFT 2
-+#define WF_LWTBL_DISPATCH_POLICY2_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY2_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY2_MASK \
-+ 0x00000030 // 5- 4
-+#define WF_LWTBL_DISPATCH_POLICY2_SHIFT 4
-+#define WF_LWTBL_DISPATCH_POLICY3_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY3_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY3_MASK \
-+ 0x000000c0 // 7- 6
-+#define WF_LWTBL_DISPATCH_POLICY3_SHIFT 6
-+#define WF_LWTBL_DISPATCH_POLICY4_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY4_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY4_MASK \
-+ 0x00000300 // 9- 8
-+#define WF_LWTBL_DISPATCH_POLICY4_SHIFT 8
-+#define WF_LWTBL_DISPATCH_POLICY5_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY5_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY5_MASK \
-+ 0x00000c00 // 11-10
-+#define WF_LWTBL_DISPATCH_POLICY5_SHIFT 10
-+#define WF_LWTBL_DISPATCH_POLICY6_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY6_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY6_MASK \
-+ 0x00003000 // 13-12
-+#define WF_LWTBL_DISPATCH_POLICY6_SHIFT 12
-+#define WF_LWTBL_DISPATCH_POLICY7_DW 29
-+#define WF_LWTBL_DISPATCH_POLICY7_ADDR 116
-+#define WF_LWTBL_DISPATCH_POLICY7_MASK \
-+ 0x0000c000 // 15-14
-+#define WF_LWTBL_DISPATCH_POLICY7_SHIFT 14
-+#define WF_LWTBL_OWN_MLD_ID_DW 29
-+#define WF_LWTBL_OWN_MLD_ID_ADDR 116
-+#define WF_LWTBL_OWN_MLD_ID_MASK \
-+ 0x003f0000 // 21-16
-+#define WF_LWTBL_OWN_MLD_ID_SHIFT 16
-+#define WF_LWTBL_EMLSR0_DW 29
-+#define WF_LWTBL_EMLSR0_ADDR 116
-+#define WF_LWTBL_EMLSR0_MASK \
-+ 0x00400000 // 22-22
-+#define WF_LWTBL_EMLSR0_SHIFT 22
-+#define WF_LWTBL_EMLMR0_DW 29
-+#define WF_LWTBL_EMLMR0_ADDR 116
-+#define WF_LWTBL_EMLMR0_MASK \
-+ 0x00800000 // 23-23
-+#define WF_LWTBL_EMLMR0_SHIFT 23
-+#define WF_LWTBL_EMLSR1_DW 29
-+#define WF_LWTBL_EMLSR1_ADDR 116
-+#define WF_LWTBL_EMLSR1_MASK \
-+ 0x01000000 // 24-24
-+#define WF_LWTBL_EMLSR1_SHIFT 24
-+#define WF_LWTBL_EMLMR1_DW 29
-+#define WF_LWTBL_EMLMR1_ADDR 116
-+#define WF_LWTBL_EMLMR1_MASK \
-+ 0x02000000 // 25-25
-+#define WF_LWTBL_EMLMR1_SHIFT 25
-+#define WF_LWTBL_EMLSR2_DW 29
-+#define WF_LWTBL_EMLSR2_ADDR 116
-+#define WF_LWTBL_EMLSR2_MASK \
-+ 0x04000000 // 26-26
-+#define WF_LWTBL_EMLSR2_SHIFT 26
-+#define WF_LWTBL_EMLMR2_DW 29
-+#define WF_LWTBL_EMLMR2_ADDR 116
-+#define WF_LWTBL_EMLMR2_MASK \
-+ 0x08000000 // 27-27
-+#define WF_LWTBL_EMLMR2_SHIFT 27
-+#define WF_LWTBL_STR_BITMAP_DW 29
-+#define WF_LWTBL_STR_BITMAP_ADDR 116
-+#define WF_LWTBL_STR_BITMAP_MASK \
-+ 0xe0000000 // 31-29
-+#define WF_LWTBL_STR_BITMAP_SHIFT 29
-+// DW30
-+#define WF_LWTBL_DISPATCH_ORDER_DW 30
-+#define WF_LWTBL_DISPATCH_ORDER_ADDR 120
-+#define WF_LWTBL_DISPATCH_ORDER_MASK \
-+ 0x0000007f // 6- 0
-+#define WF_LWTBL_DISPATCH_ORDER_SHIFT 0
-+#define WF_LWTBL_DISPATCH_RATIO_DW 30
-+#define WF_LWTBL_DISPATCH_RATIO_ADDR 120
-+#define WF_LWTBL_DISPATCH_RATIO_MASK \
-+ 0x00003f80 // 13- 7
-+#define WF_LWTBL_DISPATCH_RATIO_SHIFT 7
-+#define WF_LWTBL_LINK_MGF_DW 30
-+#define WF_LWTBL_LINK_MGF_ADDR 120
-+#define WF_LWTBL_LINK_MGF_MASK \
-+ 0xffff0000 // 31-16
-+#define WF_LWTBL_LINK_MGF_SHIFT 16
-+// DW31
-+#define WF_LWTBL_BFTX_TB_DW 31
-+#define WF_LWTBL_BFTX_TB_ADDR 124
-+#define WF_LWTBL_BFTX_TB_MASK \
-+ 0x00800000 // 23-23
-+#define WF_LWTBL_DROP_DW 31
-+#define WF_LWTBL_DROP_ADDR 124
-+#define WF_LWTBL_DROP_MASK \
-+ 0x01000000 // 24-24
-+#define WF_LWTBL_DROP_SHIFT 24
-+#define WF_LWTBL_CASCAD_DW 31
-+#define WF_LWTBL_CASCAD_ADDR 124
-+#define WF_LWTBL_CASCAD_MASK \
-+ 0x02000000 // 25-25
-+#define WF_LWTBL_CASCAD_SHIFT 25
-+#define WF_LWTBL_ALL_ACK_DW 31
-+#define WF_LWTBL_ALL_ACK_ADDR 124
-+#define WF_LWTBL_ALL_ACK_MASK \
-+ 0x04000000 // 26-26
-+#define WF_LWTBL_ALL_ACK_SHIFT 26
-+#define WF_LWTBL_MPDU_SIZE_DW 31
-+#define WF_LWTBL_MPDU_SIZE_ADDR 124
-+#define WF_LWTBL_MPDU_SIZE_MASK \
-+ 0x18000000 // 28-27
-+#define WF_LWTBL_MPDU_SIZE_SHIFT 27
-+#define WF_LWTBL_RXD_DUP_MODE_DW 31
-+#define WF_LWTBL_RXD_DUP_MODE_ADDR 124
-+#define WF_LWTBL_RXD_DUP_MODE_MASK \
-+ 0x60000000 // 30-29
-+#define WF_LWTBL_RXD_DUP_MODE_SHIFT 29
-+#define WF_LWTBL_ACK_EN_DW 31
-+#define WF_LWTBL_ACK_EN_ADDR 128
-+#define WF_LWTBL_ACK_EN_MASK \
-+ 0x80000000 // 31-31
-+#define WF_LWTBL_ACK_EN_SHIFT 31
-+// DW32
-+#define WF_LWTBL_OM_INFO_DW 32
-+#define WF_LWTBL_OM_INFO_ADDR 128
-+#define WF_LWTBL_OM_INFO_MASK \
-+ 0x00000fff // 11- 0
-+#define WF_LWTBL_OM_INFO_SHIFT 0
-+#define WF_LWTBL_OM_INFO_EHT_DW 32
-+#define WF_LWTBL_OM_INFO_EHT_ADDR 128
-+#define WF_LWTBL_OM_INFO_EHT_MASK \
-+ 0x0000f000 // 15-12
-+#define WF_LWTBL_OM_INFO_EHT_SHIFT 12
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_DW 32
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_ADDR 128
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK \
-+ 0x00010000 // 16-16
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_SHIFT 16
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_DW 32
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_ADDR 128
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_MASK \
-+ 0x1ffe0000 // 28-17
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT 17
-+// DW33
-+#define WF_LWTBL_USER_RSSI_DW 33
-+#define WF_LWTBL_USER_RSSI_ADDR 132
-+#define WF_LWTBL_USER_RSSI_MASK \
-+ 0x000001ff // 8- 0
-+#define WF_LWTBL_USER_RSSI_SHIFT 0
-+#define WF_LWTBL_USER_SNR_DW 33
-+#define WF_LWTBL_USER_SNR_ADDR 132
-+#define WF_LWTBL_USER_SNR_MASK \
-+ 0x00007e00 // 14- 9
-+#define WF_LWTBL_USER_SNR_SHIFT 9
-+#define WF_LWTBL_RAPID_REACTION_RATE_DW 33
-+#define WF_LWTBL_RAPID_REACTION_RATE_ADDR 132
-+#define WF_LWTBL_RAPID_REACTION_RATE_MASK \
-+ 0x0fff0000 // 27-16
-+#define WF_LWTBL_RAPID_REACTION_RATE_SHIFT 16
-+#define WF_LWTBL_HT_AMSDU_DW 33
-+#define WF_LWTBL_HT_AMSDU_ADDR 132
-+#define WF_LWTBL_HT_AMSDU_MASK \
-+ 0x40000000 // 30-30
-+#define WF_LWTBL_HT_AMSDU_SHIFT 30
-+#define WF_LWTBL_AMSDU_CROSS_LG_DW 33
-+#define WF_LWTBL_AMSDU_CROSS_LG_ADDR 132
-+#define WF_LWTBL_AMSDU_CROSS_LG_MASK \
-+ 0x80000000 // 31-31
-+#define WF_LWTBL_AMSDU_CROSS_LG_SHIFT 31
-+// DW34
-+#define WF_LWTBL_RESP_RCPI0_DW 34
-+#define WF_LWTBL_RESP_RCPI0_ADDR 136
-+#define WF_LWTBL_RESP_RCPI0_MASK \
-+ 0x000000ff // 7- 0
-+#define WF_LWTBL_RESP_RCPI0_SHIFT 0
-+#define WF_LWTBL_RESP_RCPI1_DW 34
-+#define WF_LWTBL_RESP_RCPI1_ADDR 136
-+#define WF_LWTBL_RESP_RCPI1_MASK \
-+ 0x0000ff00 // 15- 8
-+#define WF_LWTBL_RESP_RCPI1_SHIFT 8
-+#define WF_LWTBL_RESP_RCPI2_DW 34
-+#define WF_LWTBL_RESP_RCPI2_ADDR 136
-+#define WF_LWTBL_RESP_RCPI2_MASK \
-+ 0x00ff0000 // 23-16
-+#define WF_LWTBL_RESP_RCPI2_SHIFT 16
-+#define WF_LWTBL_RESP_RCPI3_DW 34
-+#define WF_LWTBL_RESP_RCPI3_ADDR 136
-+#define WF_LWTBL_RESP_RCPI3_MASK \
-+ 0xff000000 // 31-24
-+#define WF_LWTBL_RESP_RCPI3_SHIFT 24
-+// DW35
-+#define WF_LWTBL_SNR_RX0_DW 35
-+#define WF_LWTBL_SNR_RX0_ADDR 140
-+#define WF_LWTBL_SNR_RX0_MASK \
-+ 0x0000003f // 5- 0
-+#define WF_LWTBL_SNR_RX0_SHIFT 0
-+#define WF_LWTBL_SNR_RX1_DW 35
-+#define WF_LWTBL_SNR_RX1_ADDR 140
-+#define WF_LWTBL_SNR_RX1_MASK \
-+ 0x00000fc0 // 11- 6
-+#define WF_LWTBL_SNR_RX1_SHIFT 6
-+#define WF_LWTBL_SNR_RX2_DW 35
-+#define WF_LWTBL_SNR_RX2_ADDR 140
-+#define WF_LWTBL_SNR_RX2_MASK \
-+ 0x0003f000 // 17-12
-+#define WF_LWTBL_SNR_RX2_SHIFT 12
-+#define WF_LWTBL_SNR_RX3_DW 35
-+#define WF_LWTBL_SNR_RX3_ADDR 140
-+#define WF_LWTBL_SNR_RX3_MASK \
-+ 0x00fc0000 // 23-18
-+#define WF_LWTBL_SNR_RX3_SHIFT 18
-+
-+/* WTBL Group - Packet Number */
-+/* DW 2 */
-+#define WTBL_PN0_MASK BITS(0, 7)
-+#define WTBL_PN0_OFFSET 0
-+#define WTBL_PN1_MASK BITS(8, 15)
-+#define WTBL_PN1_OFFSET 8
-+#define WTBL_PN2_MASK BITS(16, 23)
-+#define WTBL_PN2_OFFSET 16
-+#define WTBL_PN3_MASK BITS(24, 31)
-+#define WTBL_PN3_OFFSET 24
-+
-+/* DW 3 */
-+#define WTBL_PN4_MASK BITS(0, 7)
-+#define WTBL_PN4_OFFSET 0
-+#define WTBL_PN5_MASK BITS(8, 15)
-+#define WTBL_PN5_OFFSET 8
-+
-+/* DW 4 */
-+#define WTBL_BIPN0_MASK BITS(0, 7)
-+#define WTBL_BIPN0_OFFSET 0
-+#define WTBL_BIPN1_MASK BITS(8, 15)
-+#define WTBL_BIPN1_OFFSET 8
-+#define WTBL_BIPN2_MASK BITS(16, 23)
-+#define WTBL_BIPN2_OFFSET 16
-+#define WTBL_BIPN3_MASK BITS(24, 31)
-+#define WTBL_BIPN3_OFFSET 24
-+
-+/* DW 5 */
-+#define WTBL_BIPN4_MASK BITS(0, 7)
-+#define WTBL_BIPN4_OFFSET 0
-+#define WTBL_BIPN5_MASK BITS(8, 15)
-+#define WTBL_BIPN5_OFFSET 8
-+
-+/* UWTBL DW 6 */
-+#define WTBL_AMSDU_LEN_MASK BITS(0, 5)
-+#define WTBL_AMSDU_LEN_OFFSET 0
-+#define WTBL_AMSDU_NUM_MASK BITS(6, 10)
-+#define WTBL_AMSDU_NUM_OFFSET 6
-+#define WTBL_AMSDU_EN_MASK BIT(11)
-+#define WTBL_AMSDU_EN_OFFSET 11
-+
-+/* UWTBL DW 8 */
-+#define WTBL_SEC_ADDR_MODE_MASK BITS(20, 21)
-+#define WTBL_SEC_ADDR_MODE_OFFSET 20
-+
-+/* LWTBL Rate field */
-+#define WTBL_RATE_TX_RATE_MASK BITS(0, 5)
-+#define WTBL_RATE_TX_RATE_OFFSET 0
-+#define WTBL_RATE_TX_MODE_MASK BITS(6, 9)
-+#define WTBL_RATE_TX_MODE_OFFSET 6
-+#define WTBL_RATE_NSTS_MASK BITS(10, 13)
-+#define WTBL_RATE_NSTS_OFFSET 10
-+#define WTBL_RATE_STBC_MASK BIT(14)
-+#define WTBL_RATE_STBC_OFFSET 14
-+
-+/***** WTBL(LMAC) DW Offset *****/
-+/* LMAC WTBL Group - Peer Unique Information */
-+#define WTBL_GROUP_PEER_INFO_DW_0 0
-+#define WTBL_GROUP_PEER_INFO_DW_1 1
-+
-+/* WTBL Group - TxRx Capability/Information */
-+#define WTBL_GROUP_TRX_CAP_DW_2 2
-+#define WTBL_GROUP_TRX_CAP_DW_3 3
-+#define WTBL_GROUP_TRX_CAP_DW_4 4
-+#define WTBL_GROUP_TRX_CAP_DW_5 5
-+#define WTBL_GROUP_TRX_CAP_DW_6 6
-+#define WTBL_GROUP_TRX_CAP_DW_7 7
-+#define WTBL_GROUP_TRX_CAP_DW_8 8
-+#define WTBL_GROUP_TRX_CAP_DW_9 9
-+
-+/* WTBL Group - Auto Rate Table*/
-+#define WTBL_GROUP_AUTO_RATE_1_2 10
-+#define WTBL_GROUP_AUTO_RATE_3_4 11
-+#define WTBL_GROUP_AUTO_RATE_5_6 12
-+#define WTBL_GROUP_AUTO_RATE_7_8 13
-+
-+/* WTBL Group - Tx Counter */
-+#define WTBL_GROUP_TX_CNT_LINE_1 14
-+#define WTBL_GROUP_TX_CNT_LINE_2 15
-+#define WTBL_GROUP_TX_CNT_LINE_3 16
-+#define WTBL_GROUP_TX_CNT_LINE_4 17
-+#define WTBL_GROUP_TX_CNT_LINE_5 18
-+#define WTBL_GROUP_TX_CNT_LINE_6 19
-+
-+/* WTBL Group - Admission Control Counter */
-+#define WTBL_GROUP_ADM_CNT_LINE_1 20
-+#define WTBL_GROUP_ADM_CNT_LINE_2 21
-+#define WTBL_GROUP_ADM_CNT_LINE_3 22
-+#define WTBL_GROUP_ADM_CNT_LINE_4 23
-+#define WTBL_GROUP_ADM_CNT_LINE_5 24
-+#define WTBL_GROUP_ADM_CNT_LINE_6 25
-+#define WTBL_GROUP_ADM_CNT_LINE_7 26
-+#define WTBL_GROUP_ADM_CNT_LINE_8 27
-+
-+/* WTBL Group -MLO Info */
-+#define WTBL_GROUP_MLO_INFO_LINE_1 28
-+#define WTBL_GROUP_MLO_INFO_LINE_2 29
-+#define WTBL_GROUP_MLO_INFO_LINE_3 30
-+
-+/* WTBL Group -RESP Info */
-+#define WTBL_GROUP_RESP_INFO_DW_31 31
-+
-+/* WTBL Group -RX DUP Info */
-+#define WTBL_GROUP_RX_DUP_INFO_DW_32 32
-+
-+/* WTBL Group - Rx Statistics Counter */
-+#define WTBL_GROUP_RX_STAT_CNT_LINE_1 33
-+#define WTBL_GROUP_RX_STAT_CNT_LINE_2 34
-+#define WTBL_GROUP_RX_STAT_CNT_LINE_3 35
-+
-+/* UWTBL Group - HW AMSDU */
-+#define UWTBL_HW_AMSDU_DW WF_UWTBL_AMSDU_CFG_DW
-+
-+/* LWTBL DW 4 */
-+#define WTBL_DIS_RHTR WF_LWTBL_DIS_RHTR_MASK
-+
-+/* UWTBL DW 5 */
-+#define WTBL_KEY_LINK_DW_KEY_LOC0_MASK BITS(0, 10)
-+#define WTBL_PSM WF_LWTBL_PSM_MASK
-+
-+/* Need to sync with FW define */
-+#define INVALID_KEY_ENTRY WTBL_KEY_LINK_DW_KEY_LOC0_MASK
-+
-+// RATE
-+#define WTBL_RATE_TX_RATE_MASK BITS(0, 5)
-+#define WTBL_RATE_TX_RATE_OFFSET 0
-+#define WTBL_RATE_TX_MODE_MASK BITS(6, 9)
-+#define WTBL_RATE_TX_MODE_OFFSET 6
-+#define WTBL_RATE_NSTS_MASK BITS(10, 13)
-+#define WTBL_RATE_NSTS_OFFSET 10
-+#define WTBL_RATE_STBC_MASK BIT(14)
-+#define WTBL_RATE_STBC_OFFSET 14
-+#endif
-+
-+#endif
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-new file mode 100644
-index 00000000..678b009e
---- /dev/null
-+++ b/mt7996/mtk_debugfs.c
-@@ -0,0 +1,2484 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2023 MediaTek Inc.
-+ */
-+#include "mt7996.h"
-+#include "../mt76.h"
-+#include "mcu.h"
-+#include "mac.h"
-+#include "eeprom.h"
-+#include "mtk_debug.h"
-+#include "mtk_mcu.h"
-+#include "coredump.h"
-+
-+#ifdef CONFIG_MTK_DEBUG
-+
-+/* AGG INFO */
-+static int
-+mt7996_agginfo_read_per_band(struct seq_file *s, int band_idx)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u64 total_burst, total_ampdu, ampdu_cnt[16];
-+ u32 value, idx, row_idx, col_idx, start_range, agg_rang_sel[16], burst_cnt[16], band_offset = 0;
-+ u8 partial_str[16] = {}, full_str[64] = {};
-+
-+ switch (band_idx) {
-+ case 0:
-+ band_offset = 0;
-+ break;
-+ case 1:
-+ band_offset = BN1_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
-+ break;
-+ case 2:
-+ band_offset = IP1_BN0_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
-+ break;
-+ default:
-+ return 0;
-+ }
-+
-+ seq_printf(s, "Band %d AGG Status\n", band_idx);
-+ seq_printf(s, "===============================\n");
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR0_ADDR + band_offset);
-+ seq_printf(s, "AC00 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC01 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT);
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR1_ADDR + band_offset);
-+ seq_printf(s, "AC02 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC03 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT);
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR2_ADDR + band_offset);
-+ seq_printf(s, "AC10 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC11 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT);
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR3_ADDR + band_offset);
-+ seq_printf(s, "AC12 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC13 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT);
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR4_ADDR + band_offset);
-+ seq_printf(s, "AC20 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC21 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT);
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR5_ADDR + band_offset);
-+ seq_printf(s, "AC22 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC23 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT);
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR6_ADDR + band_offset);
-+ seq_printf(s, "AC30 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC31 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT);
-+ value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR7_ADDR + band_offset);
-+ seq_printf(s, "AC32 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT);
-+ seq_printf(s, "AC33 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK) >> BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT);
-+
-+ switch (band_idx) {
-+ case 0:
-+ band_offset = 0;
-+ break;
-+ case 1:
-+ band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+ break;
-+ case 2:
-+ band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+ break;
-+ default:
-+ return 0;
-+ }
-+
-+ seq_printf(s, "===AMPDU Related Counters===\n");
-+
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC0_ADDR + band_offset);
-+ agg_rang_sel[0] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT;
-+ agg_rang_sel[1] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT;
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC1_ADDR + band_offset);
-+ agg_rang_sel[2] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT;
-+ agg_rang_sel[3] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT;
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC2_ADDR + band_offset);
-+ agg_rang_sel[4] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT;
-+ agg_rang_sel[5] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT;
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC3_ADDR + band_offset);
-+ agg_rang_sel[6] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT;
-+ agg_rang_sel[7] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT;
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC4_ADDR + band_offset);
-+ agg_rang_sel[8] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT;
-+ agg_rang_sel[9] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT;
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC5_ADDR + band_offset);
-+ agg_rang_sel[10] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT;
-+ agg_rang_sel[11] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT;
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC6_ADDR + band_offset);
-+ agg_rang_sel[12] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT;
-+ agg_rang_sel[13] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT;
-+ value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC7_ADDR + band_offset);
-+ agg_rang_sel[14] = (value & BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK) >> BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT;
-+
-+ burst_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR0_ADDR + band_offset);
-+ burst_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR1_ADDR + band_offset);
-+ burst_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR2_ADDR + band_offset);
-+ burst_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR3_ADDR + band_offset);
-+ burst_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR4_ADDR + band_offset);
-+ burst_cnt[5] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR5_ADDR + band_offset);
-+ burst_cnt[6] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR6_ADDR + band_offset);
-+ burst_cnt[7] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR7_ADDR + band_offset);
-+ burst_cnt[8] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR8_ADDR + band_offset);
-+ burst_cnt[9] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR9_ADDR + band_offset);
-+ burst_cnt[10] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR10_ADDR + band_offset);
-+ burst_cnt[11] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR11_ADDR + band_offset);
-+ burst_cnt[12] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR12_ADDR + band_offset);
-+ burst_cnt[13] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR13_ADDR + band_offset);
-+ burst_cnt[14] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR14_ADDR + band_offset);
-+ burst_cnt[15] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR15_ADDR + band_offset);
-+
-+ start_range = 1;
-+ total_burst = 0;
-+ total_ampdu = 0;
-+ agg_rang_sel[15] = 1023;
-+
-+ /* Need to add 1 after read from AGG_RANG_SEL CR */
-+ for (idx = 0; idx < 16; idx++) {
-+ agg_rang_sel[idx]++;
-+ total_burst += burst_cnt[idx];
-+
-+ if (start_range == agg_rang_sel[idx])
-+ ampdu_cnt[idx] = (u64) start_range * burst_cnt[idx];
-+ else
-+ ampdu_cnt[idx] = (u64) ((start_range + agg_rang_sel[idx]) >> 1) * burst_cnt[idx];
-+
-+ start_range = agg_rang_sel[idx] + 1;
-+ total_ampdu += ampdu_cnt[idx];
-+ }
-+
-+ start_range = 1;
-+ sprintf(full_str, "%13s ", "Tx Agg Range:");
-+
-+ for (row_idx = 0; row_idx < 4; row_idx++) {
-+ for (col_idx = 0; col_idx < 4; col_idx++, idx++) {
-+ idx = 4 * row_idx + col_idx;
-+
-+ if (start_range == agg_rang_sel[idx])
-+ sprintf(partial_str, "%d", agg_rang_sel[idx]);
-+ else
-+ sprintf(partial_str, "%d~%d", start_range, agg_rang_sel[idx]);
-+
-+ start_range = agg_rang_sel[idx] + 1;
-+ sprintf(full_str + strlen(full_str), "%-11s ", partial_str);
-+ }
-+
-+ idx = 4 * row_idx;
-+
-+ seq_printf(s, "%s\n", full_str);
-+ seq_printf(s, "%13s 0x%-9x 0x%-9x 0x%-9x 0x%-9x\n",
-+ row_idx ? "" : "Burst count:",
-+ burst_cnt[idx], burst_cnt[idx + 1],
-+ burst_cnt[idx + 2], burst_cnt[idx + 3]);
-+
-+ if (total_burst != 0) {
-+ if (row_idx == 0)
-+ sprintf(full_str, "%13s ",
-+ "Burst ratio:");
-+ else
-+ sprintf(full_str, "%13s ", "");
-+
-+ for (col_idx = 0; col_idx < 4; col_idx++) {
-+ u64 count = (u64) burst_cnt[idx + col_idx] * 100;
-+
-+ sprintf(partial_str, "(%llu%%)",
-+ div64_u64(count, total_burst));
-+ sprintf(full_str + strlen(full_str),
-+ "%-11s ", partial_str);
-+ }
-+
-+ seq_printf(s, "%s\n", full_str);
-+
-+ if (row_idx == 0)
-+ sprintf(full_str, "%13s ",
-+ "MDPU ratio:");
-+ else
-+ sprintf(full_str, "%13s ", "");
-+
-+ for (col_idx = 0; col_idx < 4; col_idx++) {
-+ u64 count = ampdu_cnt[idx + col_idx] * 100;
-+
-+ sprintf(partial_str, "(%llu%%)",
-+ div64_u64(count, total_ampdu));
-+ sprintf(full_str + strlen(full_str),
-+ "%-11s ", partial_str);
-+ }
-+
-+ seq_printf(s, "%s\n", full_str);
-+ }
-+
-+ sprintf(full_str, "%13s ", "");
-+ }
-+
-+ return 0;
-+}
-+
-+static int mt7996_agginfo_read_band0(struct seq_file *s, void *data)
-+{
-+ mt7996_agginfo_read_per_band(s, MT_BAND0);
-+ return 0;
-+}
-+
-+static int mt7996_agginfo_read_band1(struct seq_file *s, void *data)
-+{
-+ mt7996_agginfo_read_per_band(s, MT_BAND1);
-+ return 0;
-+}
-+
-+static int mt7996_agginfo_read_band2(struct seq_file *s, void *data)
-+{
-+ mt7996_agginfo_read_per_band(s, MT_BAND2);
-+ return 0;
-+}
-+
-+/* AMSDU INFO */
-+static int mt7996_amsdu_result_read(struct seq_file *s, void *data)
-+{
-+#define HW_MSDU_CNT_ADDR 0xf400
-+#define HW_MSDU_NUM_MAX 33
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u32 ple_stat[HW_MSDU_NUM_MAX] = {0}, total_amsdu = 0;
-+ u8 i;
-+
-+ for (i = 0; i < HW_MSDU_NUM_MAX; i++)
-+ ple_stat[i] = mt76_rr(dev, HW_MSDU_CNT_ADDR + i * 0x04);
-+
-+ seq_printf(s, "TXD counter status of MSDU:\n");
-+
-+ for (i = 0; i < HW_MSDU_NUM_MAX; i++)
-+ total_amsdu += ple_stat[i];
-+
-+ for (i = 0; i < HW_MSDU_NUM_MAX; i++) {
-+ seq_printf(s, "AMSDU pack count of %d MSDU in TXD: 0x%x ", i, ple_stat[i]);
-+ if (total_amsdu != 0)
-+ seq_printf(s, "(%d%%)\n", ple_stat[i] * 100 / total_amsdu);
-+ else
-+ seq_printf(s, "\n");
-+ }
-+
-+ return 0;
-+}
-+
-+/* DBG MODLE */
-+static int
-+mt7996_fw_debug_module_set(void *data, u64 module)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ dev->dbg.fw_dbg_module = module;
-+ return 0;
-+}
-+
-+static int
-+mt7996_fw_debug_module_get(void *data, u64 *module)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ *module = dev->dbg.fw_dbg_module;
-+ return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_module, mt7996_fw_debug_module_get,
-+ mt7996_fw_debug_module_set, "%lld\n");
-+
-+static int
-+mt7996_fw_debug_level_set(void *data, u64 level)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ dev->dbg.fw_dbg_lv = level;
-+ mt7996_mcu_fw_dbg_ctrl(dev, dev->dbg.fw_dbg_module, dev->dbg.fw_dbg_lv);
-+ return 0;
-+}
-+
-+static int
-+mt7996_fw_debug_level_get(void *data, u64 *level)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ *level = dev->dbg.fw_dbg_lv;
-+ return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_level, mt7996_fw_debug_level_get,
-+ mt7996_fw_debug_level_set, "%lld\n");
-+
-+/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_set */
-+static int
-+mt7996_wa_set(void *data, u64 val)
-+{
-+ struct mt7996_dev *dev = data;
-+ u32 arg1, arg2, arg3;
-+
-+ arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
-+ arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
-+ arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
-+
-+ return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
-+ arg1, arg2, arg3);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_set, NULL, mt7996_wa_set,
-+ "0x%llx\n");
-+
-+/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_query */
-+static int
-+mt7996_wa_query(void *data, u64 val)
-+{
-+ struct mt7996_dev *dev = data;
-+ u32 arg1, arg2, arg3;
-+
-+ arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
-+ arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
-+ arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
-+
-+ return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY),
-+ arg1, arg2, arg3);
-+ return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_query, NULL, mt7996_wa_query,
-+ "0x%llx\n");
-+
-+static int mt7996_dump_version(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ seq_printf(s, "Version: 3.3.24.2\n");
-+
-+ if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
-+ return 0;
-+
-+ seq_printf(s, "Rom Patch Build Time: %.16s\n", dev->patch_build_date);
-+ seq_printf(s, "WM Patch Build Time: %.15s, Mode: %s\n",
-+ dev->ram_build_date[MT7996_RAM_TYPE_WM],
-+ dev->testmode_enable ? "Testmode" : "Normal mode");
-+ seq_printf(s, "WA Patch Build Time: %.15s\n",
-+ dev->ram_build_date[MT7996_RAM_TYPE_WA]);
-+ seq_printf(s, "DSP Patch Build Time: %.15s\n",
-+ dev->ram_build_date[MT7996_RAM_TYPE_DSP]);
-+ return 0;
-+}
-+
-+/* fw wm call trace info dump */
-+void mt7996_show_lp_history(struct seq_file *s, u32 type)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ struct mt7996_crash_data *crash_data;
-+ struct mt7996_coredump *dump;
-+ u64 now = 0;
-+ int i = 0;
-+ u8 fw_type = !!type;
-+
-+ mutex_lock(&dev->dump_mutex);
-+
-+ crash_data = mt7996_coredump_new(dev, fw_type);
-+ if (!crash_data) {
-+ mutex_unlock(&dev->dump_mutex);
-+ seq_printf(s, "the coredump is disable!\n");
-+ return;
-+ }
-+ mutex_unlock(&dev->dump_mutex);
-+
-+ dump = mt7996_coredump_build(dev, fw_type, false);
-+ if (!dump) {
-+ seq_printf(s, "no call stack data found!\n");
-+ return;
-+ }
-+
-+ seq_printf(s, "\x1b[32m%s log output\x1b[0m\n", dump->fw_type);
-+ seq_printf(s, "\x1b[32mfw status: %s\n", dump->fw_state);
-+ mt7996_dump_version(s, NULL);
-+ /* PC log */
-+ now = jiffies;
-+ for (i = 0; i < 10; i++)
-+ seq_printf(s, "\tCurrent PC=%x\n", dump->pc_cur[i]);
-+
-+ seq_printf(s, "PC log contorl=0x%x(T=%llu)(latest PC index = 0x%x)\n",
-+ dump->pc_dbg_ctrl, now, dump->pc_cur_idx);
-+ for (i = 0; i < 32; i++)
-+ seq_printf(s, "\tPC log(%d)=0x%08x\n", i, dump->pc_stack[i]);
-+
-+ /* LR log */
-+ now = jiffies;
-+ seq_printf(s, "\nLR log contorl=0x%x(T=%llu)(latest LR index = 0x%x)\n",
-+ dump->lr_dbg_ctrl, now, dump->lr_cur_idx);
-+ for (i = 0; i < 32; i++)
-+ seq_printf(s, "\tLR log(%d)=0x%08x\n", i, dump->lr_stack[i]);
-+
-+ vfree(dump);
-+}
-+
-+static int mt7996_fw_wa_info_read(struct seq_file *s, void *data)
-+{
-+ seq_printf(s, "======[ShowPcLpHistory]======\n");
-+ mt7996_show_lp_history(s, MT7996_RAM_TYPE_WA);
-+ seq_printf(s, "======[End ShowPcLpHistory]==\n");
-+
-+ return 0;
-+}
-+
-+static int mt7996_fw_wm_info_read(struct seq_file *s, void *data)
-+{
-+ seq_printf(s, "======[ShowPcLpHistory]======\n");
-+ mt7996_show_lp_history(s, MT7996_RAM_TYPE_WM);
-+ seq_printf(s, "======[End ShowPcLpHistory]==\n");
-+
-+ return 0;
-+}
-+
-+/* dma info dump */
-+static void
-+dump_dma_tx_ring_info(struct seq_file *s, struct mt7996_dev *dev, char *str1, char *str2, u32 ring_base)
-+{
-+ u32 base, cnt, cidx, didx, queue_cnt;
-+
-+ base= mt76_rr(dev, ring_base);
-+ cnt = mt76_rr(dev, ring_base + 4);
-+ cidx = mt76_rr(dev, ring_base + 8);
-+ didx = mt76_rr(dev, ring_base + 12);
-+ queue_cnt = (cidx >= didx) ? (cidx - didx) : (cidx - didx + cnt);
-+
-+ seq_printf(s, "%20s %6s %10x %15x %10x %10x %10x\n", str1, str2, base, cnt, cidx, didx, queue_cnt);
-+}
-+
-+static void
-+dump_dma_rx_ring_info(struct seq_file *s, struct mt7996_dev *dev, char *str1, char *str2, u32 ring_base)
-+{
-+ u32 base, ctrl1, cnt, cidx, didx, queue_cnt;
-+
-+ base= mt76_rr(dev, ring_base);
-+ ctrl1 = mt76_rr(dev, ring_base + 4);
-+ cidx = mt76_rr(dev, ring_base + 8) & 0xfff;
-+ didx = mt76_rr(dev, ring_base + 12) & 0xfff;
-+ cnt = ctrl1 & 0xfff;
-+ queue_cnt = (didx > cidx) ? (didx - cidx - 1) : (didx - cidx + cnt - 1);
-+
-+ seq_printf(s, "%20s %6s %10x %10x(%3x) %10x %10x %10x\n",
-+ str1, str2, base, ctrl1, cnt, cidx, didx, queue_cnt);
-+}
-+
-+static void
-+mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
-+{
-+ u32 sys_ctrl[10];
-+
-+ /* HOST DMA0 information */
-+ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR);
-+ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR);
-+ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR);
-+
-+ seq_printf(s, "HOST_DMA Configuration\n");
-+ seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
-+ "DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
-+ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+ "DMA0", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+
-+ if (dev->hif2) {
-+ /* HOST DMA1 information */
-+ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR);
-+ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR);
-+ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR);
-+
-+ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+ "DMA0P1", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+ (sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+ }
-+
-+ seq_printf(s, "HOST_DMA0 Ring Configuration\n");
-+ seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
-+ "Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
-+ dump_dma_tx_ring_info(s, dev, "T0:TXD0(H2MAC)", "STA",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T1:TXD1(H2MAC)", "STA",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T2:TXD2(H2MAC)", "STA",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T3:", "STA",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T4:", "STA",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T5:", "STA",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T6:", "STA",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T16:FWDL", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T17:Cmd(H2WM)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T18:TXD0(H2WA)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T19:TXD1(H2WA)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T20:Cmd(H2WA)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T22:TXD3(H2WA)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR);
-+
-+
-+ dump_dma_rx_ring_info(s, dev, "R0:Event(WM2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R1:Event(WA2H)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R2:TxDone0(WA2H)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R4:Data0(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R12:MSDU_PG2(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "IND:IND_CMD(MAC2H)", "Both",
-+ WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR);
-+
-+ if (dev->hif2) {
-+ seq_printf(s, "HOST_DMA0 PCIe1 Ring Configuration\n");
-+ seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
-+ "Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
-+ dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
-+
-+ dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
-+ }
-+
-+ /* MCU DMA information */
-+ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR);
-+ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR);
-+ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR);
-+
-+ seq_printf(s, "MCU_DMA Configuration\n");
-+ seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
-+ "DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
-+ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+ "DMA0", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
-+ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+ (sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+
-+ seq_printf(s, "MCU_DMA0 Ring Configuration\n");
-+ seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
-+ "Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
-+ dump_dma_tx_ring_info(s, dev, "T0:Event(WM2H)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T1:Event(WA2H)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T2:TxDone0(WA2H)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T3:TxDone1(WA2H)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T4:TXD(WM2MAC)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T5:TXCMD(WM2MAC)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T6:TXD(WA2MAC)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R0:FWDL", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R1:Cmd(H2WM)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R2:TXD0(H2WA)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R3:TXD1(H2WA)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R4:Cmd(H2WA)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R5:Data0(MAC2WM)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R6:TxDone(MAC2WM)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R7:SPL/RPT(MAC2WM)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R8:TxDone(MAC2WA)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R9:Data1(MAC2WM)", "Both",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R10:TXD2(H2WA)", "AP",
-+ WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
-+
-+ /* MEM DMA information */
-+ sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR);
-+ sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR);
-+ sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR);
-+
-+ seq_printf(s, "MEM_DMA Configuration\n");
-+ seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
-+ "DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
-+ seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+ "MEM", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
-+ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+ (sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+ >> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+
-+ seq_printf(s, "MEM_DMA Ring Configuration\n");
-+ seq_printf(s, "%20s %6s %10s %10s %10s %10s %10s\n",
-+ "Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
-+ dump_dma_tx_ring_info(s, dev, "T0:CmdEvent(WM2WA)", "AP",
-+ WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR);
-+ dump_dma_tx_ring_info(s, dev, "T1:CmdEvent(WA2WM)", "AP",
-+ WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R0:CmdEvent(WM2WA)", "AP",
-+ WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR);
-+ dump_dma_rx_ring_info(s, dev, "R1:CmdEvent(WA2WM)", "AP",
-+ WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR);
-+}
-+
-+static int mt7996_trinfo_read(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ mt7996_show_dma_info(s, dev);
-+ return 0;
-+}
-+
-+/* MIB INFO */
-+static int mt7996_mibinfo_read_per_band(struct seq_file *s, int band_idx)
-+{
-+#define BSS_NUM 4
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u8 bss_nums = BSS_NUM;
-+ u32 idx;
-+ u32 mac_val, band_offset = 0, band_offset_umib = 0;
-+ u32 msdr6, msdr9, msdr18;
-+ u32 rvsr0, rscr26, rscr35, mctr5, mctr6, msr0, msr1, msr2;
-+ u32 tbcr0, tbcr1, tbcr2, tbcr3, tbcr4;
-+ u32 btscr[7];
-+ u32 tdrcr[5];
-+ u32 mbtocr[16], mbtbcr[16], mbrocr[16], mbrbcr[16];
-+ u32 btcr, btbcr, brocr, brbcr, btdcr, brdcr;
-+ u32 mu_cnt[5];
-+ u32 ampdu_cnt[3];
-+ u64 per;
-+
-+ switch (band_idx) {
-+ case 0:
-+ band_offset = 0;
-+ band_offset_umib = 0;
-+ break;
-+ case 1:
-+ band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+ band_offset_umib = WF_UMIB_TOP_B1BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
-+ break;
-+ case 2:
-+ band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+ band_offset_umib = WF_UMIB_TOP_B2BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
-+ break;
-+ default:
-+ return true;
-+ }
-+
-+ seq_printf(s, "Band %d MIB Status\n", band_idx);
-+ seq_printf(s, "===============================\n");
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_M0SCR0_ADDR + band_offset);
-+ seq_printf(s, "MIB Status Control=0x%x\n", mac_val);
-+
-+ msdr6 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR6_ADDR + band_offset);
-+ rvsr0 = mt76_rr(dev, BN0_WF_MIB_TOP_RVSR0_ADDR + band_offset);
-+ rscr35 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR35_ADDR + band_offset);
-+ msdr9 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR9_ADDR + band_offset);
-+ rscr26 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR26_ADDR + band_offset);
-+ mctr5 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR5_ADDR + band_offset);
-+ mctr6 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR6_ADDR + band_offset);
-+ msdr18 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR18_ADDR + band_offset);
-+ msr0 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR0_ADDR + band_offset);
-+ msr1 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR1_ADDR + band_offset);
-+ msr2 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR2_ADDR + band_offset);
-+ ampdu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR0_ADDR + band_offset);
-+ ampdu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR3_ADDR + band_offset);
-+ ampdu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR4_ADDR + band_offset);
-+ ampdu_cnt[1] &= BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK;
-+ ampdu_cnt[2] &= BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK;
-+
-+ seq_printf(s, "===Phy/Timing Related Counters===\n");
-+ seq_printf(s, "\tChannelIdleCnt=0x%x\n",
-+ msdr6 & BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK);
-+ seq_printf(s, "\tCCA_NAV_Tx_Time=0x%x\n",
-+ msdr9 & BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK);
-+ seq_printf(s, "\tRx_MDRDY_CNT=0x%x\n",
-+ rscr26 & BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK);
-+ seq_printf(s, "\tCCK_MDRDY_TIME=0x%x, OFDM_MDRDY_TIME=0x%x",
-+ msr0 & BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK,
-+ msr1 & BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK);
-+ seq_printf(s, ", OFDM_GREEN_MDRDY_TIME=0x%x\n",
-+ msr2 & BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK);
-+ seq_printf(s, "\tPrim CCA Time=0x%x\n",
-+ mctr5 & BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK);
-+ seq_printf(s, "\tSec CCA Time=0x%x\n",
-+ mctr6 & BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK);
-+ seq_printf(s, "\tPrim ED Time=0x%x\n",
-+ msdr18 & BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK);
-+
-+ seq_printf(s, "===Tx Related Counters(Generic)===\n");
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR18_ADDR + band_offset);
-+ dev->dbg.bcn_total_cnt[band_idx] +=
-+ (mac_val & BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK);
-+ seq_printf(s, "\tBeaconTxCnt=0x%x\n", dev->dbg.bcn_total_cnt[band_idx]);
-+ dev->dbg.bcn_total_cnt[band_idx] = 0;
-+
-+ tbcr0 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR0_ADDR + band_offset);
-+ seq_printf(s, "\tTx 20MHz Cnt=0x%x\n",
-+ tbcr0 & BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK);
-+ tbcr1 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR1_ADDR + band_offset);
-+ seq_printf(s, "\tTx 40MHz Cnt=0x%x\n",
-+ tbcr1 & BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK);
-+ tbcr2 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR2_ADDR + band_offset);
-+ seq_printf(s, "\tTx 80MHz Cnt=0x%x\n",
-+ tbcr2 & BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK);
-+ tbcr3 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR3_ADDR + band_offset);
-+ seq_printf(s, "\tTx 160MHz Cnt=0x%x\n",
-+ tbcr3 & BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK);
-+ tbcr4 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR4_ADDR + band_offset);
-+ seq_printf(s, "\tTx 320MHz Cnt=0x%x\n",
-+ tbcr4 & BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK);
-+ seq_printf(s, "\tAMPDU Cnt=0x%x\n", ampdu_cnt[0]);
-+ seq_printf(s, "\tAMPDU MPDU Cnt=0x%x\n", ampdu_cnt[1]);
-+ seq_printf(s, "\tAMPDU MPDU Ack Cnt=0x%x\n", ampdu_cnt[2]);
-+ per = (ampdu_cnt[2] == 0 ?
-+ 0 : 1000 * (ampdu_cnt[1] - ampdu_cnt[2]) / ampdu_cnt[1]);
-+ seq_printf(s, "\tAMPDU MPDU PER=%llu.%1llu%%\n", per / 10, per % 10);
-+
-+ seq_printf(s, "===MU Related Counters===\n");
-+ mu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BSCR2_ADDR + band_offset);
-+ mu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR5_ADDR + band_offset);
-+ mu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR6_ADDR + band_offset);
-+ mu_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR8_ADDR + band_offset);
-+ mu_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR7_ADDR + band_offset);
-+
-+ seq_printf(s, "\tMUBF_TX_COUNT=0x%x\n",
-+ mu_cnt[0] & BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK);
-+ seq_printf(s, "\tMU_TX_MPDU_COUNT(Ok+Fail)=0x%x\n", mu_cnt[1]);
-+ seq_printf(s, "\tMU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[2]);
-+ seq_printf(s, "\tMU_TO_MU_FAIL_PPDU_COUNT=0x%x\n", mu_cnt[3]);
-+ seq_printf(s, "\tSU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[4]);
-+
-+ seq_printf(s, "===Rx Related Counters(Generic)===\n");
-+ seq_printf(s, "\tVector Mismacth Cnt=0x%x\n",
-+ rvsr0 & BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK);
-+ seq_printf(s, "\tDelimiter Fail Cnt=0x%x\n",
-+ rscr35 & BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK);
-+
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR1_ADDR + band_offset);
-+ seq_printf(s, "\tRxFCSErrCnt=0x%x\n",
-+ (mac_val & BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK));
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR33_ADDR + band_offset);
-+ seq_printf(s, "\tRxFifoFullCnt=0x%x\n",
-+ (mac_val & BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK));
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR36_ADDR + band_offset);
-+ seq_printf(s, "\tRxLenMismatch=0x%x\n",
-+ (mac_val & BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK));
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR31_ADDR + band_offset);
-+ seq_printf(s, "\tRxMPDUCnt=0x%x\n",
-+ (mac_val & BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK));
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR27_ADDR + band_offset);
-+ seq_printf(s, "\tRx AMPDU Cnt=0x%x\n", mac_val);
-+ mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR28_ADDR + band_offset);
-+ seq_printf(s, "\tRx Total ByteCnt=0x%x\n", mac_val);
-+
-+
-+ /* Per-BSS T/RX Counters */
-+ seq_printf(s, "===Per-BSS Related Tx/Rx Counters===\n");
-+ seq_printf(s, "BSS Idx TxCnt/DataCnt TxByteCnt RxOkCnt/DataCnt RxByteCnt\n");
-+ for (idx = 0; idx < bss_nums; idx++) {
-+ btcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTCR_ADDR + band_offset + idx * 4);
-+ btdcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTDCR_ADDR + band_offset + idx * 4);
-+ btbcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + idx * 4);
-+
-+ brocr = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + idx * 4);
-+ brdcr = mt76_rr(dev, WF_UMIB_TOP_B0BRDCR_ADDR + band_offset_umib + idx * 4);
-+ brbcr = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + idx * 4);
-+
-+ seq_printf(s, "%d\t 0x%x/0x%x\t 0x%x \t 0x%x/0x%x \t 0x%x\n",
-+ idx, btcr, btdcr, btbcr, brocr, brdcr, brbcr);
-+ }
-+
-+ seq_printf(s, "===Per-BSS Related MIB Counters===\n");
-+ seq_printf(s, "BSS Idx RTSTx/RetryCnt BAMissCnt AckFailCnt FrmRetry1/2/3Cnt\n");
-+
-+ /* Per-BSS TX Status */
-+ for (idx = 0; idx < bss_nums; idx++) {
-+ btscr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR5_ADDR + band_offset + idx * 4);
-+ btscr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR6_ADDR + band_offset + idx * 4);
-+ btscr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR0_ADDR + band_offset + idx * 4);
-+ btscr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR1_ADDR + band_offset + idx * 4);
-+ btscr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR2_ADDR + band_offset + idx * 4);
-+ btscr[5] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR3_ADDR + band_offset + idx * 4);
-+ btscr[6] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR4_ADDR + band_offset + idx * 4);
-+
-+ seq_printf(s, "%d:\t0x%x/0x%x 0x%x \t 0x%x \t 0x%x/0x%x/0x%x\n",
-+ idx, (btscr[0] & BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK),
-+ (btscr[1] & BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK),
-+ (btscr[2] & BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK),
-+ (btscr[3] & BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK),
-+ (btscr[4] & BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK),
-+ (btscr[5] & BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK),
-+ (btscr[6] & BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK));
-+ }
-+
-+ /* Dummy delimiter insertion result */
-+ seq_printf(s, "===Dummy delimiter insertion result===\n");
-+ tdrcr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR0_ADDR + band_offset);
-+ tdrcr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR1_ADDR + band_offset);
-+ tdrcr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR2_ADDR + band_offset);
-+ tdrcr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR3_ADDR + band_offset);
-+ tdrcr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR4_ADDR + band_offset);
-+
-+ seq_printf(s, "Range0 = %d\t Range1 = %d\t Range2 = %d\t Range3 = %d\t Range4 = %d\n",
-+ tdrcr[0],
-+ tdrcr[1],
-+ tdrcr[2],
-+ tdrcr[3],
-+ tdrcr[4]);
-+
-+ /* Per-MBSS T/RX Counters */
-+ seq_printf(s, "===Per-MBSS Related Tx/Rx Counters===\n");
-+ seq_printf(s, "MBSSIdx TxOkCnt TxByteCnt RxOkCnt RxByteCnt\n");
-+
-+ for (idx = 0; idx < 16; idx++) {
-+ mbtocr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTOCR_ADDR + band_offset + (bss_nums + idx) * 4);
-+ mbtbcr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + (bss_nums + idx) * 4);
-+
-+ mbrocr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
-+ mbrbcr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
-+ }
-+
-+ for (idx = 0; idx < 16; idx++) {
-+ seq_printf(s, "%d\t 0x%x\t 0x%x \t 0x%x \t 0x%x\n",
-+ idx, mbtocr[idx], mbtbcr[idx], mbrocr[idx], mbrbcr[idx]);
-+ }
-+
-+ return 0;
-+}
-+
-+static int mt7996_mibinfo_band0(struct seq_file *s, void *data)
-+{
-+ mt7996_mibinfo_read_per_band(s, MT_BAND0);
-+ return 0;
-+}
-+
-+static int mt7996_mibinfo_band1(struct seq_file *s, void *data)
-+{
-+ mt7996_mibinfo_read_per_band(s, MT_BAND1);
-+ return 0;
-+}
-+
-+static int mt7996_mibinfo_band2(struct seq_file *s, void *data)
-+{
-+ mt7996_mibinfo_read_per_band(s, MT_BAND2);
-+ return 0;
-+}
-+
-+/* WTBL INFO */
-+static int
-+mt7996_wtbl_read_raw(struct mt7996_dev *dev, u16 idx,
-+ enum mt7996_wtbl_type type, u16 start_dw,
-+ u16 len, void *buf)
-+{
-+ u32 *dest_cpy = (u32 *)buf;
-+ u32 size_dw = len;
-+ u32 src = 0;
-+
-+ if (!buf)
-+ return 0xFF;
-+
-+ if (type == WTBL_TYPE_LMAC) {
-+ mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
-+ FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
-+ src = LWTBL_IDX2BASE(idx, start_dw);
-+ } else if (type == WTBL_TYPE_UMAC) {
-+ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+ src = UWTBL_IDX2BASE(idx, start_dw);
-+ } else if (type == WTBL_TYPE_KEY) {
-+ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ MT_DBG_UWTBL_TOP_WDUCR_TARGET |
-+ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+ src = KEYTBL_IDX2BASE(idx, start_dw);
-+ }
-+
-+ while (size_dw--) {
-+ *dest_cpy++ = mt76_rr(dev, src);
-+ src += 4;
-+ };
-+
-+ return 0;
-+}
-+
-+#if 0
-+static int
-+mt7996_wtbl_write_raw(struct mt7996_dev *dev, u16 idx,
-+ enum mt7996_wtbl_type type, u16 start_dw,
-+ u32 val)
-+{
-+ u32 addr = 0;
-+
-+ if (type == WTBL_TYPE_LMAC) {
-+ mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
-+ FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
-+ addr = LWTBL_IDX2BASE(idx, start_dw);
-+ } else if (type == WTBL_TYPE_UMAC) {
-+ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+ addr = UWTBL_IDX2BASE(idx, start_dw);
-+ } else if (type == WTBL_TYPE_KEY) {
-+ mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ MT_DBG_UWTBL_TOP_WDUCR_TARGET |
-+ FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+ addr = KEYTBL_IDX2BASE(idx, start_dw);
-+ }
-+
-+ mt76_wr(dev, addr, val);
-+
-+ return 0;
-+}
-+#endif
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW0[] = {
-+ {"MUAR_IDX", WF_LWTBL_MUAR_MASK, WF_LWTBL_MUAR_SHIFT,false},
-+ {"RCA1", WF_LWTBL_RCA1_MASK, NO_SHIFT_DEFINE, false},
-+ {"KID", WF_LWTBL_KID_MASK, WF_LWTBL_KID_SHIFT, false},
-+ {"RCID", WF_LWTBL_RCID_MASK, NO_SHIFT_DEFINE, false},
-+ {"BAND", WF_LWTBL_BAND_MASK, WF_LWTBL_BAND_SHIFT,false},
-+ {"RV", WF_LWTBL_RV_MASK, NO_SHIFT_DEFINE, false},
-+ {"RCA2", WF_LWTBL_RCA2_MASK, NO_SHIFT_DEFINE, false},
-+ {"WPI_FLAG", WF_LWTBL_WPI_FLAG_MASK, NO_SHIFT_DEFINE,true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw0_1(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LinkAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
-+ lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
-+
-+ /* LMAC WTBL DW 0 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 0/1\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_PEER_INFO_DW_0*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW0[i].name) {
-+
-+ if (WTBL_LMAC_DW0[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW0[i].name,
-+ (dw_value & WTBL_LMAC_DW0[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW0[i].name,
-+ (dw_value & WTBL_LMAC_DW0[i].mask) >> WTBL_LMAC_DW0[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse *WTBL_LMAC_DW2;
-+static const struct berse_wtbl_parse WTBL_LMAC_DW2_7996[] = {
-+ {"AID", WF_LWTBL_AID_MASK, WF_LWTBL_AID_SHIFT, false},
-+ {"GID_SU", WF_LWTBL_GID_SU_MASK, NO_SHIFT_DEFINE, false},
-+ {"SPP_EN", WF_LWTBL_SPP_EN_MASK, NO_SHIFT_DEFINE, false},
-+ {"WPI_EVEN", WF_LWTBL_WPI_EVEN_MASK, NO_SHIFT_DEFINE, false},
-+ {"AAD_OM", WF_LWTBL_AAD_OM_MASK, NO_SHIFT_DEFINE, false},
-+ {"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT, true},
-+ {"FROM_DS", WF_LWTBL_FD_MASK, NO_SHIFT_DEFINE, false},
-+ {"TO_DS", WF_LWTBL_TD_MASK, NO_SHIFT_DEFINE, false},
-+ {"SW", WF_LWTBL_SW_MASK, NO_SHIFT_DEFINE, false},
-+ {"UL", WF_LWTBL_UL_MASK, NO_SHIFT_DEFINE, false},
-+ {"TX_POWER_SAVE", WF_LWTBL_TX_PS_MASK, NO_SHIFT_DEFINE, true},
-+ {"QOS", WF_LWTBL_QOS_MASK, NO_SHIFT_DEFINE, false},
-+ {"HT", WF_LWTBL_HT_MASK, NO_SHIFT_DEFINE, false},
-+ {"VHT", WF_LWTBL_VHT_MASK, NO_SHIFT_DEFINE, false},
-+ {"HE", WF_LWTBL_HE_MASK, NO_SHIFT_DEFINE, false},
-+ {"EHT", WF_LWTBL_EHT_MASK, NO_SHIFT_DEFINE, false},
-+ {"MESH", WF_LWTBL_MESH_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW2_7992[] = {
-+ {"AID", WF_LWTBL_AID_MASK, WF_LWTBL_AID_SHIFT, false},
-+ {"GID_SU", WF_LWTBL_GID_SU_MASK, NO_SHIFT_DEFINE, false},
-+ {"DUAL_PTEC_EN", WF_LWTBL_DUAL_PTEC_EN_MASK, NO_SHIFT_DEFINE, false},
-+ {"DUAL_CTS_CAP", WF_LWTBL_DUAL_CTS_CAP_MASK, NO_SHIFT_DEFINE, false},
-+ {"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT, true},
-+ {"FROM_DS", WF_LWTBL_FD_MASK, NO_SHIFT_DEFINE, false},
-+ {"TO_DS", WF_LWTBL_TD_MASK, NO_SHIFT_DEFINE, false},
-+ {"SW", WF_LWTBL_SW_MASK, NO_SHIFT_DEFINE, false},
-+ {"UL", WF_LWTBL_UL_MASK, NO_SHIFT_DEFINE, false},
-+ {"TX_POWER_SAVE", WF_LWTBL_TX_PS_MASK, NO_SHIFT_DEFINE, true},
-+ {"QOS", WF_LWTBL_QOS_MASK, NO_SHIFT_DEFINE, false},
-+ {"HT", WF_LWTBL_HT_MASK, NO_SHIFT_DEFINE, false},
-+ {"VHT", WF_LWTBL_VHT_MASK, NO_SHIFT_DEFINE, false},
-+ {"HE", WF_LWTBL_HE_MASK, NO_SHIFT_DEFINE, false},
-+ {"EHT", WF_LWTBL_EHT_MASK, NO_SHIFT_DEFINE, false},
-+ {"MESH", WF_LWTBL_MESH_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw2(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 2 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 2\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW2[i].name) {
-+
-+ if (WTBL_LMAC_DW2[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW2[i].name,
-+ (dw_value & WTBL_LMAC_DW2[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW2[i].name,
-+ (dw_value & WTBL_LMAC_DW2[i].mask) >> WTBL_LMAC_DW2[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW3[] = {
-+ {"WMM_Q", WF_LWTBL_WMM_Q_MASK, WF_LWTBL_WMM_Q_SHIFT, false},
-+ {"EHT_SIG_MCS", WF_LWTBL_EHT_SIG_MCS_MASK, WF_LWTBL_EHT_SIG_MCS_SHIFT, false},
-+ {"HDRT_MODE", WF_LWTBL_HDRT_MODE_MASK, NO_SHIFT_DEFINE, false},
-+ {"BEAM_CHG", WF_LWTBL_BEAM_CHG_MASK, NO_SHIFT_DEFINE, false},
-+ {"EHT_LTF_SYM_NUM", WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK, WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT, true},
-+ {"PFMU_IDX", WF_LWTBL_PFMU_IDX_MASK, WF_LWTBL_PFMU_IDX_SHIFT, false},
-+ {"ULPF_IDX", WF_LWTBL_ULPF_IDX_MASK, WF_LWTBL_ULPF_IDX_SHIFT, false},
-+ {"RIBF", WF_LWTBL_RIBF_MASK, NO_SHIFT_DEFINE, false},
-+ {"ULPF", WF_LWTBL_ULPF_MASK, NO_SHIFT_DEFINE, false},
-+ {"BYPASS_TXSMM", WF_LWTBL_BYPASS_TXSMM_MASK, NO_SHIFT_DEFINE, true},
-+ {"TBF_HT", WF_LWTBL_TBF_HT_MASK, NO_SHIFT_DEFINE, false},
-+ {"TBF_VHT", WF_LWTBL_TBF_VHT_MASK, NO_SHIFT_DEFINE, false},
-+ {"TBF_HE", WF_LWTBL_TBF_HE_MASK, NO_SHIFT_DEFINE, false},
-+ {"TBF_EHT", WF_LWTBL_TBF_EHT_MASK, NO_SHIFT_DEFINE, false},
-+ {"IGN_FBK", WF_LWTBL_IGN_FBK_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw3(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 3 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 3\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_3*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW3[i].name) {
-+
-+ if (WTBL_LMAC_DW3[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW3[i].name,
-+ (dw_value & WTBL_LMAC_DW3[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW3[i].name,
-+ (dw_value & WTBL_LMAC_DW3[i].mask) >> WTBL_LMAC_DW3[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW4[] = {
-+ {"NEGOTIATED_WINSIZE0", WF_LWTBL_NEGOTIATED_WINSIZE0_MASK, WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT, false},
-+ {"WINSIZE1", WF_LWTBL_NEGOTIATED_WINSIZE1_MASK, WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT, false},
-+ {"WINSIZE2", WF_LWTBL_NEGOTIATED_WINSIZE2_MASK, WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT, false},
-+ {"WINSIZE3", WF_LWTBL_NEGOTIATED_WINSIZE3_MASK, WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT, true},
-+ {"WINSIZE4", WF_LWTBL_NEGOTIATED_WINSIZE4_MASK, WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT, false},
-+ {"WINSIZE5", WF_LWTBL_NEGOTIATED_WINSIZE5_MASK, WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT, false},
-+ {"WINSIZE6", WF_LWTBL_NEGOTIATED_WINSIZE6_MASK, WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT, false},
-+ {"WINSIZE7", WF_LWTBL_NEGOTIATED_WINSIZE7_MASK, WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT, true},
-+ {"PE", WF_LWTBL_PE_MASK, WF_LWTBL_PE_SHIFT, false},
-+ {"DIS_RHTR", WF_LWTBL_DIS_RHTR_MASK, NO_SHIFT_DEFINE, false},
-+ {"LDPC_HT", WF_LWTBL_LDPC_HT_MASK, NO_SHIFT_DEFINE, false},
-+ {"LDPC_VHT", WF_LWTBL_LDPC_VHT_MASK, NO_SHIFT_DEFINE, false},
-+ {"LDPC_HE", WF_LWTBL_LDPC_HE_MASK, NO_SHIFT_DEFINE, false},
-+ {"LDPC_EHT", WF_LWTBL_LDPC_EHT_MASK, NO_SHIFT_DEFINE, true},
-+ {"BA_MODE", WF_LWTBL_BA_MODE_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw4(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 4 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 4\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_4*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW4[i].name) {
-+ if (WTBL_LMAC_DW4[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW4[i].name,
-+ (dw_value & WTBL_LMAC_DW4[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW4[i].name,
-+ (dw_value & WTBL_LMAC_DW4[i].mask) >> WTBL_LMAC_DW4[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse *WTBL_LMAC_DW5;
-+static const struct berse_wtbl_parse WTBL_LMAC_DW5_7996[] = {
-+ {"AF", WF_LWTBL_AF_MASK, WF_LWTBL_AF_SHIFT, false},
-+ {"AF_HE", WF_LWTBL_AF_HE_MASK, WF_LWTBL_AF_HE_SHIFT,false},
-+ {"RTS", WF_LWTBL_RTS_MASK, NO_SHIFT_DEFINE, false},
-+ {"SMPS", WF_LWTBL_SMPS_MASK, NO_SHIFT_DEFINE, false},
-+ {"DYN_BW", WF_LWTBL_DYN_BW_MASK, NO_SHIFT_DEFINE, true},
-+ {"MMSS", WF_LWTBL_MMSS_MASK, WF_LWTBL_MMSS_SHIFT,false},
-+ {"USR", WF_LWTBL_USR_MASK, NO_SHIFT_DEFINE, false},
-+ {"SR_RATE", WF_LWTBL_SR_R_MASK, WF_LWTBL_SR_R_SHIFT,false},
-+ {"SR_ABORT", WF_LWTBL_SR_ABORT_MASK, NO_SHIFT_DEFINE, true},
-+ {"TX_POWER_OFFSET", WF_LWTBL_TX_POWER_OFFSET_MASK, WF_LWTBL_TX_POWER_OFFSET_SHIFT, false},
-+ {"LTF_EHT", WF_LWTBL_LTF_EHT_MASK, WF_LWTBL_LTF_EHT_SHIFT, false},
-+ {"GI_EHT", WF_LWTBL_GI_EHT_MASK, WF_LWTBL_GI_EHT_SHIFT, false},
-+ {"DOPPL", WF_LWTBL_DOPPL_MASK, NO_SHIFT_DEFINE, false},
-+ {"TXOP_PS_CAP", WF_LWTBL_TXOP_PS_CAP_MASK, NO_SHIFT_DEFINE, false},
-+ {"DONOT_UPDATE_I_PSM", WF_LWTBL_DU_I_PSM_MASK, NO_SHIFT_DEFINE, true},
-+ {"I_PSM", WF_LWTBL_I_PSM_MASK, NO_SHIFT_DEFINE, false},
-+ {"PSM", WF_LWTBL_PSM_MASK, NO_SHIFT_DEFINE, false},
-+ {"SKIP_TX", WF_LWTBL_SKIP_TX_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW5_7992[] = {
-+ {"AF", WF_LWTBL_AF_MASK_7992, WF_LWTBL_AF_SHIFT, false},
-+ {"RTS", WF_LWTBL_RTS_MASK, NO_SHIFT_DEFINE, false},
-+ {"SMPS", WF_LWTBL_SMPS_MASK, NO_SHIFT_DEFINE, false},
-+ {"DYN_BW", WF_LWTBL_DYN_BW_MASK, NO_SHIFT_DEFINE, true},
-+ {"MMSS", WF_LWTBL_MMSS_MASK, WF_LWTBL_MMSS_SHIFT,false},
-+ {"USR", WF_LWTBL_USR_MASK, NO_SHIFT_DEFINE, false},
-+ {"SR_RATE", WF_LWTBL_SR_R_MASK, WF_LWTBL_SR_R_SHIFT,false},
-+ {"SR_ABORT", WF_LWTBL_SR_ABORT_MASK, NO_SHIFT_DEFINE, true},
-+ {"TX_POWER_OFFSET", WF_LWTBL_TX_POWER_OFFSET_MASK, WF_LWTBL_TX_POWER_OFFSET_SHIFT, false},
-+ {"LTF_EHT", WF_LWTBL_LTF_EHT_MASK, WF_LWTBL_LTF_EHT_SHIFT, false},
-+ {"GI_EHT", WF_LWTBL_GI_EHT_MASK, WF_LWTBL_GI_EHT_SHIFT, false},
-+ {"DOPPL", WF_LWTBL_DOPPL_MASK, NO_SHIFT_DEFINE, false},
-+ {"TXOP_PS_CAP", WF_LWTBL_TXOP_PS_CAP_MASK, NO_SHIFT_DEFINE, false},
-+ {"DONOT_UPDATE_I_PSM", WF_LWTBL_DU_I_PSM_MASK, NO_SHIFT_DEFINE, true},
-+ {"I_PSM", WF_LWTBL_I_PSM_MASK, NO_SHIFT_DEFINE, false},
-+ {"PSM", WF_LWTBL_PSM_MASK, NO_SHIFT_DEFINE, false},
-+ {"SKIP_TX", WF_LWTBL_SKIP_TX_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw5(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 5 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 5\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW5[i].name) {
-+ if (WTBL_LMAC_DW5[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW5[i].name,
-+ (dw_value & WTBL_LMAC_DW5[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW5[i].name,
-+ (dw_value & WTBL_LMAC_DW5[i].mask) >> WTBL_LMAC_DW5[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW6[] = {
-+ {"CBRN", WF_LWTBL_CBRN_MASK, WF_LWTBL_CBRN_SHIFT, false},
-+ {"DBNSS_EN", WF_LWTBL_DBNSS_EN_MASK, NO_SHIFT_DEFINE, false},
-+ {"BAF_EN", WF_LWTBL_BAF_EN_MASK, NO_SHIFT_DEFINE, false},
-+ {"RDGBA", WF_LWTBL_RDGBA_MASK, NO_SHIFT_DEFINE, false},
-+ {"RDG", WF_LWTBL_R_MASK, NO_SHIFT_DEFINE, false},
-+ {"SPE_IDX", WF_LWTBL_SPE_IDX_MASK, WF_LWTBL_SPE_IDX_SHIFT, true},
-+ {"G2", WF_LWTBL_G2_MASK, NO_SHIFT_DEFINE, false},
-+ {"G4", WF_LWTBL_G4_MASK, NO_SHIFT_DEFINE, false},
-+ {"G8", WF_LWTBL_G8_MASK, NO_SHIFT_DEFINE, false},
-+ {"G16", WF_LWTBL_G16_MASK, NO_SHIFT_DEFINE, true},
-+ {"G2_LTF", WF_LWTBL_G2_LTF_MASK, WF_LWTBL_G2_LTF_SHIFT, false},
-+ {"G4_LTF", WF_LWTBL_G4_LTF_MASK, WF_LWTBL_G4_LTF_SHIFT, false},
-+ {"G8_LTF", WF_LWTBL_G8_LTF_MASK, WF_LWTBL_G8_LTF_SHIFT, false},
-+ {"G16_LTF", WF_LWTBL_G16_LTF_MASK, WF_LWTBL_G16_LTF_SHIFT, true},
-+ {"G2_HE", WF_LWTBL_G2_HE_MASK, WF_LWTBL_G2_HE_SHIFT, false},
-+ {"G4_HE", WF_LWTBL_G4_HE_MASK, WF_LWTBL_G4_HE_SHIFT, false},
-+ {"G8_HE", WF_LWTBL_G8_HE_MASK, WF_LWTBL_G8_HE_SHIFT, false},
-+ {"G16_HE", WF_LWTBL_G16_HE_MASK, WF_LWTBL_G16_HE_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw6(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 6 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 6\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_6*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW6[i].name) {
-+ if (WTBL_LMAC_DW6[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW6[i].name,
-+ (dw_value & WTBL_LMAC_DW6[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW6[i].name,
-+ (dw_value & WTBL_LMAC_DW6[i].mask) >> WTBL_LMAC_DW6[i].shift);
-+ i++;
-+ }
-+}
-+
-+static void parse_fmac_lwtbl_dw7(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ int i = 0;
-+
-+ /* LMAC WTBL DW 7 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 7\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_7*4]);
-+ dw_value = *addr;
-+
-+ for (i = 0; i < 8; i++) {
-+ seq_printf(s, "\tBA_WIN_SIZE%u:%lu\n", i, ((dw_value & BITS(i*4, i*4+3)) >> i*4));
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW8[] = {
-+ {"RTS_FAIL_CNT_AC0", WF_LWTBL_AC0_RTS_FAIL_CNT_MASK, WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT, false},
-+ {"AC1", WF_LWTBL_AC1_RTS_FAIL_CNT_MASK, WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT, false},
-+ {"AC2", WF_LWTBL_AC2_RTS_FAIL_CNT_MASK, WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT, false},
-+ {"AC3", WF_LWTBL_AC3_RTS_FAIL_CNT_MASK, WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT, true},
-+ {"PARTIAL_AID", WF_LWTBL_PARTIAL_AID_MASK, WF_LWTBL_PARTIAL_AID_SHIFT, false},
-+ {"CHK_PER", WF_LWTBL_CHK_PER_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw8(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 8 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 8\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_8*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW8[i].name) {
-+ if (WTBL_LMAC_DW8[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW8[i].name,
-+ (dw_value & WTBL_LMAC_DW8[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW8[i].name,
-+ (dw_value & WTBL_LMAC_DW8[i].mask) >> WTBL_LMAC_DW8[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse *WTBL_LMAC_DW9;
-+static const struct berse_wtbl_parse WTBL_LMAC_DW9_7996[] = {
-+ {"RX_AVG_MPDU_SIZE", WF_LWTBL_RX_AVG_MPDU_SIZE_MASK, WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT, false},
-+ {"PRITX_SW_MODE", WF_LWTBL_PRITX_SW_MODE_MASK, NO_SHIFT_DEFINE, false},
-+ {"PRITX_ERSU", WF_LWTBL_PRITX_ERSU_MASK, NO_SHIFT_DEFINE, false},
-+ {"PRITX_PLR", WF_LWTBL_PRITX_PLR_MASK, NO_SHIFT_DEFINE, true},
-+ {"PRITX_DCM", WF_LWTBL_PRITX_DCM_MASK, NO_SHIFT_DEFINE, false},
-+ {"PRITX_ER106T", WF_LWTBL_PRITX_ER106T_MASK, NO_SHIFT_DEFINE, true},
-+ /* {"FCAP(0:20 1:~40)", WTBL_FCAP_20_TO_160_MHZ, WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
-+ {"MPDU_FAIL_CNT", WF_LWTBL_MPDU_FAIL_CNT_MASK, WF_LWTBL_MPDU_FAIL_CNT_SHIFT, false},
-+ {"MPDU_OK_CNT", WF_LWTBL_MPDU_OK_CNT_MASK, WF_LWTBL_MPDU_OK_CNT_SHIFT, false},
-+ {"RATE_IDX", WF_LWTBL_RATE_IDX_MASK, WF_LWTBL_RATE_IDX_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW9_7992[] = {
-+ {"RX_AVG_MPDU_SIZE", WF_LWTBL_RX_AVG_MPDU_SIZE_MASK, WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT, false},
-+ {"PRITX_SW_MODE", WF_LWTBL_PRITX_SW_MODE_MASK_7992, NO_SHIFT_DEFINE, false},
-+ {"PRITX_ERSU", WF_LWTBL_PRITX_ERSU_MASK_7992, NO_SHIFT_DEFINE, false},
-+ {"PRITX_PLR", WF_LWTBL_PRITX_PLR_MASK_7992, NO_SHIFT_DEFINE, true},
-+ {"PRITX_DCM", WF_LWTBL_PRITX_DCM_MASK, NO_SHIFT_DEFINE, false},
-+ {"PRITX_ER106T", WF_LWTBL_PRITX_ER106T_MASK, NO_SHIFT_DEFINE, true},
-+ /* {"FCAP(0:20 1:~40)", WTBL_FCAP_20_TO_160_MHZ, WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
-+ {"MPDU_FAIL_CNT", WF_LWTBL_MPDU_FAIL_CNT_MASK, WF_LWTBL_MPDU_FAIL_CNT_SHIFT, false},
-+ {"MPDU_OK_CNT", WF_LWTBL_MPDU_OK_CNT_MASK, WF_LWTBL_MPDU_OK_CNT_SHIFT, false},
-+ {"RATE_IDX", WF_LWTBL_RATE_IDX_MASK, WF_LWTBL_RATE_IDX_SHIFT, true},
-+ {NULL,}
-+};
-+
-+char *fcap_name[] = {"20MHz", "20/40MHz", "20/40/80MHz", "20/40/80/160/80+80MHz", "20/40/80/160/80+80/320MHz"};
-+
-+static void parse_fmac_lwtbl_dw9(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 9 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 9\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_9*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW9[i].name) {
-+ if (WTBL_LMAC_DW9[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW9[i].name,
-+ (dw_value & WTBL_LMAC_DW9[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW9[i].name,
-+ (dw_value & WTBL_LMAC_DW9[i].mask) >> WTBL_LMAC_DW9[i].shift);
-+ i++;
-+ }
-+
-+ /* FCAP parser */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "FCAP:%s\n", fcap_name[(dw_value & WF_LWTBL_FCAP_MASK) >> WF_LWTBL_FCAP_SHIFT]);
-+}
-+
-+#define HW_TX_RATE_TO_MODE(_x) (((_x) & WTBL_RATE_TX_MODE_MASK) >> WTBL_RATE_TX_MODE_OFFSET)
-+#define HW_TX_RATE_TO_MCS(_x, _mode) ((_x) & WTBL_RATE_TX_RATE_MASK >> WTBL_RATE_TX_RATE_OFFSET)
-+#define HW_TX_RATE_TO_NSS(_x) (((_x) & WTBL_RATE_NSTS_MASK) >> WTBL_RATE_NSTS_OFFSET)
-+#define HW_TX_RATE_TO_STBC(_x) (((_x) & WTBL_RATE_STBC_MASK) >> WTBL_RATE_STBC_OFFSET)
-+
-+#define MAX_TX_MODE 16
-+static char *HW_TX_MODE_STR[] = {"CCK", "OFDM", "HT-Mix", "HT-GF", "VHT",
-+ "N/A", "N/A", "N/A",
-+ "HE_SU", "HE_EXT_SU", "HE_TRIG", "HE_MU",
-+ "N/A",
-+ "EHT_EXT_SU", "EHT_TRIG", "EHT_MU",
-+ "N/A"};
-+static char *HW_TX_RATE_CCK_STR[] = {"1M", "2Mlong", "5.5Mlong", "11Mlong", "N/A", "2Mshort", "5.5Mshort", "11Mshort", "N/A"};
-+static char *HW_TX_RATE_OFDM_STR[] = {"6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"};
-+
-+static char *hw_rate_ofdm_str(uint16_t ofdm_idx)
-+{
-+ switch (ofdm_idx) {
-+ case 11: /* 6M */
-+ return HW_TX_RATE_OFDM_STR[0];
-+
-+ case 15: /* 9M */
-+ return HW_TX_RATE_OFDM_STR[1];
-+
-+ case 10: /* 12M */
-+ return HW_TX_RATE_OFDM_STR[2];
-+
-+ case 14: /* 18M */
-+ return HW_TX_RATE_OFDM_STR[3];
-+
-+ case 9: /* 24M */
-+ return HW_TX_RATE_OFDM_STR[4];
-+
-+ case 13: /* 36M */
-+ return HW_TX_RATE_OFDM_STR[5];
-+
-+ case 8: /* 48M */
-+ return HW_TX_RATE_OFDM_STR[6];
-+
-+ case 12: /* 54M */
-+ return HW_TX_RATE_OFDM_STR[7];
-+
-+ default:
-+ return HW_TX_RATE_OFDM_STR[8];
-+ }
-+}
-+
-+static char *hw_rate_str(u8 mode, uint16_t rate_idx)
-+{
-+ if (mode == 0)
-+ return rate_idx < 8 ? HW_TX_RATE_CCK_STR[rate_idx] : HW_TX_RATE_CCK_STR[8];
-+ else if (mode == 1)
-+ return hw_rate_ofdm_str(rate_idx);
-+ else
-+ return "MCS";
-+}
-+
-+static void
-+parse_rate(struct seq_file *s, uint16_t rate_idx, uint16_t txrate)
-+{
-+ uint16_t txmode, mcs, nss, stbc;
-+
-+ txmode = HW_TX_RATE_TO_MODE(txrate);
-+ mcs = HW_TX_RATE_TO_MCS(txrate, txmode);
-+ nss = HW_TX_RATE_TO_NSS(txrate);
-+ stbc = HW_TX_RATE_TO_STBC(txrate);
-+
-+ seq_printf(s, "\tRate%d(0x%x):TxMode=%d(%s), TxRate=%d(%s), Nsts=%d, STBC=%d\n",
-+ rate_idx + 1, txrate,
-+ txmode, (txmode < MAX_TX_MODE ? HW_TX_MODE_STR[txmode] : HW_TX_MODE_STR[MAX_TX_MODE]),
-+ mcs, hw_rate_str(txmode, mcs), nss, stbc);
-+}
-+
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW10[] = {
-+ {"RATE1", WF_LWTBL_RATE1_MASK, WF_LWTBL_RATE1_SHIFT},
-+ {"RATE2", WF_LWTBL_RATE2_MASK, WF_LWTBL_RATE2_SHIFT},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw10(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 10 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 10\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_1_2*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW10[i].name) {
-+ parse_rate(s, i, (dw_value & WTBL_LMAC_DW10[i].mask) >> WTBL_LMAC_DW10[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW11[] = {
-+ {"RATE3", WF_LWTBL_RATE3_MASK, WF_LWTBL_RATE3_SHIFT},
-+ {"RATE4", WF_LWTBL_RATE4_MASK, WF_LWTBL_RATE4_SHIFT},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw11(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 11 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 11\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_3_4*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW11[i].name) {
-+ parse_rate(s, i+2, (dw_value & WTBL_LMAC_DW11[i].mask) >> WTBL_LMAC_DW11[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW12[] = {
-+ {"RATE5", WF_LWTBL_RATE5_MASK, WF_LWTBL_RATE5_SHIFT},
-+ {"RATE6", WF_LWTBL_RATE6_MASK, WF_LWTBL_RATE6_SHIFT},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw12(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 12 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 12\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_5_6*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW12[i].name) {
-+ parse_rate(s, i+4, (dw_value & WTBL_LMAC_DW12[i].mask) >> WTBL_LMAC_DW12[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW13[] = {
-+ {"RATE7", WF_LWTBL_RATE7_MASK, WF_LWTBL_RATE7_SHIFT},
-+ {"RATE8", WF_LWTBL_RATE8_MASK, WF_LWTBL_RATE8_SHIFT},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw13(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 13 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 13\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_7_8*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW13[i].name) {
-+ parse_rate(s, i+6, (dw_value & WTBL_LMAC_DW13[i].mask) >> WTBL_LMAC_DW13[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW14_BMC[] = {
-+ {"CIPHER_IGTK", WF_LWTBL_CIPHER_SUIT_IGTK_MASK, WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT, false},
-+ {"CIPHER_BIGTK", WF_LWTBL_CIPHER_SUIT_BIGTK_MASK, WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW14[] = {
-+ {"RATE1_TX_CNT", WF_LWTBL_RATE1_TX_CNT_MASK, WF_LWTBL_RATE1_TX_CNT_SHIFT, false},
-+ {"RATE1_FAIL_CNT", WF_LWTBL_RATE1_FAIL_CNT_MASK, WF_LWTBL_RATE1_FAIL_CNT_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw14(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr, *muar_addr = 0;
-+ u32 dw_value, muar_dw_value = 0;
-+ u16 i = 0;
-+
-+ /* DUMP DW14 for BMC entry only */
-+ muar_addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
-+ muar_dw_value = *muar_addr;
-+ if (((muar_dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT)
-+ == MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
-+ /* LMAC WTBL DW 14 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 14\n");
-+ addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW14_BMC[i].name) {
-+ if (WTBL_LMAC_DW14_BMC[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14_BMC[i].name,
-+ (dw_value & WTBL_LMAC_DW14_BMC[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14_BMC[i].name,
-+ (dw_value & WTBL_LMAC_DW14_BMC[i].mask) >> WTBL_LMAC_DW14_BMC[i].shift);
-+ i++;
-+ }
-+ } else {
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 14\n");
-+ addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW14[i].name) {
-+ if (WTBL_LMAC_DW14[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14[i].name,
-+ (dw_value & WTBL_LMAC_DW14[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14[i].name,
-+ (dw_value & WTBL_LMAC_DW14[i].mask) >> WTBL_LMAC_DW14[i].shift);
-+ i++;
-+ }
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW28[] = {
-+ {"RELATED_IDX0", WF_LWTBL_RELATED_IDX0_MASK, WF_LWTBL_RELATED_IDX0_SHIFT, false},
-+ {"RELATED_BAND0", WF_LWTBL_RELATED_BAND0_MASK, WF_LWTBL_RELATED_BAND0_SHIFT, false},
-+ {"PRI_MLD_BAND", WF_LWTBL_PRIMARY_MLD_BAND_MASK, WF_LWTBL_PRIMARY_MLD_BAND_SHIFT, true},
-+ {"RELATED_IDX1", WF_LWTBL_RELATED_IDX1_MASK, WF_LWTBL_RELATED_IDX1_SHIFT, false},
-+ {"RELATED_BAND1", WF_LWTBL_RELATED_BAND1_MASK, WF_LWTBL_RELATED_BAND1_SHIFT, false},
-+ {"SEC_MLD_BAND", WF_LWTBL_SECONDARY_MLD_BAND_MASK, WF_LWTBL_SECONDARY_MLD_BAND_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw28(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 28 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 28\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_1*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW28[i].name) {
-+ if (WTBL_LMAC_DW28[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW28[i].name,
-+ (dw_value & WTBL_LMAC_DW28[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW28[i].name,
-+ (dw_value & WTBL_LMAC_DW28[i].mask) >>
-+ WTBL_LMAC_DW28[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW29[] = {
-+ {"DISPATCH_POLICY_MLD_TID0", WF_LWTBL_DISPATCH_POLICY0_MASK, WF_LWTBL_DISPATCH_POLICY0_SHIFT, false},
-+ {"MLD_TID1", WF_LWTBL_DISPATCH_POLICY1_MASK, WF_LWTBL_DISPATCH_POLICY1_SHIFT, false},
-+ {"MLD_TID2", WF_LWTBL_DISPATCH_POLICY2_MASK, WF_LWTBL_DISPATCH_POLICY2_SHIFT, false},
-+ {"MLD_TID3", WF_LWTBL_DISPATCH_POLICY3_MASK, WF_LWTBL_DISPATCH_POLICY3_SHIFT, true},
-+ {"MLD_TID4", WF_LWTBL_DISPATCH_POLICY4_MASK, WF_LWTBL_DISPATCH_POLICY4_SHIFT, false},
-+ {"MLD_TID5", WF_LWTBL_DISPATCH_POLICY5_MASK, WF_LWTBL_DISPATCH_POLICY5_SHIFT, false},
-+ {"MLD_TID6", WF_LWTBL_DISPATCH_POLICY6_MASK, WF_LWTBL_DISPATCH_POLICY6_SHIFT, false},
-+ {"MLD_TID7", WF_LWTBL_DISPATCH_POLICY7_MASK, WF_LWTBL_DISPATCH_POLICY7_SHIFT, true},
-+ {"OMLD_ID", WF_LWTBL_OWN_MLD_ID_MASK, WF_LWTBL_OWN_MLD_ID_SHIFT, false},
-+ {"EMLSR0", WF_LWTBL_EMLSR0_MASK, NO_SHIFT_DEFINE, false},
-+ {"EMLMR0", WF_LWTBL_EMLMR0_MASK, NO_SHIFT_DEFINE, false},
-+ {"EMLSR1", WF_LWTBL_EMLSR1_MASK, NO_SHIFT_DEFINE, false},
-+ {"EMLMR1", WF_LWTBL_EMLMR1_MASK, NO_SHIFT_DEFINE, true},
-+ {"EMLSR2", WF_LWTBL_EMLSR2_MASK, NO_SHIFT_DEFINE, false},
-+ {"EMLMR2", WF_LWTBL_EMLMR2_MASK, NO_SHIFT_DEFINE, false},
-+ {"STR_BITMAP", WF_LWTBL_STR_BITMAP_MASK, WF_LWTBL_STR_BITMAP_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw29(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 29 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 29\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_2*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW29[i].name) {
-+ if (WTBL_LMAC_DW29[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW29[i].name,
-+ (dw_value & WTBL_LMAC_DW29[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW29[i].name,
-+ (dw_value & WTBL_LMAC_DW29[i].mask) >>
-+ WTBL_LMAC_DW29[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW30[] = {
-+ {"DISPATCH_ORDER", WF_LWTBL_DISPATCH_ORDER_MASK, WF_LWTBL_DISPATCH_ORDER_SHIFT, false},
-+ {"DISPATCH_RATIO", WF_LWTBL_DISPATCH_RATIO_MASK, WF_LWTBL_DISPATCH_RATIO_SHIFT, false},
-+ {"LINK_MGF", WF_LWTBL_LINK_MGF_MASK, WF_LWTBL_LINK_MGF_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw30(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 30 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 30\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_3*4]);
-+ dw_value = *addr;
-+
-+
-+ while (WTBL_LMAC_DW30[i].name) {
-+ if (WTBL_LMAC_DW30[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW30[i].name,
-+ (dw_value & WTBL_LMAC_DW30[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW30[i].name,
-+ (dw_value & WTBL_LMAC_DW30[i].mask) >> WTBL_LMAC_DW30[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW31[] = {
-+ {"BFTX_TB", WF_LWTBL_BFTX_TB_MASK, NO_SHIFT_DEFINE, false},
-+ {"DROP", WF_LWTBL_DROP_MASK, NO_SHIFT_DEFINE, false},
-+ {"CASCAD", WF_LWTBL_CASCAD_MASK, NO_SHIFT_DEFINE, false},
-+ {"ALL_ACK", WF_LWTBL_ALL_ACK_MASK, NO_SHIFT_DEFINE, false},
-+ {"MPDU_SIZE", WF_LWTBL_MPDU_SIZE_MASK, WF_LWTBL_MPDU_SIZE_SHIFT, false},
-+ {"RXD_DUP_MODE", WF_LWTBL_RXD_DUP_MODE_MASK, WF_LWTBL_RXD_DUP_MODE_SHIFT, true},
-+ {"ACK_EN", WF_LWTBL_ACK_EN_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw31(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 31 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 31\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_RESP_INFO_DW_31*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW31[i].name) {
-+ if (WTBL_LMAC_DW31[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW31[i].name,
-+ (dw_value & WTBL_LMAC_DW31[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW31[i].name,
-+ (dw_value & WTBL_LMAC_DW31[i].mask) >>
-+ WTBL_LMAC_DW31[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW32[] = {
-+ {"OM_INFO", WF_LWTBL_OM_INFO_MASK, WF_LWTBL_OM_INFO_SHIFT, false},
-+ {"OM_INFO_EHT", WF_LWTBL_OM_INFO_EHT_MASK, WF_LWTBL_OM_INFO_EHT_SHIFT, false},
-+ {"RXD_DUP_FOR_OM_CHG", WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK, NO_SHIFT_DEFINE, false},
-+ {"RXD_DUP_WHITE_LIST", WF_LWTBL_RXD_DUP_WHITE_LIST_MASK, WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT, false},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw32(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 32 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 32\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_DUP_INFO_DW_32*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW32[i].name) {
-+ if (WTBL_LMAC_DW32[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW32[i].name,
-+ (dw_value & WTBL_LMAC_DW32[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW32[i].name,
-+ (dw_value & WTBL_LMAC_DW32[i].mask) >>
-+ WTBL_LMAC_DW32[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW33[] = {
-+ {"USER_RSSI", WF_LWTBL_USER_RSSI_MASK, WF_LWTBL_USER_RSSI_SHIFT, false},
-+ {"USER_SNR", WF_LWTBL_USER_SNR_MASK, WF_LWTBL_USER_SNR_SHIFT, false},
-+ {"RAPID_REACTION_RATE", WF_LWTBL_RAPID_REACTION_RATE_MASK, WF_LWTBL_RAPID_REACTION_RATE_SHIFT, true},
-+ {"HT_AMSDU(Read Only)", WF_LWTBL_HT_AMSDU_MASK, NO_SHIFT_DEFINE, false},
-+ {"AMSDU_CROSS_LG(Read Only)", WF_LWTBL_AMSDU_CROSS_LG_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw33(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 33 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 33\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_1*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_LMAC_DW33[i].name) {
-+ if (WTBL_LMAC_DW33[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW33[i].name,
-+ (dw_value & WTBL_LMAC_DW33[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW33[i].name,
-+ (dw_value & WTBL_LMAC_DW33[i].mask) >>
-+ WTBL_LMAC_DW33[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW34[] = {
-+ {"RESP_RCPI0", WF_LWTBL_RESP_RCPI0_MASK, WF_LWTBL_RESP_RCPI0_SHIFT, false},
-+ {"RCPI1", WF_LWTBL_RESP_RCPI1_MASK, WF_LWTBL_RESP_RCPI1_SHIFT, false},
-+ {"RCPI2", WF_LWTBL_RESP_RCPI2_MASK, WF_LWTBL_RESP_RCPI2_SHIFT, false},
-+ {"RCPI3", WF_LWTBL_RESP_RCPI3_MASK, WF_LWTBL_RESP_RCPI3_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw34(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 34 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 34\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_2*4]);
-+ dw_value = *addr;
-+
-+
-+ while (WTBL_LMAC_DW34[i].name) {
-+ if (WTBL_LMAC_DW34[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW34[i].name,
-+ (dw_value & WTBL_LMAC_DW34[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW34[i].name,
-+ (dw_value & WTBL_LMAC_DW34[i].mask) >>
-+ WTBL_LMAC_DW34[i].shift);
-+ i++;
-+ }
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW35[] = {
-+ {"SNR 0", WF_LWTBL_SNR_RX0_MASK, WF_LWTBL_SNR_RX0_SHIFT, false},
-+ {"SNR 1", WF_LWTBL_SNR_RX1_MASK, WF_LWTBL_SNR_RX1_SHIFT, false},
-+ {"SNR 2", WF_LWTBL_SNR_RX2_MASK, WF_LWTBL_SNR_RX2_SHIFT, false},
-+ {"SNR 3", WF_LWTBL_SNR_RX3_MASK, WF_LWTBL_SNR_RX3_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw35(struct seq_file *s, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ /* LMAC WTBL DW 35 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "LWTBL DW 35\n");
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_3*4]);
-+ dw_value = *addr;
-+
-+
-+ while (WTBL_LMAC_DW35[i].name) {
-+ if (WTBL_LMAC_DW35[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW35[i].name,
-+ (dw_value & WTBL_LMAC_DW35[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW35[i].name,
-+ (dw_value & WTBL_LMAC_DW35[i].mask) >>
-+ WTBL_LMAC_DW35[i].shift);
-+ i++;
-+ }
-+}
-+
-+static void parse_fmac_lwtbl_rx_stats(struct seq_file *s, u8 *lwtbl)
-+{
-+ parse_fmac_lwtbl_dw33(s, lwtbl);
-+ parse_fmac_lwtbl_dw34(s, lwtbl);
-+ parse_fmac_lwtbl_dw35(s, lwtbl);
-+}
-+
-+static void parse_fmac_lwtbl_mlo_info(struct seq_file *s, u8 *lwtbl)
-+{
-+ parse_fmac_lwtbl_dw28(s, lwtbl);
-+ parse_fmac_lwtbl_dw29(s, lwtbl);
-+ parse_fmac_lwtbl_dw30(s, lwtbl);
-+}
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW9[] = {
-+ {"RELATED_IDX0", WF_UWTBL_RELATED_IDX0_MASK, WF_UWTBL_RELATED_IDX0_SHIFT, false},
-+ {"RELATED_BAND0", WF_UWTBL_RELATED_BAND0_MASK, WF_UWTBL_RELATED_BAND0_SHIFT, false},
-+ {"PRI_MLD_BAND", WF_UWTBL_PRIMARY_MLD_BAND_MASK, WF_UWTBL_PRIMARY_MLD_BAND_SHIFT, true},
-+ {"RELATED_IDX1", WF_UWTBL_RELATED_IDX1_MASK, WF_UWTBL_RELATED_IDX1_SHIFT, false},
-+ {"RELATED_BAND1", WF_UWTBL_RELATED_BAND1_MASK, WF_UWTBL_RELATED_BAND1_SHIFT, false},
-+ {"SEC_MLD_BAND", WF_UWTBL_SECONDARY_MLD_BAND_MASK, WF_UWTBL_SECONDARY_MLD_BAND_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_uwtbl_mlo_info(struct seq_file *s, u8 *uwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "MldAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
-+ uwtbl[4], uwtbl[5], uwtbl[6], uwtbl[7], uwtbl[0], uwtbl[1]);
-+
-+ /* UMAC WTBL DW 0 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "UWTBL DW 0\n");
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_OWN_MLD_ID_DW*4]);
-+ dw_value = *addr;
-+
-+ seq_printf(s, "\t%s:%u\n", "OMLD_ID",
-+ (dw_value & WF_UWTBL_OWN_MLD_ID_MASK) >> WF_UWTBL_OWN_MLD_ID_SHIFT);
-+
-+ /* UMAC WTBL DW 9 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "UWTBL DW 9\n");
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_RELATED_IDX0_DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_UMAC_DW9[i].name) {
-+
-+ if (WTBL_UMAC_DW9[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW9[i].name,
-+ (dw_value & WTBL_UMAC_DW9[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW9[i].name,
-+ (dw_value & WTBL_UMAC_DW9[i].mask) >>
-+ WTBL_UMAC_DW9[i].shift);
-+ i++;
-+ }
-+}
-+
-+static bool
-+is_wtbl_bigtk_exist(u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+
-+ addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
-+ dw_value = *addr;
-+ if (((dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT) ==
-+ MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
-+ addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_BIGTK_DW*4]);
-+ dw_value = *addr;
-+ if (((dw_value & WF_LWTBL_CIPHER_SUIT_BIGTK_MASK) >>
-+ WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT) != IGTK_CIPHER_SUIT_NONE)
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW2[] = {
-+ {"PN0", WTBL_PN0_MASK, WTBL_PN0_OFFSET, false},
-+ {"PN1", WTBL_PN1_MASK, WTBL_PN1_OFFSET, false},
-+ {"PN2", WTBL_PN2_MASK, WTBL_PN2_OFFSET, true},
-+ {"PN3", WTBL_PN3_MASK, WTBL_PN3_OFFSET, false},
-+ {NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW3[] = {
-+ {"PN4", WTBL_PN4_MASK, WTBL_PN4_OFFSET, false},
-+ {"PN5", WTBL_PN5_MASK, WTBL_PN5_OFFSET, true},
-+ {"COM_SN", WF_UWTBL_COM_SN_MASK, WF_UWTBL_COM_SN_SHIFT, true},
-+ {NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW4_BIPN[] = {
-+ {"BIPN0", WTBL_BIPN0_MASK, WTBL_BIPN0_OFFSET, false},
-+ {"BIPN1", WTBL_BIPN1_MASK, WTBL_BIPN1_OFFSET, false},
-+ {"BIPN2", WTBL_BIPN2_MASK, WTBL_BIPN2_OFFSET, true},
-+ {"BIPN3", WTBL_BIPN3_MASK, WTBL_BIPN3_OFFSET, false},
-+ {NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW5_BIPN[] = {
-+ {"BIPN4", WTBL_BIPN4_MASK, WTBL_BIPN4_OFFSET, false},
-+ {"BIPN5", WTBL_BIPN5_MASK, WTBL_BIPN5_OFFSET, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_uwtbl_pn(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u16 i = 0;
-+
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "UWTBL PN\n");
-+
-+ /* UMAC WTBL DW 2/3 */
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_PN_31_0__DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_UMAC_DW2[i].name) {
-+ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW2[i].name,
-+ (dw_value & WTBL_UMAC_DW2[i].mask) >>
-+ WTBL_UMAC_DW2[i].shift);
-+ i++;
-+ }
-+
-+ i = 0;
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_PN_47_32__DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_UMAC_DW3[i].name) {
-+ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW3[i].name,
-+ (dw_value & WTBL_UMAC_DW3[i].mask) >>
-+ WTBL_UMAC_DW3[i].shift);
-+ i++;
-+ }
-+
-+
-+ /* UMAC WTBL DW 4/5 for BIGTK */
-+ if (is_wtbl_bigtk_exist(lwtbl) == true) {
-+ i = 0;
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_31_0__DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_UMAC_DW4_BIPN[i].name) {
-+ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW4_BIPN[i].name,
-+ (dw_value & WTBL_UMAC_DW4_BIPN[i].mask) >>
-+ WTBL_UMAC_DW4_BIPN[i].shift);
-+ i++;
-+ }
-+
-+ i = 0;
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_47_32__DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_UMAC_DW5_BIPN[i].name) {
-+ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW5_BIPN[i].name,
-+ (dw_value & WTBL_UMAC_DW5_BIPN[i].mask) >>
-+ WTBL_UMAC_DW5_BIPN[i].shift);
-+ i++;
-+ }
-+ }
-+}
-+
-+static void parse_fmac_uwtbl_sn(struct seq_file *s, u8 *uwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 u2SN = 0;
-+
-+ /* UMAC WTBL DW SN part */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "UWTBL SN\n");
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID0_SN_DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID0_SN_MASK) >> WF_UWTBL_TID0_SN_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "TID0_AC0_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID1_SN_DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID1_SN_MASK) >> WF_UWTBL_TID1_SN_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "TID1_AC1_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_7_0__DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID2_SN_7_0__MASK) >>
-+ WF_UWTBL_TID2_SN_7_0__SHIFT;
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_11_8__DW*4]);
-+ u2SN |= (((*addr) & WF_UWTBL_TID2_SN_11_8__MASK) >>
-+ WF_UWTBL_TID2_SN_11_8__SHIFT) << 8;
-+ seq_printf(s, "\t%s:%u\n", "TID2_AC2_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID3_SN_DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID3_SN_MASK) >> WF_UWTBL_TID3_SN_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "TID3_AC3_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID4_SN_DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID4_SN_MASK) >> WF_UWTBL_TID4_SN_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "TID4_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_3_0__DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID5_SN_3_0__MASK) >>
-+ WF_UWTBL_TID5_SN_3_0__SHIFT;
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_11_4__DW*4]);
-+ u2SN |= (((*addr) & WF_UWTBL_TID5_SN_11_4__MASK) >>
-+ WF_UWTBL_TID5_SN_11_4__SHIFT) << 4;
-+ seq_printf(s, "\t%s:%u\n", "TID5_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID6_SN_DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID6_SN_MASK) >> WF_UWTBL_TID6_SN_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "TID6_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_TID7_SN_DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_TID7_SN_MASK) >> WF_UWTBL_TID7_SN_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "TID7_SN", u2SN);
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_COM_SN_DW*4]);
-+ u2SN = ((*addr) & WF_UWTBL_COM_SN_MASK) >> WF_UWTBL_COM_SN_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "COM_SN", u2SN);
-+}
-+
-+static void dump_key_table(
-+ struct seq_file *s,
-+ uint16_t keyloc0,
-+ uint16_t keyloc1,
-+ uint16_t keyloc2
-+)
-+{
-+#define ONE_KEY_ENTRY_LEN_IN_DW 8
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u8 keytbl[ONE_KEY_ENTRY_LEN_IN_DW*4] = {0};
-+ uint16_t x;
-+
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "\t%s:%d\n", "keyloc0", keyloc0);
-+ if (keyloc0 != INVALID_KEY_ENTRY) {
-+
-+ /* Don't swap below two lines, halWtblReadRaw will
-+ * write new value WF_WTBLON_TOP_WDUCR_ADDR
-+ */
-+ mt7996_wtbl_read_raw(dev, keyloc0,
-+ WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
-+ seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+ KEYTBL_IDX2BASE(keyloc0, 0));
-+ for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
-+ seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
-+ x,
-+ keytbl[x * 4 + 3],
-+ keytbl[x * 4 + 2],
-+ keytbl[x * 4 + 1],
-+ keytbl[x * 4]);
-+ }
-+ }
-+
-+ seq_printf(s, "\t%s:%d\n", "keyloc1", keyloc1);
-+ if (keyloc1 != INVALID_KEY_ENTRY) {
-+ /* Don't swap below two lines, halWtblReadRaw will
-+ * write new value WF_WTBLON_TOP_WDUCR_ADDR
-+ */
-+ mt7996_wtbl_read_raw(dev, keyloc1,
-+ WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
-+ seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+ KEYTBL_IDX2BASE(keyloc1, 0));
-+ for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
-+ seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
-+ x,
-+ keytbl[x * 4 + 3],
-+ keytbl[x * 4 + 2],
-+ keytbl[x * 4 + 1],
-+ keytbl[x * 4]);
-+ }
-+ }
-+
-+ seq_printf(s, "\t%s:%d\n", "keyloc2", keyloc2);
-+ if (keyloc2 != INVALID_KEY_ENTRY) {
-+ /* Don't swap below two lines, halWtblReadRaw will
-+ * write new value WF_WTBLON_TOP_WDUCR_ADDR
-+ */
-+ mt7996_wtbl_read_raw(dev, keyloc2,
-+ WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
-+ seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+ KEYTBL_IDX2BASE(keyloc2, 0));
-+ for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
-+ seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
-+ x,
-+ keytbl[x * 4 + 3],
-+ keytbl[x * 4 + 2],
-+ keytbl[x * 4 + 1],
-+ keytbl[x * 4]);
-+ }
-+ }
-+}
-+
-+static void parse_fmac_uwtbl_key_info(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ uint16_t keyloc0 = INVALID_KEY_ENTRY;
-+ uint16_t keyloc1 = INVALID_KEY_ENTRY;
-+ uint16_t keyloc2 = INVALID_KEY_ENTRY;
-+
-+ /* UMAC WTBL DW 7 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "UWTBL key info\n");
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC0_DW*4]);
-+ dw_value = *addr;
-+ keyloc0 = (dw_value & WF_UWTBL_KEY_LOC0_MASK) >> WF_UWTBL_KEY_LOC0_SHIFT;
-+ keyloc1 = (dw_value & WF_UWTBL_KEY_LOC1_MASK) >> WF_UWTBL_KEY_LOC1_SHIFT;
-+
-+ seq_printf(s, "\t%s:%u/%u\n", "Key Loc 0/1", keyloc0, keyloc1);
-+
-+ /* UMAC WTBL DW 6 for BIGTK */
-+ if (is_wtbl_bigtk_exist(lwtbl) == true) {
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC2_DW*4]);
-+ dw_value = *addr;
-+ keyloc2 = (dw_value & WF_UWTBL_KEY_LOC2_MASK) >>
-+ WF_UWTBL_KEY_LOC2_SHIFT;
-+ seq_printf(s, "\t%s:%u\n", "Key Loc 2", keyloc2);
-+ }
-+
-+ /* Parse KEY link */
-+ dump_key_table(s, keyloc0, keyloc1, keyloc2);
-+}
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW8[] = {
-+ {"UWTBL_WMM_Q", WF_UWTBL_WMM_Q_MASK, WF_UWTBL_WMM_Q_SHIFT, false},
-+ {"UWTBL_QOS", WF_UWTBL_QOS_MASK, NO_SHIFT_DEFINE, false},
-+ {"UWTBL_HT_VHT_HE", WF_UWTBL_HT_MASK, NO_SHIFT_DEFINE, false},
-+ {"UWTBL_HDRT_MODE", WF_UWTBL_HDRT_MODE_MASK, NO_SHIFT_DEFINE, true},
-+ {NULL,}
-+};
-+
-+static void parse_fmac_uwtbl_msdu_info(struct seq_file *s, u8 *uwtbl)
-+{
-+ u32 *addr = 0;
-+ u32 dw_value = 0;
-+ u32 amsdu_len = 0;
-+ u16 i = 0;
-+
-+ /* UMAC WTBL DW 8 */
-+ seq_printf(s, "\t\n");
-+ seq_printf(s, "UWTBL DW8\n");
-+
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_AMSDU_CFG_DW*4]);
-+ dw_value = *addr;
-+
-+ while (WTBL_UMAC_DW8[i].name) {
-+
-+ if (WTBL_UMAC_DW8[i].shift == NO_SHIFT_DEFINE)
-+ seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW8[i].name,
-+ (dw_value & WTBL_UMAC_DW8[i].mask) ? 1 : 0);
-+ else
-+ seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW8[i].name,
-+ (dw_value & WTBL_UMAC_DW8[i].mask) >>
-+ WTBL_UMAC_DW8[i].shift);
-+ i++;
-+ }
-+
-+ /* UMAC WTBL DW 8 - SEC_ADDR_MODE */
-+ addr = (u32 *)&(uwtbl[WF_UWTBL_SEC_ADDR_MODE_DW*4]);
-+ dw_value = *addr;
-+ seq_printf(s, "\t%s:%lu\n", "SEC_ADDR_MODE",
-+ (dw_value & WTBL_SEC_ADDR_MODE_MASK) >> WTBL_SEC_ADDR_MODE_OFFSET);
-+
-+ /* UMAC WTBL DW 8 - AMSDU_CFG */
-+ seq_printf(s, "\t%s:%d\n", "HW AMSDU Enable",
-+ (dw_value & WTBL_AMSDU_EN_MASK) ? 1 : 0);
-+
-+ amsdu_len = (dw_value & WTBL_AMSDU_LEN_MASK) >> WTBL_AMSDU_LEN_OFFSET;
-+ if (amsdu_len == 0)
-+ seq_printf(s, "\t%s:invalid (WTBL value=0x%x)\n", "HW AMSDU Len",
-+ amsdu_len);
-+ else if (amsdu_len == 1)
-+ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+ 1,
-+ 255,
-+ amsdu_len);
-+ else if (amsdu_len == 2)
-+ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+ 256,
-+ 511,
-+ amsdu_len);
-+ else if (amsdu_len == 3)
-+ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+ 512,
-+ 767,
-+ amsdu_len);
-+ else
-+ seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+ 256 * (amsdu_len - 1),
-+ 256 * (amsdu_len - 1) + 255,
-+ amsdu_len);
-+
-+ seq_printf(s, "\t%s:%lu (WTBL value=0x%lx)\n", "HW AMSDU Num",
-+ ((dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET) + 1,
-+ (dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET);
-+}
-+
-+static int mt7996_wtbl_read(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u8 lwtbl[LWTBL_LEN_IN_DW * 4] = {0};
-+ u8 uwtbl[UWTBL_LEN_IN_DW * 4] = {0};
-+ int x;
-+
-+ mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_LMAC, 0,
-+ LWTBL_LEN_IN_DW, lwtbl);
-+ seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
-+ seq_printf(s, "LMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+ MT_DBG_WTBLON_TOP_WDUCR_ADDR,
-+ mt76_rr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR),
-+ LWTBL_IDX2BASE(dev->wlan_idx, 0));
-+ for (x = 0; x < LWTBL_LEN_IN_DW; x++) {
-+ seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
-+ x,
-+ lwtbl[x * 4 + 3],
-+ lwtbl[x * 4 + 2],
-+ lwtbl[x * 4 + 1],
-+ lwtbl[x * 4]);
-+ }
-+
-+ /* Parse LWTBL */
-+ parse_fmac_lwtbl_dw0_1(s, lwtbl);
-+ parse_fmac_lwtbl_dw2(s, lwtbl);
-+ parse_fmac_lwtbl_dw3(s, lwtbl);
-+ parse_fmac_lwtbl_dw4(s, lwtbl);
-+ parse_fmac_lwtbl_dw5(s, lwtbl);
-+ parse_fmac_lwtbl_dw6(s, lwtbl);
-+ parse_fmac_lwtbl_dw7(s, lwtbl);
-+ parse_fmac_lwtbl_dw8(s, lwtbl);
-+ parse_fmac_lwtbl_dw9(s, lwtbl);
-+ parse_fmac_lwtbl_dw10(s, lwtbl);
-+ parse_fmac_lwtbl_dw11(s, lwtbl);
-+ parse_fmac_lwtbl_dw12(s, lwtbl);
-+ parse_fmac_lwtbl_dw13(s, lwtbl);
-+ parse_fmac_lwtbl_dw14(s, lwtbl);
-+ parse_fmac_lwtbl_mlo_info(s, lwtbl);
-+ parse_fmac_lwtbl_dw31(s, lwtbl);
-+ parse_fmac_lwtbl_dw32(s, lwtbl);
-+ parse_fmac_lwtbl_rx_stats(s, lwtbl);
-+
-+ mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_UMAC, 0,
-+ UWTBL_LEN_IN_DW, uwtbl);
-+ seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
-+ seq_printf(s, "UMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+ MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+ mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+ UWTBL_IDX2BASE(dev->wlan_idx, 0));
-+ for (x = 0; x < UWTBL_LEN_IN_DW; x++) {
-+ seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
-+ x,
-+ uwtbl[x * 4 + 3],
-+ uwtbl[x * 4 + 2],
-+ uwtbl[x * 4 + 1],
-+ uwtbl[x * 4]);
-+ }
-+
-+ /* Parse UWTBL */
-+ parse_fmac_uwtbl_mlo_info(s, uwtbl);
-+ parse_fmac_uwtbl_pn(s, uwtbl, lwtbl);
-+ parse_fmac_uwtbl_sn(s, uwtbl);
-+ parse_fmac_uwtbl_key_info(s, uwtbl, lwtbl);
-+ parse_fmac_uwtbl_msdu_info(s, uwtbl);
-+
-+ return 0;
-+}
-+
-+static int mt7996_sta_info(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u8 lwtbl[LWTBL_LEN_IN_DW*4] = {0};
-+ u16 i = 0;
-+
-+ for (i=0; i < mt7996_wtbl_size(dev); i++) {
-+ mt7996_wtbl_read_raw(dev, i, WTBL_TYPE_LMAC, 0,
-+ LWTBL_LEN_IN_DW, lwtbl);
-+
-+ if (lwtbl[4] || lwtbl[5] || lwtbl[6] || lwtbl[7] || lwtbl[0] || lwtbl[1]) {
-+ u32 *addr, dw_value;
-+
-+ seq_printf(s, "wcid:%d\tAddr: %02x:%02x:%02x:%02x:%02x:%02x",
-+ i, lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
-+
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
-+ dw_value = *addr;
-+ seq_printf(s, "\t%s:%u", WTBL_LMAC_DW2[0].name,
-+ (dw_value & WTBL_LMAC_DW2[0].mask) >> WTBL_LMAC_DW2[0].shift);
-+
-+ addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
-+ dw_value = *addr;
-+ seq_printf(s, "\tPSM:%u\n", !!(dw_value & WF_LWTBL_PSM_MASK));
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int mt7996_token_read(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ int msdu_id;
-+ struct mt76_txwi_cache *txwi;
-+
-+ seq_printf(s, "Token from host:\n");
-+ spin_lock_bh(&dev->mt76.token_lock);
-+ idr_for_each_entry(&dev->mt76.token, txwi, msdu_id) {
-+ seq_printf(s, "%4d (pending time %u ms)\n", msdu_id,
-+ jiffies_to_msecs(jiffies - txwi->jiffies));
-+ }
-+ spin_unlock_bh(&dev->mt76.token_lock);
-+ seq_printf(s, "\n");
-+
-+ return 0;
-+}
-+
-+int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ u32 device_id = (dev->mt76.rev) >> 16;
-+ int i = 0;
-+ static const struct mt7996_dbg_reg_desc dbg_reg_s[] = {
-+ { 0x7990, mt7996_dbg_offs },
-+ { 0x7992, mt7992_dbg_offs },
-+ };
-+
-+ for (i = 0; i < ARRAY_SIZE(dbg_reg_s); i++) {
-+ if (device_id == dbg_reg_s[i].id) {
-+ dev->dbg_reg = &dbg_reg_s[i];
-+ break;
-+ }
-+ }
-+
-+ if (is_mt7996(&dev->mt76)) {
-+ WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7996;
-+ WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7996;
-+ WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7996;
-+ } else {
-+ WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7992;
-+ WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7992;
-+ WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7992;
-+ }
-+
-+ /* agg */
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info0", dir,
-+ mt7996_agginfo_read_band0);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info1", dir,
-+ mt7996_agginfo_read_band1);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info2", dir,
-+ mt7996_agginfo_read_band2);
-+ /* amsdu */
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "amsdu_info", dir,
-+ mt7996_amsdu_result_read);
-+
-+ debugfs_create_file("fw_debug_module", 0600, dir, dev,
-+ &fops_fw_debug_module);
-+ debugfs_create_file("fw_debug_level", 0600, dir, dev,
-+ &fops_fw_debug_level);
-+ debugfs_create_file("fw_wa_query", 0600, dir, dev, &fops_wa_query);
-+ debugfs_create_file("fw_wa_set", 0600, dir, dev, &fops_wa_set);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "fw_version", dir,
-+ mt7996_dump_version);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wa_info", dir,
-+ mt7996_fw_wa_info_read);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wm_info", dir,
-+ mt7996_fw_wm_info_read);
-+
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info0", dir,
-+ mt7996_mibinfo_band0);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info1", dir,
-+ mt7996_mibinfo_band1);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info2", dir,
-+ mt7996_mibinfo_band2);
-+
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "sta_info", dir,
-+ mt7996_sta_info);
-+
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
-+ mt7996_trinfo_read);
-+
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
-+ mt7996_wtbl_read);
-+
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
-+
-+ debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
-+
-+ return 0;
-+}
-+
-+#endif
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-new file mode 100644
-index 00000000..e8870166
---- /dev/null
-+++ b/mt7996/mtk_mcu.c
-@@ -0,0 +1,18 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2023 MediaTek Inc.
-+ */
-+
-+#include <linux/firmware.h>
-+#include <linux/fs.h>
-+#include "mt7996.h"
-+#include "mcu.h"
-+#include "mac.h"
-+#include "mtk_mcu.h"
-+
-+#ifdef CONFIG_MTK_DEBUG
-+
-+
-+
-+
-+#endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-new file mode 100644
-index 00000000..e741aa27
---- /dev/null
-+++ b/mt7996/mtk_mcu.h
-@@ -0,0 +1,16 @@
-+/* SPDX-License-Identifier: ISC */
-+/*
-+ * Copyright (C) 2023 MediaTek Inc.
-+ */
-+
-+#ifndef __MT7996_MTK_MCU_H
-+#define __MT7996_MTK_MCU_H
-+
-+#include "../mt76_connac_mcu.h"
-+
-+#ifdef CONFIG_MTK_DEBUG
-+
-+
-+#endif
-+
-+#endif
-diff --git a/tools/fwlog.c b/tools/fwlog.c
-index e5d4a105..3c6a61d7 100644
---- a/tools/fwlog.c
-+++ b/tools/fwlog.c
-@@ -26,7 +26,7 @@ static const char *debugfs_path(const char *phyname, const char *file)
- return path;
- }
-
--static int mt76_set_fwlog_en(const char *phyname, bool en)
-+static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
- {
- FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
-
-@@ -35,7 +35,13 @@ static int mt76_set_fwlog_en(const char *phyname, bool en)
- return 1;
- }
-
-- fprintf(f, "7");
-+ if (en && val)
-+ fprintf(f, "%s", val);
-+ else if (en)
-+ fprintf(f, "7");
-+ else
-+ fprintf(f, "0");
-+
- fclose(f);
-
- return 0;
-@@ -76,6 +82,7 @@ static void handle_signal(int sig)
-
- int mt76_fwlog(const char *phyname, int argc, char **argv)
- {
-+#define BUF_SIZE 1504
- struct sockaddr_in local = {
- .sin_family = AF_INET,
- .sin_addr.s_addr = INADDR_ANY,
-@@ -84,9 +91,9 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- .sin_family = AF_INET,
- .sin_port = htons(55688),
- };
-- char buf[1504];
-+ char *buf = calloc(BUF_SIZE, sizeof(char));
- int ret = 0;
-- int yes = 1;
-+ /* int yes = 1; */
- int s, fd;
-
- if (argc < 1) {
-@@ -105,13 +112,13 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- return 1;
- }
-
-- setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
-+ /* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
- if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
- perror("bind");
- return 1;
- }
-
-- if (mt76_set_fwlog_en(phyname, true))
-+ if (mt76_set_fwlog_en(phyname, true, argv[1]))
- return 1;
-
- fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
-@@ -145,8 +152,8 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- if (!r)
- continue;
-
-- if (len > sizeof(buf)) {
-- fprintf(stderr, "Length error: %d > %d\n", len, (int)sizeof(buf));
-+ if (len > BUF_SIZE) {
-+ fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
- ret = 1;
- break;
- }
-@@ -171,7 +178,7 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- close(fd);
-
- out:
-- mt76_set_fwlog_en(phyname, false);
-+ mt76_set_fwlog_en(phyname, false, NULL);
-
- return ret;
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1001-mtk-wifi-mt76-mt7996-support-record-muru-algo-log-wh.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1001-mtk-wifi-mt76-mt7996-support-record-muru-algo-log-wh.patch
deleted file mode 100644
index eab9300..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1001-mtk-wifi-mt76-mt7996-support-record-muru-algo-log-wh.patch
+++ /dev/null
@@ -1,144 +0,0 @@
-From c772657b0835b02b512b101dc2111c99d6c3448c Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 28 Nov 2023 16:01:33 +0800
-Subject: [PATCH 1001/1044] mtk: wifi: mt76: mt7996: support record muru algo
- log when record fw log
-
-Support record muru algorithm debug log in firmware when we use
-chihuahua tool to record fw log. This can help us to check some key
-point of muru algorithm result, like bsrp status, airtime busy status,
-ru candidate...
-Corresponding to Logan driver, it is the same as execute the iwpriv
-command: iwpriv rax0 set muruDbgInfo=[category]-1
-
-Disable muru debug log when we stop record fwlog. Without this commit,
-if we run $ echo 2 > fw_debug_wm after recording fwlog, it will print
-out too many fw debug log.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/debugfs.c | 35 +++++++++++++++++++++++++++++++++++
- mt7996/mt7996.h | 1 +
- mt7996/mtk_mcu.c | 21 +++++++++++++++++++++
- mt7996/mtk_mcu.h | 3 +++
- 4 files changed, 60 insertions(+)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 1637b39d..ea78166b 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -423,6 +423,36 @@ remove_buf_file_cb(struct dentry *f)
- return 0;
- }
-
-+static int
-+mt7996_fw_debug_muru_set(void *data)
-+{
-+ struct mt7996_dev *dev = data;
-+ enum {
-+ DEBUG_BSRP_STATUS = 256,
-+ DEBUG_TX_DATA_BYTE_CONUT,
-+ DEBUG_RX_DATA_BYTE_CONUT,
-+ DEBUG_RX_TOTAL_BYTE_CONUT,
-+ DEBUG_INVALID_TID_BSR,
-+ DEBUG_UL_LONG_TERM_PPDU_TYPE,
-+ DEBUG_DL_LONG_TERM_PPDU_TYPE,
-+ DEBUG_PPDU_CLASS_TRIG_ONOFF,
-+ DEBUG_AIRTIME_BUSY_STATUS,
-+ DEBUG_UL_OFDMA_MIMO_STATUS,
-+ DEBUG_RU_CANDIDATE,
-+ DEBUG_MEC_UPDATE_AMSDU,
-+ } debug;
-+ int ret;
-+
-+ for (debug = DEBUG_BSRP_STATUS; debug <= DEBUG_MEC_UPDATE_AMSDU; debug++) {
-+ ret = mt7996_mcu_muru_dbg_info(dev, debug,
-+ dev->fw_debug_bin & BIT(0));
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
- static int
- mt7996_fw_debug_bin_set(void *data, u64 val)
- {
-@@ -431,6 +461,7 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
- .remove_buf_file = remove_buf_file_cb,
- };
- struct mt7996_dev *dev = data;
-+ int ret;
-
- if (!dev->relay_fwlog) {
- dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir,
-@@ -443,6 +474,10 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
-
- relay_reset(dev->relay_fwlog);
-
-+ ret = mt7996_fw_debug_muru_set(dev);
-+ if (ret)
-+ return ret;
-+
- return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
- }
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 34159f97..29976860 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -677,6 +677,7 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
-
- #ifdef CONFIG_MTK_DEBUG
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
-+int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index e8870166..c16b25ab 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -15,4 +15,25 @@
-
-
-
-+int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
-+{
-+ struct {
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 item;
-+ u8 __rsv2[2];
-+ __le32 value;
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_CMD_MURU_DBG_INFO),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .item = cpu_to_le16(item),
-+ .value = cpu_to_le32(val),
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
-+ sizeof(req), true);
-+}
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index e741aa27..7f4d4e02 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -10,6 +10,9 @@
-
- #ifdef CONFIG_MTK_DEBUG
-
-+enum {
-+ UNI_CMD_MURU_DBG_INFO = 0x18,
-+};
-
- #endif
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1002-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1002-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch
deleted file mode 100644
index b05982a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1002-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From 90b67bfa2b8f0bfbfd32fd93fb898d20b1e6b984 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Thu, 8 Jun 2023 17:32:33 +0800
-Subject: [PATCH 1002/1044] mtk: wifi: mt76: mt7996: add check for hostapd
- config he_ldpc
-
-Add check for hostapd config he_ldpc.
-This capabilities is checked in mcu_beacon_check_caps in 7915.
-
-Add check for STA LDPC cap, if STA only have BCC we should not overwrite the phy_cap with config he_ldpc.
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- mt7996/mcu.c | 12 +++++++++---
- 1 file changed, 9 insertions(+), 3 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index e92a3e53..b488a78f 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1185,7 +1185,8 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
- }
-
- static void
--mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
-+mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
-+ struct ieee80211_sta *sta)
- {
- struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
- struct ieee80211_he_mcs_nss_supp mcs_map;
-@@ -1205,6 +1206,11 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- he->he_phy_cap[i] = elem->phy_cap_info[i];
- }
-
-+ if (vif->type == NL80211_IFTYPE_AP &&
-+ (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
-+ u8p_replace_bits(&he->he_phy_cap[1], vif->bss_conf.he_ldpc,
-+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
-+
- mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
- switch (sta->deflink.bandwidth) {
- case IEEE80211_STA_RX_BW_160:
-@@ -2111,7 +2117,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- * update sta_rec_he here.
- */
- if (changed)
-- mt7996_mcu_sta_he_tlv(skb, sta);
-+ mt7996_mcu_sta_he_tlv(skb, vif, sta);
-
- /* sta_rec_ra accommodates BW, NSS and only MCS range format
- * i.e 0-{7,8,9} for VHT.
-@@ -2199,7 +2205,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- /* starec amsdu */
- mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
- /* starec he */
-- mt7996_mcu_sta_he_tlv(skb, sta);
-+ mt7996_mcu_sta_he_tlv(skb, vif, sta);
- /* starec he 6g*/
- mt7996_mcu_sta_he_6g_tlv(skb, sta);
- /* starec eht */
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1003-mtk-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1003-mtk-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch
deleted file mode 100644
index 4b48730..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1003-mtk-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From a086447ff00030ee7cc2d6b813a0c099ba288990 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 28 Dec 2022 22:24:25 +0800
-Subject: [PATCH 1003/1044] mtk: wifi: mt76: testmode: add atenl support in
- mt7996
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- testmode.c | 3 ++-
- testmode.h | 2 ++
- 2 files changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/testmode.c b/testmode.c
-index ca4feccf..37783160 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -613,7 +613,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
-
- if (dev->test_mtd.name &&
- (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
-- nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
-+ nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset) ||
-+ nla_put_u8(msg, MT76_TM_ATTR_BAND_IDX, phy->band_idx)))
- goto out;
-
- if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
-diff --git a/testmode.h b/testmode.h
-index 5e2792d8..a40cd74b 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -17,6 +17,7 @@
- *
- * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
- * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
-+ * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
- *
- * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
- * state to MT76_TM_STATE_TX_FRAMES (u32)
-@@ -56,6 +57,7 @@ enum mt76_testmode_attr {
-
- MT76_TM_ATTR_MTD_PART,
- MT76_TM_ATTR_MTD_OFFSET,
-+ MT76_TM_ATTR_BAND_IDX,
-
- MT76_TM_ATTR_TX_COUNT,
- MT76_TM_ATTR_TX_LENGTH,
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1004-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1004-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch
deleted file mode 100644
index 73e9626..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1004-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch
+++ /dev/null
@@ -1,2354 +0,0 @@
-From 4c3bdf16f108c081731b1a73a3bd7730657e81d3 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 1 Mar 2023 11:59:16 +0800
-Subject: [PATCH 1004/1044] mtk: wifi: mt76: testmode: add basic testmode
- support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add testmode eeprom buffer mode support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Fix power & freq offset issue for iTest power cal & tx/rx verifcation
-1. Wait for fw to tx. Otherwise, iTest testing tool cannot get the
-accurate tx power.
-2. In crystal mode, freq offset is set in 6G band and forwarded to 5G
-and 2G band. Therefore, we should avoid reseting freq offset to 0 when
-6G interface is off.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-edcca return err in testmode; therefore, bypass it when we are in testmode idle state or testmode bf is on
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- eeprom.c | 6 +-
- mac80211.c | 3 +-
- mt76.h | 36 +++
- mt76_connac_mcu.h | 2 +
- mt7996/Makefile | 1 +
- mt7996/eeprom.c | 35 ++-
- mt7996/eeprom.h | 1 +
- mt7996/init.c | 8 +
- mt7996/mac.c | 3 +-
- mt7996/main.c | 26 ++
- mt7996/mcu.c | 59 +++-
- mt7996/mcu.h | 33 +++
- mt7996/mt7996.h | 28 +-
- mt7996/testmode.c | 740 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/testmode.h | 299 +++++++++++++++++++
- testmode.c | 123 ++++++--
- testmode.h | 85 +++++-
- tools/fields.c | 102 ++++++-
- 18 files changed, 1542 insertions(+), 48 deletions(-)
- create mode 100644 mt7996/testmode.c
- create mode 100644 mt7996/testmode.h
-
-diff --git a/eeprom.c b/eeprom.c
-index 7d5cf28f..85bd2a29 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -94,8 +94,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l
- }
-
- #ifdef CONFIG_NL80211_TESTMODE
-- dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
-- dev->test_mtd.offset = offset;
-+ if (len == dev->eeprom.size) {
-+ dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
-+ dev->test_mtd.offset = offset;
-+ }
- #endif
-
- out_put_node:
-diff --git a/mac80211.c b/mac80211.c
-index 6e8ac6f4..fea19e19 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -846,7 +846,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
- }
-
- #ifdef CONFIG_NL80211_TESTMODE
-- if (phy->test.state == MT76_TM_STATE_RX_FRAMES) {
-+ if (!(phy->test.flag & MT_TM_FW_RX_COUNT) &&
-+ phy->test.state == MT76_TM_STATE_RX_FRAMES) {
- phy->test.rx_stats.packets[q]++;
- if (status->flag & RX_FLAG_FAILED_FCS_CRC)
- phy->test.rx_stats.fcs_error[q]++;
-diff --git a/mt76.h b/mt76.h
-index 8cf21f98..f2b1e0c2 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -695,14 +695,21 @@ struct mt76_testmode_ops {
- int (*set_params)(struct mt76_phy *phy, struct nlattr **tb,
- enum mt76_testmode_state new_state);
- int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
-+ void (*reset_rx_stats)(struct mt76_phy *phy);
-+ void (*tx_stop)(struct mt76_phy *phy);
-+ int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
- };
-
-+#define MT_TM_FW_RX_COUNT BIT(0)
-+
- struct mt76_testmode_data {
- enum mt76_testmode_state state;
-
- u32 param_set[DIV_ROUND_UP(NUM_MT76_TM_ATTRS, 32)];
- struct sk_buff *tx_skb;
-
-+ u8 sku_en;
-+
- u32 tx_count;
- u16 tx_mpdu_len;
-
-@@ -712,6 +719,7 @@ struct mt76_testmode_data {
- u8 tx_rate_sgi;
- u8 tx_rate_ldpc;
- u8 tx_rate_stbc;
-+ u16 tx_preamble_puncture;
- u8 tx_ltf;
-
- u8 tx_antenna_mask;
-@@ -721,6 +729,9 @@ struct mt76_testmode_data {
- u32 tx_time;
- u32 tx_ipg;
-
-+ bool ibf;
-+ bool ebf;
-+
- u32 freq_offset;
-
- u8 tx_power[4];
-@@ -735,7 +746,16 @@ struct mt76_testmode_data {
- struct {
- u64 packets[__MT_RXQ_MAX];
- u64 fcs_error[__MT_RXQ_MAX];
-+ u64 len_mismatch;
- } rx_stats;
-+ u8 flag;
-+
-+ struct {
-+ u8 type;
-+ u8 enable;
-+ } cfg;
-+
-+ u8 aid;
- };
-
- struct mt76_vif {
-@@ -1439,6 +1459,22 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
- int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
- int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
-
-+static inline void
-+mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
-+{
-+#ifdef CONFIG_NL80211_TESTMODE
-+ td->param_set[idx / 32] |= BIT(idx % 32);
-+#endif
-+}
-+
-+static inline bool
-+mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
-+{
-+#ifdef CONFIG_NL80211_TESTMODE
-+ return td->param_set[idx / 32] & BIT(idx % 32);
-+#endif
-+}
-+
- static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
- {
- #ifdef CONFIG_NL80211_TESTMODE
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 482782f7..2e148011 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1266,12 +1266,14 @@ enum {
- MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
- MCU_UNI_CMD_RA = 0x2f,
- MCU_UNI_CMD_MURU = 0x31,
-+ MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32,
- MCU_UNI_CMD_BF = 0x33,
- MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
- MCU_UNI_CMD_THERMAL = 0x35,
- MCU_UNI_CMD_VOW = 0x37,
- MCU_UNI_CMD_PP = 0x38,
- MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
-+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- MCU_UNI_CMD_RRO = 0x57,
- MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
- MCU_UNI_CMD_PER_STA_INFO = 0x6d,
-diff --git a/mt7996/Makefile b/mt7996/Makefile
-index a056b40e..7bb17f44 100644
---- a/mt7996/Makefile
-+++ b/mt7996/Makefile
-@@ -8,5 +8,6 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
- debugfs.o mmio.o
-
- mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
-+mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
-
- mt7996e-y += mtk_debugfs.o mtk_mcu.o
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 121a3c95..2299793d 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -6,6 +6,11 @@
- #include <linux/firmware.h>
- #include "mt7996.h"
- #include "eeprom.h"
-+#include <linux/moduleparam.h>
-+
-+static bool testmode_enable;
-+module_param(testmode_enable, bool, 0644);
-+MODULE_PARM_DESC(testmode_enable, "Enable testmode");
-
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
-@@ -43,6 +48,9 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
-
- static char *mt7996_eeprom_name(struct mt7996_dev *dev)
- {
-+ if (dev->testmode_enable)
-+ return MT7996_EEPROM_DEFAULT_TM;
-+
- switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
- if (dev->chip_sku == MT7996_SKU_404)
-@@ -92,21 +100,36 @@ out:
- return ret;
- }
-
--static int mt7996_eeprom_load(struct mt7996_dev *dev)
-+int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
- {
-+ u8 *eeprom;
- int ret;
-
-+ /* load eeprom in flash or bin file mode to determine fw mode */
- ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
- if (ret < 0)
- return ret;
-
- if (ret) {
- dev->flash_mode = true;
-- } else {
-- u8 free_block_num;
-- u32 block_num, i;
-- u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
-+ eeprom = dev->mt76.eeprom.data;
-+ /* testmode enable priority: eeprom field > module parameter */
-+ dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
-+ testmode_enable;
-+ }
-+
-+ return ret;
-+}
-+
-+static int mt7996_eeprom_load(struct mt7996_dev *dev)
-+{
-+ int ret;
-+ u8 free_block_num;
-+ u32 block_num, i;
-+ u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
-
-+ /* flash or bin file mode eeprom is loaded before mcu init */
-+ if (!dev->flash_mode) {
- ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
- if (ret < 0)
- return ret;
-@@ -118,7 +141,7 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
- /* read eeprom data from efuse */
- block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
- for (i = 0; i < block_num; i++) {
-- ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size);
-+ ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
- if (ret < 0)
- return ret;
- }
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 72c38ad3..de3ff4e2 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
- MT_EE_MAC_ADDR = 0x004,
- MT_EE_MAC_ADDR2 = 0x00a,
- MT_EE_WIFI_CONF = 0x190,
-+ MT_EE_TESTMODE_EN = 0x1af,
- MT_EE_MAC_ADDR3 = 0x2c0,
- MT_EE_RATE_DELTA_2G = 0x1400,
- MT_EE_RATE_DELTA_5G = 0x147d,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 0e3cdc05..d4d1a60b 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -966,6 +966,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
-
- set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
-
-+ ret = mt7996_eeprom_check_fw_mode(dev);
-+ if (ret < 0)
-+ return ret;
-+
- ret = mt7996_mcu_init(dev);
- if (ret)
- return ret;
-@@ -1384,6 +1388,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
-
- mt7996_init_wiphy(hw, &dev->mt76.mmio.wed);
-
-+#ifdef CONFIG_NL80211_TESTMODE
-+ dev->mt76.test_ops = &mt7996_testmode_ops;
-+#endif
-+
- ret = mt76_register_device(&dev->mt76, true, mt76_rates,
- ARRAY_SIZE(mt76_rates));
- if (ret)
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 1f53d230..603f6c0d 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -685,7 +685,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- *info);
- }
-
-- if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
-+ if (rxv && mode >= MT_PHY_TYPE_HE_SU && mode < MT_PHY_TYPE_EHT_SU &&
-+ !(status->flag & RX_FLAG_8023))
- mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode);
-
- if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
-diff --git a/mt7996/main.c b/mt7996/main.c
-index ad2c6a9d..40b5cee3 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -23,6 +23,18 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
- return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- }
-
-+static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
-+{
-+ struct mt7996_phy *phy;
-+ int i;
-+
-+ for (i = 0; i < __MT_MAX_BAND; i++) {
-+ phy = __mt7996_phy(dev, i);
-+ if (phy)
-+ mt76_testmode_set_state(phy->mt76, MT76_TM_STATE_OFF);
-+ }
-+}
-+
- int mt7996_run(struct ieee80211_hw *hw)
- {
- struct mt7996_dev *dev = mt7996_hw_dev(hw);
-@@ -37,6 +49,8 @@ int mt7996_run(struct ieee80211_hw *hw)
- goto out;
- }
-
-+ mt7996_testmode_disable_all(dev);
-+
- mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
-
- ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
-@@ -291,6 +305,11 @@ int mt7996_set_channel(struct mt7996_phy *phy)
-
- mt76_set_channel(phy->mt76);
-
-+ if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
-+ mt7996_tm_update_channel(phy);
-+ goto out;
-+ }
-+
- ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
- if (ret)
- goto out;
-@@ -398,6 +417,11 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
- int ret;
-
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-+ if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
-+ ret = mt7996_mcu_edcca_enable(phy, true);
-+ if (ret)
-+ return ret;
-+ }
- ieee80211_stop_queues(hw);
- ret = mt7996_set_channel(phy);
- if (ret)
-@@ -1507,6 +1531,8 @@ const struct ieee80211_ops mt7996_ops = {
- .sta_set_decap_offload = mt7996_sta_set_decap_offload,
- .add_twt_setup = mt7996_mac_add_twt_setup,
- .twt_teardown_request = mt7996_twt_teardown_request,
-+ CFG80211_TESTMODE_CMD(mt76_testmode_cmd)
-+ CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
- #ifdef CONFIG_MAC80211_DEBUGFS
- .sta_add_debugfs = mt7996_sta_add_debugfs,
- #endif
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index b488a78f..d8795d30 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2863,8 +2863,12 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
- {
- int ret;
-
-- ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
-- MT7996_RAM_TYPE_WM);
-+ if (dev->testmode_enable)
-+ ret = __mt7996_load_ram(dev, "WM_TM", fw_name(dev, FIRMWARE_WM_TM),
-+ MT7996_RAM_TYPE_WM_TM);
-+ else
-+ ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
-+ MT7996_RAM_TYPE_WM);
- if (ret)
- return ret;
-
-@@ -3555,17 +3559,9 @@ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
- &req, sizeof(req), true);
- }
-
--int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
-+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
- {
-- struct {
-- u8 _rsv[4];
--
-- __le16 tag;
-- __le16 len;
-- __le32 addr;
-- __le32 valid;
-- u8 data[16];
-- } __packed req = {
-+ struct mt7996_mcu_eeprom_info req = {
- .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
- .len = cpu_to_le16(sizeof(req) - 4),
- .addr = cpu_to_le32(round_down(offset,
-@@ -3574,6 +3570,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
- struct sk_buff *skb;
- bool valid;
- int ret;
-+ u8 *buf = read_buf;
-
- ret = mt76_mcu_send_and_get_msg(&dev->mt76,
- MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
-@@ -3584,7 +3581,9 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
- valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
- if (valid) {
- u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
-- u8 *buf = (u8 *)dev->mt76.eeprom.data + addr;
-+
-+ if (!buf)
-+ buf = (u8 *)dev->mt76.eeprom.data + addr;
-
- skb_pull(skb, 48);
- memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
-@@ -4577,3 +4576,37 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, bool auto_mode,
- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
- &req, sizeof(req), false);
- }
-+
-+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct tx_power_ctrl req = {
-+ .tag = cpu_to_le16(power_ctrl_id),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .power_ctrl_id = power_ctrl_id,
-+ .band_idx = phy->mt76->band_idx,
-+ };
-+
-+ switch (power_ctrl_id) {
-+ case UNI_TXPOWER_SKU_POWER_LIMIT_CTRL:
-+ req.sku_enable = !!data;
-+ break;
-+ case UNI_TXPOWER_PERCENTAGE_CTRL:
-+ req.percentage_ctrl_enable = !!data;
-+ break;
-+ case UNI_TXPOWER_PERCENTAGE_DROP_CTRL:
-+ req.power_drop_level = data;
-+ break;
-+ case UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL:
-+ req.bf_backoff_enable = !!data;
-+ break;
-+ case UNI_TXPOWER_ATE_MODE_CTRL:
-+ req.ate_mode_enable = !!data;
-+ break;
-+ default:
-+ req.sku_enable = !!data;
-+ }
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
-+ &req, sizeof(req), false);
-+}
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 238c4c53..325c3c97 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -157,6 +157,16 @@ struct mt7996_mcu_eeprom {
- __le16 buf_len;
- } __packed;
-
-+struct mt7996_mcu_eeprom_info {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ __le32 addr;
-+ __le32 valid;
-+ u8 data[MT7996_EEPROM_BLOCK_SIZE];
-+} __packed;
-+
- struct mt7996_mcu_phy_rx_info {
- u8 category;
- u8 rate;
-@@ -889,8 +899,31 @@ enum {
- UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
- };
-
-+struct tx_power_ctrl {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 power_ctrl_id;
-+ union {
-+ bool sku_enable;
-+ bool ate_mode_enable;
-+ bool percentage_ctrl_enable;
-+ bool bf_backoff_enable;
-+ u8 power_drop_level;
-+ };
-+ u8 band_idx;
-+ u8 rsv[1];
-+} __packed;
-+
- enum {
-+ UNI_TXPOWER_SKU_POWER_LIMIT_CTRL = 0,
-+ UNI_TXPOWER_PERCENTAGE_CTRL = 1,
-+ UNI_TXPOWER_PERCENTAGE_DROP_CTRL = 2,
-+ UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
- UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
-+ UNI_TXPOWER_ATE_MODE_CTRL = 6,
- };
-
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 29976860..5af55492 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -32,25 +32,30 @@
- #define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin"
- #define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin"
- #define MT7996_FIRMWARE_DSP "mediatek/mt7996/mt7996_dsp.bin"
-+#define MT7996_FIRMWARE_WM_TM "mediatek/mt7996/mt7996_wm_tm.bin"
- #define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin"
-
- #define MT7992_FIRMWARE_WA "mediatek/mt7996/mt7992_wa.bin"
- #define MT7992_FIRMWARE_WM "mediatek/mt7996/mt7992_wm.bin"
- #define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin"
-+#define MT7992_FIRMWARE_WM_TM "mediatek/mt7996/mt7992_wm_tm.bin"
- #define MT7992_ROM_PATCH "mediatek/mt7996/mt7992_rom_patch.bin"
-
- #define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin"
- #define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin"
- #define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin"
-+#define MT7992_FIRMWARE_WM_TM_24 "mediatek/mt7996/mt7992_wm_tm_24.bin"
- #define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin"
-
- #define MT7992_FIRMWARE_WA_23 "mediatek/mt7996/mt7992_wa_23.bin"
- #define MT7992_FIRMWARE_WM_23 "mediatek/mt7996/mt7992_wm_23.bin"
- #define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin"
-+#define MT7992_FIRMWARE_WM_TM_23 "mediatek/mt7996/mt7992_wm_tm_23.bin"
- #define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
-
- #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
- #define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
-+#define MT7996_EEPROM_DEFAULT_TM "mediatek/mt7996/mt7996_eeprom_tm.bin"
- #define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_EXT "mediatek/mt7996/mt7992_eeprom_2e5e.bin"
- #define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin"
-@@ -126,6 +131,7 @@ enum mt7992_sku_type {
-
- enum mt7996_ram_type {
- MT7996_RAM_TYPE_WM,
-+ MT7996_RAM_TYPE_WM_TM = MT7996_RAM_TYPE_WM,
- MT7996_RAM_TYPE_WA,
- MT7996_RAM_TYPE_DSP,
- __MT7996_RAM_TYPE_MAX,
-@@ -273,6 +279,21 @@ struct mt7996_phy {
- struct mt76_channel_state state_ts;
-
- bool has_aux_rx;
-+
-+#ifdef CONFIG_NL80211_TESTMODE
-+ struct {
-+ u32 *reg_backup;
-+
-+ s32 last_freq_offset;
-+ u8 last_rcpi[4];
-+ s8 last_rssi[4];
-+ s8 last_ib_rssi[4];
-+ s8 last_wb_rssi[4];
-+ u8 last_snr;
-+
-+ u8 spe_idx;
-+ } test;
-+#endif
- };
-
- struct mt7996_dev {
-@@ -353,6 +374,8 @@ struct mt7996_dev {
- spinlock_t lock;
- } wed_rro;
-
-+ bool testmode_enable;
-+
- bool ibf;
- u8 fw_debug_wm;
- u8 fw_debug_wa;
-@@ -467,6 +490,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
-+extern const struct mt76_testmode_ops mt7996_testmode_ops;
-
- struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- void __iomem *mem_base, u32 device_id);
-@@ -476,6 +500,7 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
- int mt7996_register_device(struct mt7996_dev *dev);
- void mt7996_unregister_device(struct mt7996_dev *dev);
- int mt7996_eeprom_init(struct mt7996_dev *dev);
-+int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
- int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
- int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
- struct ieee80211_channel *chan);
-@@ -528,7 +553,7 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
- int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, void *data, u32 field);
- int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
--int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset);
-+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
- int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
- int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap);
- int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
-@@ -563,6 +588,7 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
- void mt7996_mcu_exit(struct mt7996_dev *dev);
- int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
-+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
-
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-new file mode 100644
-index 00000000..96079c22
---- /dev/null
-+++ b/mt7996/testmode.c
-@@ -0,0 +1,740 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2022 MediaTek Inc.
-+ */
-+
-+#include "mt7996.h"
-+#include "mac.h"
-+#include "mcu.h"
-+#include "testmode.h"
-+
-+enum {
-+ TM_CHANGED_TXPOWER,
-+ TM_CHANGED_FREQ_OFFSET,
-+ TM_CHANGED_SKU_EN,
-+ TM_CHANGED_TX_LENGTH,
-+ TM_CHANGED_TX_TIME,
-+ TM_CHANGED_CFG,
-+
-+ /* must be last */
-+ NUM_TM_CHANGED
-+};
-+
-+static const u8 tm_change_map[] = {
-+ [TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
-+ [TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
-+ [TM_CHANGED_SKU_EN] = MT76_TM_ATTR_SKU_EN,
-+ [TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
-+ [TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
-+ [TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
-+};
-+
-+static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
-+{
-+ static const u8 width_to_bw[][NUM_BW_MAP] = {
-+ [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
-+ [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
-+ [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
-+ [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
-+ [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
-+ [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
-+ [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
-+ [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
-+ [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
-+ };
-+
-+ if (width >= ARRAY_SIZE(width_to_bw))
-+ return 0;
-+
-+ return width_to_bw[width][method];
-+}
-+
-+static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
-+{
-+ static const u8 rate_to_phy[] = {
-+ [MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
-+ [MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
-+ [MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
-+ [MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
-+ [MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
-+ [MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
-+ [MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
-+ [MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
-+ [MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
-+ [MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
-+ [MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
-+ };
-+
-+ if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
-+ return -EINVAL;
-+
-+ return rate_to_phy[tx_rate_mode];
-+}
-+
-+static int
-+mt7996_tm_check_antenna(struct mt7996_phy *phy)
-+{
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_dev *dev = phy->dev;
-+ u8 band_idx = phy->mt76->band_idx;
-+ u32 chainmask = phy->mt76->chainmask;
-+ u32 aux_rx_mask;
-+
-+ chainmask = chainmask >> dev->chainshift[band_idx];
-+ aux_rx_mask = BIT(fls(chainmask)) * phy->has_aux_rx;
-+ if (td->tx_antenna_mask & ~(chainmask | aux_rx_mask)) {
-+ dev_err(dev->mt76.dev,
-+ "tx antenna mask 0x%x exceeds hw limit (chainmask 0x%x, has aux rx: %s)\n",
-+ td->tx_antenna_mask, chainmask, phy->has_aux_rx ? "yes" : "no");
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_set(struct mt7996_dev *dev, u32 func_idx, u32 data)
-+{
-+ struct mt7996_tm_req req = {
-+ .rf_test = {
-+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+ .len = cpu_to_le16(sizeof(req.rf_test)),
-+ .action = RF_ACTION_SET,
-+ .op.rf.func_idx = func_idx,
-+ .op.rf.param.func_data = cpu_to_le32(data),
-+ },
-+ };
-+ bool wait = (data == RF_CMD(START_TX)) ? true : false;
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
-+ sizeof(req), wait);
-+}
-+
-+static int
-+mt7996_tm_get(struct mt7996_dev *dev, u32 func_idx, u32 data, u32 *result)
-+{
-+ struct mt7996_tm_req req = {
-+ .rf_test = {
-+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+ .len = cpu_to_le16(sizeof(req.rf_test)),
-+ .action = RF_ACTION_GET,
-+ .op.rf.func_idx = func_idx,
-+ .op.rf.param.func_data = cpu_to_le32(data),
-+ },
-+ };
-+ struct mt7996_tm_event *event;
-+ struct sk_buff *skb;
-+ int ret;
-+
-+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_CTRL),
-+ &req, sizeof(req), true, &skb);
-+ if (ret)
-+ return ret;
-+
-+ event = (struct mt7996_tm_event *)skb->data;
-+ *result = event->result.payload_length;
-+
-+ dev_kfree_skb(skb);
-+
-+ return ret;
-+}
-+
-+static void
-+mt7996_tm_set_antenna(struct mt7996_phy *phy, u32 func_idx)
-+{
-+#define SPE_INDEX_MASK BIT(31)
-+#define TX_ANTENNA_MASK GENMASK(3, 0)
-+#define RX_ANTENNA_MASK GENMASK(20, 16) /* RX antenna mask at most 5 bit */
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ u32 antenna_mask;
-+
-+ if (!mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA))
-+ return;
-+
-+ if (func_idx == SET_ID(TX_PATH))
-+ antenna_mask = td->tx_spe_idx ? (SPE_INDEX_MASK | td->tx_spe_idx) :
-+ td->tx_antenna_mask & TX_ANTENNA_MASK;
-+ else if (func_idx == SET_ID(RX_PATH))
-+ antenna_mask = u32_encode_bits(td->tx_antenna_mask, RX_ANTENNA_MASK);
-+ else
-+ return;
-+
-+ mt7996_tm_set(dev, func_idx, antenna_mask);
-+}
-+
-+static void
-+mt7996_tm_set_mac_addr(struct mt7996_dev *dev, u8 *addr, u32 func_idx)
-+{
-+#define REMAIN_PART_TAG BIT(18)
-+ u32 own_mac_first = 0, own_mac_remain = 0;
-+ int len = sizeof(u32);
-+
-+ memcpy(&own_mac_first, addr, len);
-+ mt7996_tm_set(dev, func_idx, own_mac_first);
-+ /* Set the remain part of mac address */
-+ memcpy(&own_mac_remain, addr + len, ETH_ALEN - len);
-+ mt7996_tm_set(dev, func_idx | REMAIN_PART_TAG, own_mac_remain);
-+}
-+
-+static int
-+mt7996_tm_rf_switch_mode(struct mt7996_dev *dev, u32 op_mode)
-+{
-+ struct mt7996_tm_req req = {
-+ .rf_test = {
-+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+ .len = cpu_to_le16(sizeof(req.rf_test)),
-+ .action = RF_ACTION_SWITCH_TO_RF_TEST,
-+ .op.op_mode = cpu_to_le32(op_mode),
-+ },
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
-+ sizeof(req), false);
-+}
-+
-+static void
-+mt7996_tm_init(struct mt7996_phy *phy, bool en)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
-+
-+ if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
-+ return;
-+
-+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(ATE_MODE), en);
-+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), !en);
-+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), !en);
-+
-+ mt7996_tm_rf_switch_mode(dev, rf_test_mode);
-+
-+ mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
-+ mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en);
-+
-+ mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
-+
-+ /* use firmware counter for RX stats */
-+ phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
-+}
-+
-+static void
-+mt7996_tm_update_channel(struct mt7996_phy *phy)
-+{
-+#define CHAN_FREQ_BW_80P80_TAG (SET_ID(CHAN_FREQ) | BIT(16))
-+ struct mt7996_dev *dev = phy->dev;
-+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+ struct ieee80211_channel *chan = chandef->chan;
-+ u8 width = chandef->width;
-+ static const u8 ch_band[] = {
-+ [NL80211_BAND_2GHZ] = 0,
-+ [NL80211_BAND_5GHZ] = 1,
-+ [NL80211_BAND_6GHZ] = 2,
-+ };
-+
-+ if (!chan || !chandef) {
-+ dev_info(dev->mt76.dev, "chandef not found, channel update failed!\n");
-+ return;
-+ }
-+
-+ /* system bw */
-+ mt7996_tm_set(dev, SET_ID(CBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+
-+ if (width == NL80211_CHAN_WIDTH_80P80) {
-+ width = NL80211_CHAN_WIDTH_160;
-+ mt7996_tm_set(dev, CHAN_FREQ_BW_80P80_TAG, chandef->center_freq2 * 1000);
-+ }
-+
-+ /* TODO: define per-packet bw */
-+ /* per-packet bw */
-+ mt7996_tm_set(dev, SET_ID(DBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+
-+ /* control channel selection index */
-+ mt7996_tm_set(dev, SET_ID(PRIMARY_CH), 0);
-+ mt7996_tm_set(dev, SET_ID(BAND), ch_band[chan->band]);
-+
-+ /* trigger switch channel calibration */
-+ mt7996_tm_set(dev, SET_ID(CHAN_FREQ), chandef->center_freq1 * 1000);
-+
-+ // TODO: update power limit table
-+}
-+
-+static void
-+mt7996_tm_tx_stop(struct mt76_phy *mphy)
-+{
-+ struct mt76_testmode_data *td = &mphy->test;
-+ struct mt7996_phy *phy = mphy->priv;
-+ struct mt7996_dev *dev = phy->dev;
-+
-+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
-+ td->tx_pending = 0;
-+}
-+
-+static void
-+mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
-+{
-+#define FRAME_CONTROL 0x88
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_dev *dev = phy->dev;
-+
-+ //TODO: RU operation, replace mcs, nss, and ldpc
-+ if (en) {
-+ mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
-+ mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
-+ mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
-+ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
-+
-+ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
-+ mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
-+
-+ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME)) {
-+ mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
-+ mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
-+ } else {
-+ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
-+ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
-+ }
-+
-+ mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
-+ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
-+ mt7996_tm_set(dev, SET_ID(STBC), td->tx_rate_stbc);
-+ mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
-+ mt7996_tm_set(dev, SET_ID(IBF_ENABLE), td->ibf);
-+ mt7996_tm_set(dev, SET_ID(EBF_ENABLE), td->ebf);
-+ mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
-+ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
-+ mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
-+ mt7996_tm_set(dev, SET_ID(AID_OFFSET), 0);
-+ mt7996_tm_set(dev, SET_ID(PUNCTURE), td->tx_preamble_puncture);
-+
-+ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
-+ mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
-+ mt7996_tm_update_channel(phy);
-+
-+ /* trigger firmware to start TX */
-+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
-+ } else {
-+ mt7996_tm_tx_stop(phy->mt76);
-+ }
-+}
-+
-+static int
-+mt7996_tm_rx_stats_user_ctrl(struct mt7996_phy *phy, u16 user_idx)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_rx_req req = {
-+ .band = phy->mt76->band_idx,
-+ .user_ctrl = {
-+ .tag = cpu_to_le16(UNI_TM_RX_STAT_SET_USER_CTRL),
-+ .len = cpu_to_le16(sizeof(req.user_ctrl)),
-+ .band_idx = phy->mt76->band_idx,
-+ .user_idx = cpu_to_le16(user_idx),
-+ },
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_RX_STAT), &req,
-+ sizeof(req), false);
-+}
-+
-+static void
-+mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
-+{
-+#define RX_MU_DISABLE 0xf800
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_dev *dev = phy->dev;
-+ int ret;
-+
-+ if (en) {
-+ ret = mt7996_tm_rx_stats_user_ctrl(phy, td->aid);
-+ if (ret) {
-+ dev_info(dev->mt76.dev, "Set RX stats user control failed!\n");
-+ return;
-+ }
-+
-+ mt7996_tm_update_channel(phy);
-+
-+ if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
-+ if (td->aid)
-+ ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), td->aid);
-+ else
-+ ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
-+ }
-+ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
-+ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
-+ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
-+
-+ mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
-+
-+ /* trigger firmware to start RX */
-+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_RX));
-+ } else {
-+ /* trigger firmware to stop RX */
-+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
-+ }
-+}
-+
-+static void
-+mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
-+{
-+#define CONT_WAVE_MODE_OFDM 3
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_dev *dev = phy->dev;
-+
-+ if (en) {
-+ mt7996_tm_update_channel(phy);
-+ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
-+ /* fix payload is OFDM */
-+ mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
-+ mt7996_tm_set(dev, SET_ID(ANT_MASK), td->tx_antenna_mask);
-+
-+ /* trigger firmware to start CONT TX */
-+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(CONT_WAVE));
-+ } else {
-+ /* trigger firmware to stop CONT TX */
-+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
-+ }
-+}
-+
-+static void
-+mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
-+{
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_dev *dev = phy->dev;
-+
-+ if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) {
-+ mt7996_tm_set(dev, SET_ID(FREQ_OFFSET), td->freq_offset);
-+ mt7996_tm_set(dev, SET_ID(FREQ_OFFSET_C2), td->freq_offset);
-+ }
-+ if (changed & BIT(TM_CHANGED_TXPOWER))
-+ mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
-+ if (changed & BIT(TM_CHANGED_SKU_EN)) {
-+ mt7996_tm_update_channel(phy);
-+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
-+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
-+ mt7996_mcu_set_txpower_sku(phy);
-+ }
-+ if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
-+ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
-+ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
-+ }
-+ if (changed & BIT(TM_CHANGED_TX_TIME)) {
-+ mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
-+ mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
-+ }
-+ if (changed & BIT(TM_CHANGED_CFG)) {
-+ u32 func_idx = td->cfg.enable ? SET_ID(CFG_ON) : SET_ID(CFG_OFF);
-+
-+ mt7996_tm_set(dev, func_idx, td->cfg.type);
-+ }
-+}
-+
-+static int
-+mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
-+{
-+ struct mt76_testmode_data *td = &mphy->test;
-+ struct mt7996_phy *phy = mphy->priv;
-+ enum mt76_testmode_state prev_state = td->state;
-+
-+ mphy->test.state = state;
-+
-+ if (prev_state != MT76_TM_STATE_OFF)
-+ mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
-+
-+ if (prev_state == MT76_TM_STATE_TX_FRAMES ||
-+ state == MT76_TM_STATE_TX_FRAMES)
-+ mt7996_tm_set_tx_frames(phy, state == MT76_TM_STATE_TX_FRAMES);
-+ else if (prev_state == MT76_TM_STATE_RX_FRAMES ||
-+ state == MT76_TM_STATE_RX_FRAMES)
-+ mt7996_tm_set_rx_frames(phy, state == MT76_TM_STATE_RX_FRAMES);
-+ else if (prev_state == MT76_TM_STATE_TX_CONT ||
-+ state == MT76_TM_STATE_TX_CONT)
-+ mt7996_tm_set_tx_cont(phy, state == MT76_TM_STATE_TX_CONT);
-+ else if (prev_state == MT76_TM_STATE_OFF ||
-+ state == MT76_TM_STATE_OFF)
-+ mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
-+
-+ if ((state == MT76_TM_STATE_IDLE &&
-+ prev_state == MT76_TM_STATE_OFF) ||
-+ (state == MT76_TM_STATE_OFF &&
-+ prev_state == MT76_TM_STATE_IDLE)) {
-+ u32 changed = 0;
-+ int i, ret;
-+
-+ for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
-+ u16 cur = tm_change_map[i];
-+
-+ if (mt76_testmode_param_present(td, cur))
-+ changed |= BIT(i);
-+ }
-+
-+ ret = mt7996_tm_check_antenna(phy);
-+ if (ret)
-+ return ret;
-+
-+ mt7996_tm_update_params(phy, changed);
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
-+ enum mt76_testmode_state new_state)
-+{
-+ struct mt76_testmode_data *td = &mphy->test;
-+ struct mt7996_phy *phy = mphy->priv;
-+ struct mt7996_dev *dev = phy->dev;
-+ u32 changed = 0;
-+ int i, ret;
-+
-+ BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
-+
-+ if (new_state == MT76_TM_STATE_OFF ||
-+ td->state == MT76_TM_STATE_OFF)
-+ return 0;
-+
-+ ret = mt7996_tm_check_antenna(phy);
-+ if (ret)
-+ return ret;
-+
-+ for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
-+ if (tb[tm_change_map[i]])
-+ changed |= BIT(i);
-+ }
-+
-+ mt7996_tm_set(dev, SET_ID(BAND_IDX), mphy->band_idx);
-+ mt7996_tm_update_params(phy, changed);
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_get_rx_stats(struct mt7996_phy *phy)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_rx_req req = {
-+ .band = phy->mt76->band_idx,
-+ .rx_stat_all = {
-+ .tag = cpu_to_le16(UNI_TM_RX_STAT_GET_ALL_V2),
-+ .len = cpu_to_le16(sizeof(req.rx_stat_all)),
-+ .band_idx = phy->mt76->band_idx,
-+ },
-+ };
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_tm_rx_event *rx_stats;
-+ struct mt7996_tm_rx_event_stat_all *rx_stats_all;
-+ struct sk_buff *skb;
-+ enum mt76_rxq_id qid;
-+ int i, ret = 0;
-+ u32 mac_rx_mdrdy_cnt;
-+ u16 mac_rx_len_mismatch, fcs_err_count;
-+
-+ if (td->state != MT76_TM_STATE_RX_FRAMES)
-+ return 0;
-+
-+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_RX_STAT),
-+ &req, sizeof(req), true, &skb);
-+
-+ if (ret)
-+ return ret;
-+
-+ rx_stats = (struct mt7996_tm_rx_event *)skb->data;
-+ rx_stats_all = &rx_stats->rx_stat_all;
-+
-+ phy->test.last_freq_offset = le32_to_cpu(rx_stats_all->user_info[0].freq_offset);
-+ phy->test.last_snr = le32_to_cpu(rx_stats_all->user_info[0].snr);
-+ for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++) {
-+ phy->test.last_rcpi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rcpi);
-+ phy->test.last_rssi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rssi);
-+ phy->test.last_ib_rssi[i] = rx_stats_all->fagc[i].ib_rssi;
-+ phy->test.last_wb_rssi[i] = rx_stats_all->fagc[i].wb_rssi;
-+ }
-+
-+ if (phy->mt76->band_idx == 2)
-+ qid = MT_RXQ_BAND2;
-+ else if (phy->mt76->band_idx == 1)
-+ qid = MT_RXQ_BAND1;
-+ else
-+ qid = MT_RXQ_MAIN;
-+
-+ fcs_err_count = le16_to_cpu(rx_stats_all->band_info.mac_rx_fcs_err_cnt);
-+ mac_rx_len_mismatch = le16_to_cpu(rx_stats_all->band_info.mac_rx_len_mismatch);
-+ mac_rx_mdrdy_cnt = le32_to_cpu(rx_stats_all->band_info.mac_rx_mdrdy_cnt);
-+ td->rx_stats.packets[qid] += mac_rx_mdrdy_cnt;
-+ td->rx_stats.packets[qid] += fcs_err_count;
-+ td->rx_stats.fcs_error[qid] += fcs_err_count;
-+ td->rx_stats.len_mismatch += mac_rx_len_mismatch;
-+
-+ dev_kfree_skb(skb);
-+
-+ return ret;
-+}
-+
-+static void
-+mt7996_tm_reset_trx_stats(struct mt76_phy *mphy)
-+{
-+ struct mt7996_phy *phy = mphy->priv;
-+ struct mt7996_dev *dev = phy->dev;
-+
-+ memset(&mphy->test.rx_stats, 0, sizeof(mphy->test.rx_stats));
-+ mt7996_tm_set(dev, SET_ID(TRX_COUNTER_RESET), 0);
-+}
-+
-+static int
-+mt7996_tm_get_tx_stats(struct mt7996_phy *phy)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ int ret;
-+
-+ if (td->state != MT76_TM_STATE_TX_FRAMES)
-+ return 0;
-+
-+ ret = mt7996_tm_get(dev, GET_ID(TXED_COUNT), 0, &td->tx_done);
-+ if (ret)
-+ return ret;
-+
-+ td->tx_pending = td->tx_count - td->tx_done;
-+
-+ return ret;
-+}
-+
-+static int
-+mt7996_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
-+{
-+ struct mt7996_phy *phy = mphy->priv;
-+ void *rx, *rssi;
-+ int i;
-+
-+ mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
-+ mt7996_tm_get_rx_stats(phy);
-+ mt7996_tm_get_tx_stats(phy);
-+
-+ rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
-+ if (!rx)
-+ return -ENOMEM;
-+
-+ if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset))
-+ return -ENOMEM;
-+
-+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
-+ if (!rssi)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++)
-+ if (nla_put_u8(msg, i, phy->test.last_rcpi[i]))
-+ return -ENOMEM;
-+
-+ nla_nest_end(msg, rssi);
-+
-+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RSSI);
-+ if (!rssi)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < ARRAY_SIZE(phy->test.last_rssi); i++)
-+ if (nla_put_s8(msg, i, phy->test.last_rssi[i]))
-+ return -ENOMEM;
-+
-+ nla_nest_end(msg, rssi);
-+
-+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
-+ if (!rssi)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++)
-+ if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i]))
-+ return -ENOMEM;
-+
-+ nla_nest_end(msg, rssi);
-+
-+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
-+ if (!rssi)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++)
-+ if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i]))
-+ return -ENOMEM;
-+
-+ nla_nest_end(msg, rssi);
-+
-+ if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, phy->test.last_snr))
-+ return -ENOMEM;
-+
-+ nla_nest_end(msg, rx);
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
-+{
-+ struct mt7996_mcu_eeprom_info req = {
-+ .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ };
-+ u8 read_buf[MT76_TM_EEPROM_BLOCK_SIZE], *eeprom = dev->mt76.eeprom.data;
-+ int i, ret = -EINVAL;
-+
-+ /* prevent from damaging chip id in efuse */
-+ if (mt76_chip(&dev->mt76) != get_unaligned_le16(eeprom))
-+ goto out;
-+
-+ for (i = 0; i < MT7996_EEPROM_SIZE; i += MT76_TM_EEPROM_BLOCK_SIZE) {
-+ req.addr = cpu_to_le32(i);
-+ memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
-+
-+ ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
-+ continue;
-+
-+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
-+ &req, sizeof(req), true);
-+ if (ret)
-+ return ret;
-+ }
-+
-+out:
-+ return ret;
-+}
-+
-+static int
-+mt7996_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
-+{
-+ struct mt7996_phy *phy = mphy->priv;
-+ struct mt7996_dev *dev = phy->dev;
-+ u8 *eeprom = dev->mt76.eeprom.data;
-+ int ret = 0;
-+
-+ if (offset >= MT7996_EEPROM_SIZE)
-+ return -EINVAL;
-+
-+ switch (action) {
-+ case MT76_TM_EEPROM_ACTION_UPDATE_DATA:
-+ memcpy(eeprom + offset, val, MT76_TM_EEPROM_BLOCK_SIZE);
-+ break;
-+ case MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE:
-+ ret = mt7996_mcu_set_eeprom(dev);
-+ break;
-+ case MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE:
-+ ret = mt7996_tm_write_back_to_efuse(dev);
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+const struct mt76_testmode_ops mt7996_testmode_ops = {
-+ .set_state = mt7996_tm_set_state,
-+ .set_params = mt7996_tm_set_params,
-+ .dump_stats = mt7996_tm_dump_stats,
-+ .reset_rx_stats = mt7996_tm_reset_trx_stats,
-+ .tx_stop = mt7996_tm_tx_stop,
-+ .set_eeprom = mt7996_tm_set_eeprom,
-+};
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-new file mode 100644
-index 00000000..319ef257
---- /dev/null
-+++ b/mt7996/testmode.h
-@@ -0,0 +1,299 @@
-+/* SPDX-License-Identifier: ISC */
-+/* Copyright (C) 2020 MediaTek Inc. */
-+
-+#ifndef __MT7996_TESTMODE_H
-+#define __MT7996_TESTMODE_H
-+
-+enum {
-+ TM_CBW_20MHZ,
-+ TM_CBW_40MHZ,
-+ TM_CBW_80MHZ,
-+ TM_CBW_10MHZ,
-+ TM_CBW_5MHZ,
-+ TM_CBW_160MHZ,
-+ TM_CBW_8080MHZ,
-+ TM_CBW_320MHZ = 12,
-+};
-+
-+/* BW defined in FW hal_cal_flow_rom.h */
-+enum {
-+ FW_CDBW_20MHZ,
-+ FW_CDBW_40MHZ,
-+ FW_CDBW_80MHZ,
-+ FW_CDBW_160MHZ,
-+ FW_CDBW_320MHZ,
-+ FW_CDBW_5MHZ,
-+ FW_CDBW_10MHZ,
-+ FW_CDBW_8080MHZ,
-+};
-+
-+enum bw_mapping_method {
-+ BW_MAP_NL_TO_FW,
-+ BW_MAP_NL_TO_TM,
-+
-+ NUM_BW_MAP,
-+};
-+
-+struct mt7996_tm_rf_test {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 action;
-+ u8 icap_len;
-+ u8 _rsv[2];
-+ union {
-+ __le32 op_mode;
-+ __le32 freq;
-+
-+ struct {
-+ __le32 func_idx;
-+ union {
-+ __le32 func_data;
-+ __le32 cal_dump;
-+
-+ u8 _pad[80];
-+ } param;
-+ } rf;
-+ } op;
-+} __packed;
-+
-+struct mt7996_tm_req {
-+ u8 _rsv[4];
-+
-+ struct mt7996_tm_rf_test rf_test;
-+} __packed;
-+
-+struct mt7996_tm_rf_test_result {
-+ __le32 func_idx;
-+ __le32 payload_length;
-+ u8 event[0];
-+};
-+
-+struct mt7996_tm_event {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ struct mt7996_tm_rf_test_result result;
-+} __packed;
-+
-+enum {
-+ RF_ACTION_SWITCH_TO_RF_TEST,
-+ RF_ACTION_IN_RF_TEST,
-+ RF_ACTION_SET = 3,
-+ RF_ACTION_GET,
-+};
-+
-+enum {
-+ RF_OPER_NORMAL,
-+ RF_OPER_RF_TEST,
-+ RF_OPER_ICAP,
-+ RF_OPER_ICAP_OVERLAP,
-+ RF_OPER_WIFI_SPECTRUM,
-+};
-+
-+enum {
-+ UNI_RF_TEST_CTRL,
-+};
-+
-+#define RF_CMD(cmd) RF_TEST_CMD_##cmd
-+
-+enum {
-+ RF_TEST_CMD_STOP_TEST = 0,
-+ RF_TEST_CMD_START_TX = 1,
-+ RF_TEST_CMD_START_RX = 2,
-+ RF_TEST_CMD_CONT_WAVE = 10,
-+ RF_TEST_CMD_TX_COMMIT = 18,
-+ RF_TEST_CMD_RX_COMMIT = 19,
-+};
-+
-+#define SET_ID(id) RF_TEST_ID_SET_##id
-+#define GET_ID(id) RF_TEST_ID_GET_##id
-+
-+enum {
-+ RF_TEST_ID_SET_COMMAND = 1,
-+ RF_TEST_ID_SET_POWER = 2,
-+ RF_TEST_ID_SET_TX_RATE = 3,
-+ RF_TEST_ID_SET_TX_MODE = 4,
-+ RF_TEST_ID_SET_TX_LEN = 6,
-+ RF_TEST_ID_SET_TX_COUNT = 7,
-+ RF_TEST_ID_SET_IPG = 8,
-+ RF_TEST_ID_SET_GI = 16,
-+ RF_TEST_ID_SET_STBC = 17,
-+ RF_TEST_ID_SET_CHAN_FREQ = 18,
-+ RF_TEST_ID_GET_TXED_COUNT = 32,
-+ RF_TEST_ID_SET_CONT_WAVE_MODE = 65,
-+ RF_TEST_ID_SET_DA = 68,
-+ RF_TEST_ID_SET_SA = 69,
-+ RF_TEST_ID_SET_CBW = 71,
-+ RF_TEST_ID_SET_DBW = 72,
-+ RF_TEST_ID_SET_PRIMARY_CH = 73,
-+ RF_TEST_ID_SET_ENCODE_MODE = 74,
-+ RF_TEST_ID_SET_BAND = 90,
-+ RF_TEST_ID_SET_TRX_COUNTER_RESET = 91,
-+ RF_TEST_ID_SET_MAC_HEADER = 101,
-+ RF_TEST_ID_SET_SEQ_CTRL = 102,
-+ RF_TEST_ID_SET_PAYLOAD = 103,
-+ RF_TEST_ID_SET_BAND_IDX = 104,
-+ RF_TEST_ID_SET_RX_PATH = 106,
-+ RF_TEST_ID_SET_FREQ_OFFSET = 107,
-+ RF_TEST_ID_GET_FREQ_OFFSET = 108,
-+ RF_TEST_ID_SET_TX_PATH = 113,
-+ RF_TEST_ID_SET_NSS = 114,
-+ RF_TEST_ID_SET_ANT_MASK = 115,
-+ RF_TEST_ID_SET_IBF_ENABLE = 126,
-+ RF_TEST_ID_SET_EBF_ENABLE = 127,
-+ RF_TEST_ID_GET_TX_POWER = 136,
-+ RF_TEST_ID_SET_RX_MU_AID = 157,
-+ RF_TEST_ID_SET_HW_TX_MODE = 167,
-+ RF_TEST_ID_SET_PUNCTURE = 168,
-+ RF_TEST_ID_SET_FREQ_OFFSET_C2 = 171,
-+ RF_TEST_ID_GET_FREQ_OFFSET_C2 = 172,
-+ RF_TEST_ID_SET_CFG_ON = 176,
-+ RF_TEST_ID_SET_CFG_OFF = 177,
-+ RF_TEST_ID_SET_BSSID = 189,
-+ RF_TEST_ID_SET_TX_TIME = 190,
-+ RF_TEST_ID_SET_MAX_PE = 191,
-+ RF_TEST_ID_SET_AID_OFFSET = 204,
-+};
-+
-+#define POWER_CTRL(type) UNI_TXPOWER_##type##_CTRL
-+
-+struct mt7996_tm_rx_stat_user_ctrl {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 band_idx;
-+ u8 rsv;
-+ __le16 user_idx;
-+} __packed;
-+
-+struct mt7996_tm_rx_stat_all {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 band_idx;
-+ u8 rsv[3];
-+} __packed;
-+
-+struct mt7996_tm_rx_req {
-+ u8 band;
-+ u8 _rsv[3];
-+
-+ union {
-+ struct mt7996_tm_rx_stat_user_ctrl user_ctrl;
-+ struct mt7996_tm_rx_stat_all rx_stat_all;
-+ };
-+} __packed;
-+
-+enum {
-+ UNI_TM_RX_STAT_SET_USER_CTRL = 7,
-+ UNI_TM_RX_STAT_GET_ALL_V2 = 9,
-+};
-+
-+struct rx_band_info {
-+ /* mac part */
-+ __le16 mac_rx_fcs_err_cnt;
-+ __le16 mac_rx_len_mismatch;
-+ __le16 mac_rx_fcs_ok_cnt;
-+ u8 rsv1[2];
-+ __le32 mac_rx_mdrdy_cnt;
-+
-+ /* phy part */
-+ __le16 phy_rx_fcs_err_cnt_cck;
-+ __le16 phy_rx_fcs_err_cnt_ofdm;
-+ __le16 phy_rx_pd_cck;
-+ __le16 phy_rx_pd_ofdm;
-+ __le16 phy_rx_sig_err_cck;
-+ __le16 phy_rx_sfd_err_cck;
-+ __le16 phy_rx_sig_err_ofdm;
-+ __le16 phy_rx_tag_err_ofdm;
-+ __le16 phy_rx_mdrdy_cnt_cck;
-+ __le16 phy_rx_mdrdy_cnt_ofdm;
-+} __packed;
-+
-+struct rx_band_info_ext {
-+ /* mac part */
-+ __le32 mac_rx_mpdu_cnt;
-+
-+ /* phy part */
-+ u8 rsv[4];
-+} __packed;
-+
-+struct rx_common_info {
-+ __le16 rx_fifo_full;
-+ u8 rsv[2];
-+ __le32 aci_hit_low;
-+ __le32 aci_hit_high;
-+} __packed;
-+
-+struct rx_common_info_ext {
-+ __le32 driver_rx_count;
-+ __le32 sinr;
-+ __le32 mu_pkt_count;
-+
-+ /* mac part */
-+ u8 _rsv[4];
-+
-+ /* phy part */
-+ u8 sig_mcs;
-+ u8 rsv[3];
-+} __packed;
-+
-+struct rx_rxv_info {
-+ __le16 rcpi;
-+ s16 rssi;
-+ s16 snr;
-+ s16 adc_rssi;
-+} __packed;
-+
-+struct rx_rssi_info {
-+ s8 ib_rssi;
-+ s8 wb_rssi;
-+ u8 rsv[2];
-+} __packed;
-+
-+struct rx_user_info {
-+ s32 freq_offset;
-+ s32 snr;
-+ __le32 fcs_err_count;
-+} __packed;
-+
-+struct rx_user_info_ext {
-+ s8 ne_var_db_all_user;
-+ u8 rsv[3];
-+} __packed;
-+
-+#define MAX_ANTENNA_NUM 8
-+#define MAX_USER_NUM 16
-+
-+struct mt7996_tm_rx_event_stat_all {
-+ __le16 tag;
-+ __le16 len;
-+
-+ struct rx_band_info band_info;
-+ struct rx_band_info_ext band_info_ext;
-+ struct rx_common_info common_info;
-+ struct rx_common_info_ext common_info_ext;
-+
-+ /* RXV info */
-+ struct rx_rxv_info rxv_info[MAX_ANTENNA_NUM];
-+
-+ /* RSSI info */
-+ struct rx_rssi_info fagc[MAX_ANTENNA_NUM];
-+ struct rx_rssi_info inst[MAX_ANTENNA_NUM];
-+
-+ /* User info */
-+ struct rx_user_info user_info[MAX_USER_NUM];
-+ struct rx_user_info_ext user_info_ext[MAX_USER_NUM];
-+} __packed;
-+
-+struct mt7996_tm_rx_event {
-+ u8 _rsv[4];
-+
-+ union {
-+ struct mt7996_tm_rx_event_stat_all rx_stat_all;
-+ };
-+} __packed;
-+
-+#endif
-diff --git a/testmode.c b/testmode.c
-index 37783160..44f3a5bf 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -2,11 +2,13 @@
- /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-
- #include <linux/random.h>
-+#include "mt76_connac.h"
- #include "mt76.h"
-
- const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
- [MT76_TM_ATTR_RESET] = { .type = NLA_FLAG },
- [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
- [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
- [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
- [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
-@@ -82,6 +84,11 @@ mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode)
- IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991)
- return IEEE80211_MAX_MPDU_LEN_VHT_7991;
- return IEEE80211_MAX_MPDU_LEN_VHT_11454;
-+ case MT76_TM_TX_MODE_EHT_SU:
-+ case MT76_TM_TX_MODE_EHT_TRIG:
-+ case MT76_TM_TX_MODE_EHT_MU:
-+ /* TODO: check the limit */
-+ return UINT_MAX;
- case MT76_TM_TX_MODE_CCK:
- case MT76_TM_TX_MODE_OFDM:
- default:
-@@ -183,6 +190,9 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
- u8 max_nss = hweight8(phy->antenna_mask);
- int ret;
-
-+ if (is_mt7996(phy->dev))
-+ return 0;
-+
- ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
- if (ret)
- return ret;
-@@ -275,7 +285,9 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
- td->tx_queued = 0;
- td->tx_done = 0;
- td->tx_pending = td->tx_count;
-- mt76_worker_schedule(&dev->tx_worker);
-+
-+ if (!is_mt7996(dev))
-+ mt76_worker_schedule(&dev->tx_worker);
- }
-
- static void
-@@ -284,6 +296,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
- struct mt76_testmode_data *td = &phy->test;
- struct mt76_dev *dev = phy->dev;
-
-+ if (is_mt7996(dev) && dev->test_ops->tx_stop) {
-+ dev->test_ops->tx_stop(phy);
-+ return;
-+ }
-+
- mt76_worker_disable(&dev->tx_worker);
-
- td->tx_pending = 0;
-@@ -296,22 +313,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
- mt76_testmode_free_skb(phy);
- }
-
--static inline void
--mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
--{
-- td->param_set[idx / 32] |= BIT(idx % 32);
--}
--
--static inline bool
--mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
--{
-- return td->param_set[idx / 32] & BIT(idx % 32);
--}
--
- static void
- mt76_testmode_init_defaults(struct mt76_phy *phy)
- {
- struct mt76_testmode_data *td = &phy->test;
-+ u8 addr[ETH_ALEN] = {phy->band_idx, 0x11, 0x22, 0xaa, 0xbb, 0xcc};
-
- if (td->tx_mpdu_len > 0)
- return;
-@@ -319,11 +325,18 @@ mt76_testmode_init_defaults(struct mt76_phy *phy)
- td->tx_mpdu_len = 1024;
- td->tx_count = 1;
- td->tx_rate_mode = MT76_TM_TX_MODE_OFDM;
-+ td->tx_rate_idx = 7;
- td->tx_rate_nss = 1;
-+ /* 0xffff for OFDMA no puncture */
-+ td->tx_preamble_puncture = ~(td->tx_preamble_puncture & 0);
-+ td->tx_ipg = 50;
-
-- memcpy(td->addr[0], phy->macaddr, ETH_ALEN);
-- memcpy(td->addr[1], phy->macaddr, ETH_ALEN);
-- memcpy(td->addr[2], phy->macaddr, ETH_ALEN);
-+ /* rx stat user config */
-+ td->aid = 1;
-+
-+ memcpy(td->addr[0], addr, ETH_ALEN);
-+ memcpy(td->addr[1], addr, ETH_ALEN);
-+ memcpy(td->addr[2], addr, ETH_ALEN);
- }
-
- static int
-@@ -353,7 +366,7 @@ __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state)
- if (state == MT76_TM_STATE_TX_FRAMES)
- mt76_testmode_tx_start(phy);
- else if (state == MT76_TM_STATE_RX_FRAMES) {
-- memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats));
-+ dev->test_ops->reset_rx_stats(phy);
- }
-
- phy->test.state = state;
-@@ -404,6 +417,44 @@ mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max)
- return 0;
- }
-
-+static int
-+mt76_testmode_set_eeprom(struct mt76_phy *phy, struct nlattr **tb)
-+{
-+ struct mt76_dev *dev = phy->dev;
-+ u8 action, val[MT76_TM_EEPROM_BLOCK_SIZE];
-+ u32 offset = 0;
-+ int err = -EINVAL;
-+
-+ if (!dev->test_ops->set_eeprom)
-+ return -EOPNOTSUPP;
-+
-+ if (mt76_tm_get_u8(tb[MT76_TM_ATTR_EEPROM_ACTION], &action,
-+ 0, MT76_TM_EEPROM_ACTION_MAX))
-+ goto out;
-+
-+ if (tb[MT76_TM_ATTR_EEPROM_OFFSET]) {
-+ struct nlattr *cur;
-+ int rem, idx = 0;
-+
-+ offset = nla_get_u32(tb[MT76_TM_ATTR_EEPROM_OFFSET]);
-+ if (!!(offset % MT76_TM_EEPROM_BLOCK_SIZE) ||
-+ !tb[MT76_TM_ATTR_EEPROM_VAL])
-+ goto out;
-+
-+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_EEPROM_VAL], rem) {
-+ if (nla_len(cur) != 1 || idx >= ARRAY_SIZE(val))
-+ goto out;
-+
-+ val[idx++] = nla_get_u8(cur);
-+ }
-+ }
-+
-+ err = dev->test_ops->set_eeprom(phy, offset, val, action);
-+
-+out:
-+ return err;
-+}
-+
- int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- void *data, int len)
- {
-@@ -427,6 +478,11 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-
- mutex_lock(&dev->mutex);
-
-+ if (tb[MT76_TM_ATTR_EEPROM_ACTION]) {
-+ err = mt76_testmode_set_eeprom(phy, tb);
-+ goto out;
-+ }
-+
- if (tb[MT76_TM_ATTR_RESET]) {
- mt76_testmode_set_state(phy, MT76_TM_STATE_OFF);
- memset(td, 0, sizeof(*td));
-@@ -434,6 +490,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-
- mt76_testmode_init_defaults(phy);
-
-+ if (tb[MT76_TM_ATTR_SKU_EN])
-+ td->sku_en = nla_get_u8(tb[MT76_TM_ATTR_SKU_EN]);
-+
- if (tb[MT76_TM_ATTR_TX_COUNT])
- td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]);
-
-@@ -454,7 +513,8 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE],
- &td->tx_duty_cycle, 0, 99) ||
- mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
-- &td->tx_power_control, 0, 1))
-+ &td->tx_power_control, 0, 1) ||
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
- goto out;
-
- if (tb[MT76_TM_ATTR_TX_LENGTH]) {
-@@ -494,7 +554,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- idx >= ARRAY_SIZE(td->tx_power))
- goto out;
-
-- td->tx_power[idx++] = nla_get_u8(cur);
-+ err = mt76_tm_get_u8(cur, &td->tx_power[idx++], 0, 63);
-+ if (err)
-+ return err;
- }
- }
-
-@@ -512,6 +574,22 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- }
- }
-
-+ if (tb[MT76_TM_ATTR_CFG]) {
-+ struct nlattr *cur;
-+ int rem, idx = 0;
-+
-+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_CFG], rem) {
-+ if (nla_len(cur) != 1 || idx >= 2)
-+ goto out;
-+
-+ if (idx == 0)
-+ td->cfg.type = nla_get_u8(cur);
-+ else
-+ td->cfg.enable = nla_get_u8(cur);
-+ idx++;
-+ }
-+ }
-+
- if (dev->test_ops->set_params) {
- err = dev->test_ops->set_params(phy, tb, state);
- if (err)
-@@ -561,6 +639,9 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
- nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets,
- MT76_TM_STATS_ATTR_PAD) ||
- nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error,
-+ MT76_TM_STATS_ATTR_PAD) ||
-+ nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
-+ td->rx_stats.len_mismatch,
- MT76_TM_STATS_ATTR_PAD))
- return -EMSGSIZE;
-
-@@ -625,6 +706,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
- nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
- nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
-+ nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
-+ nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
- (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
- nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
- (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
-@@ -640,7 +723,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
- nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) ||
- (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) &&
-- nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
-+ nla_put_u32(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
- goto out;
-
- if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
-diff --git a/testmode.h b/testmode.h
-index a40cd74b..96872e8c 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -5,7 +5,8 @@
- #ifndef __MT76_TESTMODE_H
- #define __MT76_TESTMODE_H
-
--#define MT76_TM_TIMEOUT 10
-+#define MT76_TM_TIMEOUT 10
-+#define MT76_TM_EEPROM_BLOCK_SIZE 16
-
- /**
- * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
-@@ -19,6 +20,7 @@
- * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
- * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
- *
-+ * @MT76_TM_ATTR_SKU_EN: config txpower sku is enabled or disabled in testmode (u8)
- * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
- * state to MT76_TM_STATE_TX_FRAMES (u32)
- * @MT76_TM_ATTR_TX_PENDING: pending frames during MT76_TM_STATE_TX_FRAMES (u32)
-@@ -39,6 +41,11 @@
- *
- * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
- *
-+ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
-+ * @MT76_TM_ATTR_PRECAL_INFO: group size, dpd size, dpd_info, transmit size,
-+ * eeprom cal indicator (u32),
-+ * dpd_info = [dpd_per_chan_size, chan_num_2g,
-+ * chan_num_5g, chan_num_6g]
- * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8)
- *
- * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8)
-@@ -48,6 +55,29 @@
- * @MT76_TM_ATTR_DRV_DATA: driver specific netlink attrs (nested)
- *
- * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested)
-+ *
-+ * @MT76_TM_ATTR_EEPROM_ACTION: eeprom setting actions
-+ * (u8, see &enum mt76_testmode_eeprom_action)
-+ * @MT76_TM_ATTR_EEPROM_OFFSET: offset of eeprom data block for writing (u32)
-+ * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
-+ * (nested, u8 attrs)
-+ *
-+ * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
-+ * @MT76_TM_ATTR_TXBF_ACT: txbf setting actions (u8)
-+ * @MT76_TM_ATTR_TXBF_PARAM: txbf parameters (nested)
-+ *
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_CH: config the channel of background chain (ZWDFS) (u8)
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH: config the center channel of background chain (ZWDFS) (u8)
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_BW: config the bandwidth of background chain (ZWDFS) (u8)
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_PATH: config the tx path of background chain (ZWDFS) (u8)
-+ *
-+ * @MT76_TM_ATTR_IPI_THRESHOLD: config the IPI index you want to read (u8)
-+ * @MT76_TM_ATTR_IPI_PERIOD: config the time period for reading
-+ * the histogram of specific IPI index (u8)
-+ * @MT76_TM_ATTR_IPI_ANTENNA_INDEX: config the antenna index for reading
-+ * the histogram of specific IPI index (u8)
-+ * @MT76_TM_ATTR_IPI_RESET: Reset the IPI counter
-+ *
- */
- enum mt76_testmode_attr {
- MT76_TM_ATTR_UNSPEC,
-@@ -59,6 +89,7 @@ enum mt76_testmode_attr {
- MT76_TM_ATTR_MTD_OFFSET,
- MT76_TM_ATTR_BAND_IDX,
-
-+ MT76_TM_ATTR_SKU_EN,
- MT76_TM_ATTR_TX_COUNT,
- MT76_TM_ATTR_TX_LENGTH,
- MT76_TM_ATTR_TX_RATE_MODE,
-@@ -76,6 +107,8 @@ enum mt76_testmode_attr {
- MT76_TM_ATTR_FREQ_OFFSET,
-
- MT76_TM_ATTR_STATS,
-+ MT76_TM_ATTR_PRECAL,
-+ MT76_TM_ATTR_PRECAL_INFO,
-
- MT76_TM_ATTR_TX_SPE_IDX,
-
-@@ -86,6 +119,27 @@ enum mt76_testmode_attr {
- MT76_TM_ATTR_DRV_DATA,
-
- MT76_TM_ATTR_MAC_ADDRS,
-+ MT76_TM_ATTR_AID,
-+ MT76_TM_ATTR_RU_ALLOC,
-+ MT76_TM_ATTR_RU_IDX,
-+
-+ MT76_TM_ATTR_EEPROM_ACTION,
-+ MT76_TM_ATTR_EEPROM_OFFSET,
-+ MT76_TM_ATTR_EEPROM_VAL,
-+
-+ MT76_TM_ATTR_CFG,
-+ MT76_TM_ATTR_TXBF_ACT,
-+ MT76_TM_ATTR_TXBF_PARAM,
-+
-+ MT76_TM_ATTR_OFF_CH_SCAN_CH,
-+ MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
-+ MT76_TM_ATTR_OFF_CH_SCAN_BW,
-+ MT76_TM_ATTR_OFF_CH_SCAN_PATH,
-+
-+ MT76_TM_ATTR_IPI_THRESHOLD,
-+ MT76_TM_ATTR_IPI_PERIOD,
-+ MT76_TM_ATTR_IPI_ANTENNA_INDEX,
-+ MT76_TM_ATTR_IPI_RESET,
-
- /* keep last */
- NUM_MT76_TM_ATTRS,
-@@ -103,6 +157,8 @@ enum mt76_testmode_attr {
- * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64)
- * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet
- * see &enum mt76_testmode_rx_attr
-+ * @MT76_TM_STATS_ATTR_RX_LEN_MISMATCH: number of rx packets with length
-+ * mismatch error (u64)
- */
- enum mt76_testmode_stats_attr {
- MT76_TM_STATS_ATTR_UNSPEC,
-@@ -115,6 +171,7 @@ enum mt76_testmode_stats_attr {
- MT76_TM_STATS_ATTR_RX_PACKETS,
- MT76_TM_STATS_ATTR_RX_FCS_ERROR,
- MT76_TM_STATS_ATTR_LAST_RX,
-+ MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
-
- /* keep last */
- NUM_MT76_TM_STATS_ATTRS,
-@@ -127,6 +184,7 @@ enum mt76_testmode_stats_attr {
- *
- * @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32)
- * @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
-+ * @MT76_TM_RX_ATTR_RSSI: received signal strength indicator (array, s8)
- * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
- * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
- * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8)
-@@ -136,6 +194,7 @@ enum mt76_testmode_rx_attr {
-
- MT76_TM_RX_ATTR_FREQ_OFFSET,
- MT76_TM_RX_ATTR_RCPI,
-+ MT76_TM_RX_ATTR_RSSI,
- MT76_TM_RX_ATTR_IB_RSSI,
- MT76_TM_RX_ATTR_WB_RSSI,
- MT76_TM_RX_ATTR_SNR,
-@@ -179,6 +238,9 @@ enum mt76_testmode_state {
- * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU
- * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based
- * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO
-+ * @MT76_TM_TX_MODE_EHT_SU: 802.11be single-user MIMO
-+ * @MT76_TM_TX_MODE_EHT_TRIG: 802.11be trigger-based
-+ * @MT76_TM_TX_MODE_EHT_MU: 802.11be multi-user MIMO
- */
- enum mt76_testmode_tx_mode {
- MT76_TM_TX_MODE_CCK,
-@@ -189,12 +251,33 @@ enum mt76_testmode_tx_mode {
- MT76_TM_TX_MODE_HE_EXT_SU,
- MT76_TM_TX_MODE_HE_TB,
- MT76_TM_TX_MODE_HE_MU,
-+ MT76_TM_TX_MODE_EHT_SU,
-+ MT76_TM_TX_MODE_EHT_TRIG,
-+ MT76_TM_TX_MODE_EHT_MU,
-
- /* keep last */
- NUM_MT76_TM_TX_MODES,
- MT76_TM_TX_MODE_MAX = NUM_MT76_TM_TX_MODES - 1,
- };
-
-+/**
-+ * enum mt76_testmode_eeprom_action - eeprom setting actions
-+ *
-+ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
-+ * eeprom data block
-+ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
-+ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
-+ */
-+enum mt76_testmode_eeprom_action {
-+ MT76_TM_EEPROM_ACTION_UPDATE_DATA,
-+ MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE,
-+ MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE,
-+
-+ /* keep last */
-+ NUM_MT76_TM_EEPROM_ACTION,
-+ MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
-+};
-+
- extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
-
- #endif
-diff --git a/tools/fields.c b/tools/fields.c
-index e3f69089..055f90f3 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
- [MT76_TM_STATE_IDLE] = "idle",
- [MT76_TM_STATE_TX_FRAMES] = "tx_frames",
- [MT76_TM_STATE_RX_FRAMES] = "rx_frames",
-+ [MT76_TM_STATE_TX_CONT] = "tx_cont",
- };
-
- static const char * const testmode_tx_mode[] = {
-@@ -21,6 +22,9 @@ static const char * const testmode_tx_mode[] = {
- [MT76_TM_TX_MODE_HE_EXT_SU] = "he_ext_su",
- [MT76_TM_TX_MODE_HE_TB] = "he_tb",
- [MT76_TM_TX_MODE_HE_MU] = "he_mu",
-+ [MT76_TM_TX_MODE_EHT_SU] = "eht_su",
-+ [MT76_TM_TX_MODE_EHT_TRIG] = "eht_tb",
-+ [MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
- };
-
- static void print_enum(const struct tm_field *field, struct nlattr *attr)
-@@ -65,7 +69,7 @@ static bool parse_u8(const struct tm_field *field, int idx,
-
- static void print_u8(const struct tm_field *field, struct nlattr *attr)
- {
-- printf("%d", nla_get_u8(attr));
-+ printf("%u", nla_get_u8(attr));
- }
-
- static void print_s8(const struct tm_field *field, struct nlattr *attr)
-@@ -86,12 +90,12 @@ static void print_s32(const struct tm_field *field, struct nlattr *attr)
-
- static void print_u32(const struct tm_field *field, struct nlattr *attr)
- {
-- printf("%d", nla_get_u32(attr));
-+ printf("%u", nla_get_u32(attr));
- }
-
- static void print_u64(const struct tm_field *field, struct nlattr *attr)
- {
-- printf("%lld", (unsigned long long)nla_get_u64(attr));
-+ printf("%llu", (unsigned long long)nla_get_u64(attr));
- }
-
- static bool parse_flag(const struct tm_field *field, int idx,
-@@ -201,6 +205,62 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
- printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
- }
-
-+static bool parse_mac(const struct tm_field *field, int idx,
-+ struct nl_msg *msg, const char *val)
-+{
-+#define ETH_ALEN 6
-+ bool ret = true;
-+ char *str, *cur, *ap;
-+ void *a;
-+
-+ str = strdup(val);
-+ ap = str;
-+
-+ a = nla_nest_start(msg, idx);
-+
-+ idx = 0;
-+ while ((cur = strsep(&ap, ",")) != NULL) {
-+ unsigned char addr[ETH_ALEN];
-+ char *val, *tmp = cur;
-+ int i = 0;
-+
-+ while ((val = strsep(&tmp, ":")) != NULL) {
-+ if (i >= ETH_ALEN)
-+ break;
-+
-+ addr[i++] = strtoul(val, NULL, 16);
-+ }
-+
-+ nla_put(msg, idx, ETH_ALEN, addr);
-+
-+ idx++;
-+ }
-+
-+ nla_nest_end(msg, a);
-+
-+ free(str);
-+
-+ return ret;
-+}
-+
-+static void print_mac(const struct tm_field *field, struct nlattr *attr)
-+{
-+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
-+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
-+ unsigned char addr[3][6];
-+ struct nlattr *cur;
-+ int idx = 0;
-+ int rem;
-+
-+ nla_for_each_nested(cur, attr, rem) {
-+ if (nla_len(cur) != 6)
-+ continue;
-+ memcpy(addr[idx++], nla_data(cur), 6);
-+ }
-+
-+ printf("" MACSTR "," MACSTR "," MACSTR "",
-+ MAC2STR(addr[0]), MAC2STR(addr[1]), MAC2STR(addr[2]));
-+}
-
- #define FIELD_GENERIC(_field, _name, ...) \
- [FIELD_NAME(_field)] = { \
-@@ -250,10 +310,18 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
- ##__VA_ARGS__ \
- )
-
-+#define FIELD_MAC(_field, _name) \
-+ [FIELD_NAME(_field)] = { \
-+ .name = _name, \
-+ .parse = parse_mac, \
-+ .print = print_mac \
-+ }
-+
- #define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
- static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
- FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
- FIELD_ARRAY_RO(u8, RCPI, "rcpi"),
-+ FIELD_ARRAY_RO(s8, RSSI, "rssi"),
- FIELD_ARRAY_RO(s8, IB_RSSI, "ib_rssi"),
- FIELD_ARRAY_RO(s8, WB_RSSI, "wb_rssi"),
- FIELD_RO(s8, SNR, "snr"),
-@@ -261,6 +329,7 @@ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
- static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
- [MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
- [MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
-+ [MT76_TM_RX_ATTR_RSSI] = { .type = NLA_NESTED },
- [MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
- [MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
- [MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
-@@ -274,6 +343,7 @@ static const struct tm_field stats_fields[NUM_MT76_TM_STATS_ATTRS] = {
- FIELD_RO(u32, TX_DONE, "tx_done"),
- FIELD_RO(u64, RX_PACKETS, "rx_packets"),
- FIELD_RO(u64, RX_FCS_ERROR, "rx_fcs_error"),
-+ FIELD_RO(u64, RX_LEN_MISMATCH, "rx_len_mismatch"),
- FIELD_NESTED_RO(LAST_RX, rx, "last_"),
- };
- static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
-@@ -282,6 +352,7 @@ static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
- [MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
- [MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
- [MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
-+ [MT76_TM_STATS_ATTR_RX_LEN_MISMATCH] = { .type = NLA_U64 },
- };
- #undef FIELD_NAME
-
-@@ -291,6 +362,7 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- FIELD_ENUM(STATE, "state", testmode_state),
- FIELD_RO(string, MTD_PART, "mtd_part"),
- FIELD_RO(u32, MTD_OFFSET, "mtd_offset"),
-+ FIELD(u8, SKU_EN, "sku_en"),
- FIELD(u32, TX_COUNT, "tx_count"),
- FIELD(u32, TX_LENGTH, "tx_length"),
- FIELD_ENUM(TX_RATE_MODE, "tx_rate_mode", testmode_tx_mode),
-@@ -300,12 +372,20 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
- FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
- FIELD(u8, TX_LTF, "tx_ltf"),
-+ FIELD(u8, TX_DUTY_CYCLE, "tx_duty_cycle"),
-+ FIELD(u32, TX_IPG, "tx_ipg"),
-+ FIELD(u32, TX_TIME, "tx_time"),
- FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
- FIELD_ARRAY(u8, TX_POWER, "tx_power"),
- FIELD(u8, TX_ANTENNA, "tx_antenna"),
- FIELD(u32, FREQ_OFFSET, "freq_offset"),
-+ FIELD(u8, AID, "aid"),
-+ FIELD(u8, RU_ALLOC, "ru_alloc"),
-+ FIELD(u8, RU_IDX, "ru_idx"),
-+ FIELD_MAC(MAC_ADDRS, "mac_addrs"),
- FIELD_NESTED_RO(STATS, stats, "",
- .print_extra = print_extra_stats),
-+
- };
- #undef FIELD_NAME
-
-@@ -313,6 +393,7 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
- [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
- [MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
- [MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
-+ [MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
- [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
- [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
- [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
-@@ -322,10 +403,25 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
- [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
- [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
- [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 },
-+ [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
- [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
- [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 },
- [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
-+ [MT76_TM_ATTR_AID] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_RU_ALLOC] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_RU_IDX] = { .type = NLA_U8 },
- [MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
-+ [MT76_TM_ATTR_TXBF_ACT] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
-+ [MT76_TM_ATTR_IPI_ANTENNA_INDEX] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
- };
-
- const struct tm_field msg_field = {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1005-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1005-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch
deleted file mode 100644
index 4d70c33..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1005-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch
+++ /dev/null
@@ -1,899 +0,0 @@
-From 10b5c0f3d23bd9f556654364584d8eb2a9424c6f Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 31 Mar 2023 11:27:24 +0800
-Subject: [PATCH 1005/1044] mtk: wifi: mt76: testmode: add testmode
- pre-calibration support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mac80211.c | 21 ---
- mt76.h | 22 +++
- mt76_connac_mcu.h | 2 +
- mt7996/eeprom.c | 66 +++++++
- mt7996/eeprom.h | 47 +++++
- mt7996/mcu.c | 5 +
- mt7996/mt7996.h | 7 +
- mt7996/testmode.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/testmode.h | 20 ++-
- testmode.c | 12 ++
- testmode.h | 8 +
- tools/fields.c | 8 +
- 12 files changed, 632 insertions(+), 23 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index fea19e19..e7d02d15 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -7,27 +7,6 @@
- #include <net/page_pool.h>
- #include "mt76.h"
-
--#define CHAN2G(_idx, _freq) { \
-- .band = NL80211_BAND_2GHZ, \
-- .center_freq = (_freq), \
-- .hw_value = (_idx), \
-- .max_power = 30, \
--}
--
--#define CHAN5G(_idx, _freq) { \
-- .band = NL80211_BAND_5GHZ, \
-- .center_freq = (_freq), \
-- .hw_value = (_idx), \
-- .max_power = 30, \
--}
--
--#define CHAN6G(_idx, _freq) { \
-- .band = NL80211_BAND_6GHZ, \
-- .center_freq = (_freq), \
-- .hw_value = (_idx), \
-- .max_power = 30, \
--}
--
- static const struct ieee80211_channel mt76_channels_2ghz[] = {
- CHAN2G(1, 2412),
- CHAN2G(2, 2417),
-diff --git a/mt76.h b/mt76.h
-index f2b1e0c2..14c5fcb1 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -18,6 +18,27 @@
- #include "util.h"
- #include "testmode.h"
-
-+#define CHAN2G(_idx, _freq) { \
-+ .band = NL80211_BAND_2GHZ, \
-+ .center_freq = (_freq), \
-+ .hw_value = (_idx), \
-+ .max_power = 30, \
-+}
-+
-+#define CHAN5G(_idx, _freq) { \
-+ .band = NL80211_BAND_5GHZ, \
-+ .center_freq = (_freq), \
-+ .hw_value = (_idx), \
-+ .max_power = 30, \
-+}
-+
-+#define CHAN6G(_idx, _freq) { \
-+ .band = NL80211_BAND_6GHZ, \
-+ .center_freq = (_freq), \
-+ .hw_value = (_idx), \
-+ .max_power = 30, \
-+}
-+
- #define MT_MCU_RING_SIZE 32
- #define MT_RX_BUF_SIZE 2048
- #define MT_SKB_HEAD_LEN 256
-@@ -698,6 +719,7 @@ struct mt76_testmode_ops {
- void (*reset_rx_stats)(struct mt76_phy *phy);
- void (*tx_stop)(struct mt76_phy *phy);
- int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
-+ int (*dump_precal)(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type);
- };
-
- #define MT_TM_FW_RX_COUNT BIT(0)
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 2e148011..54310413 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1042,8 +1042,10 @@ enum {
- MCU_UNI_EVENT_RDD_REPORT = 0x11,
- MCU_UNI_EVENT_ROC = 0x27,
- MCU_UNI_EVENT_TX_DONE = 0x2d,
-+ MCU_UNI_EVENT_BF = 0x33,
- MCU_UNI_EVENT_THERMAL = 0x35,
- MCU_UNI_EVENT_NIC_CAPAB = 0x43,
-+ MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
- MCU_UNI_EVENT_WED_RRO = 0x57,
- MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
- MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 2299793d..3aeffdfb 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -12,6 +12,42 @@ static bool testmode_enable;
- module_param(testmode_enable, bool, 0644);
- MODULE_PARM_DESC(testmode_enable, "Enable testmode");
-
-+const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
-+ CHAN2G(3, 2422),
-+ CHAN2G(7, 2442),
-+ CHAN2G(11, 2462)
-+};
-+
-+const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
-+ CHAN5G(50, 5250),
-+ CHAN5G(114, 5570),
-+ CHAN5G(163, 5815)
-+};
-+
-+const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
-+ CHAN6G(15, 6025),
-+ CHAN6G(47, 6185),
-+ CHAN6G(79, 6345),
-+ CHAN6G(111, 6505),
-+ CHAN6G(143, 6665),
-+ CHAN6G(175, 6825),
-+ CHAN6G(207, 6985)
-+};
-+
-+const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
-+ CHAN6G(31, 6105),
-+ CHAN6G(63, 6265),
-+ CHAN6G(95, 6425),
-+ CHAN6G(127, 6585),
-+ CHAN6G(159, 6745),
-+ CHAN6G(191, 6905)
-+};
-+
-+const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
-+const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
-+const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
-+const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
-+
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
- #define FEM_INT 0
-@@ -74,6 +110,36 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
- }
- }
-
-+int
-+mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
-+{
-+ /* handle different sku */
-+ static const u8 band_to_idx[] = {
-+ [NL80211_BAND_2GHZ] = MT_BAND0,
-+ [NL80211_BAND_5GHZ] = MT_BAND1,
-+ [NL80211_BAND_6GHZ] = MT_BAND2,
-+ };
-+ struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
-+ struct mt76_phy *mphy;
-+ int dpd_size;
-+
-+ if (!phy)
-+ return 0;
-+
-+ mphy = phy->mt76;
-+
-+ if (band == NL80211_BAND_2GHZ)
-+ dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
-+ else if (band == NL80211_BAND_5GHZ)
-+ dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-+ dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+ else
-+ dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-+ (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
-+
-+ return dpd_size;
-+}
-+
- static int
- mt7996_eeprom_load_default(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index de3ff4e2..849b8bca 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
- MT_EE_MAC_ADDR = 0x004,
- MT_EE_MAC_ADDR2 = 0x00a,
- MT_EE_WIFI_CONF = 0x190,
-+ MT_EE_DO_PRE_CAL = 0x1a5,
- MT_EE_TESTMODE_EN = 0x1af,
- MT_EE_MAC_ADDR3 = 0x2c0,
- MT_EE_RATE_DELTA_2G = 0x1400,
-@@ -32,6 +33,52 @@ enum mt7996_eeprom_field {
- #define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0)
- #define MT_EE_WIFI_PA_LNA_CONFIG GENMASK(1, 0)
-
-+#define MT_EE_WIFI_CAL_GROUP_2G BIT(0)
-+#define MT_EE_WIFI_CAL_GROUP_5G BIT(1)
-+#define MT_EE_WIFI_CAL_GROUP_6G BIT(2)
-+#define MT_EE_WIFI_CAL_GROUP GENMASK(2, 0)
-+#define MT_EE_WIFI_CAL_DPD_2G BIT(3)
-+#define MT_EE_WIFI_CAL_DPD_5G BIT(4)
-+#define MT_EE_WIFI_CAL_DPD_6G BIT(5)
-+#define MT_EE_WIFI_CAL_DPD GENMASK(5, 3)
-+
-+#define MT_EE_CAL_UNIT 1024
-+#define MT_EE_CAL_GROUP_SIZE_2G (4 * MT_EE_CAL_UNIT)
-+#define MT_EE_CAL_GROUP_SIZE_5G (45 * MT_EE_CAL_UNIT)
-+#define MT_EE_CAL_GROUP_SIZE_6G (125 * MT_EE_CAL_UNIT)
-+#define MT_EE_CAL_ADCDCOC_SIZE_2G (4 * 4)
-+#define MT_EE_CAL_ADCDCOC_SIZE_5G (4 * 4)
-+#define MT_EE_CAL_ADCDCOC_SIZE_6G (4 * 5)
-+#define MT_EE_CAL_GROUP_SIZE (MT_EE_CAL_GROUP_SIZE_2G + \
-+ MT_EE_CAL_GROUP_SIZE_5G + \
-+ MT_EE_CAL_GROUP_SIZE_6G + \
-+ MT_EE_CAL_ADCDCOC_SIZE_2G + \
-+ MT_EE_CAL_ADCDCOC_SIZE_5G + \
-+ MT_EE_CAL_ADCDCOC_SIZE_6G)
-+
-+#define DPD_PER_CH_LEGACY_SIZE (4 * MT_EE_CAL_UNIT)
-+#define DPD_PER_CH_MEM_SIZE (13 * MT_EE_CAL_UNIT)
-+#define DPD_PER_CH_OTFG0_SIZE (2 * MT_EE_CAL_UNIT)
-+#define DPD_PER_CH_BW20_SIZE (DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
-+#define DPD_PER_CH_GT_BW20_SIZE (DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
-+#define MT_EE_CAL_DPD_SIZE (780 * MT_EE_CAL_UNIT)
-+
-+extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
-+extern const u32 dpd_2g_bw20_ch_num;
-+extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
-+extern const u32 dpd_5g_bw160_ch_num;
-+extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
-+extern const u32 dpd_6g_bw160_ch_num;
-+extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
-+extern const u32 dpd_6g_bw320_ch_num;
-+
-+#define RF_DPD_FLAT_CAL BIT(28)
-+#define RF_PRE_CAL BIT(29)
-+#define RF_DPD_FLAT_5G_CAL GENMASK(29, 28)
-+#define RF_DPD_FLAT_5G_MEM_CAL (BIT(30) | BIT(28))
-+#define RF_DPD_FLAT_6G_CAL GENMASK(30, 28)
-+#define RF_DPD_FLAT_6G_MEM_CAL (BIT(31) | BIT(28))
-+
- #define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3)
- #define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(2, 0)
- #define MT_EE_WIFI_CONF2_TX_PATH_BAND2 GENMASK(5, 3)
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index d8795d30..0e2c1f1c 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -712,6 +712,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- case MCU_UNI_EVENT_WED_RRO:
- mt7996_mcu_wed_rro_event(dev, skb);
- break;
-+#ifdef CONFIG_NL80211_TESTMODE
-+ case MCU_UNI_EVENT_TESTMODE_CTRL:
-+ mt7996_tm_rf_test_event(dev, skb);
-+ break;
-+#endif
- default:
- break;
- }
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 5af55492..e7609c63 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -385,6 +385,9 @@ struct mt7996_dev {
- struct dentry *debugfs_dir;
- struct rchan *relay_fwlog;
-
-+ void *cal;
-+ u32 cur_prek_offset;
-+
- struct {
- u16 table_mask;
- u8 n_agrt;
-@@ -505,6 +508,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
- int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
- struct ieee80211_channel *chan);
- s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
-+int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
- int mt7996_dma_init(struct mt7996_dev *dev);
- void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
- void mt7996_dma_prefetch(struct mt7996_dev *dev);
-@@ -589,6 +593,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
- int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
- int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
-+#ifdef CONFIG_NL80211_TESTMODE
-+void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
-+#endif
-
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 96079c22..36be0ff8 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -7,6 +7,8 @@
- #include "mac.h"
- #include "mcu.h"
- #include "testmode.h"
-+#include "eeprom.h"
-+#include "mtk_mcu.h"
-
- enum {
- TM_CHANGED_TXPOWER,
-@@ -397,6 +399,436 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
- }
- }
-
-+static int
-+mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
-+{
-+ u8 *eeprom;
-+ u32 i, group_size, dpd_size, size, offs, *pre_cal;
-+ int ret = 0;
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_dev *mdev = &dev->mt76;
-+ struct mt7996_tm_req req = {
-+ .rf_test = {
-+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+ .len = cpu_to_le16(sizeof(req.rf_test)),
-+ .action = RF_ACTION_IN_RF_TEST,
-+ .icap_len = RF_TEST_ICAP_LEN,
-+ .op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
-+ .op.rf.param.cal_param.func_data = cpu_to_le32(RF_PRE_CAL),
-+ .op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
-+ },
-+ };
-+
-+ if (!dev->flash_mode) {
-+ dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
-+ return -EOPNOTSUPP;
-+ }
-+
-+ eeprom = mdev->eeprom.data;
-+ dev->cur_prek_offset = 0;
-+ group_size = MT_EE_CAL_GROUP_SIZE;
-+ dpd_size = MT_EE_CAL_DPD_SIZE;
-+ size = group_size + dpd_size;
-+ offs = MT_EE_DO_PRE_CAL;
-+
-+ switch (state) {
-+ case MT76_TM_STATE_GROUP_PREK:
-+ if (!dev->cal) {
-+ dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
-+ if (!dev->cal)
-+ return -ENOMEM;
-+ }
-+
-+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
-+ sizeof(req), false);
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
-+ 30 * HZ);
-+
-+ if (ret) {
-+ dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
-+ return ret;
-+ }
-+
-+ if (!ret)
-+ eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
-+ break;
-+ case MT76_TM_STATE_GROUP_PREK_DUMP:
-+ pre_cal = (u32 *)dev->cal;
-+ if (!pre_cal) {
-+ dev_info(dev->mt76.dev, "Not group pre-cal yet!\n");
-+ return ret;
-+ }
-+ dev_info(dev->mt76.dev, "Group Pre-Cal:\n");
-+ for (i = 0; i < (group_size / sizeof(u32)); i += 4) {
-+ dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
-+ i * sizeof(u32), pre_cal[i], pre_cal[i + 1],
-+ pre_cal[i + 2], pre_cal[i + 3]);
-+ }
-+ break;
-+ case MT76_TM_STATE_GROUP_PREK_CLEAN:
-+ pre_cal = (u32 *)dev->cal;
-+ if (!pre_cal)
-+ return ret;
-+ memset(pre_cal, 0, group_size);
-+ eeprom[offs] &= ~MT_EE_WIFI_CAL_GROUP;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return ret;
-+}
-+
-+static int
-+mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
-+ const struct ieee80211_channel *chan_list, u32 channel_size,
-+ enum nl80211_chan_width width, u32 func_data)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_phy *mphy = phy->mt76;
-+ struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
-+ struct ieee80211_channel chan_backup;
-+ int i, ret;
-+
-+ if (!chan_list)
-+ return -EOPNOTSUPP;
-+
-+ req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
-+
-+ memcpy(&chan_backup, chandef->chan, sizeof(struct ieee80211_channel));
-+ memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
-+
-+ for (i = 0; i < channel_size; i++) {
-+ memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
-+ chandef->width = width;
-+
-+ /* set channel switch reason */
-+ mphy->hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
-+ mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
-+
-+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), req,
-+ sizeof(*req), false);
-+ if (ret) {
-+ dev_err(dev->mt76.dev, "DPD Pre-cal: mcu send msg failed!\n");
-+ goto out;
-+ }
-+ }
-+
-+out:
-+ mphy->hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
-+ memcpy(chandef, &chandef_backup, sizeof(struct cfg80211_chan_def));
-+ memcpy(chandef->chan, &chan_backup, sizeof(struct ieee80211_channel));
-+ mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
-+
-+ return ret;
-+}
-+
-+static int
-+mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_dev *mdev = &dev->mt76;
-+ struct mt76_phy *mphy = phy->mt76;
-+ struct mt7996_tm_req req = {
-+ .rf_test = {
-+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+ .len = cpu_to_le16(sizeof(req.rf_test)),
-+ .action = RF_ACTION_IN_RF_TEST,
-+ .icap_len = RF_TEST_ICAP_LEN,
-+ .op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
-+ .op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
-+ },
-+ };
-+ u32 i, j, group_size, dpd_size, size, offs, *pre_cal;
-+ u32 wait_on_prek_offset = 0;
-+ u8 do_precal, *eeprom;
-+ int ret = 0;
-+
-+ if (!dev->flash_mode) {
-+ dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
-+ return -EOPNOTSUPP;
-+ }
-+
-+ eeprom = mdev->eeprom.data;
-+ dev->cur_prek_offset = 0;
-+ group_size = MT_EE_CAL_GROUP_SIZE;
-+ dpd_size = MT_EE_CAL_DPD_SIZE;
-+ size = group_size + dpd_size;
-+ offs = MT_EE_DO_PRE_CAL;
-+
-+ if (!dev->cal && state < MT76_TM_STATE_DPD_DUMP) {
-+ dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
-+ if (!dev->cal)
-+ return -ENOMEM;
-+ }
-+
-+ switch (state) {
-+ case MT76_TM_STATE_DPD_2G:
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
-+ dpd_2g_bw20_ch_num,
-+ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
-+ wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait,
-+ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+ do_precal = MT_EE_WIFI_CAL_DPD_2G;
-+ break;
-+ case MT76_TM_STATE_DPD_5G:
-+ /* 5g channel bw20 calibration */
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_5g.sband.channels,
-+ mphy->sband_5g.sband.n_channels,
-+ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
-+ if (ret)
-+ return ret;
-+ wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait,
-+ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+ /* 5g channel bw160 calibration */
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
-+ dpd_5g_bw160_ch_num,
-+ NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
-+ wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait,
-+ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+ do_precal = MT_EE_WIFI_CAL_DPD_5G;
-+ break;
-+ case MT76_TM_STATE_DPD_6G:
-+ /* 6g channel bw20 calibration */
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_6g.sband.channels,
-+ mphy->sband_6g.sband.n_channels,
-+ NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
-+ if (ret)
-+ return ret;
-+ wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait,
-+ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+ /* 6g channel bw160 calibration */
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
-+ dpd_6g_bw160_ch_num,
-+ NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
-+ if (ret)
-+ return ret;
-+ wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait,
-+ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+ /* 6g channel bw320 calibration */
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
-+ dpd_6g_bw320_ch_num,
-+ NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
-+ wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait,
-+ dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+ do_precal = MT_EE_WIFI_CAL_DPD_6G;
-+ break;
-+ case MT76_TM_STATE_DPD_DUMP:
-+ if (!dev->cal) {
-+ dev_info(dev->mt76.dev, "Not DPD pre-cal yet!\n");
-+ return ret;
-+ }
-+ pre_cal = (u32 *)dev->cal;
-+ dev_info(dev->mt76.dev, "DPD Pre-Cal:\n");
-+ for (i = 0; i < dpd_size / sizeof(u32); i += 4) {
-+ j = i + (group_size / sizeof(u32));
-+ dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
-+ j * sizeof(u32), pre_cal[j], pre_cal[j + 1],
-+ pre_cal[j + 2], pre_cal[j + 3]);
-+ }
-+ return 0;
-+ case MT76_TM_STATE_DPD_CLEAN:
-+ pre_cal = (u32 *)dev->cal;
-+ if (!pre_cal)
-+ return ret;
-+ memset(pre_cal + (group_size / sizeof(u32)), 0, dpd_size);
-+ do_precal = MT_EE_WIFI_CAL_DPD;
-+ eeprom[offs] &= ~do_precal;
-+ return 0;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ if (!ret)
-+ eeprom[offs] |= do_precal;
-+
-+ return ret;
-+}
-+
-+static int
-+mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type)
-+{
-+#define DPD_PER_CHAN_SIZE_MASK GENMASK(31, 30)
-+#define DPD_2G_RATIO_MASK GENMASK(29, 20)
-+#define DPD_5G_RATIO_MASK GENMASK(19, 10)
-+#define DPD_6G_RATIO_MASK GENMASK(9, 0)
-+ struct mt7996_phy *phy = mphy->priv;
-+ struct mt7996_dev *dev = phy->dev;
-+ u32 i, group_size, dpd_size, total_size, size, dpd_info = 0;
-+ u32 dpd_size_2g, dpd_size_5g, dpd_size_6g;
-+ u32 base, offs, transmit_size = 1000;
-+ u8 *pre_cal, *eeprom;
-+ void *precal;
-+ enum prek_ops {
-+ PREK_GET_INFO,
-+ PREK_SYNC_ALL,
-+ PREK_SYNC_GROUP,
-+ PREK_SYNC_DPD_2G,
-+ PREK_SYNC_DPD_5G,
-+ PREK_SYNC_DPD_6G,
-+ PREK_CLEAN_GROUP,
-+ PREK_CLEAN_DPD,
-+ };
-+
-+ if (!dev->cal) {
-+ dev_info(dev->mt76.dev, "Not pre-cal yet!\n");
-+ return 0;
-+ }
-+
-+ group_size = MT_EE_CAL_GROUP_SIZE;
-+ dpd_size = MT_EE_CAL_DPD_SIZE;
-+ total_size = group_size + dpd_size;
-+ pre_cal = dev->cal;
-+ eeprom = dev->mt76.eeprom.data;
-+ offs = MT_EE_DO_PRE_CAL;
-+
-+ dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-+ dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+ dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+
-+ switch (type) {
-+ case PREK_SYNC_ALL:
-+ base = 0;
-+ size = total_size;
-+ break;
-+ case PREK_SYNC_GROUP:
-+ base = 0;
-+ size = group_size;
-+ break;
-+ case PREK_SYNC_DPD_2G:
-+ base = group_size;
-+ size = dpd_size_2g;
-+ break;
-+ case PREK_SYNC_DPD_5G:
-+ base = group_size + dpd_size_2g;
-+ size = dpd_size_5g;
-+ break;
-+ case PREK_SYNC_DPD_6G:
-+ base = group_size + dpd_size_2g + dpd_size_5g;
-+ size = dpd_size_6g;
-+ break;
-+ case PREK_GET_INFO:
-+ break;
-+ default:
-+ return 0;
-+ }
-+
-+ if (!flag) {
-+ if (eeprom[offs] & MT_EE_WIFI_CAL_DPD) {
-+ dpd_info |= u32_encode_bits(1, DPD_PER_CHAN_SIZE_MASK) |
-+ u32_encode_bits(dpd_size_2g / MT_EE_CAL_UNIT,
-+ DPD_2G_RATIO_MASK) |
-+ u32_encode_bits(dpd_size_5g / MT_EE_CAL_UNIT,
-+ DPD_5G_RATIO_MASK) |
-+ u32_encode_bits(dpd_size_6g / MT_EE_CAL_UNIT,
-+ DPD_6G_RATIO_MASK);
-+ }
-+ dev->cur_prek_offset = 0;
-+ precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL_INFO);
-+ if (!precal)
-+ return -ENOMEM;
-+ nla_put_u32(msg, 0, group_size);
-+ nla_put_u32(msg, 1, dpd_size);
-+ nla_put_u32(msg, 2, dpd_info);
-+ nla_put_u32(msg, 3, transmit_size);
-+ nla_put_u32(msg, 4, eeprom[offs]);
-+ nla_nest_end(msg, precal);
-+ } else {
-+ precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL);
-+ if (!precal)
-+ return -ENOMEM;
-+
-+ transmit_size = (dev->cur_prek_offset + transmit_size < size) ?
-+ transmit_size : (size - dev->cur_prek_offset);
-+ for (i = 0; i < transmit_size; i++) {
-+ if (nla_put_u8(msg, i, pre_cal[base + dev->cur_prek_offset + i]))
-+ return -ENOMEM;
-+ }
-+ dev->cur_prek_offset += transmit_size;
-+
-+ nla_nest_end(msg, precal);
-+ }
-+
-+ return 0;
-+}
-+
-+static void
-+mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *result,
-+ struct mt7996_tm_rf_test_data *data)
-+{
-+ u32 base, dpd_size_2g, dpd_size_5g, dpd_size_6g, cal_idx, cal_type, len = 0;
-+ u8 *pre_cal;
-+
-+ pre_cal = dev->cal;
-+ dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-+ dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+ dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+
-+ cal_idx = le32_to_cpu(data->cal_idx);
-+ cal_type = le32_to_cpu(data->cal_type);
-+ len = le32_to_cpu(result->payload_length);
-+ len = len - sizeof(struct mt7996_tm_rf_test_data);
-+
-+ switch (cal_type) {
-+ case RF_PRE_CAL:
-+ base = 0;
-+ break;
-+ case RF_DPD_FLAT_CAL:
-+ base = MT_EE_CAL_GROUP_SIZE;
-+ break;
-+ case RF_DPD_FLAT_5G_CAL:
-+ case RF_DPD_FLAT_5G_MEM_CAL:
-+ base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g;
-+ break;
-+ case RF_DPD_FLAT_6G_CAL:
-+ case RF_DPD_FLAT_6G_MEM_CAL:
-+ base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g + dpd_size_5g;
-+ break;
-+ default:
-+ dev_info(dev->mt76.dev, "Unknown calibration type!\n");
-+ return;
-+ }
-+ pre_cal += (base + dev->cur_prek_offset);
-+
-+ memcpy(pre_cal, data->cal_data, len);
-+ dev->cur_prek_offset += len;
-+}
-+
-+void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+ struct mt7996_tm_event *event;
-+ struct mt7996_tm_rf_test_result *result;
-+ struct mt7996_tm_rf_test_data *data;
-+ static u32 event_type;
-+
-+ skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
-+ event = (struct mt7996_tm_event *)skb->data;
-+ result = (struct mt7996_tm_rf_test_result *)&event->result;
-+ data = (struct mt7996_tm_rf_test_data *)result->data;
-+
-+ event_type = le32_to_cpu(result->func_idx);
-+
-+ switch (event_type) {
-+ case RF_TEST_RE_CAL:
-+ mt7996_tm_re_cal_event(dev, result, data);
-+ break;
-+ default:
-+ break;
-+ }
-+}
-+
- static void
- mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- {
-@@ -454,6 +886,10 @@ mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
- else if (prev_state == MT76_TM_STATE_OFF ||
- state == MT76_TM_STATE_OFF)
- mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
-+ else if (state >= MT76_TM_STATE_GROUP_PREK && state <= MT76_TM_STATE_GROUP_PREK_CLEAN)
-+ return mt7996_tm_group_prek(phy, state);
-+ else if (state >= MT76_TM_STATE_DPD_2G && state <= MT76_TM_STATE_DPD_CLEAN)
-+ return mt7996_tm_dpd_prek(phy, state);
-
- if ((state == MT76_TM_STATE_IDLE &&
- prev_state == MT76_TM_STATE_OFF) ||
-@@ -737,4 +1173,5 @@ const struct mt76_testmode_ops mt7996_testmode_ops = {
- .reset_rx_stats = mt7996_tm_reset_trx_stats,
- .tx_stop = mt7996_tm_tx_stop,
- .set_eeprom = mt7996_tm_set_eeprom,
-+ .dump_precal = mt7996_tm_dump_precal,
- };
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index 319ef257..9bfb86f2 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -34,6 +34,12 @@ enum bw_mapping_method {
- NUM_BW_MAP,
- };
-
-+struct tm_cal_param {
-+ __le32 func_data;
-+ u8 band_idx;
-+ u8 rsv[3];
-+};
-+
- struct mt7996_tm_rf_test {
- __le16 tag;
- __le16 len;
-@@ -50,7 +56,7 @@ struct mt7996_tm_rf_test {
- union {
- __le32 func_data;
- __le32 cal_dump;
--
-+ struct tm_cal_param cal_param;
- u8 _pad[80];
- } param;
- } rf;
-@@ -63,10 +69,16 @@ struct mt7996_tm_req {
- struct mt7996_tm_rf_test rf_test;
- } __packed;
-
-+struct mt7996_tm_rf_test_data {
-+ __le32 cal_idx;
-+ __le32 cal_type;
-+ u8 cal_data[0];
-+} __packed;
-+
- struct mt7996_tm_rf_test_result {
- __le32 func_idx;
- __le32 payload_length;
-- u8 event[0];
-+ u8 data[0];
- };
-
- struct mt7996_tm_event {
-@@ -77,6 +89,8 @@ struct mt7996_tm_event {
- struct mt7996_tm_rf_test_result result;
- } __packed;
-
-+#define RF_TEST_RE_CAL 0x01
-+
- enum {
- RF_ACTION_SWITCH_TO_RF_TEST,
- RF_ACTION_IN_RF_TEST,
-@@ -84,6 +98,8 @@ enum {
- RF_ACTION_GET,
- };
-
-+#define RF_TEST_ICAP_LEN 120
-+
- enum {
- RF_OPER_NORMAL,
- RF_OPER_RF_TEST,
-diff --git a/testmode.c b/testmode.c
-index 44f3a5bf..cd8cb655 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -674,6 +674,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
-
- mutex_lock(&dev->mutex);
-
-+ if (tb[MT76_TM_ATTR_PRECAL] || tb[MT76_TM_ATTR_PRECAL_INFO]) {
-+ int flag, type;
-+
-+ err = -EINVAL;
-+ flag = tb[MT76_TM_ATTR_PRECAL] ? 1 : 0;
-+ type = flag ? nla_get_u8(tb[MT76_TM_ATTR_PRECAL_INFO]) : 0;
-+ if (dev->test_ops->dump_precal)
-+ err = dev->test_ops->dump_precal(phy, msg, flag, type);
-+
-+ goto out;
-+ }
-+
- if (tb[MT76_TM_ATTR_STATS]) {
- err = -EINVAL;
-
-diff --git a/testmode.h b/testmode.h
-index 96872e8c..d6601cdc 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -220,6 +220,14 @@ enum mt76_testmode_state {
- MT76_TM_STATE_TX_FRAMES,
- MT76_TM_STATE_RX_FRAMES,
- MT76_TM_STATE_TX_CONT,
-+ MT76_TM_STATE_GROUP_PREK,
-+ MT76_TM_STATE_GROUP_PREK_DUMP,
-+ MT76_TM_STATE_GROUP_PREK_CLEAN,
-+ MT76_TM_STATE_DPD_2G,
-+ MT76_TM_STATE_DPD_5G,
-+ MT76_TM_STATE_DPD_6G,
-+ MT76_TM_STATE_DPD_DUMP,
-+ MT76_TM_STATE_DPD_CLEAN,
- MT76_TM_STATE_ON,
-
- /* keep last */
-diff --git a/tools/fields.c b/tools/fields.c
-index 055f90f3..b0122763 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -11,6 +11,14 @@ static const char * const testmode_state[] = {
- [MT76_TM_STATE_TX_FRAMES] = "tx_frames",
- [MT76_TM_STATE_RX_FRAMES] = "rx_frames",
- [MT76_TM_STATE_TX_CONT] = "tx_cont",
-+ [MT76_TM_STATE_GROUP_PREK] = "group_prek",
-+ [MT76_TM_STATE_GROUP_PREK_DUMP] = "group_prek_dump",
-+ [MT76_TM_STATE_GROUP_PREK_CLEAN] = "group_prek_clean",
-+ [MT76_TM_STATE_DPD_2G] = "dpd_2g",
-+ [MT76_TM_STATE_DPD_5G] = "dpd_5g",
-+ [MT76_TM_STATE_DPD_6G] = "dpd_6g",
-+ [MT76_TM_STATE_DPD_DUMP] = "dpd_dump",
-+ [MT76_TM_STATE_DPD_CLEAN] = "dpd_clean",
- };
-
- static const char * const testmode_tx_mode[] = {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1006-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1006-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch
deleted file mode 100644
index df56fb5..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1006-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch
+++ /dev/null
@@ -1,304 +0,0 @@
-From 50d5f66613427d37f2f714ee4334e36eaa9039f7 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 8 May 2023 09:03:50 +0800
-Subject: [PATCH 1006/1044] mtk: wifi: mt76: mt7996: enable SCS feature for
- mt7996 driver
-
-Enable Smart Carrier Sense algorithn by default to improve performance
-in a noisy environment.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt76_connac_mcu.h | 1 +
- mt7996/init.c | 1 +
- mt7996/mac.c | 2 +
- mt7996/main.c | 7 +++
- mt7996/mcu.c | 105 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h | 6 +++
- mt7996/mt7996.h | 15 +++++++
- mt7996/mtk_debugfs.c | 11 +++++
- 8 files changed, 148 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 54310413..4a3ea0b7 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1261,6 +1261,7 @@ enum {
- MCU_UNI_CMD_GET_STAT_INFO = 0x23,
- MCU_UNI_CMD_SNIFFER = 0x24,
- MCU_UNI_CMD_SR = 0x25,
-+ MCU_UNI_CMD_SCS = 0x26,
- MCU_UNI_CMD_ROC = 0x27,
- MCU_UNI_CMD_SET_DBDC_PARMS = 0x28,
- MCU_UNI_CMD_TXPOWER = 0x2b,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index d4d1a60b..23a9b88b 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -1374,6 +1374,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
- dev->mt76.phy.priv = &dev->phy;
- INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
- INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
-+ INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
- INIT_LIST_HEAD(&dev->sta_rc_list);
- INIT_LIST_HEAD(&dev->twt_list);
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 603f6c0d..c9f45abe 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1795,6 +1795,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
- cancel_delayed_work_sync(&phy2->mt76->mac_work);
- if (phy3)
- cancel_delayed_work_sync(&phy3->mt76->mac_work);
-+ cancel_delayed_work_sync(&dev->scs_work);
-
- mutex_lock(&dev->mt76.mutex);
- for (i = 0; i < 10; i++) {
-@@ -1830,6 +1831,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
- ieee80211_queue_delayed_work(phy3->mt76->hw,
- &phy3->mt76->mac_work,
- MT7996_WATCHDOG_TIME);
-+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
- }
-
- void mt7996_mac_reset_work(struct work_struct *work)
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 40b5cee3..ffb1f81b 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -73,11 +73,17 @@ int mt7996_run(struct ieee80211_hw *hw)
- if (ret)
- goto out;
-
-+ ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
-+ if (ret)
-+ goto out;
-+
- set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-
- ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
- MT7996_WATCHDOG_TIME);
-
-+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
-+
- if (!running)
- mt7996_mac_reset_counters(phy);
-
-@@ -105,6 +111,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
- struct mt7996_phy *phy = mt7996_hw_phy(hw);
-
- cancel_delayed_work_sync(&phy->mt76->mac_work);
-+ cancel_delayed_work_sync(&dev->scs_work);
-
- mutex_lock(&dev->mt76.mutex);
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 0e2c1f1c..62452d6e 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4615,3 +4615,108 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
- &req, sizeof(req), false);
- }
-+
-+int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
-+{
-+ struct mt7996_scs_ctrl ctrl = phy->scs_ctrl;
-+ struct {
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 _rsv2[6];
-+ s8 min_rssi;
-+ u8 _rsv3;
-+ } __packed req = {
-+ .band_idx = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(UNI_CMD_SCS_SEND_DATA),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+
-+ .min_rssi = ctrl.sta_min_rssi,
-+ };
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
-+ &req, sizeof(req), false);
-+}
-+
-+void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
-+{
-+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+ struct mt7996_phy *poll_phy = (struct mt7996_phy *) data;
-+
-+ if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
-+ poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
-+}
-+
-+void mt7996_mcu_scs_sta_poll(struct work_struct *work)
-+{
-+ struct mt7996_dev *dev = container_of(work, struct mt7996_dev,
-+ scs_work.work);
-+ bool scs_enable_flag = false;
-+ u8 i;
-+
-+ for (i = 0; i < __MT_MAX_BAND; i++) {
-+ struct mt7996_phy *phy;
-+
-+ switch (i) {
-+ case MT_BAND0:
-+ phy = dev->mphy.priv;
-+ break;
-+ case MT_BAND1:
-+ phy = mt7996_phy2(dev);
-+ break;
-+ case MT_BAND2:
-+ phy = mt7996_phy3(dev);
-+ break;
-+ default:
-+ phy = NULL;
-+ break;
-+ }
-+
-+ if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state) ||
-+ !phy->scs_ctrl.scs_enable)
-+ continue;
-+
-+ ieee80211_iterate_stations_atomic(phy->mt76->hw,
-+ mt7996_sta_rssi_work, phy);
-+
-+ scs_enable_flag = true;
-+ if (mt7996_mcu_set_scs_stats(phy))
-+ dev_err(dev->mt76.dev, "Failed to send scs mcu cmd\n");
-+ phy->scs_ctrl.sta_min_rssi = 0;
-+ }
-+
-+ if (scs_enable_flag)
-+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
-+}
-+
-+
-+int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct {
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 scs_enable;
-+ u8 _rsv2[3];
-+ } __packed req = {
-+ .band_idx = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(UNI_CMD_SCS_ENABLE),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .scs_enable = enable,
-+ };
-+
-+ phy->scs_ctrl.scs_enable = enable;
-+
-+ if (enable == SCS_ENABLE)
-+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
-+ &req, sizeof(req), false);
-+}
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 325c3c97..4f4994d8 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -960,6 +960,12 @@ enum {
- UNI_CMD_PP_EN_CTRL,
- };
-
-+enum {
-+ UNI_CMD_SCS_SEND_DATA,
-+ UNI_CMD_SCS_SET_PD_THR_RANGE = 2,
-+ UNI_CMD_SCS_ENABLE,
-+};
-+
- #define MT7996_PATCH_SEC GENMASK(31, 24)
- #define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8)
- #define MT7996_PATCH_AES_KEY GENMASK(7, 0)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index e7609c63..384157c8 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -233,6 +233,16 @@ struct mt7996_hif {
- int irq;
- };
-
-+struct mt7996_scs_ctrl {
-+ u8 scs_enable;
-+ s8 sta_min_rssi;
-+};
-+
-+enum {
-+ SCS_DISABLE = 0,
-+ SCS_ENABLE,
-+};
-+
- struct mt7996_wed_rro_addr {
- u32 head_low;
- u32 head_high : 4;
-@@ -280,6 +290,8 @@ struct mt7996_phy {
-
- bool has_aux_rx;
-
-+ struct mt7996_scs_ctrl scs_ctrl;
-+
- #ifdef CONFIG_NL80211_TESTMODE
- struct {
- u32 *reg_backup;
-@@ -326,6 +338,7 @@ struct mt7996_dev {
- struct work_struct rc_work;
- struct work_struct dump_work;
- struct work_struct reset_work;
-+ struct delayed_work scs_work;
- wait_queue_head_t reset_wait;
- struct {
- u32 state;
-@@ -596,6 +609,8 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
-+int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
-+void mt7996_mcu_scs_sta_poll(struct work_struct *work);
-
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 678b009e..7e4ac77c 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2407,6 +2407,16 @@ static int mt7996_token_read(struct seq_file *s, void *data)
- return 0;
- }
-
-+static int
-+mt7996_scs_enable_set(void *data, u64 val)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ return mt7996_mcu_set_scs(phy, (u8) val);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
-+ mt7996_scs_enable_set, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -2477,6 +2487,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
-
- debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
-+ debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
-
- return 0;
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1007-mtk-wifi-mt76-mt7996-add-txpower-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1007-mtk-wifi-mt76-mt7996-add-txpower-support.patch
deleted file mode 100644
index e96a9fb..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1007-mtk-wifi-mt76-mt7996-add-txpower-support.patch
+++ /dev/null
@@ -1,675 +0,0 @@
-From 668d96f2685432f602994676ff31f826fb98a99f Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 24 Mar 2023 23:35:30 +0800
-Subject: [PATCH 1007/1044] mtk: wifi: mt76: mt7996: add txpower support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 34 +++++
- mt7996/eeprom.h | 42 ++++++
- mt7996/mcu.h | 2 +
- mt7996/mt7996.h | 1 +
- mt7996/mtk_debugfs.c | 326 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.c | 23 +++
- mt7996/mtk_mcu.h | 92 ++++++++++++
- mt7996/regs.h | 29 ++--
- 8 files changed, 538 insertions(+), 11 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 3aeffdfb..acc33cfe 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -404,3 +404,37 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
-
- return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
- }
-+
-+const u8 mt7996_sku_group_len[] = {
-+ [SKU_CCK] = 4,
-+ [SKU_OFDM] = 8,
-+ [SKU_HT20] = 8,
-+ [SKU_HT40] = 9,
-+ [SKU_VHT20] = 12,
-+ [SKU_VHT40] = 12,
-+ [SKU_VHT80] = 12,
-+ [SKU_VHT160] = 12,
-+ [SKU_HE26] = 12,
-+ [SKU_HE52] = 12,
-+ [SKU_HE106] = 12,
-+ [SKU_HE242] = 12,
-+ [SKU_HE484] = 12,
-+ [SKU_HE996] = 12,
-+ [SKU_HE2x996] = 12,
-+ [SKU_EHT26] = 16,
-+ [SKU_EHT52] = 16,
-+ [SKU_EHT106] = 16,
-+ [SKU_EHT242] = 16,
-+ [SKU_EHT484] = 16,
-+ [SKU_EHT996] = 16,
-+ [SKU_EHT2x996] = 16,
-+ [SKU_EHT4x996] = 16,
-+ [SKU_EHT26_52] = 16,
-+ [SKU_EHT26_106] = 16,
-+ [SKU_EHT484_242] = 16,
-+ [SKU_EHT996_484] = 16,
-+ [SKU_EHT996_484_242] = 16,
-+ [SKU_EHT2x996_484] = 16,
-+ [SKU_EHT3x996] = 16,
-+ [SKU_EHT3x996_484] = 16,
-+};
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 849b8bca..23d4929d 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -123,4 +123,46 @@ mt7996_get_channel_group_6g(int channel)
- return DIV_ROUND_UP(channel - 29, 32);
- }
-
-+enum mt7996_sku_rate_group {
-+ SKU_CCK,
-+ SKU_OFDM,
-+
-+ SKU_HT20,
-+ SKU_HT40,
-+
-+ SKU_VHT20,
-+ SKU_VHT40,
-+ SKU_VHT80,
-+ SKU_VHT160,
-+
-+ SKU_HE26,
-+ SKU_HE52,
-+ SKU_HE106,
-+ SKU_HE242,
-+ SKU_HE484,
-+ SKU_HE996,
-+ SKU_HE2x996,
-+
-+ SKU_EHT26,
-+ SKU_EHT52,
-+ SKU_EHT106,
-+ SKU_EHT242,
-+ SKU_EHT484,
-+ SKU_EHT996,
-+ SKU_EHT2x996,
-+ SKU_EHT4x996,
-+ SKU_EHT26_52,
-+ SKU_EHT26_106,
-+ SKU_EHT484_242,
-+ SKU_EHT996_484,
-+ SKU_EHT996_484_242,
-+ SKU_EHT2x996_484,
-+ SKU_EHT3x996,
-+ SKU_EHT3x996_484,
-+
-+ MAX_SKU_RATE_GROUP_NUM,
-+};
-+
-+extern const u8 mt7996_sku_group_len[MAX_SKU_RATE_GROUP_NUM];
-+
- #endif
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 4f4994d8..887d9b49 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -911,6 +911,7 @@ struct tx_power_ctrl {
- bool ate_mode_enable;
- bool percentage_ctrl_enable;
- bool bf_backoff_enable;
-+ u8 show_info_category;
- u8 power_drop_level;
- };
- u8 band_idx;
-@@ -924,6 +925,7 @@ enum {
- UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
- UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
- UNI_TXPOWER_ATE_MODE_CTRL = 6,
-+ UNI_TXPOWER_SHOW_INFO = 7,
- };
-
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 384157c8..18a6a46d 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -609,6 +609,7 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
-+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
- int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
- void mt7996_mcu_scs_sta_poll(struct work_struct *work);
-
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 7e4ac77c..c47d65c9 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2417,6 +2417,328 @@ mt7996_scs_enable_set(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
- mt7996_scs_enable_set, "%lld\n");
-
-+static int
-+mt7996_txpower_level_set(void *data, u64 val)
-+{
-+ struct mt7996_phy *phy = data;
-+ int ret;
-+
-+ if (val > 100)
-+ return -EINVAL;
-+
-+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_CTRL, !!val);
-+ if (ret)
-+ return ret;
-+
-+ return mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_DROP_CTRL, val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_txpower_level, NULL,
-+ mt7996_txpower_level_set, "%lld\n");
-+
-+static ssize_t
-+mt7996_get_txpower_info(struct file *file, char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mt7996_phy *phy = file->private_data;
-+ struct mt7996_mcu_txpower_event *event;
-+ struct txpower_basic_info *basic_info;
-+ static const size_t size = 2048;
-+ int len = 0;
-+ ssize_t ret;
-+ char *buf;
-+
-+ buf = kzalloc(size, GFP_KERNEL);
-+ event = kzalloc(sizeof(*event), GFP_KERNEL);
-+ if (!buf || !event) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ ret = mt7996_mcu_get_tx_power_info(phy, BASIC_INFO, event);
-+ if (ret ||
-+ le32_to_cpu(event->basic_info.category) != UNI_TXPOWER_BASIC_INFO)
-+ goto out;
-+
-+ basic_info = &event->basic_info;
-+
-+ len += scnprintf(buf + len, size - len,
-+ "======================== BASIC INFO ========================\n");
-+ len += scnprintf(buf + len, size - len, " Band Index: %d, Channel Band: %d\n",
-+ basic_info->band_idx, basic_info->band);
-+ len += scnprintf(buf + len, size - len, " PA Type: %s\n",
-+ basic_info->is_epa ? "ePA" : "iPA");
-+ len += scnprintf(buf + len, size - len, " LNA Type: %s\n",
-+ basic_info->is_elna ? "eLNA" : "iLNA");
-+
-+ len += scnprintf(buf + len, size - len,
-+ "------------------------------------------------------------\n");
-+ len += scnprintf(buf + len, size - len, " SKU: %s\n",
-+ basic_info->sku_enable ? "enable" : "disable");
-+ len += scnprintf(buf + len, size - len, " Percentage Control: %s\n",
-+ basic_info->percentage_ctrl_enable ? "enable" : "disable");
-+ len += scnprintf(buf + len, size - len, " Power Drop: %d [dBm]\n",
-+ basic_info->power_drop_level >> 1);
-+ len += scnprintf(buf + len, size - len, " Backoff: %s\n",
-+ basic_info->bf_backoff_enable ? "enable" : "disable");
-+ len += scnprintf(buf + len, size - len, " TX Front-end Loss: %d, %d, %d, %d\n",
-+ basic_info->front_end_loss_tx[0], basic_info->front_end_loss_tx[1],
-+ basic_info->front_end_loss_tx[2], basic_info->front_end_loss_tx[3]);
-+ len += scnprintf(buf + len, size - len, " RX Front-end Loss: %d, %d, %d, %d\n",
-+ basic_info->front_end_loss_rx[0], basic_info->front_end_loss_rx[1],
-+ basic_info->front_end_loss_rx[2], basic_info->front_end_loss_rx[3]);
-+ len += scnprintf(buf + len, size - len,
-+ " MU TX Power Mode: %s\n",
-+ basic_info->mu_tx_power_manual_enable ? "manual" : "auto");
-+ len += scnprintf(buf + len, size - len,
-+ " MU TX Power (Auto / Manual): %d / %d [0.5 dBm]\n",
-+ basic_info->mu_tx_power_auto, basic_info->mu_tx_power_manual);
-+ len += scnprintf(buf + len, size - len,
-+ " Thermal Compensation: %s\n",
-+ basic_info->thermal_compensate_enable ? "enable" : "disable");
-+ len += scnprintf(buf + len, size - len,
-+ " Theraml Compensation Value: %d\n",
-+ basic_info->thermal_compensate_value);
-+
-+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+
-+out:
-+ kfree(buf);
-+ kfree(event);
-+ return ret;
-+}
-+
-+static const struct file_operations mt7996_txpower_info_fops = {
-+ .read = mt7996_get_txpower_info,
-+ .open = simple_open,
-+ .owner = THIS_MODULE,
-+ .llseek = default_llseek,
-+};
-+
-+#define mt7996_txpower_puts(rate) \
-+({ \
-+ len += scnprintf(buf + len, size - len, "%-21s:", #rate " (TMAC)"); \
-+ for (i = 0; i < mt7996_sku_group_len[SKU_##rate]; i++, offs++) \
-+ len += scnprintf(buf + len, size - len, " %6d", \
-+ event->phy_rate_info.frame_power[offs][band_idx]); \
-+ len += scnprintf(buf + len, size - len, "\n"); \
-+})
-+
-+static ssize_t
-+mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mt7996_phy *phy = file->private_data;
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_mcu_txpower_event *event;
-+ u8 band_idx = phy->mt76->band_idx;
-+ static const size_t size = 5120;
-+ int i, offs = 0, len = 0;
-+ ssize_t ret;
-+ char *buf;
-+ u32 reg;
-+
-+ buf = kzalloc(size, GFP_KERNEL);
-+ event = kzalloc(sizeof(*event), GFP_KERNEL);
-+ if (!buf || !event) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ ret = mt7996_mcu_get_tx_power_info(phy, PHY_RATE_INFO, event);
-+ if (ret ||
-+ le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_PHY_RATE_INFO)
-+ goto out;
-+
-+ len += scnprintf(buf + len, size - len,
-+ "\nPhy %d TX Power Table (Channel %d)\n",
-+ band_idx, phy->mt76->chandef.chan->hw_value);
-+ len += scnprintf(buf + len, size - len, "%-21s %6s %6s %6s %6s\n",
-+ " ", "1m", "2m", "5m", "11m");
-+ mt7996_txpower_puts(CCK);
-+
-+ len += scnprintf(buf + len, size - len,
-+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+ " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m",
-+ "54m");
-+ mt7996_txpower_puts(OFDM);
-+
-+ len += scnprintf(buf + len, size - len,
-+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4",
-+ "mcs5", "mcs6", "mcs7");
-+ mt7996_txpower_puts(HT20);
-+
-+ len += scnprintf(buf + len, size - len,
-+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
-+ "mcs6", "mcs7", "mcs32");
-+ mt7996_txpower_puts(HT40);
-+
-+ len += scnprintf(buf + len, size - len,
-+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
-+ "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
-+ mt7996_txpower_puts(VHT20);
-+ mt7996_txpower_puts(VHT40);
-+ mt7996_txpower_puts(VHT80);
-+ mt7996_txpower_puts(VHT160);
-+ mt7996_txpower_puts(HE26);
-+ mt7996_txpower_puts(HE52);
-+ mt7996_txpower_puts(HE106);
-+ mt7996_txpower_puts(HE242);
-+ mt7996_txpower_puts(HE484);
-+ mt7996_txpower_puts(HE996);
-+ mt7996_txpower_puts(HE2x996);
-+
-+ len += scnprintf(buf + len, size - len,
-+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s ",
-+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", "mcs6", "mcs7");
-+ len += scnprintf(buf + len, size - len,
-+ "%6s %6s %6s %6s %6s %6s %6s %6s\n",
-+ "mcs8", "mcs9", "mcs10", "mcs11", "mcs12", "mcs13", "mcs14", "mcs15");
-+ mt7996_txpower_puts(EHT26);
-+ mt7996_txpower_puts(EHT52);
-+ mt7996_txpower_puts(EHT106);
-+ mt7996_txpower_puts(EHT242);
-+ mt7996_txpower_puts(EHT484);
-+ mt7996_txpower_puts(EHT996);
-+ mt7996_txpower_puts(EHT2x996);
-+ mt7996_txpower_puts(EHT4x996);
-+ mt7996_txpower_puts(EHT26_52);
-+ mt7996_txpower_puts(EHT26_106);
-+ mt7996_txpower_puts(EHT484_242);
-+ mt7996_txpower_puts(EHT996_484);
-+ mt7996_txpower_puts(EHT996_484_242);
-+ mt7996_txpower_puts(EHT2x996_484);
-+ mt7996_txpower_puts(EHT3x996);
-+ mt7996_txpower_puts(EHT3x996_484);
-+
-+ len += scnprintf(buf + len, size - len, "\nePA Gain: %d\n",
-+ event->phy_rate_info.epa_gain);
-+ len += scnprintf(buf + len, size - len, "Max Power Bound: %d\n",
-+ event->phy_rate_info.max_power_bound);
-+ len += scnprintf(buf + len, size - len, "Min Power Bound: %d\n",
-+ event->phy_rate_info.min_power_bound);
-+
-+ reg = MT_WF_PHYDFE_BAND_TPC_CTRL_STAT0(band_idx);
-+ len += scnprintf(buf + len, size - len,
-+ "BBP TX Power (target power from TMAC) : %6ld [0.5 dBm]\n",
-+ mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER_TMAC));
-+ len += scnprintf(buf + len, size - len,
-+ "BBP TX Power (target power from RMAC) : %6ld [0.5 dBm]\n",
-+ mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER_RMAC));
-+ len += scnprintf(buf + len, size - len,
-+ "BBP TX Power (TSSI module power input) : %6ld [0.5 dBm]\n",
-+ mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER_TSSI));
-+
-+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+
-+out:
-+ kfree(buf);
-+ kfree(event);
-+ return ret;
-+}
-+
-+static const struct file_operations mt7996_txpower_sku_fops = {
-+ .read = mt7996_get_txpower_sku,
-+ .open = simple_open,
-+ .owner = THIS_MODULE,
-+ .llseek = default_llseek,
-+};
-+
-+#define mt7996_txpower_path_puts(rate, arr_length) \
-+({ \
-+ len += scnprintf(buf + len, size - len, "%-23s:", #rate " (TMAC)"); \
-+ for (i = 0; i < arr_length; i++, offs++) \
-+ len += scnprintf(buf + len, size - len, " %4d", \
-+ event->backoff_table_info.frame_power[offs]); \
-+ len += scnprintf(buf + len, size - len, "\n"); \
-+})
-+
-+static ssize_t
-+mt7996_get_txpower_path(struct file *file, char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mt7996_phy *phy = file->private_data;
-+ struct mt7996_mcu_txpower_event *event;
-+ static const size_t size = 5120;
-+ int i, offs = 0, len = 0;
-+ ssize_t ret;
-+ char *buf;
-+
-+ buf = kzalloc(size, GFP_KERNEL);
-+ event = kzalloc(sizeof(*event), GFP_KERNEL);
-+ if (!buf || !event) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ ret = mt7996_mcu_get_tx_power_info(phy, BACKOFF_TABLE_INFO, event);
-+ if (ret ||
-+ le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO)
-+ goto out;
-+
-+ len += scnprintf(buf + len, size - len, "\n%*c", 25, ' ');
-+ len += scnprintf(buf + len, size - len, "1T1S/2T1S/3T1S/4T1S/5T1S/2T2S/3T2S/4T2S/5T2S/"
-+ "3T3S/4T3S/5T3S/4T4S/5T4S/5T5S\n");
-+
-+ mt7996_txpower_path_puts(CCK, 5);
-+ mt7996_txpower_path_puts(OFDM, 5);
-+ mt7996_txpower_path_puts(BF-OFDM, 4);
-+
-+ mt7996_txpower_path_puts(RU26, 15);
-+ mt7996_txpower_path_puts(BF-RU26, 15);
-+ mt7996_txpower_path_puts(RU52, 15);
-+ mt7996_txpower_path_puts(BF-RU52, 15);
-+ mt7996_txpower_path_puts(RU26_52, 15);
-+ mt7996_txpower_path_puts(BF-RU26_52, 15);
-+ mt7996_txpower_path_puts(RU106, 15);
-+ mt7996_txpower_path_puts(BF-RU106, 15);
-+ mt7996_txpower_path_puts(RU106_52, 15);
-+ mt7996_txpower_path_puts(BF-RU106_52, 15);
-+
-+ mt7996_txpower_path_puts(BW20/RU242, 15);
-+ mt7996_txpower_path_puts(BF-BW20/RU242, 15);
-+ mt7996_txpower_path_puts(BW40/RU484, 15);
-+ mt7996_txpower_path_puts(BF-BW40/RU484, 15);
-+ mt7996_txpower_path_puts(RU242_484, 15);
-+ mt7996_txpower_path_puts(BF-RU242_484, 15);
-+ mt7996_txpower_path_puts(BW80/RU996, 15);
-+ mt7996_txpower_path_puts(BF-BW80/RU996, 15);
-+ mt7996_txpower_path_puts(RU484_996, 15);
-+ mt7996_txpower_path_puts(BF-RU484_996, 15);
-+ mt7996_txpower_path_puts(RU242_484_996, 15);
-+ mt7996_txpower_path_puts(BF-RU242_484_996, 15);
-+ mt7996_txpower_path_puts(BW160/RU996x2, 15);
-+ mt7996_txpower_path_puts(BF-BW160/RU996x2, 15);
-+ mt7996_txpower_path_puts(RU484_996x2, 15);
-+ mt7996_txpower_path_puts(BF-RU484_996x2, 15);
-+ mt7996_txpower_path_puts(RU996x3, 15);
-+ mt7996_txpower_path_puts(BF-RU996x3, 15);
-+ mt7996_txpower_path_puts(RU484_996x3, 15);
-+ mt7996_txpower_path_puts(BF-RU484_996x3, 15);
-+ mt7996_txpower_path_puts(BW320/RU996x4, 15);
-+ mt7996_txpower_path_puts(BF-BW320/RU996x4, 15);
-+
-+ len += scnprintf(buf + len, size - len, "\nBackoff table: %s\n",
-+ event->backoff_table_info.backoff_en ? "enable" : "disable");
-+
-+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+
-+out:
-+ kfree(buf);
-+ kfree(event);
-+ return ret;
-+}
-+
-+static const struct file_operations mt7996_txpower_path_fops = {
-+ .read = mt7996_get_txpower_path,
-+ .open = simple_open,
-+ .owner = THIS_MODULE,
-+ .llseek = default_llseek,
-+};
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -2480,6 +2802,10 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
-
- debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
- mt7996_trinfo_read);
-+ debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
-+ debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
-+ debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
-+ debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
-
- debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
- mt7996_wtbl_read);
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index c16b25ab..e56ddd8f 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -12,8 +12,31 @@
-
- #ifdef CONFIG_MTK_DEBUG
-
-+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct tx_power_ctrl req = {
-+ .tag = cpu_to_le16(UNI_TXPOWER_SHOW_INFO),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .power_ctrl_id = UNI_TXPOWER_SHOW_INFO,
-+ .show_info_category = category,
-+ .band_idx = phy->mt76->band_idx,
-+ };
-+ struct sk_buff *skb;
-+ int ret;
-
-+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
-+ MCU_WM_UNI_CMD_QUERY(TXPOWER),
-+ &req, sizeof(req), true, &skb);
-+ if (ret)
-+ return ret;
-
-+ memcpy(event, skb->data, sizeof(struct mt7996_mcu_txpower_event));
-+
-+ dev_kfree_skb(skb);
-+
-+ return 0;
-+}
-
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
- {
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 7f4d4e02..c30418ca 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -14,6 +14,98 @@ enum {
- UNI_CMD_MURU_DBG_INFO = 0x18,
- };
-
-+struct txpower_basic_info {
-+ u8 category;
-+ u8 rsv1;
-+
-+ /* basic info */
-+ u8 band_idx;
-+ u8 band;
-+
-+ /* board type info */
-+ bool is_epa;
-+ bool is_elna;
-+
-+ /* power percentage info */
-+ bool percentage_ctrl_enable;
-+ s8 power_drop_level;
-+
-+ /* frond-end loss TX info */
-+ s8 front_end_loss_tx[4];
-+
-+ /* frond-end loss RX info */
-+ s8 front_end_loss_rx[4];
-+
-+ /* thermal info */
-+ bool thermal_compensate_enable;
-+ s8 thermal_compensate_value;
-+ u8 rsv2;
-+
-+ /* TX power max/min limit info */
-+ s8 max_power_bound;
-+ s8 min_power_bound;
-+
-+ /* power limit info */
-+ bool sku_enable;
-+ bool bf_backoff_enable;
-+
-+ /* MU TX power info */
-+ bool mu_tx_power_manual_enable;
-+ s8 mu_tx_power_auto;
-+ s8 mu_tx_power_manual;
-+ u8 rsv3;
-+};
-+
-+struct txpower_phy_rate_info {
-+ u8 category;
-+ u8 band_idx;
-+ u8 band;
-+ u8 epa_gain;
-+
-+ /* rate power info [dBm] */
-+ s8 frame_power[MT7996_SKU_RATE_NUM][__MT_MAX_BAND];
-+
-+ /* TX power max/min limit info */
-+ s8 max_power_bound;
-+ s8 min_power_bound;
-+ u8 rsv1;
-+};
-+
-+struct txpower_backoff_table_info {
-+ u8 category;
-+ u8 band_idx;
-+ u8 band;
-+ u8 backoff_en;
-+
-+ s8 frame_power[MT7996_SKU_PATH_NUM];
-+ u8 rsv[3];
-+};
-+
-+struct mt7996_mcu_txpower_event {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ union {
-+ struct txpower_basic_info basic_info;
-+ struct txpower_phy_rate_info phy_rate_info;
-+ struct txpower_backoff_table_info backoff_table_info;
-+ };
-+};
-+
-+enum txpower_category {
-+ BASIC_INFO,
-+ BACKOFF_TABLE_INFO,
-+ PHY_RATE_INFO,
-+};
-+
-+enum txpower_event {
-+ UNI_TXPOWER_BASIC_INFO = 0,
-+ UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO = 3,
-+ UNI_TXPOWER_PHY_RATE_INFO = 5,
-+};
-+
- #endif
-
- #endif
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 4c20a67d..e94f9a90 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -693,24 +693,31 @@ enum offs_rev {
- ((_wf) << 16) + (ofs))
- #define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
-
--/* PHYRX CTRL */
--#define MT_WF_PHYRX_BAND_BASE 0x83080000
--#define MT_WF_PHYRX_BAND(_band, ofs) (MT_WF_PHYRX_BAND_BASE + \
-+/* PHY CTRL */
-+#define MT_WF_PHY_BAND_BASE 0x83080000
-+#define MT_WF_PHY_BAND(_band, ofs) (MT_WF_PHY_BAND_BASE + \
- ((_band) << 20) + (ofs))
-
--#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band) MT_WF_PHYRX_BAND(_band, 0x1054)
--#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band) MT_WF_PHYRX_BAND(_band, 0x1058)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band) MT_WF_PHYRX_BAND(_band, 0x105c)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band) MT_WF_PHYRX_BAND(_band, 0x1060)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band) MT_WF_PHYRX_BAND(_band, 0x1064)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band) MT_WF_PHYRX_BAND(_band, 0x1068)
-+#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band) MT_WF_PHY_BAND(_band, 0x1054)
-+#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band) MT_WF_PHY_BAND(_band, 0x1058)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band) MT_WF_PHY_BAND(_band, 0x105c)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band) MT_WF_PHY_BAND(_band, 0x1060)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band) MT_WF_PHY_BAND(_band, 0x1064)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band) MT_WF_PHY_BAND(_band, 0x1068)
-
--#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHYRX_BAND(_band, 0x2004)
-+/* PHYRX CTRL */
-+#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHY_BAND(_band, 0x2004)
- #define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0)
- #define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9)
-
-+/* PHYDFE CTRL */
-+#define MT_WF_PHYDFE_BAND_TPC_CTRL_STAT0(_phy) MT_WF_PHY_BAND(_phy, 0xe7a0)
-+#define MT_WF_PHY_TPC_POWER_TMAC GENMASK(15, 8)
-+#define MT_WF_PHY_TPC_POWER_RMAC GENMASK(23, 16)
-+#define MT_WF_PHY_TPC_POWER_TSSI GENMASK(31, 24)
-+
- /* PHYRX CSD BAND */
--#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHYRX_BAND(_band, 0x8230)
-+#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHY_BAND(_band, 0x8230)
- #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18)
- #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29)
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1008-mtk-wifi-mt76-mt7996-add-single-sku.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1008-mtk-wifi-mt76-mt7996-add-single-sku.patch
deleted file mode 100644
index 9099d48..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1008-mtk-wifi-mt76-mt7996-add-single-sku.patch
+++ /dev/null
@@ -1,277 +0,0 @@
-From 28aa63ac8f3468494eb58e6dfb424dc3507a3396 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Mon, 10 Jul 2023 19:56:16 +0800
-Subject: [PATCH 1008/1044] mtk: wifi: mt76: mt7996: add single sku
-
-Add single sku and default enable sku.
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- eeprom.c | 50 ++++++++++++++++++++++++++++++++++++++++++-----
- mt76.h | 9 +++++++++
- mt76_connac_mcu.c | 2 +-
- mt7996/init.c | 2 ++
- mt7996/main.c | 9 +++++++++
- mt7996/mcu.c | 41 ++++++++++++++++++++++++++++++++++----
- mt7996/mt7996.h | 1 +
- 7 files changed, 104 insertions(+), 10 deletions(-)
-
-diff --git a/eeprom.c b/eeprom.c
-index 85bd2a29..c5be2843 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -341,6 +341,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- struct ieee80211_channel *chan,
- struct mt76_power_limits *dest,
-+ struct mt76_power_path_limits *dest_path,
- s8 target_power)
- {
- struct mt76_dev *dev = phy->dev;
-@@ -348,16 +349,20 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- const __be32 *val;
- char name[16];
- u32 mcs_rates = dev->drv->mcs_rates;
-- u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
- char band;
- size_t len;
-- s8 max_power = 0;
-+ s8 max_power = -127;
-+ s8 max_power_backoff = -127;
- s8 txs_delta;
-+ int n_chains = hweight16(phy->chainmask);
-+ s8 target_power_combine = target_power + mt76_tx_power_nss_delta(n_chains);
-
- if (!mcs_rates)
-- mcs_rates = 10;
-+ mcs_rates = 12;
-
- memset(dest, target_power, sizeof(*dest));
-+ if (dest_path != NULL)
-+ memset(dest_path, 0, sizeof(*dest_path));
-
- if (!IS_ENABLED(CONFIG_OF))
- return target_power;
-@@ -405,12 +410,47 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- ARRAY_SIZE(dest->mcs), val, len,
- target_power, txs_delta, &max_power);
-
-- val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
-+ val = mt76_get_of_array(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
- mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
- ARRAY_SIZE(dest->ru), val, len,
- target_power, txs_delta, &max_power);
-
-- return max_power;
-+ val = mt76_get_of_array(np, "rates-eht", &len, ARRAY_SIZE(dest->eht[0]) + 1);
-+ mt76_apply_multi_array_limit(dest->eht[0], ARRAY_SIZE(dest->eht[0]),
-+ ARRAY_SIZE(dest->eht), val, len,
-+ target_power, txs_delta, &max_power);
-+
-+ if (dest_path == NULL)
-+ return max_power;
-+
-+ max_power_backoff = max_power;
-+
-+ val = mt76_get_of_array(np, "paths-cck", &len, ARRAY_SIZE(dest_path->cck));
-+ mt76_apply_array_limit(dest_path->cck, ARRAY_SIZE(dest_path->cck), val,
-+ target_power_combine, txs_delta, &max_power_backoff);
-+
-+ val = mt76_get_of_array(np, "paths-ofdm", &len, ARRAY_SIZE(dest_path->ofdm));
-+ mt76_apply_array_limit(dest_path->ofdm, ARRAY_SIZE(dest_path->ofdm), val,
-+ target_power_combine, txs_delta, &max_power_backoff);
-+
-+ val = mt76_get_of_array(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest_path->ofdm_bf));
-+ mt76_apply_array_limit(dest_path->ofdm_bf, ARRAY_SIZE(dest_path->ofdm_bf), val,
-+ target_power_combine, txs_delta, &max_power_backoff);
-+
-+ val = mt76_get_of_array(np, "paths-ru", &len, ARRAY_SIZE(dest_path->ru[0]) + 1);
-+ mt76_apply_multi_array_limit(dest_path->ru[0], ARRAY_SIZE(dest_path->ru[0]),
-+ ARRAY_SIZE(dest_path->ru), val, len,
-+ target_power_combine, txs_delta, &max_power_backoff);
-+
-+ val = mt76_get_of_array(np, "paths-ru-bf", &len, ARRAY_SIZE(dest_path->ru_bf[0]) + 1);
-+ mt76_apply_multi_array_limit(dest_path->ru_bf[0], ARRAY_SIZE(dest_path->ru_bf[0]),
-+ ARRAY_SIZE(dest_path->ru_bf), val, len,
-+ target_power_combine, txs_delta, &max_power_backoff);
-+
-+ if (max_power_backoff == target_power_combine)
-+ return max_power;
-+
-+ return max_power_backoff;
- }
- EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
-
-diff --git a/mt76.h b/mt76.h
-index 14c5fcb1..630b3903 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -1054,6 +1054,14 @@ struct mt76_power_limits {
- s8 eht[16][16];
- };
-
-+struct mt76_power_path_limits {
-+ s8 cck[5];
-+ s8 ofdm[5];
-+ s8 ofdm_bf[4];
-+ s8 ru[16][15];
-+ s8 ru_bf[16][15];
-+};
-+
- struct mt76_ethtool_worker_info {
- u64 *data;
- int idx;
-@@ -1664,6 +1672,7 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
- s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- struct ieee80211_channel *chan,
- struct mt76_power_limits *dest,
-+ struct mt76_power_path_limits *dest_path,
- s8 target_power);
-
- static inline bool mt76_queue_is_wed_tx_free(struct mt76_queue *q)
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 42f12672..75bbb7cc 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -2150,7 +2150,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
- sar_power = mt76_get_sar_power(phy, &chan, reg_power);
-
- mt76_get_rate_power_limits(phy, &chan, limits,
-- sar_power);
-+ NULL, sar_power);
-
- tx_power_tlv.last_msg = ch_list[idx] == last_ch;
- sku_tlbv.channel = ch_list[idx];
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 23a9b88b..5f937b26 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -295,6 +295,7 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
- int nss_delta = mt76_tx_power_nss_delta(nss);
- int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
- struct mt76_power_limits limits;
-+ struct mt76_power_path_limits limits_path;
-
- for (i = 0; i < sband->n_channels; i++) {
- struct ieee80211_channel *chan = &sband->channels[i];
-@@ -303,6 +304,7 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
- target_power += pwr_delta;
- target_power = mt76_get_rate_power_limits(phy->mt76, chan,
- &limits,
-+ &limits_path,
- target_power);
- target_power += nss_delta;
- target_power = DIV_ROUND_UP(target_power, 2);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index ffb1f81b..ecfc3dcf 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -77,6 +77,15 @@ int mt7996_run(struct ieee80211_hw *hw)
- if (ret)
- goto out;
-
-+#ifdef CONFIG_MTK_DEBUG
-+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
-+ !dev->dbg.sku_disable);
-+#else
-+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL, true);
-+#endif
-+ if (ret)
-+ goto out;
-+
- set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-
- ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 62452d6e..e103601f 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4496,6 +4496,7 @@ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
- int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- {
- #define TX_POWER_LIMIT_TABLE_RATE 0
-+#define TX_POWER_LIMIT_TABLE_PATH 1
- struct mt7996_dev *dev = phy->dev;
- struct mt76_phy *mphy = phy->mt76;
- struct ieee80211_hw *hw = mphy->hw;
-@@ -4509,22 +4510,23 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- u8 band_idx;
- } __packed req = {
- .tag = cpu_to_le16(UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL),
-- .len = cpu_to_le16(sizeof(req) + MT7996_SKU_RATE_NUM - 4),
-+ .len = cpu_to_le16(sizeof(req) + MT7996_SKU_PATH_NUM - 4),
- .power_ctrl_id = UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL,
- .power_limit_type = TX_POWER_LIMIT_TABLE_RATE,
- .band_idx = phy->mt76->band_idx,
- };
- struct mt76_power_limits la = {};
-+ struct mt76_power_path_limits la_path = {};
- struct sk_buff *skb;
-- int i, tx_power;
-+ int i, ret, tx_power;
-
- tx_power = mt7996_get_power_bound(phy, hw->conf.power_level);
- tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
-- &la, tx_power);
-+ &la, &la_path, tx_power);
- mphy->txpower_cur = tx_power;
-
- skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
-- sizeof(req) + MT7996_SKU_RATE_NUM);
-+ sizeof(req) + MT7996_SKU_PATH_NUM);
- if (!skb)
- return -ENOMEM;
-
-@@ -4548,6 +4550,37 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- /* eht */
- skb_put_data(skb, &la.eht[0], sizeof(la.eht));
-
-+ /* padding */
-+ skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM);
-+
-+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
-+ MCU_WM_UNI_CMD(TXPOWER), true);
-+ if (ret)
-+ return ret;
-+
-+ /* only set per-path power table when it's configured */
-+ if (!la_path.ofdm[0])
-+ return 0;
-+
-+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
-+ sizeof(req) + MT7996_SKU_PATH_NUM);
-+ if (!skb)
-+ return -ENOMEM;
-+ req.power_limit_type = TX_POWER_LIMIT_TABLE_PATH;
-+
-+ skb_put_data(skb, &req, sizeof(req));
-+ skb_put_data(skb, &la_path.cck, sizeof(la_path.cck));
-+ skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
-+ skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
-+
-+ for (i = 0; i < 32; i++) {
-+ bool bf = i % 2;
-+ u8 idx = i / 2;
-+ s8 *buf = bf ? la_path.ru_bf[idx] : la_path.ru[idx];
-+
-+ skb_put_data(skb, buf, sizeof(la_path.ru[0]));
-+ }
-+
- return mt76_mcu_skb_send_msg(&dev->mt76, skb,
- MCU_WM_UNI_CMD(TXPOWER), true);
- }
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 18a6a46d..7e3d381e 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -71,6 +71,7 @@
- #define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
-
- #define MT7996_SKU_RATE_NUM 417
-+#define MT7996_SKU_PATH_NUM 494
-
- #define MT7996_MAX_TWT_AGRT 16
- #define MT7996_MAX_STA_TWT_AGRT 8
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1009-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1009-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch
deleted file mode 100644
index 59222a4..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1009-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch
+++ /dev/null
@@ -1,366 +0,0 @@
-From c342da92eba77fea4b62298df80f6b3614c0635f Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 31 Mar 2023 11:36:34 +0800
-Subject: [PATCH 1009/1044] mtk: wifi: mt76: mt7996: add binfile mode support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Fix binfile cannot sync precal data to atenl
-Binfile is viewed as efuse mode in atenl, so atenl does not allocate
-precal memory for its eeprom file
-Use mtd offset == 0xFFFFFFFF to determine whether it is binfile or flash mode
-Add support for loading precal in binfile mode
-
-Align upstream
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- eeprom.c | 25 +++++++++++
- mt76.h | 3 ++
- mt7996/eeprom.c | 103 ++++++++++++++++++++++++++++++++++++++++---
- mt7996/eeprom.h | 7 +++
- mt7996/mt7996.h | 4 ++
- mt7996/mtk_debugfs.c | 41 +++++++++++++++++
- testmode.h | 2 +-
- 7 files changed, 179 insertions(+), 6 deletions(-)
-
-diff --git a/eeprom.c b/eeprom.c
-index c5be2843..adb87924 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -161,6 +161,31 @@ static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len)
- return mt76_get_of_data_from_nvmem(dev, eep, "eeprom", len);
- }
-
-+bool mt76_check_bin_file_mode(struct mt76_dev *dev)
-+{
-+ struct device_node *np = dev->dev->of_node;
-+ const char *bin_file_name = NULL;
-+
-+ if (!np)
-+ return false;
-+
-+ of_property_read_string(np, "bin_file_name", &bin_file_name);
-+
-+ dev->bin_file_name = bin_file_name;
-+ if (dev->bin_file_name) {
-+ dev_info(dev->dev, "Using bin file %s\n", dev->bin_file_name);
-+#ifdef CONFIG_NL80211_TESTMODE
-+ dev->test_mtd.name = devm_kstrdup(dev->dev, bin_file_name, GFP_KERNEL);
-+ dev->test_mtd.offset = -1;
-+#endif
-+ }
-+
-+ of_node_put(np);
-+
-+ return dev->bin_file_name ? true : false;
-+}
-+EXPORT_SYMBOL_GPL(mt76_check_bin_file_mode);
-+
- void
- mt76_eeprom_override(struct mt76_phy *phy)
- {
-diff --git a/mt76.h b/mt76.h
-index 630b3903..b2cc1085 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -949,6 +949,8 @@ struct mt76_dev {
- struct mt76_usb usb;
- struct mt76_sdio sdio;
- };
-+
-+ const char *bin_file_name;
- };
-
- /* per-phy stats. */
-@@ -1220,6 +1222,7 @@ void mt76_eeprom_override(struct mt76_phy *phy);
- int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len);
- int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep,
- const char *cell_name, int len);
-+bool mt76_check_bin_file_mode(struct mt76_dev *dev);
-
- struct mt76_queue *
- mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index acc33cfe..c334f849 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -82,10 +82,17 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
- }
- }
-
--static char *mt7996_eeprom_name(struct mt7996_dev *dev)
-+const char *mt7996_eeprom_name(struct mt7996_dev *dev)
- {
-- if (dev->testmode_enable)
-- return MT7996_EEPROM_DEFAULT_TM;
-+ if (dev->bin_file_mode)
-+ return dev->mt76.bin_file_name;
-+
-+ if (dev->testmode_enable) {
-+ if (is_mt7992(&dev->mt76))
-+ return MT7992_EEPROM_DEFAULT_TM;
-+ else
-+ return MT7996_EEPROM_DEFAULT_TM;
-+ }
-
- switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
-@@ -152,7 +159,10 @@ mt7996_eeprom_load_default(struct mt7996_dev *dev)
- return ret;
-
- if (!fw || !fw->data) {
-- dev_err(dev->mt76.dev, "Invalid default bin\n");
-+ if (dev->bin_file_mode)
-+ dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
-+ else
-+ dev_err(dev->mt76.dev, "Invalid default bin\n");
- ret = -EINVAL;
- goto out;
- }
-@@ -166,18 +176,45 @@ out:
- return ret;
- }
-
-+static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
-+{
-+ int ret = 1;
-+
-+ /* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
-+ dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
-+ if (dev->bin_file_mode) {
-+ dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
-+ dev->mt76.eeprom.data = devm_kzalloc(dev->mt76.dev, dev->mt76.eeprom.size,
-+ GFP_KERNEL);
-+ if (!dev->mt76.eeprom.data)
-+ return -ENOMEM;
-+
-+ if (mt7996_eeprom_load_default(dev))
-+ return 0;
-+
-+ if (mt7996_check_eeprom(dev))
-+ return 0;
-+ } else {
-+ ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
-+ }
-+
-+ return ret;
-+}
-+
- int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
- {
- u8 *eeprom;
- int ret;
-
- /* load eeprom in flash or bin file mode to determine fw mode */
-- ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
-+ ret = mt7996_eeprom_load_flash(dev);
-+
- if (ret < 0)
- return ret;
-
- if (ret) {
- dev->flash_mode = true;
-+ dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
- eeprom = dev->mt76.eeprom.data;
- /* testmode enable priority: eeprom field > module parameter */
- dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
-@@ -211,6 +248,7 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
- if (ret < 0)
- return ret;
- }
-+ dev->eeprom_mode = EFUSE_MODE;
- }
-
- return mt7996_check_eeprom(dev);
-@@ -337,6 +375,59 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
- return mt7996_eeprom_parse_band_config(phy);
- }
-
-+static int
-+mt7996_eeprom_load_precal_binfile(struct mt7996_dev *dev, u32 offs, u32 size)
-+{
-+ const struct firmware *fw = NULL;
-+ int ret;
-+
-+ ret = request_firmware(&fw, dev->mt76.bin_file_name, dev->mt76.dev);
-+ if (ret)
-+ return ret;
-+
-+ if (!fw || !fw->data) {
-+ dev_err(dev->mt76.dev, "Invalid bin (bin file mode), load precal fail\n");
-+ ret = -EINVAL;
-+ goto out;
-+ }
-+
-+ memcpy(dev->cal, fw->data + offs, size);
-+
-+out:
-+ release_firmware(fw);
-+
-+ return ret;
-+}
-+
-+static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
-+{
-+ struct mt76_dev *mdev = &dev->mt76;
-+ u8 *eeprom = mdev->eeprom.data;
-+ u32 offs = MT_EE_DO_PRE_CAL;
-+ u32 size, val = eeprom[offs];
-+ int ret;
-+
-+ mt7996_eeprom_init_precal(dev);
-+
-+ if (!dev->flash_mode || !val)
-+ return 0;
-+
-+ size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
-+
-+ dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
-+ if (!dev->cal)
-+ return -ENOMEM;
-+
-+ if (dev->bin_file_mode)
-+ return mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
-+
-+ ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
-+ if (!ret)
-+ return ret;
-+
-+ return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
-+}
-+
- int mt7996_eeprom_init(struct mt7996_dev *dev)
- {
- int ret;
-@@ -351,6 +442,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- return ret;
-
- dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
-+ dev->bin_file_mode = false;
-+ dev->eeprom_mode = DEFAULT_BIN_MODE;
- ret = mt7996_eeprom_load_default(dev);
- if (ret)
- return ret;
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 23d4929d..8b555aeb 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -100,6 +100,13 @@ enum mt7996_eeprom_band {
- MT_EE_BAND_SEL_6GHZ,
- };
-
-+enum mt7915_eeprom_mode {
-+ DEFAULT_BIN_MODE,
-+ EFUSE_MODE,
-+ FLASH_MODE,
-+ BIN_FILE_MODE,
-+};
-+
- static inline int
- mt7996_get_channel_group_5g(int channel)
- {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 7e3d381e..2f067988 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -62,6 +62,7 @@
- #define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_23_EXT "mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
-+#define MT7992_EEPROM_DEFAULT_TM "mediatek/mt7996/mt7992_eeprom_tm.bin"
- #define MT7996_EEPROM_SIZE 7680
- #define MT7996_EEPROM_BLOCK_SIZE 16
- #define MT7996_TOKEN_SIZE 16384
-@@ -389,6 +390,8 @@ struct mt7996_dev {
- } wed_rro;
-
- bool testmode_enable;
-+ bool bin_file_mode;
-+ u8 eeprom_mode;
-
- bool ibf;
- u8 fw_debug_wm;
-@@ -516,6 +519,7 @@ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
- u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
- int mt7996_register_device(struct mt7996_dev *dev);
- void mt7996_unregister_device(struct mt7996_dev *dev);
-+const char *mt7996_eeprom_name(struct mt7996_dev *dev);
- int mt7996_eeprom_init(struct mt7996_dev *dev);
- int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
- int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index c47d65c9..09652ef5 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2739,6 +2739,44 @@ static const struct file_operations mt7996_txpower_path_fops = {
- .llseek = default_llseek,
- };
-
-+static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ struct mt76_dev *mdev = &dev->mt76;
-+#ifdef CONFIG_NL80211_TESTMODE
-+ const char *mtd_name = mdev->test_mtd.name;
-+ u32 mtd_offset = mdev->test_mtd.offset;
-+#else
-+ const char *mtd_name = NULL;
-+ u32 mtd_offset;
-+#endif
-+
-+ seq_printf(s, "Current eeprom mode:\n");
-+
-+ switch (dev->eeprom_mode) {
-+ case DEFAULT_BIN_MODE:
-+ seq_printf(s, " default bin mode\n filename = %s\n", mt7996_eeprom_name(dev));
-+ break;
-+ case EFUSE_MODE:
-+ seq_printf(s, " efuse mode\n");
-+ break;
-+ case FLASH_MODE:
-+ if (mtd_name)
-+ seq_printf(s, " flash mode\n mtd name = %s\n flash offset = 0x%x\n",
-+ mtd_name, mtd_offset);
-+ else
-+ seq_printf(s, " flash mode\n");
-+ break;
-+ case BIN_FILE_MODE:
-+ seq_printf(s, " bin file mode\n filename = %s\n", mt7996_eeprom_name(dev));
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ return 0;
-+}
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -2807,6 +2845,9 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
- debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
-
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "eeprom_mode", dir,
-+ mt7996_show_eeprom_mode);
-+
- debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
- mt7996_wtbl_read);
-
-diff --git a/testmode.h b/testmode.h
-index d6601cdc..5d677f8c 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -16,7 +16,7 @@
- * @MT76_TM_ATTR_RESET: reset parameters to default (flag)
- * @MT76_TM_ATTR_STATE: test state (u32), see &enum mt76_testmode_state
- *
-- * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
-+ * @MT76_TM_ATTR_MTD_PART: mtd partition or binfile used for eeprom data (string)
- * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
- * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
- *
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1010-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1010-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch
deleted file mode 100644
index 1ca02bf..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1010-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch
+++ /dev/null
@@ -1,285 +0,0 @@
-From e72fe68d4a6356a2fae17c21971c1fd5a5d9384d Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 1 Mar 2023 12:12:51 +0800
-Subject: [PATCH 1010/1044] mtk: wifi: mt76: mt7996: add normal mode
- pre-calibration support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76_connac_mcu.h | 1 +
- mt7996/eeprom.c | 4 ++
- mt7996/eeprom.h | 2 +
- mt7996/init.c | 6 ++
- mt7996/main.c | 6 ++
- mt7996/mcu.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mt7996.h | 3 +
- 7 files changed, 188 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 4a3ea0b7..3c82c05d 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1277,6 +1277,7 @@ enum {
- MCU_UNI_CMD_PP = 0x38,
- MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
- MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
-+ MCU_UNI_CMD_PRECAL_RESULT = 0x47,
- MCU_UNI_CMD_RRO = 0x57,
- MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
- MCU_UNI_CMD_PER_STA_INFO = 0x6d,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index c334f849..d93b1558 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -449,6 +449,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- return ret;
- }
-
-+ ret = mt7996_eeprom_load_precal(dev);
-+ if (ret)
-+ return ret;
-+
- ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
- if (ret < 0)
- return ret;
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 8b555aeb..8f0f87b6 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -25,6 +25,8 @@ enum mt7996_eeprom_field {
- MT_EE_TX0_POWER_6G = 0x1310,
-
- __MT_EE_MAX = 0x1dff,
-+ /* 0x1e10 ~ 0x2d644 used to save group cal data */
-+ MT_EE_PRECAL = 0x1e10,
- };
-
- #define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0)
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 5f937b26..c135da9c 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -984,6 +984,12 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
- if (ret < 0)
- return ret;
-
-+ if (dev->flash_mode) {
-+ ret = mt7996_mcu_apply_group_cal(dev);
-+ if (ret)
-+ return ret;
-+ }
-+
- /* Beacon and mgmt frames should occupy wcid 0 */
- idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
- if (idx)
-diff --git a/mt7996/main.c b/mt7996/main.c
-index ecfc3dcf..07a14917 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -321,6 +321,12 @@ int mt7996_set_channel(struct mt7996_phy *phy)
-
- mt76_set_channel(phy->mt76);
-
-+ if (dev->flash_mode) {
-+ ret = mt7996_mcu_apply_tx_dpd(phy);
-+ if (ret)
-+ goto out;
-+ }
-+
- if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
- mt7996_tm_update_channel(phy);
- goto out;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index e103601f..0ee17ff9 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3629,6 +3629,172 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
- return 0;
- }
-
-+static int mt7996_mcu_set_pre_cal(struct mt7996_dev *dev, u32 idx,
-+ u8 *cal, u32 len, u32 cal_id)
-+{
-+#define PRECAL_CMD_PRE_CAL_RESULT 0x0
-+ struct {
-+ /* fixed field */
-+ u8 action;
-+ u8 dest;
-+ u8 attribute;
-+ u8 tag_num;
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le32 cal_id;
-+ s8 precal;
-+ u8 band;
-+ u8 rsv[2];
-+ __le32 idx;
-+ __le32 cal_len;
-+ } req = {
-+ .tag = cpu_to_le16(PRECAL_CMD_PRE_CAL_RESULT),
-+ .len = cpu_to_le16(sizeof(req) - 4 + len),
-+ .cal_id = cpu_to_le32(cal_id),
-+ .idx = cpu_to_le32(idx),
-+ .cal_len = cpu_to_le32(len),
-+ };
-+ struct sk_buff *skb;
-+
-+ if (!len)
-+ return 0;
-+
-+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len);
-+ if (!skb)
-+ return -ENOMEM;
-+
-+ skb_put_data(skb, &req, sizeof(req));
-+ skb_put_data(skb, cal, len);
-+
-+ return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(PRECAL_RESULT), false);
-+}
-+
-+int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev)
-+{
-+ u8 *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
-+ u32 idx = 0, total_idx = MT_EE_CAL_GROUP_SIZE / MT_EE_CAL_UNIT;
-+ u32 offs = MT_EE_DO_PRE_CAL;
-+ int ret = 0;
-+
-+ if (!(eeprom[offs] & MT_EE_WIFI_CAL_GROUP))
-+ return 0;
-+
-+ for (idx = 0; idx < total_idx; idx++, cal += MT_EE_CAL_UNIT) {
-+ ret = mt7996_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, RF_PRE_CAL);
-+ if (ret)
-+ goto out;
-+ }
-+
-+ ret = mt7996_mcu_set_pre_cal(dev, total_idx, cal,
-+ MT_EE_CAL_GROUP_SIZE % MT_EE_CAL_UNIT, RF_PRE_CAL);
-+
-+out:
-+ return ret;
-+}
-+
-+int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_phy *mphy = phy->mt76;
-+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+ enum nl80211_band band = chandef->chan->band;
-+ enum nl80211_chan_width bw = chandef->width;
-+ const struct ieee80211_channel *chan_list;
-+ u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
-+ u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
-+ u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
-+ u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
-+ int idx, i, ret;
-+
-+ dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-+ dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+
-+ switch (band) {
-+ case NL80211_BAND_2GHZ:
-+ dpd_mask = MT_EE_WIFI_CAL_DPD_2G;
-+ /* channel 14 don't need DPD cal */
-+ if (channel >= 1 && channel <= 4)
-+ channel = 3;
-+ else if (channel >= 5 && channel <= 9)
-+ channel = 7;
-+ else if (channel >= 10 && channel <= 13)
-+ channel = 11;
-+ else
-+ return 0;
-+ cal_id = RF_DPD_FLAT_CAL;
-+ chan_list = dpd_2g_ch_list_bw20;
-+ chan_list_size = dpd_2g_bw20_ch_num;
-+ break;
-+ case NL80211_BAND_5GHZ:
-+ dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
-+ cal_id = RF_DPD_FLAT_5G_CAL;
-+ chan_list = mphy->sband_5g.sband.channels;
-+ chan_list_size = mphy->sband_5g.sband.n_channels;
-+ base_offset += dpd_size_2g;
-+ if (bw == NL80211_CHAN_WIDTH_160) {
-+ base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+ cal_id = RF_DPD_FLAT_5G_MEM_CAL;
-+ chan_list = dpd_5g_ch_list_bw160;
-+ chan_list_size = dpd_5g_bw160_ch_num;
-+ } else if (bw > NL80211_CHAN_WIDTH_20) {
-+ /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
-+ channel -= 2;
-+ }
-+ break;
-+ case NL80211_BAND_6GHZ:
-+ dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
-+ cal_id = RF_DPD_FLAT_6G_CAL;
-+ chan_list = mphy->sband_6g.sband.channels;
-+ chan_list_size = mphy->sband_6g.sband.n_channels;
-+ base_offset += dpd_size_2g + dpd_size_5g;
-+ if (bw == NL80211_CHAN_WIDTH_160) {
-+ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
-+ chan_list = dpd_6g_ch_list_bw160;
-+ chan_list_size = dpd_6g_bw160_ch_num;
-+ } else if (bw == NL80211_CHAN_WIDTH_320) {
-+ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-+ dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
-+ chan_list = dpd_6g_ch_list_bw320;
-+ chan_list_size = dpd_6g_bw320_ch_num;
-+ } else if (bw > NL80211_CHAN_WIDTH_20) {
-+ /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
-+ channel -= 2;
-+ }
-+ break;
-+ default:
-+ dpd_mask = 0;
-+ break;
-+ }
-+
-+ if (!(eeprom[offs] & dpd_mask))
-+ return 0;
-+
-+ for (idx = 0; idx < chan_list_size; idx++)
-+ if (channel == chan_list[idx].hw_value)
-+ break;
-+ if (idx == chan_list_size)
-+ return -EINVAL;
-+
-+ cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
-+
-+ for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
-+ ret = mt7996_mcu_set_pre_cal(dev, i, cal, MT_EE_CAL_UNIT, cal_id);
-+ if (ret)
-+ return ret;
-+
-+ cal += MT_EE_CAL_UNIT;
-+ }
-+
-+ return ret;
-+}
-+
- int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
- {
- #define NIC_CAP 3
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 2f067988..69db055d 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -611,6 +611,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
- int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
- int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
-+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
-+int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev);
-+int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1011-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1011-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch
deleted file mode 100644
index 20f17b5..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1011-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch
+++ /dev/null
@@ -1,490 +0,0 @@
-From ca5e57a566fb60705ae7d6b0767542b32ee42afc Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 22 Mar 2023 11:19:52 +0800
-Subject: [PATCH 1011/1044] mtk: wifi: mt76: testmode: add testmode ZWDFS
- verification support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76.h | 8 ++
- mt7996/mt7996.h | 1 +
- mt7996/testmode.c | 248 ++++++++++++++++++++++++++++++++++++++++++++--
- mt7996/testmode.h | 44 ++++++++
- testmode.c | 22 +++-
- tools/fields.c | 15 +++
- 6 files changed, 326 insertions(+), 12 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index b2cc1085..a75277fe 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -778,6 +778,14 @@ struct mt76_testmode_data {
- } cfg;
-
- u8 aid;
-+
-+ u8 offchan_ch;
-+ u8 offchan_center_ch;
-+ u8 offchan_bw;
-+
-+ u8 ipi_threshold;
-+ u32 ipi_period;
-+ u8 ipi_reset;
- };
-
- struct mt76_vif {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 69db055d..2daca449 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -289,6 +289,7 @@ struct mt7996_phy {
-
- struct mt76_mib_stats mib;
- struct mt76_channel_state state_ts;
-+ struct delayed_work ipi_work;
-
- bool has_aux_rx;
-
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 36be0ff8..26ae5827 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -17,6 +17,12 @@ enum {
- TM_CHANGED_TX_LENGTH,
- TM_CHANGED_TX_TIME,
- TM_CHANGED_CFG,
-+ TM_CHANGED_OFF_CHAN_CH,
-+ TM_CHANGED_OFF_CHAN_CENTER_CH,
-+ TM_CHANGED_OFF_CHAN_BW,
-+ TM_CHANGED_IPI_THRESHOLD,
-+ TM_CHANGED_IPI_PERIOD,
-+ TM_CHANGED_IPI_RESET,
-
- /* must be last */
- NUM_TM_CHANGED
-@@ -29,20 +35,31 @@ static const u8 tm_change_map[] = {
- [TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
- [TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
- [TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
-+ [TM_CHANGED_OFF_CHAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
-+ [TM_CHANGED_OFF_CHAN_CENTER_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
-+ [TM_CHANGED_OFF_CHAN_BW] = MT76_TM_ATTR_OFF_CH_SCAN_BW,
-+ [TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
-+ [TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
-+ [TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
- };
-
--static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
-+static void mt7996_tm_ipi_work(struct work_struct *work);
-+
-+static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
- {
-- static const u8 width_to_bw[][NUM_BW_MAP] = {
-- [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
-- [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
-- [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
-- [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
-- [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
-- [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
-- [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
-- [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
-- [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
-+ static const u32 width_to_bw[][NUM_BW_MAP] = {
-+ [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
-+ FIRST_CONTROL_CHAN_BITMAP_BW40},
-+ [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
-+ FIRST_CONTROL_CHAN_BITMAP_BW80},
-+ [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
-+ [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
-+ FIRST_CONTROL_CHAN_BITMAP_BW160},
-+ [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
-+ [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
-+ [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
-+ [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
-+ [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
- };
-
- if (width >= ARRAY_SIZE(width_to_bw))
-@@ -217,6 +234,9 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
-
- /* use firmware counter for RX stats */
- phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
-+
-+ if (en)
-+ INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
- }
-
- static void
-@@ -829,6 +849,204 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
- }
- }
-
-+static u8
-+mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
-+{
-+ struct mt76_phy *mphy = phy->mt76;
-+ const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
-+ u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
-+ u16 first_control = 0, control_chan = chandef->chan->hw_value;
-+
-+ bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
-+ if (!bitmap)
-+ return control_chan;
-+
-+ width_mhz = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_MHZ);
-+ offset = width_mhz / 10 - 2;
-+
-+ for (i = 0; i < size; i++) {
-+ if (!((1 << i) & bitmap))
-+ continue;
-+
-+ if (control_chan >= chan[i].hw_value)
-+ first_control = chan[i].hw_value;
-+ else
-+ break;
-+ }
-+
-+ if (i == size || first_control == 0)
-+ return control_chan;
-+
-+ return first_control + offset;
-+}
-+
-+static int
-+mt7996_tm_set_offchan(struct mt7996_phy *phy, bool no_center)
-+{
-+ struct mt76_phy *mphy = phy->mt76;
-+ struct mt7996_dev *dev = phy->dev;
-+ struct ieee80211_hw *hw = mphy->hw;
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct cfg80211_chan_def chandef = {};
-+ struct ieee80211_channel *chan;
-+ int ret, freq = ieee80211_channel_to_frequency(td->offchan_ch, NL80211_BAND_5GHZ);
-+
-+ if (!mphy->cap.has_5ghz || !freq) {
-+ ret = -EINVAL;
-+ dev_info(dev->mt76.dev, "Failed to set offchan (invalid band or channel)!\n");
-+ goto out;
-+ }
-+
-+ chandef.width = td->offchan_bw;
-+ chan = ieee80211_get_channel(hw->wiphy, freq);
-+ chandef.chan = chan;
-+ if (no_center)
-+ td->offchan_center_ch = mt7996_tm_get_center_chan(phy, &chandef);
-+ chandef.center_freq1 = ieee80211_channel_to_frequency(td->offchan_center_ch,
-+ NL80211_BAND_5GHZ);
-+ if (!cfg80211_chandef_valid(&chandef)) {
-+ ret = -EINVAL;
-+ dev_info(dev->mt76.dev, "Failed to set offchan, chandef is invalid!\n");
-+ goto out;
-+ }
-+
-+ memset(&dev->rdd2_chandef, 0, sizeof(struct cfg80211_chan_def));
-+
-+ ret = mt7996_mcu_rdd_background_enable(phy, &chandef);
-+
-+ if (ret)
-+ goto out;
-+
-+ dev->rdd2_phy = phy;
-+ dev->rdd2_chandef = chandef;
-+
-+ return 0;
-+
-+out:
-+ td->offchan_ch = 0;
-+ td->offchan_center_ch = 0;
-+ td->offchan_bw = 0;
-+
-+ return ret;
-+}
-+
-+static void
-+mt7996_tm_ipi_hist_ctrl(struct mt7996_phy *phy, struct mt7996_tm_rdd_ipi_ctrl *data, u8 cmd)
-+{
-+#define MT_IPI_RESET 0x830a5dfc
-+#define MT_IPI_RESET_MASK BIT(28)
-+#define MT_IPI_COUNTER_BASE 0x83041000
-+#define MT_IPI_COUNTER(idx) (MT_IPI_COUNTER_BASE + ((idx) * 4))
-+ struct mt7996_dev *dev = phy->dev;
-+ bool val;
-+ int i;
-+
-+ if (cmd == RDD_SET_IPI_HIST_RESET) {
-+ val = mt76_rr(dev, MT_IPI_RESET) & MT_IPI_RESET_MASK;
-+ mt76_rmw_field(dev, MT_IPI_RESET, MT_IPI_RESET_MASK, !val);
-+ return;
-+ }
-+
-+ for (i = 0; i < POWER_INDICATE_HIST_MAX; i++)
-+ data->ipi_hist_val[i] = mt76_rr(dev, MT_IPI_COUNTER(i));
-+}
-+
-+static void
-+mt7996_tm_ipi_work(struct work_struct *work)
-+{
-+#define PRECISION 100
-+ struct mt7996_phy *phy = container_of(work, struct mt7996_phy, ipi_work.work);
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_tm_rdd_ipi_ctrl data;
-+ u32 ipi_idx, ipi_free_count, ipi_percentage;
-+ u32 ipi_hist_count_th = 0, ipi_hist_total_count = 0;
-+ u32 self_idle_ratio, ipi_idle_ratio, channel_load;
-+ u32 *ipi_hist_data;
-+ const char *power_lower_bound, *power_upper_bound;
-+ static const char * const ipi_idx_to_power_bound[] = {
-+ [RDD_IPI_HIST_0] = "-92",
-+ [RDD_IPI_HIST_1] = "-89",
-+ [RDD_IPI_HIST_2] = "-86",
-+ [RDD_IPI_HIST_3] = "-83",
-+ [RDD_IPI_HIST_4] = "-80",
-+ [RDD_IPI_HIST_5] = "-75",
-+ [RDD_IPI_HIST_6] = "-70",
-+ [RDD_IPI_HIST_7] = "-65",
-+ [RDD_IPI_HIST_8] = "-60",
-+ [RDD_IPI_HIST_9] = "-55",
-+ [RDD_IPI_HIST_10] = "inf",
-+ };
-+
-+ memset(&data, 0, sizeof(data));
-+ mt7996_tm_ipi_hist_ctrl(phy, &data, RDD_IPI_HIST_ALL_CNT);
-+
-+ ipi_hist_data = data.ipi_hist_val;
-+ for (ipi_idx = 0; ipi_idx < POWER_INDICATE_HIST_MAX; ipi_idx++) {
-+ power_lower_bound = ipi_idx ? ipi_idx_to_power_bound[ipi_idx - 1] : "-inf";
-+ power_upper_bound = ipi_idx_to_power_bound[ipi_idx];
-+
-+ dev_info(dev->mt76.dev, "IPI %d (power range: (%s, %s] dBm): ipi count = %d\n",
-+ ipi_idx, power_lower_bound, power_upper_bound, ipi_hist_data[ipi_idx]);
-+
-+ if (td->ipi_threshold <= ipi_idx && ipi_idx <= RDD_IPI_HIST_10)
-+ ipi_hist_count_th += ipi_hist_data[ipi_idx];
-+
-+ ipi_hist_total_count += ipi_hist_data[ipi_idx];
-+ }
-+
-+ ipi_free_count = ipi_hist_data[RDD_IPI_FREE_RUN_CNT];
-+
-+ dev_info(dev->mt76.dev, "IPI threshold %d: ipi_hist_count_th = %d, ipi_free_count = %d\n",
-+ td->ipi_threshold, ipi_hist_count_th, ipi_free_count);
-+ dev_info(dev->mt76.dev, "TX assert time = %d [ms]\n", data.tx_assert_time / 1000);
-+
-+ /* calculate channel load = (self idle ratio - idle ratio) / self idle ratio */
-+ if (ipi_hist_count_th >= UINT_MAX / (100 * PRECISION))
-+ ipi_percentage = 100 * PRECISION *
-+ (ipi_hist_count_th / (100 * PRECISION)) /
-+ (ipi_free_count / (100 * PRECISION));
-+ else
-+ ipi_percentage = PRECISION * 100 * ipi_hist_count_th / ipi_free_count;
-+
-+ ipi_idle_ratio = ((100 * PRECISION) - ipi_percentage) / PRECISION;
-+
-+ self_idle_ratio = PRECISION * 100 *
-+ (td->ipi_period - (data.tx_assert_time / 1000)) /
-+ td->ipi_period / PRECISION;
-+
-+ if (self_idle_ratio < ipi_idle_ratio)
-+ channel_load = 0;
-+ else
-+ channel_load = self_idle_ratio - ipi_idle_ratio;
-+
-+ if (self_idle_ratio <= td->ipi_threshold) {
-+ dev_info(dev->mt76.dev, "band[%d]: self idle ratio = %d%%, idle ratio = %d%%\n",
-+ phy->mt76->band_idx, self_idle_ratio, ipi_idle_ratio);
-+ return;
-+ }
-+
-+ channel_load = (100 * channel_load) / self_idle_ratio;
-+ dev_info(dev->mt76.dev,
-+ "band[%d]: chan load = %d%%, self idle ratio = %d%%, idle ratio = %d%%\n",
-+ phy->mt76->band_idx, channel_load, self_idle_ratio, ipi_idle_ratio);
-+}
-+
-+static int
-+mt7996_tm_set_ipi(struct mt7996_phy *phy)
-+{
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+
-+ /* reset IPI CR */
-+ mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
-+
-+ cancel_delayed_work(&phy->ipi_work);
-+ ieee80211_queue_delayed_work(phy->mt76->hw, &phy->ipi_work,
-+ msecs_to_jiffies(td->ipi_period));
-+
-+ return 0;
-+}
-+
- static void
- mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- {
-@@ -860,6 +1078,14 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
-
- mt7996_tm_set(dev, func_idx, td->cfg.type);
- }
-+ if ((changed & BIT(TM_CHANGED_OFF_CHAN_CH)) &&
-+ (changed & BIT(TM_CHANGED_OFF_CHAN_BW)))
-+ mt7996_tm_set_offchan(phy, !(changed & BIT(TM_CHANGED_OFF_CHAN_CENTER_CH)));
-+ if ((changed & BIT(TM_CHANGED_IPI_THRESHOLD)) &&
-+ (changed & BIT(TM_CHANGED_IPI_PERIOD)))
-+ mt7996_tm_set_ipi(phy);
-+ if (changed & BIT(TM_CHANGED_IPI_RESET))
-+ mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
- }
-
- static int
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index 9bfb86f2..78662b2e 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -27,9 +27,15 @@ enum {
- FW_CDBW_8080MHZ,
- };
-
-+#define FIRST_CONTROL_CHAN_BITMAP_BW40 0x5555555
-+#define FIRST_CONTROL_CHAN_BITMAP_BW80 0x111111
-+#define FIRST_CONTROL_CHAN_BITMAP_BW160 0x100101
-+
- enum bw_mapping_method {
- BW_MAP_NL_TO_FW,
- BW_MAP_NL_TO_TM,
-+ BW_MAP_NL_TO_MHZ,
-+ BW_MAP_NL_TO_CONTROL_BITMAP_5G,
-
- NUM_BW_MAP,
- };
-@@ -312,4 +318,42 @@ struct mt7996_tm_rx_event {
- };
- } __packed;
-
-+enum {
-+ RDD_SET_IPI_CR_INIT, /* CR initialization */
-+ RDD_SET_IPI_HIST_RESET, /* Reset IPI histogram counter */
-+ RDD_SET_IDLE_POWER, /* Idle power info */
-+ RDD_SET_IPI_HIST_NUM
-+};
-+
-+enum {
-+ RDD_IPI_HIST_0, /* IPI count for power <= -92 (dBm) */
-+ RDD_IPI_HIST_1, /* IPI count for -92 < power <= -89 (dBm) */
-+ RDD_IPI_HIST_2, /* IPI count for -89 < power <= -86 (dBm) */
-+ RDD_IPI_HIST_3, /* IPI count for -86 < power <= -83 (dBm) */
-+ RDD_IPI_HIST_4, /* IPI count for -83 < power <= -80 (dBm) */
-+ RDD_IPI_HIST_5, /* IPI count for -80 < power <= -75 (dBm) */
-+ RDD_IPI_HIST_6, /* IPI count for -75 < power <= -70 (dBm) */
-+ RDD_IPI_HIST_7, /* IPI count for -70 < power <= -65 (dBm) */
-+ RDD_IPI_HIST_8, /* IPI count for -65 < power <= -60 (dBm) */
-+ RDD_IPI_HIST_9, /* IPI count for -60 < power <= -55 (dBm) */
-+ RDD_IPI_HIST_10, /* IPI count for -55 < power (dBm) */
-+ RDD_IPI_FREE_RUN_CNT, /* IPI count for counter++ per 8 us */
-+ RDD_IPI_HIST_ALL_CNT, /* Get all IPI */
-+ RDD_IPI_HIST_0_TO_10_CNT, /* Get IPI histogram 0 to 10 */
-+ RDD_IPI_HIST_2_TO_10_CNT, /* Get IPI histogram 2 to 10 */
-+ RDD_TX_ASSERT_TIME, /* Get band 1 TX assert time */
-+ RDD_IPI_HIST_NUM
-+};
-+
-+#define POWER_INDICATE_HIST_MAX RDD_IPI_FREE_RUN_CNT
-+#define IPI_HIST_TYPE_NUM (POWER_INDICATE_HIST_MAX + 1)
-+
-+struct mt7996_tm_rdd_ipi_ctrl {
-+ u8 ipi_hist_idx;
-+ u8 band_idx;
-+ u8 rsv[2];
-+ __le32 ipi_hist_val[IPI_HIST_TYPE_NUM];
-+ __le32 tx_assert_time; /* unit: us */
-+} __packed;
-+
- #endif
-diff --git a/testmode.c b/testmode.c
-index cd8cb655..69147f86 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -27,6 +27,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
- [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
- [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
- [MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED },
-+ [MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_OFF_CH_SCAN_PATH] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
-+ [MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
-+ [MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
- };
- EXPORT_SYMBOL_GPL(mt76_tm_policy);
-
-@@ -499,6 +506,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- if (tb[MT76_TM_ATTR_TX_RATE_IDX])
- td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]);
-
-+ if (tb[MT76_TM_ATTR_IPI_PERIOD])
-+ td->ipi_period = nla_get_u32(tb[MT76_TM_ATTR_IPI_PERIOD]);
-+
- if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode,
- 0, MT76_TM_TX_MODE_MAX) ||
- mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss,
-@@ -514,7 +524,14 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- &td->tx_duty_cycle, 0, 99) ||
- mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
- &td->tx_power_control, 0, 1) ||
-- mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH], &td->offchan_ch, 36, 196) ||
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH], &td->offchan_center_ch,
-+ 36, 196) ||
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
-+ &td->offchan_bw, NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_THRESHOLD], &td->ipi_threshold, 0, 10) ||
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_RESET], &td->ipi_reset, 0, 1))
- goto out;
-
- if (tb[MT76_TM_ATTR_TX_LENGTH]) {
-@@ -720,6 +737,9 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
- nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
- nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
-+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CH, td->offchan_ch) ||
-+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH, td->offchan_center_ch) ||
-+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_BW, td->offchan_bw) ||
- (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
- nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
- (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
-diff --git a/tools/fields.c b/tools/fields.c
-index b0122763..77696ce7 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -35,6 +35,15 @@ static const char * const testmode_tx_mode[] = {
- [MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
- };
-
-+static const char * const testmode_offchan_bw[] = {
-+ [NL80211_CHAN_WIDTH_20_NOHT] = "NOHT",
-+ [NL80211_CHAN_WIDTH_20] = "20",
-+ [NL80211_CHAN_WIDTH_40] = "40",
-+ [NL80211_CHAN_WIDTH_80] = "80",
-+ [NL80211_CHAN_WIDTH_80P80] = "80p80",
-+ [NL80211_CHAN_WIDTH_160] = "160",
-+};
-+
- static void print_enum(const struct tm_field *field, struct nlattr *attr)
- {
- unsigned int i = nla_get_u8(attr);
-@@ -390,6 +399,12 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- FIELD(u8, AID, "aid"),
- FIELD(u8, RU_ALLOC, "ru_alloc"),
- FIELD(u8, RU_IDX, "ru_idx"),
-+ FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
-+ FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
-+ FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
-+ FIELD(u8, IPI_THRESHOLD, "ipi_threshold"),
-+ FIELD(u32, IPI_PERIOD, "ipi_period"),
-+ FIELD(u8, IPI_RESET, "ipi_reset"),
- FIELD_MAC(MAC_ADDRS, "mac_addrs"),
- FIELD_NESTED_RO(STATS, stats, "",
- .print_extra = print_extra_stats),
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1012-mtk-wifi-mt76-mt7996-add-mu-vendor-command-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1012-mtk-wifi-mt76-mt7996-add-mu-vendor-command-support.patch
deleted file mode 100644
index 8867fe8..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1012-mtk-wifi-mt76-mt7996-add-mu-vendor-command-support.patch
+++ /dev/null
@@ -1,308 +0,0 @@
-From 940f89e5dd90f5554354036614dce057ade743e1 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 13 Dec 2022 15:17:43 +0800
-Subject: [PATCH 1012/1044] mtk: wifi: mt76: mt7996: add mu vendor command
- support
-
-mtk: wifi: mt76: fix muru_onoff as all enabled by default
-
-Fix muru_onoff default value as 0xF, which means all MU & RU are
-enabled. The purpose of this commit is to align muru_onoff value with
-hostapd and mt76 driver
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/Makefile | 3 +-
- mt7996/init.c | 9 ++++++
- mt7996/mcu.c | 37 ++++++++++++++++++---
- mt7996/mcu.h | 12 +++++++
- mt7996/mt7996.h | 7 ++++
- mt7996/vendor.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 22 +++++++++++++
- 7 files changed, 169 insertions(+), 6 deletions(-)
- create mode 100644 mt7996/vendor.c
- create mode 100644 mt7996/vendor.h
-
-diff --git a/mt7996/Makefile b/mt7996/Makefile
-index 7bb17f44..6643c7a3 100644
---- a/mt7996/Makefile
-+++ b/mt7996/Makefile
-@@ -1,11 +1,12 @@
- # SPDX-License-Identifier: ISC
- EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
- EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
-+EXTRA_CFLAGS += -DCONFIG_MTK_VENDOR
-
- obj-$(CONFIG_MT7996E) += mt7996e.o
-
- mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
-- debugfs.o mmio.o
-+ debugfs.o mmio.o vendor.o
-
- mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
- mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
-diff --git a/mt7996/init.c b/mt7996/init.c
-index c135da9c..fba61c01 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -368,6 +368,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
-
- phy->slottime = 9;
- phy->beacon_rate = -1;
-+ phy->muru_onoff = OFDMA_UL | OFDMA_DL | MUMIMO_DL | MUMIMO_UL;
-
- hw->sta_data_size = sizeof(struct mt7996_sta);
- hw->vif_data_size = sizeof(struct mt7996_vif);
-@@ -616,6 +617,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- if (ret)
- goto error;
-
-+#ifdef CONFIG_MTK_VENDOR
-+ mt7996_vendor_register(phy);
-+#endif
-+
- ret = mt76_register_phy(mphy, true, mt76_rates,
- ARRAY_SIZE(mt76_rates));
- if (ret)
-@@ -1401,6 +1406,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
- dev->mt76.test_ops = &mt7996_testmode_ops;
- #endif
-
-+#ifdef CONFIG_MTK_VENDOR
-+ mt7996_vendor_register(&dev->phy);
-+#endif
-+
- ret = mt76_register_device(&dev->mt76, true, mt76_rates,
- ARRAY_SIZE(mt76_rates));
- if (ret)
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 0ee17ff9..8348d22a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1373,6 +1373,8 @@ static void
- mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
- {
-+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+ struct mt7996_phy *phy = mvif->phy;
- struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
- struct sta_rec_muru *muru;
- struct tlv *tlv;
-@@ -1384,11 +1386,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
-
- muru = (struct sta_rec_muru *)tlv;
-- muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
-- vif->bss_conf.he_mu_beamformer ||
-- vif->bss_conf.vht_mu_beamformer ||
-- vif->bss_conf.vht_mu_beamformee;
-- muru->cfg.ofdma_dl_en = true;
-+ muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
-+ vif->bss_conf.he_mu_beamformer ||
-+ vif->bss_conf.vht_mu_beamformer ||
-+ vif->bss_conf.vht_mu_beamformee) &&
-+ !!(phy->muru_onoff & MUMIMO_DL);
-+ muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
-+ muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
-+ muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
-
- if (sta->deflink.vht_cap.vht_supported)
- muru->mimo_dl.vht_mu_bfee =
-@@ -4919,3 +4924,25 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
- return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
- &req, sizeof(req), false);
- }
-+
-+#ifdef CONFIG_MTK_VENDOR
-+void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
-+{
-+ u8 mode, val;
-+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+ struct mt7996_phy *phy = mvif->phy;
-+
-+ mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
-+ val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
-+
-+ switch (mode) {
-+ case RATE_PARAM_AUTO_MU:
-+ if (val < 0 || val > 15) {
-+ printk("Wrong value! The value is between 0-15.\n");
-+ break;
-+ }
-+ phy->muru_onoff = val;
-+ break;
-+ }
-+}
-+#endif
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 887d9b49..68bf82fc 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -754,8 +754,20 @@ enum {
- RATE_PARAM_FIXED_MCS,
- RATE_PARAM_FIXED_GI = 11,
- RATE_PARAM_AUTO = 20,
-+#ifdef CONFIG_MTK_VENDOR
-+ RATE_PARAM_AUTO_MU = 32,
-+#endif
- };
-
-+#define RATE_CFG_MODE GENMASK(15, 8)
-+#define RATE_CFG_VAL GENMASK(7, 0)
-+
-+/* MURU */
-+#define OFDMA_DL BIT(0)
-+#define OFDMA_UL BIT(1)
-+#define MUMIMO_DL BIT(2)
-+#define MUMIMO_UL BIT(3)
-+
- enum {
- BF_SOUNDING_ON = 1,
- BF_HW_EN_UPDATE = 17,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 2daca449..d0b425da 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -295,6 +295,8 @@ struct mt7996_phy {
-
- struct mt7996_scs_ctrl scs_ctrl;
-
-+ u8 muru_onoff;
-+
- #ifdef CONFIG_NL80211_TESTMODE
- struct {
- u32 *reg_backup;
-@@ -733,6 +735,11 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- bool hif2, int *irq);
- u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
-
-+#ifdef CONFIG_MTK_VENDOR
-+void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
-+void mt7996_vendor_register(struct mt7996_phy *phy);
-+#endif
-+
- #ifdef CONFIG_MTK_DEBUG
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-new file mode 100644
-index 00000000..b5ecbdf1
---- /dev/null
-+++ b/mt7996/vendor.c
-@@ -0,0 +1,85 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2020, MediaTek Inc. All rights reserved.
-+ */
-+
-+#include <net/netlink.h>
-+
-+#include "mt7996.h"
-+#include "mcu.h"
-+#include "vendor.h"
-+#include "mtk_mcu.h"
-+
-+static const struct nla_policy
-+mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
-+ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+};
-+
-+static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
-+ struct wireless_dev *wdev,
-+ const void *data,
-+ int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
-+ int err;
-+ u8 val8;
-+ u32 val32 = 0;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
-+ mu_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
-+ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
-+ FIELD_PREP(RATE_CFG_VAL, val8);
-+ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+ mt7996_set_wireless_vif, &val32);
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ struct sk_buff *skb, const void *data, int data_len,
-+ unsigned long *storage)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ int len = 0;
-+
-+ if (*storage == 1)
-+ return -ENOENT;
-+ *storage = 1;
-+
-+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
-+ return -ENOMEM;
-+ len += 1;
-+
-+ return len;
-+}
-+
-+static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_MU_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_mu_ctrl,
-+ .dumpit = mt7996_vendor_mu_ctrl_dump,
-+ .policy = mu_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
-+ },
-+};
-+
-+void mt7996_vendor_register(struct mt7996_phy *phy)
-+{
-+ phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
-+ phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
-+}
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-new file mode 100644
-index 00000000..8ac3ba8e
---- /dev/null
-+++ b/mt7996/vendor.h
-@@ -0,0 +1,22 @@
-+#ifndef __MT7996_VENDOR_H
-+#define __MT7996_VENDOR_H
-+
-+#define MTK_NL80211_VENDOR_ID 0x0ce7
-+
-+enum mtk_nl80211_vendor_subcmds {
-+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+};
-+
-+enum mtk_vendor_attr_mu_ctrl {
-+ MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
-+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-+ MTK_VENDOR_ATTR_MU_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
-+};
-+
-+#endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1013-mtk-wifi-mt76-mt7996-Add-air-monitor-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1013-mtk-wifi-mt76-mt7996-Add-air-monitor-support.patch
deleted file mode 100644
index 8f43d6c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1013-mtk-wifi-mt76-mt7996-Add-air-monitor-support.patch
+++ /dev/null
@@ -1,565 +0,0 @@
-From aaf10c9fa1670faa83c7fade6f970c92239c0853 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 26 Apr 2023 04:40:05 +0800
-Subject: [PATCH 1013/1044] mtk: wifi: mt76: mt7996: Add air monitor support
-
----
- mt76_connac_mcu.h | 1 +
- mt7996/mac.c | 4 +
- mt7996/main.c | 4 +
- mt7996/mt7996.h | 35 +++++
- mt7996/vendor.c | 362 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 39 +++++
- 6 files changed, 445 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 3c82c05d..062268d6 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1251,6 +1251,7 @@ enum {
- MCU_UNI_CMD_REG_ACCESS = 0x0d,
- MCU_UNI_CMD_CHIP_CONFIG = 0x0e,
- MCU_UNI_CMD_POWER_CTRL = 0x0f,
-+ MCU_UNI_CMD_CFG_SMESH = 0x10,
- MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
- MCU_UNI_CMD_SER = 0x13,
- MCU_UNI_CMD_TWT = 0x14,
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index c9f45abe..d55e5a76 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -679,6 +679,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
- *qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
- }
-+#ifdef CONFIG_MTK_VENDOR
-+ if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
-+ mt7996_vendor_amnt_fill_rx(phy, skb);
-+#endif
- } else {
- status->flag |= RX_FLAG_8023;
- mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 07a14917..478ca7ce 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -737,6 +737,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- mt7996_mac_wtbl_update(dev, idx,
- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-
-+#ifdef CONFIG_MTK_VENDOR
-+ mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
-+#endif
-+
- ret = mt7996_mcu_add_sta(dev, vif, sta, true);
- if (ret)
- return ret;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index d0b425da..09ce3c35 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -259,6 +259,34 @@ struct mt7996_wed_rro_session_id {
- u16 id;
- };
-
-+#ifdef CONFIG_MTK_VENDOR
-+#define MT7996_AIR_MONITOR_MAX_ENTRY 16
-+#define MT7996_AIR_MONITOR_MAX_GROUP (MT7996_AIR_MONITOR_MAX_ENTRY >> 1)
-+
-+struct mt7996_air_monitor_group {
-+ bool enable;
-+ bool used[2];
-+};
-+
-+struct mt7996_air_monitor_entry {
-+ bool enable;
-+
-+ u8 group_idx;
-+ u8 group_used_idx;
-+ u8 muar_idx;
-+ u8 addr[ETH_ALEN];
-+ u32 last_seen;
-+ s8 rssi[4];
-+ struct ieee80211_sta *sta;
-+};
-+
-+struct mt7996_air_monitor_ctrl {
-+ u8 enable;
-+ struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
-+ struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
-+};
-+#endif
-+
- struct mt7996_phy {
- struct mt76_phy *mt76;
- struct mt7996_dev *dev;
-@@ -311,6 +339,10 @@ struct mt7996_phy {
- u8 spe_idx;
- } test;
- #endif
-+#ifdef CONFIG_MTK_VENDOR
-+ spinlock_t amnt_lock;
-+ struct mt7996_air_monitor_ctrl amnt_ctrl;
-+#endif
- };
-
- struct mt7996_dev {
-@@ -738,6 +770,9 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
- #ifdef CONFIG_MTK_VENDOR
- void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
- void mt7996_vendor_register(struct mt7996_phy *phy);
-+void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
-+int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
-+ struct ieee80211_sta *sta);
- #endif
-
- #ifdef CONFIG_MTK_DEBUG
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index b5ecbdf1..f3b089d7 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -16,6 +16,32 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
- [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
- };
-
-+static const struct nla_policy
-+amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
-+ [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
-+};
-+
-+static const struct nla_policy
-+amnt_set_policy[NUM_MTK_VENDOR_ATTRS_AMNT_SET] = {
-+ [MTK_VENDOR_ATTR_AMNT_SET_INDEX] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_AMNT_SET_MACADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
-+};
-+
-+static const struct nla_policy
-+amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
-+};
-+
-+struct mt7996_amnt_data {
-+ u8 idx;
-+ u8 addr[ETH_ALEN];
-+ s8 rssi[4];
-+ u32 last_seen;
-+};
-+
- static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- const void *data,
-@@ -63,6 +89,328 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- return len;
- }
-
-+void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
-+{
-+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
-+ struct mt7996_air_monitor_ctrl *ctrl = &phy->amnt_ctrl;
-+ struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
-+ __le16 fc = hdr->frame_control;
-+ u8 addr[ETH_ALEN];
-+ int i;
-+
-+ if (!ieee80211_has_fromds(fc))
-+ ether_addr_copy(addr, hdr->addr2);
-+ else if (ieee80211_has_tods(fc))
-+ ether_addr_copy(addr, hdr->addr4);
-+ else
-+ ether_addr_copy(addr, hdr->addr3);
-+
-+ spin_lock_bh(&phy->amnt_lock);
-+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) {
-+ struct mt7996_air_monitor_entry *entry;
-+
-+ if (ether_addr_equal(addr, ctrl->entry[i].addr)) {
-+ entry = &ctrl->entry[i];
-+ entry->rssi[0] = status->chain_signal[0];
-+ entry->rssi[1] = status->chain_signal[1];
-+ entry->rssi[2] = status->chain_signal[2];
-+ entry->rssi[3] = status->chain_signal[3];
-+ entry->last_seen = jiffies;
-+ break;
-+ }
-+ }
-+ spin_unlock_bh(&phy->amnt_lock);
-+}
-+
-+static int
-+mt7996_vendor_smesh_ctrl(struct mt7996_phy *phy, u8 write,
-+ u8 enable, u8 *value)
-+{
-+#define UNI_CMD_SMESH_PARAM 0
-+ struct mt7996_dev *dev = phy->dev;
-+ struct smesh_param {
-+ __le16 tag;
-+ __le16 length;
-+
-+ u8 enable;
-+ bool a2;
-+ bool a1;
-+ bool data;
-+ bool mgnt;
-+ bool ctrl;
-+ u8 padding[2];
-+ } req = {
-+ .tag = cpu_to_le16(UNI_CMD_SMESH_PARAM),
-+ .length = cpu_to_le16(sizeof(req) - 4),
-+
-+ .enable = enable,
-+ .a2 = true,
-+ .a1 = true,
-+ .data = true,
-+ .mgnt = false,
-+ .ctrl = false,
-+ };
-+ struct smesh_param *res;
-+ struct sk_buff *skb;
-+ int ret = 0;
-+
-+ if (!value)
-+ return -EINVAL;
-+
-+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD(CFG_SMESH),
-+ &req, sizeof(req), !write, &skb);
-+
-+ if (ret || write)
-+ return ret;
-+
-+ res = (struct smesh_param *) skb->data;
-+
-+ *value = res->enable;
-+
-+ dev_kfree_skb(skb);
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_vendor_amnt_muar(struct mt7996_phy *phy, u8 muar_idx, u8 *addr)
-+{
-+#define UNI_CMD_MUAR_ENTRY 2
-+ struct mt7996_dev *dev = phy->dev;
-+ struct muar_entry {
-+ __le16 tag;
-+ __le16 length;
-+
-+ bool smesh;
-+ u8 hw_bss_index;
-+ u8 muar_idx;
-+ u8 entry_add;
-+ u8 mac_addr[6];
-+ u8 padding[2];
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_CMD_MUAR_ENTRY),
-+ .length = cpu_to_le16(sizeof(req) - 4),
-+
-+ .smesh = true,
-+ .hw_bss_index = phy != &dev->phy,
-+ .muar_idx = muar_idx,
-+ .entry_add = 1,
-+ };
-+
-+ ether_addr_copy(req.mac_addr, addr);
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req,
-+ sizeof(req), true);
-+}
-+
-+static int
-+mt7996_vendor_amnt_set_en(struct mt7996_phy *phy, u8 enable)
-+{
-+ u8 status;
-+ int ret;
-+
-+ ret = mt7996_vendor_smesh_ctrl(phy, 0, enable, &status);
-+ if (ret)
-+ return ret;
-+
-+ if (status == enable)
-+ return 0;
-+
-+ ret = mt7996_vendor_smesh_ctrl(phy, 1, enable, &status);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_vendor_amnt_set_addr(struct mt7996_phy *phy, u8 index, u8 *addr)
-+{
-+ struct mt7996_air_monitor_ctrl *amnt_ctrl = &phy->amnt_ctrl;
-+ struct mt7996_air_monitor_group *group;
-+ struct mt7996_air_monitor_entry *entry;
-+ int ret, i, j;
-+
-+ if (index >= MT7996_AIR_MONITOR_MAX_ENTRY)
-+ return -1;
-+
-+ spin_lock_bh(&phy->amnt_lock);
-+ entry = &amnt_ctrl->entry[index];
-+ if (!is_zero_ether_addr(addr)) {
-+ if (entry->enable == false) {
-+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_GROUP; i++) {
-+ group = &(amnt_ctrl->group[i]);
-+ if (group->used[0] == false)
-+ j = 0;
-+ else if (group->used[1] == false)
-+ j = 1;
-+ else
-+ continue;
-+
-+ group->enable = true;
-+ group->used[j] = true;
-+ entry->enable = true;
-+ entry->group_idx = i;
-+ entry->group_used_idx = j;
-+ entry->muar_idx = 32 + 4 * i + 2 * j;
-+ break;
-+ }
-+ }
-+ } else {
-+ group = &(amnt_ctrl->group[entry->group_idx]);
-+
-+ group->used[entry->group_used_idx] = false;
-+ if (group->used[0] == false && group->used[1] == false)
-+ group->enable = false;
-+
-+ entry->enable = false;
-+ }
-+ ether_addr_copy(entry->addr, addr);
-+ amnt_ctrl->enable &= ~(1 << entry->group_idx);
-+ amnt_ctrl->enable |= entry->enable << entry->group_idx;
-+ spin_unlock_bh(&phy->amnt_lock);
-+
-+ ret = mt7996_vendor_amnt_muar(phy, entry->muar_idx, addr);
-+ if (ret)
-+ return ret;
-+
-+ return mt7996_vendor_amnt_set_en(phy, amnt_ctrl->enable);
-+}
-+
-+static int
-+mt7966_vendor_amnt_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ const void *data, int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_SET];
-+ u8 index = 0;
-+ u8 mac_addr[ETH_ALEN];
-+ int err;
-+
-+ err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
-+ amnt_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET])
-+ return -EINVAL;
-+
-+ err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_SET_MAX,
-+ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET], amnt_set_policy, NULL);
-+
-+ if (!tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX] ||
-+ !tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR])
-+ return -EINVAL;
-+
-+ index = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX]);
-+ memcpy(mac_addr, nla_data(tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]), ETH_ALEN);
-+
-+ return mt7996_vendor_amnt_set_addr(phy, index, mac_addr);
-+}
-+
-+int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
-+ struct ieee80211_sta *sta)
-+{
-+ u8 zero[ETH_ALEN] = {};
-+ int i;
-+
-+ if (!phy->amnt_ctrl.enable)
-+ return 0;
-+
-+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
-+ if (ether_addr_equal(sta->addr, phy->amnt_ctrl.entry[i].addr))
-+ return mt7996_vendor_amnt_set_addr(phy, i, zero);
-+ return 0;
-+}
-+
-+static int
-+mt7996_amnt_dump(struct mt7996_phy *phy, struct sk_buff *skb,
-+ u8 amnt_idx, int *attrtype)
-+{
-+ struct mt7996_air_monitor_entry *entry;
-+ struct mt7996_amnt_data data;
-+ u32 last_seen = 0;
-+
-+ spin_lock_bh(&phy->amnt_lock);
-+ entry = &phy->amnt_ctrl.entry[amnt_idx];
-+ if (entry->enable == 0) {
-+ spin_unlock_bh(&phy->amnt_lock);
-+ return 0;
-+ }
-+
-+ last_seen = jiffies_to_msecs(jiffies - entry->last_seen);
-+ ether_addr_copy(data.addr, entry->addr);
-+ data.rssi[0] = entry->rssi[0];
-+ data.rssi[1] = entry->rssi[1];
-+ data.rssi[2] = entry->rssi[2];
-+ data.rssi[3] = entry->rssi[3];
-+ spin_unlock_bh(&phy->amnt_lock);
-+
-+ data.idx = amnt_idx;
-+ data.last_seen = last_seen;
-+
-+ nla_put(skb, (*attrtype)++, sizeof(struct mt7996_amnt_data), &data);
-+
-+ return 1;
-+}
-+
-+static int
-+mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ struct sk_buff *skb, const void *data, int data_len,
-+ unsigned long *storage)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
-+ void *a, *b;
-+ int err = 0, attrtype = 0, i, len = 0;
-+ u8 amnt_idx;
-+
-+ if (*storage == 1)
-+ return -ENOENT;
-+ *storage = 1;
-+
-+ err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
-+ amnt_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
-+ return -EINVAL;
-+
-+ err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_DUMP_MAX,
-+ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP],
-+ amnt_dump_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX])
-+ return -EINVAL;
-+
-+ amnt_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]);
-+
-+ a = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP);
-+ b = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_DUMP_RESULT);
-+
-+ if (amnt_idx != 0xff) {
-+ len += mt7996_amnt_dump(phy, skb, amnt_idx, &attrtype);
-+ } else {
-+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
-+ len += mt7996_amnt_dump(phy, skb, i, &attrtype);
-+ }
-+
-+ nla_nest_end(skb, b);
-+
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_AMNT_DUMP_LEN, len);
-+
-+ nla_nest_end(skb, a);
-+
-+ return len + 1;
-+}
-+
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
- .info = {
-@@ -76,10 +424,24 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = mu_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7966_vendor_amnt_ctrl,
-+ .dumpit = mt7966_vendor_amnt_ctrl_dump,
-+ .policy = amnt_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
- {
- phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
- phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
-+
-+ spin_lock_init(&phy->amnt_lock);
- }
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 8ac3ba8e..2078cafa 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -4,6 +4,7 @@
- #define MTK_NL80211_VENDOR_ID 0x0ce7
-
- enum mtk_nl80211_vendor_subcmds {
-+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- };
-
-@@ -19,4 +20,42 @@ enum mtk_vendor_attr_mu_ctrl {
- NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
- };
-
-+enum mtk_vendor_attr_mnt_ctrl {
-+ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_CTRL_SET,
-+ MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
-+ MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_set {
-+ MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_SET_INDEX,
-+ MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_SET,
-+ MTK_VENDOR_ATTR_AMNT_SET_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_dump {
-+ MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
-+};
-+
-+
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1014-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1014-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch
deleted file mode 100644
index ad4b610..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1014-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From c8dbaeeaff01c35c7e27ef0958d812338e50c054 Mon Sep 17 00:00:00 2001
-From: mtk23510 <rudra.shahi@mediatek.com>
-Date: Fri, 24 Mar 2023 19:18:53 +0800
-Subject: [PATCH 1014/1044] mtk: wifi: mt76: mt7996: add driver support for
- wpa3 ocv and bp mt76
-
-Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
----
- mt7996/init.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index fba61c01..1498787f 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -391,6 +391,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
-
-+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
-+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
- if (!mdev->dev->of_node ||
- !of_property_read_bool(mdev->dev->of_node,
- "mediatek,disable-radar-background"))
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1015-mtk-wifi-mt76-mt7996-add-vendor-cmd-to-get-available.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1015-mtk-wifi-mt76-mt7996-add-vendor-cmd-to-get-available.patch
deleted file mode 100644
index dc1df69..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1015-mtk-wifi-mt76-mt7996-add-vendor-cmd-to-get-available.patch
+++ /dev/null
@@ -1,107 +0,0 @@
-From 109e8c6a16d948bb2b0b685a8d102e91bb89e4ed Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Wed, 3 May 2023 05:08:07 +0800
-Subject: [PATCH 1015/1044] mtk: wifi: mt76: mt7996: add vendor cmd to get
- available color bitmap
-
-Add a vendor cmd to notify user space available color bitmap.
-The OBSS BSS color bitmap is maintained in mac80211, so mt76 will make use of that.
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
----
- mt7996/vendor.c | 36 ++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 11 +++++++++++
- 2 files changed, 47 insertions(+)
-
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index f3b089d7..39101577 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -35,6 +35,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
- [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
- };
-
-+static struct nla_policy
-+bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
-+ [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -410,6 +415,26 @@ mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- return len + 1;
- }
-
-+static int
-+mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ struct sk_buff *skb, const void *data, int data_len,
-+ unsigned long *storage)
-+{
-+ struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
-+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-+ int len = 0;
-+
-+ if (*storage == 1)
-+ return -ENOENT;
-+ *storage = 1;
-+
-+ if (nla_put_u64_64bit(skb, MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+ ~bss_conf->used_color_bitmap, NL80211_ATTR_PAD))
-+ return -ENOMEM;
-+ len += 1;
-+
-+ return len;
-+}
-
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
-@@ -436,6 +461,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = amnt_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .dumpit = mt7996_vendor_bss_color_ctrl_dump,
-+ .policy = bss_color_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 2078cafa..eec9e74a 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -6,6 +6,7 @@
- enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
-
- enum mtk_vendor_attr_mu_ctrl {
-@@ -57,5 +58,15 @@ enum mtk_vendor_attr_mnt_dump {
- NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
- };
-
-+enum mtk_vendor_attr_bss_color_ctrl {
-+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
-+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
-+};
-
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1016-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1016-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
deleted file mode 100644
index edd7ca9..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1016-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
+++ /dev/null
@@ -1,172 +0,0 @@
-From 69f8be02d97e911e0a844ac5a1a239a848d41930 Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Fri, 19 May 2023 14:56:07 +0800
-Subject: [PATCH 1016/1044] mtk: wifi: mt76: mt7996: add debugfs for fw
- coredump.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- mt7996/debugfs.c | 19 +++++++++++++++++--
- mt7996/mac.c | 28 +++++++++++++++++++++++++---
- mt7996/mcu.h | 4 ++++
- mt7996/mt7996.h | 10 ++++++++++
- 4 files changed, 56 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index ea78166b..7a03de12 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -84,6 +84,8 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
- * 7: trigger & enable system error L4 mdp recovery.
- * 8: trigger & enable system error full recovery.
- * 9: trigger firmware crash.
-+ * 10: trigger grab wa firmware coredump.
-+ * 11: trigger grab wm firmware coredump.
- */
- case UNI_CMD_SER_QUERY:
- ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band);
-@@ -105,15 +107,25 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
- /* enable full chip reset */
- case UNI_CMD_SER_SET_RECOVER_FULL:
- mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
-- dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
-+ dev->recovery.state |= MT_MCU_CMD_WM_WDT;
- mt7996_reset(dev);
- break;
-
- /* WARNING: trigger firmware crash */
- case UNI_CMD_SER_SET_SYSTEM_ASSERT:
-+ // trigger wm assert exception
- ret = mt7996_mcu_trigger_assert(dev);
- if (ret)
- return ret;
-+ // trigger wa assert exception
-+ mt76_wr(dev, 0x89098108, 0x20);
-+ mt76_wr(dev, 0x89098118, 0x20);
-+ break;
-+ case UNI_CMD_SER_FW_COREDUMP_WA:
-+ mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WA);
-+ break;
-+ case UNI_CMD_SER_FW_COREDUMP_WM:
-+ mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WM);
- break;
- default:
- break;
-@@ -160,7 +172,10 @@ mt7996_sys_recovery_get(struct file *file, char __user *user_buf,
- "8: trigger system error full recovery\n");
- desc += scnprintf(buff + desc, bufsz - desc,
- "9: trigger firmware crash\n");
--
-+ desc += scnprintf(buff + desc, bufsz - desc,
-+ "10: trigger grab wa firmware coredump\n");
-+ desc += scnprintf(buff + desc, bufsz - desc,
-+ "11: trigger grab wm firmware coredump\n");
- /* SER statistics */
- desc += scnprintf(buff + desc, bufsz - desc,
- "\nlet's dump firmware SER statistics...\n");
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index d55e5a76..1c1b3eb5 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2080,15 +2080,36 @@ void mt7996_mac_dump_work(struct work_struct *work)
- struct mt7996_dev *dev;
-
- dev = container_of(work, struct mt7996_dev, dump_work);
-- if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
-+ if (dev->dump_state == MT7996_COREDUMP_MANUAL_WA ||
-+ READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
- mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
-
-- if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
-+ if (dev->dump_state == MT7996_COREDUMP_MANUAL_WM ||
-+ READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
- mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
-
-- queue_work(dev->mt76.wq, &dev->reset_work);
-+ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK)
-+ queue_work(dev->mt76.wq, &dev->reset_work);
-+
-+ dev->dump_state = MT7996_COREDUMP_IDLE;
- }
-
-+void mt7996_coredump(struct mt7996_dev *dev, u8 state)
-+{
-+ if (state == MT7996_COREDUMP_IDLE ||
-+ state >= __MT7996_COREDUMP_TYPE_MAX)
-+ return;
-+
-+ if (dev->dump_state != MT7996_COREDUMP_IDLE)
-+ return;
-+
-+ dev->dump_state = state;
-+ dev_info(dev->mt76.dev, "%s attempting grab coredump\n",
-+ wiphy_name(dev->mt76.hw->wiphy));
-+
-+ queue_work(dev->mt76.wq, &dev->dump_work);
-+ }
-+
- void mt7996_reset(struct mt7996_dev *dev)
- {
- if (!dev->recovery.hw_init_done)
-@@ -2106,6 +2127,7 @@ void mt7996_reset(struct mt7996_dev *dev)
-
- mt7996_irq_disable(dev, MT_INT_MCU_CMD);
- queue_work(dev->mt76.wq, &dev->dump_work);
-+ mt7996_coredump(dev, MT7996_COREDUMP_AUTO);
- return;
- }
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 68bf82fc..35f757dc 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -956,7 +956,11 @@ enum {
- UNI_CMD_SER_SET_RECOVER_L3_BF,
- UNI_CMD_SER_SET_RECOVER_L4_MDP,
- UNI_CMD_SER_SET_RECOVER_FULL,
-+ /* fw assert */
- UNI_CMD_SER_SET_SYSTEM_ASSERT,
-+ /* coredump */
-+ UNI_CMD_SER_FW_COREDUMP_WA,
-+ UNI_CMD_SER_FW_COREDUMP_WM,
- /* action */
- UNI_CMD_SER_ENABLE = 1,
- UNI_CMD_SER_SET,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 09ce3c35..dd9aa9e2 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -139,6 +139,14 @@ enum mt7996_ram_type {
- __MT7996_RAM_TYPE_MAX,
- };
-
-+enum mt7996_coredump_state {
-+ MT7996_COREDUMP_IDLE = 0,
-+ MT7996_COREDUMP_MANUAL_WA,
-+ MT7996_COREDUMP_MANUAL_WM,
-+ MT7996_COREDUMP_AUTO,
-+ __MT7996_COREDUMP_TYPE_MAX,
-+};
-+
- enum mt7996_txq_id {
- MT7996_TXQ_FWDL = 16,
- MT7996_TXQ_MCU_WM,
-@@ -388,6 +396,7 @@ struct mt7996_dev {
-
- /* protects coredump data */
- struct mutex dump_mutex;
-+ u8 dump_state;
- #ifdef CONFIG_DEV_COREDUMP
- struct {
- struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
-@@ -573,6 +582,7 @@ void mt7996_init_txpower(struct mt7996_phy *phy);
- int mt7996_txbf_init(struct mt7996_dev *dev);
- int mt7996_get_chip_sku(struct mt7996_dev *dev);
- void mt7996_reset(struct mt7996_dev *dev);
-+void mt7996_coredump(struct mt7996_dev *dev, u8 state);
- int mt7996_run(struct ieee80211_hw *hw);
- int mt7996_mcu_init(struct mt7996_dev *dev);
- int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1017-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1017-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch
deleted file mode 100644
index f9e72ad..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1017-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch
+++ /dev/null
@@ -1,163 +0,0 @@
-From 1be676a268fc923a68d8b0338b104f1ac76e0838 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Mon, 25 Dec 2023 15:17:49 +0800
-Subject: [PATCH 1017/1044] mtk: wifi: mt76: mt7996: Add mt7992 coredump
- support
-
-1. Add mt7992 coredump support
-2. fixed if new ic have not support coredump, it may cause crash when remove module
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/coredump.c | 80 ++++++++++++++++++++++++++++++++++++++---------
- mt7996/mt7996.h | 1 +
- 2 files changed, 67 insertions(+), 14 deletions(-)
-
-diff --git a/mt7996/coredump.c b/mt7996/coredump.c
-index a7f91b56..d09bcd4b 100644
---- a/mt7996/coredump.c
-+++ b/mt7996/coredump.c
-@@ -67,6 +67,44 @@ static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
- },
- };
-
-+static const struct mt7996_mem_region mt7992_wm_mem_regions[] = {
-+ {
-+ .start = 0x00800000,
-+ .len = 0x0004bfff,
-+ .name = "ULM0",
-+ },
-+ {
-+ .start = 0x00900000,
-+ .len = 0x00035fff,
-+ .name = "ULM1",
-+ },
-+ {
-+ .start = 0x02200000,
-+ .len = 0x0003ffff,
-+ .name = "ULM2",
-+ },
-+ {
-+ .start = 0x00400000,
-+ .len = 0x00027fff,
-+ .name = "SRAM",
-+ },
-+ {
-+ .start = 0xe0000000,
-+ .len = 0x0015ffff,
-+ .name = "CRAM0",
-+ },
-+ {
-+ .start = 0xe0160000,
-+ .len = 0x00c7fff,
-+ .name = "CRAM1",
-+ },
-+ {
-+ .start = 0x7c050000,
-+ .len = 0x00007fff,
-+ .name = "CONN_INFRA",
-+ },
-+};
-+
- const struct mt7996_mem_region*
- mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
- {
-@@ -80,6 +118,14 @@ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
-
- *num = ARRAY_SIZE(mt7996_wm_mem_regions);
- return &mt7996_wm_mem_regions[0];
-+ case 0x7992:
-+ if (type == MT7996_RAM_TYPE_WA) {
-+ /* mt7992 wa memory regions is the same as mt7996 */
-+ *num = ARRAY_SIZE(mt7996_wa_mem_regions);
-+ return &mt7996_wa_mem_regions[0];
-+ }
-+ *num = ARRAY_SIZE(mt7992_wm_mem_regions);
-+ return &mt7992_wm_mem_regions[0];
- default:
- return NULL;
- }
-@@ -115,7 +161,7 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
-
- lockdep_assert_held(&dev->dump_mutex);
-
-- if (!coredump_memdump)
-+ if (!coredump_memdump || !crash_data->supported)
- return NULL;
-
- guid_gen(&crash_data->guid);
-@@ -289,40 +335,46 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
- for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
- crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
- if (!crash_data)
-- return -ENOMEM;
-+ goto nomem;
-
- dev->coredump.crash_data[i] = crash_data;
-+ crash_data->supported = false;
-
- if (coredump_memdump) {
- crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
- if (!crash_data->memdump_buf_len)
- /* no memory content */
-- return 0;
-+ continue;
-
- crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
-- if (!crash_data->memdump_buf) {
-- vfree(crash_data);
-- return -ENOMEM;
-- }
-+ if (!crash_data->memdump_buf)
-+ goto nomem;
-+
-+ crash_data->supported = true;
- }
- }
-
- return 0;
-+nomem:
-+ mt7996_coredump_unregister(dev);
-+ return -ENOMEM;
- }
-
- void mt7996_coredump_unregister(struct mt7996_dev *dev)
- {
- int i;
-+ struct mt7996_crash_data *crash_data;
-
- for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
-- if (dev->coredump.crash_data[i]->memdump_buf) {
-- vfree(dev->coredump.crash_data[i]->memdump_buf);
-- dev->coredump.crash_data[i]->memdump_buf = NULL;
-- dev->coredump.crash_data[i]->memdump_buf_len = 0;
-- }
-+ crash_data = dev->coredump.crash_data[i];
-+
-+ if (!crash_data)
-+ continue;
-+
-+ if (crash_data->memdump_buf)
-+ vfree(crash_data->memdump_buf);
-
-- vfree(dev->coredump.crash_data[i]);
-- dev->coredump.crash_data[i] = NULL;
-+ vfree(crash_data);
- }
- }
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index dd9aa9e2..7ca9d57e 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -230,6 +230,7 @@ struct mt7996_vif {
- struct mt7996_crash_data {
- guid_t guid;
- struct timespec64 timestamp;
-+ bool supported;
-
- u8 *memdump_buf;
- size_t memdump_buf_len;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1018-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1018-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch
deleted file mode 100644
index b7304e5..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1018-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 839e8ed5ee7eb48dd885a1f8f26a8ea9b27517ca Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 6 Jun 2023 16:57:10 +0800
-Subject: [PATCH 1018/1044] mtk: wifi: mt76: mt7996: add support for runtime
- set in-band discovery
-
-with this patch, AP can runtime set inband discovery via hostapd_cli
-
-Usage:
-Enable FILS: hostapd_cli -i [interface] inband_discovery 2 20
-Enable UBPR: hostapd_cli -i [interface] inband_discovery 1 20
-Disable inband discovery: hostapd_cli -i [interface] inband_discovery 0 0
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
----
- mt7996/mcu.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 8348d22a..cd7f58c3 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2605,8 +2605,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- if (IS_ERR(rskb))
- return PTR_ERR(rskb);
-
-- if (changed & BSS_CHANGED_FILS_DISCOVERY &&
-- vif->bss_conf.fils_discovery.max_interval) {
-+ if (changed & BSS_CHANGED_FILS_DISCOVERY) {
- interval = vif->bss_conf.fils_discovery.max_interval;
- skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
- } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
-@@ -2641,7 +2640,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
- discov->tx_interval = interval;
- discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
-- discov->enable = true;
-+ discov->enable = !!(interval);
- discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
-
- buf = (u8 *)tlv + sizeof(*discov);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1019-mtk-wifi-mt76-mt7996-add-vendor-subcmd-EDCCA-ctrl-en.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1019-mtk-wifi-mt76-mt7996-add-vendor-subcmd-EDCCA-ctrl-en.patch
deleted file mode 100644
index fb38f6a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1019-mtk-wifi-mt76-mt7996-add-vendor-subcmd-EDCCA-ctrl-en.patch
+++ /dev/null
@@ -1,379 +0,0 @@
-From 2caf106ced738d65640e21d284e3fee9cb04c611 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Thu, 8 Jun 2023 20:21:04 +0800
-Subject: [PATCH 1019/1044] mtk: wifi: mt76: mt7996: add vendor subcmd EDCCA
- ctrl enable
-
----
- mt7996/mcu.h | 2 +
- mt7996/mt7996.h | 11 ++++
- mt7996/mtk_mcu.c | 87 +++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h | 15 ++++++
- mt7996/vendor.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 33 ++++++++++++
- 6 files changed, 280 insertions(+)
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 35f757dc..34fdfb26 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -845,6 +845,8 @@ mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower)
-
- enum {
- UNI_BAND_CONFIG_RADIO_ENABLE,
-+ UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
-+ UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
- UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
- };
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 7ca9d57e..cfa50bfe 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -786,6 +786,17 @@ int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
- struct ieee80211_sta *sta);
- #endif
-
-+int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-+int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set);
-+
-+enum edcca_bw_id {
-+ EDCCA_BW_20 = 0,
-+ EDCCA_BW_40,
-+ EDCCA_BW_80,
-+ EDCCA_BW_160,
-+ EDCCA_MAX_BW_NUM,
-+};
-+
- #ifdef CONFIG_MTK_DEBUG
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index e56ddd8f..5c54d02c 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -59,4 +59,91 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
- sizeof(req), true);
- }
-+
-+int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+ enum nl80211_band band = chandef->chan->band;
-+ struct {
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 enable;
-+ u8 std;
-+ u8 _rsv2[2];
-+ } __packed req = {
-+ .band_idx = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_ENABLE),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .enable = enable,
-+ .std = EDCCA_DEFAULT,
-+ };
-+
-+ switch (dev->mt76.region) {
-+ case NL80211_DFS_JP:
-+ req.std = EDCCA_JAPAN;
-+ break;
-+ case NL80211_DFS_FCC:
-+ if (band == NL80211_BAND_6GHZ)
-+ req.std = EDCCA_FCC;
-+ break;
-+ case NL80211_DFS_ETSI:
-+ if (band == NL80211_BAND_6GHZ)
-+ req.std = EDCCA_ETSI;
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
-+ &req, sizeof(req), true);
-+}
-+
-+int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
-+{
-+ struct {
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 threshold[4];
-+ bool init;
-+ } __packed *res, req = {
-+ .band_idx = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_THRESHOLD),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .init = false,
-+ };
-+ struct sk_buff *skb;
-+ int ret;
-+ int i;
-+
-+ for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
-+ req.threshold[i] = value[i];
-+
-+ if (set)
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
-+ &req, sizeof(req), true);
-+
-+ ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76,
-+ MCU_WM_UNI_CMD_QUERY(BAND_CONFIG),
-+ &req, sizeof(req), true, &skb);
-+
-+ if (ret)
-+ return ret;
-+
-+ res = (void *)skb->data;
-+
-+ for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
-+ value[i] = res->threshold[i];
-+
-+ dev_kfree_skb(skb);
-+
-+ return 0;
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index c30418ca..36a58ad6 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -106,6 +106,21 @@ enum txpower_event {
- UNI_TXPOWER_PHY_RATE_INFO = 5,
- };
-
-+enum {
-+ EDCCA_CTRL_SET_EN = 0,
-+ EDCCA_CTRL_SET_THRES,
-+ EDCCA_CTRL_GET_EN,
-+ EDCCA_CTRL_GET_THRES,
-+ EDCCA_CTRL_NUM,
-+};
-+
-+enum {
-+ EDCCA_DEFAULT = 0,
-+ EDCCA_FCC = 1,
-+ EDCCA_ETSI = 2,
-+ EDCCA_JAPAN = 3
-+};
-+
- #endif
-
- #endif
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 39101577..9f333d0e 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -40,6 +40,26 @@ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
- [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
- };
-
-+static const struct nla_policy
-+edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_S8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
-+ [MTK_VENDOR_ATTR_EDCCA_DUMP_MODE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -436,6 +456,106 @@ mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev
- return len;
- }
-
-+static int mt7996_vendor_edcca_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ const void *data, int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
-+ int err;
-+ u8 edcca_mode;
-+ u8 edcca_value[EDCCA_MAX_BW_NUM];
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
-+ edcca_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
-+ return -EINVAL;
-+
-+ edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
-+ if (edcca_mode == EDCCA_CTRL_SET_EN) {
-+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL])
-+ return -EINVAL;
-+
-+ edcca_value[0] =
-+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
-+
-+ err = mt7996_mcu_edcca_enable(phy, !!edcca_value[0]);
-+ if (err)
-+ return err;
-+ } else if (edcca_mode == EDCCA_CTRL_SET_THRES) {
-+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] ||
-+ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] ||
-+ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] ||
-+ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]) {
-+ return -EINVAL;
-+ }
-+ edcca_value[EDCCA_BW_20] =
-+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
-+ edcca_value[EDCCA_BW_40] =
-+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL]);
-+ edcca_value[EDCCA_BW_80] =
-+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL]);
-+ edcca_value[EDCCA_BW_160] =
-+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]);
-+
-+ err = mt7996_mcu_edcca_threshold_ctrl(phy, edcca_value, true);
-+
-+ if (err)
-+ return err;
-+ } else {
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+
-+static int
-+mt7996_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ struct sk_buff *skb, const void *data, int data_len,
-+ unsigned long *storage)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
-+ int err;
-+ u8 edcca_mode;
-+ u8 value[EDCCA_MAX_BW_NUM];
-+
-+ if (*storage == 1)
-+ return -ENOENT;
-+ *storage = 1;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
-+ edcca_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
-+ return -EINVAL;
-+
-+ edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
-+
-+ if (edcca_mode != EDCCA_CTRL_GET_THRES)
-+ return -EINVAL;
-+
-+ err = mt7996_mcu_edcca_threshold_ctrl(phy, value, false);
-+
-+ if (err)
-+ return err;
-+
-+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL, value[EDCCA_BW_20]) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL, value[EDCCA_BW_40]) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL, value[EDCCA_BW_80]) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL, value[EDCCA_BW_160]))
-+ return -ENOMEM;
-+
-+ return EDCCA_MAX_BW_NUM;
-+}
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
- .info = {
-@@ -472,6 +592,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = bss_color_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_edcca_ctrl,
-+ .dumpit = mt7996_vendor_edcca_ctrl_dump,
-+ .policy = edcca_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index eec9e74a..4465bc9d 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -6,9 +6,42 @@
- enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
-
-+enum mtk_vendor_attr_edcca_ctrl {
-+ MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
-+
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_edcca_dump {
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
-+
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
-+};
-+
- enum mtk_vendor_attr_mu_ctrl {
- MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1020-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1020-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch
deleted file mode 100644
index 0ae3fc9..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1020-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch
+++ /dev/null
@@ -1,397 +0,0 @@
-From 090298f97be235c284e47dc8302a39da2f1fa855 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 10 Jul 2023 11:47:29 +0800
-Subject: [PATCH 1020/1044] mtk: wifi: mt76: mt7996: add support spatial reuse
- debug commands
-
-This commit adds the following debug commands in debugfs:
-1. sr_enable: enable/disable spatial reuse feature. Default is on.
-2. sr_enhanced_enable: enable/disable enhanced spatial reuse feature.
-Default is on. This feature is mtk proprietary feature.
-3. sr_stats: Check the Spatial reuse tx statistics.
-4. sr_scene_cond: Check the result of mtk scene detection algorithm. Mtk
-scene detection algorithm in firmware may decide whether current
-environment can SR Tx or not.
-
-To learn more details of these commands, please check:
-https://wiki.mediatek.inc/display/APKB/mt76+Phy+feature+debug+Cheetsheet#mt76PhyfeaturedebugCheetsheet-SpatialReuse
-
----
- mt76_connac_mcu.h | 1 +
- mt7996/main.c | 6 +++
- mt7996/mcu.c | 8 ++++
- mt7996/mt7996.h | 6 +++
- mt7996/mtk_debugfs.c | 82 ++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.c | 111 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h | 56 ++++++++++++++++++++++
- 7 files changed, 270 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 062268d6..9edb580c 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1040,6 +1040,7 @@ enum {
- MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c,
- MCU_UNI_EVENT_SCAN_DONE = 0x0e,
- MCU_UNI_EVENT_RDD_REPORT = 0x11,
-+ MCU_UNI_EVENT_SR = 0x25,
- MCU_UNI_EVENT_ROC = 0x27,
- MCU_UNI_EVENT_TX_DONE = 0x2d,
- MCU_UNI_EVENT_BF = 0x33,
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 478ca7ce..9ca37e1e 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -6,6 +6,9 @@
- #include "mt7996.h"
- #include "mcu.h"
- #include "mac.h"
-+#ifdef CONFIG_MTK_DEBUG
-+#include "mtk_mcu.h"
-+#endif
-
- static bool mt7996_dev_running(struct mt7996_dev *dev)
- {
-@@ -78,6 +81,9 @@ int mt7996_run(struct ieee80211_hw *hw)
- goto out;
-
- #ifdef CONFIG_MTK_DEBUG
-+ phy->sr_enable = true;
-+ phy->enhanced_sr_enable = true;
-+
- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
- !dev->dbg.sku_disable);
- #else
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index cd7f58c3..16341a41 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -712,6 +712,14 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- case MCU_UNI_EVENT_WED_RRO:
- mt7996_mcu_wed_rro_event(dev, skb);
- break;
-+#ifdef CONFIG_MTK_DEBUG
-+ case MCU_UNI_EVENT_SR:
-+ mt7996_mcu_rx_sr_event(dev, skb);
-+ break;
-+#endif
-+ case MCU_UNI_EVENT_THERMAL:
-+ mt7996_mcu_rx_thermal_notify(dev, skb);
-+ break;
- #ifdef CONFIG_NL80211_TESTMODE
- case MCU_UNI_EVENT_TESTMODE_CTRL:
- mt7996_tm_rf_test_event(dev, skb);
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index cfa50bfe..adb0cdd1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -352,6 +352,10 @@ struct mt7996_phy {
- spinlock_t amnt_lock;
- struct mt7996_air_monitor_ctrl amnt_ctrl;
- #endif
-+#ifdef CONFIG_MTK_DEBUG
-+ bool sr_enable:1;
-+ bool enhanced_sr_enable:1;
-+#endif
- };
-
- struct mt7996_dev {
-@@ -800,6 +804,8 @@ enum edcca_bw_id {
- #ifdef CONFIG_MTK_DEBUG
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
-+int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
-+void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 09652ef5..ed01c089 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2777,6 +2777,83 @@ static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
- return 0;
- }
-
-+static int
-+mt7996_sr_enable_get(void *data, u64 *val)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ *val = phy->sr_enable;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_sr_enable_set(void *data, u64 val)
-+{
-+ struct mt7996_phy *phy = data;
-+ int ret;
-+
-+ if (!!val == phy->sr_enable)
-+ return 0;
-+
-+ ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, val, true);
-+ if (ret)
-+ return ret;
-+
-+ return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, 0, false);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enable, mt7996_sr_enable_get,
-+ mt7996_sr_enable_set, "%lld\n");
-+static int
-+mt7996_sr_enhanced_enable_get(void *data, u64 *val)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ *val = phy->enhanced_sr_enable;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_sr_enhanced_enable_set(void *data, u64 val)
-+{
-+ struct mt7996_phy *phy = data;
-+ int ret;
-+
-+ if (!!val == phy->enhanced_sr_enable)
-+ return 0;
-+
-+ ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, val, true);
-+ if (ret)
-+ return ret;
-+
-+ return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, 0, false);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enhanced_enable, mt7996_sr_enhanced_enable_get,
-+ mt7996_sr_enhanced_enable_set, "%lld\n");
-+
-+static int
-+mt7996_sr_stats_show(struct seq_file *file, void *data)
-+{
-+ struct mt7996_phy *phy = file->private;
-+
-+ mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_IND, 0, false);
-+
-+ return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(mt7996_sr_stats);
-+
-+static int
-+mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
-+{
-+ struct mt7996_phy *phy = file->private;
-+
-+ mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_SW_SD, 0, false);
-+
-+ return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -2856,6 +2933,11 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
- debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
-
-+ debugfs_create_file("sr_enable", 0600, dir, phy, &fops_sr_enable);
-+ debugfs_create_file("sr_enhanced_enable", 0600, dir, phy, &fops_sr_enhanced_enable);
-+ debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
-+ debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
-+
- return 0;
- }
-
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index 5c54d02c..dbdf8d80 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -146,4 +146,115 @@ int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
- return 0;
- }
-
-+int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set)
-+{
-+ struct {
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le32 val;
-+
-+ } __packed req = {
-+ .band_idx = phy->mt76->band_idx,
-+
-+ .tag = cpu_to_le16(action),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+
-+ .val = cpu_to_le32((u32) val),
-+ };
-+
-+ if (set)
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SR), &req,
-+ sizeof(req), false);
-+ else
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD_QUERY(SR), &req,
-+ sizeof(req), false);
-+}
-+
-+void mt7996_mcu_rx_sr_swsd(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+#define SR_SCENE_DETECTION_TIMER_PERIOD_MS 500
-+ struct mt7996_mcu_sr_swsd_event *event;
-+ static const char * const rules[] = {"1 - NO CONNECTED", "2 - NO CONGESTION",
-+ "3 - NO INTERFERENCE", "4 - SR ON"};
-+ u8 idx;
-+
-+ event = (struct mt7996_mcu_sr_swsd_event *)skb->data;
-+ idx = event->basic.band_idx;
-+
-+ dev_info(dev->mt76.dev, "Band index = %u\n", le16_to_cpu(event->basic.band_idx));
-+ dev_info(dev->mt76.dev, "Hit Rule = %s\n", rules[event->tlv[idx].rule]);
-+ dev_info(dev->mt76.dev, "Timer Period = %d(us)\n"
-+ "Congestion Ratio = %d.%1d%%\n",
-+ SR_SCENE_DETECTION_TIMER_PERIOD_MS * 1000,
-+ le32_to_cpu(event->tlv[idx].total_airtime_ratio) / 10,
-+ le32_to_cpu(event->tlv[idx].total_airtime_ratio) % 10);
-+ dev_info(dev->mt76.dev,
-+ "Total Airtime = %d(us)\n"
-+ "ChBusy = %d\n"
-+ "SrTx = %d\n"
-+ "OBSS = %d\n"
-+ "MyTx = %d\n"
-+ "MyRx = %d\n"
-+ "Interference Ratio = %d.%1d%%\n",
-+ le32_to_cpu(event->tlv[idx].total_airtime),
-+ le32_to_cpu(event->tlv[idx].channel_busy_time),
-+ le32_to_cpu(event->tlv[idx].sr_tx_airtime),
-+ le32_to_cpu(event->tlv[idx].obss_airtime),
-+ le32_to_cpu(event->tlv[idx].my_tx_airtime),
-+ le32_to_cpu(event->tlv[idx].my_rx_airtime),
-+ le32_to_cpu(event->tlv[idx].obss_airtime_ratio) / 10,
-+ le32_to_cpu(event->tlv[idx].obss_airtime_ratio) % 10);
-+}
-+
-+void mt7996_mcu_rx_sr_hw_indicator(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+ struct mt7996_mcu_sr_hw_ind_event *event;
-+
-+ event = (struct mt7996_mcu_sr_hw_ind_event *)skb->data;
-+
-+ dev_info(dev->mt76.dev, "Inter PPDU Count = %u\n",
-+ le16_to_cpu(event->inter_bss_ppdu_cnt));
-+ dev_info(dev->mt76.dev, "SR Valid Count = %u\n",
-+ le16_to_cpu(event->non_srg_valid_cnt));
-+ dev_info(dev->mt76.dev, "SR Tx Count = %u\n",
-+ le32_to_cpu(event->sr_ampdu_mpdu_cnt));
-+ dev_info(dev->mt76.dev, "SR Tx Acked Count = %u\n",
-+ le32_to_cpu(event->sr_ampdu_mpdu_acked_cnt));
-+}
-+
-+void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+ struct mt76_phy *mphy = &dev->mt76.phy;
-+ struct mt7996_phy *phy;
-+ struct mt7996_mcu_sr_common_event *event;
-+
-+ event = (struct mt7996_mcu_sr_common_event *)skb->data;
-+ mphy = dev->mt76.phys[event->basic.band_idx];
-+ if (!mphy)
-+ return;
-+
-+ phy = (struct mt7996_phy *)mphy->priv;
-+
-+ switch (le16_to_cpu(event->basic.tag)) {
-+ case UNI_EVENT_SR_CFG_SR_ENABLE:
-+ phy->sr_enable = le32_to_cpu(event->value) ? true : false;
-+ break;
-+ case UNI_EVENT_SR_HW_ESR_ENABLE:
-+ phy->enhanced_sr_enable = le32_to_cpu(event->value) ? true : false;
-+ break;
-+ case UNI_EVENT_SR_SW_SD:
-+ mt7996_mcu_rx_sr_swsd(dev, skb);
-+ break;
-+ case UNI_EVENT_SR_HW_IND:
-+ mt7996_mcu_rx_sr_hw_indicator(dev, skb);
-+ break;
-+ default:
-+ dev_info(dev->mt76.dev, "Unknown SR event tag %d\n",
-+ le16_to_cpu(event->basic.tag));
-+ }
-+}
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 36a58ad6..098e63ae 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -121,6 +121,62 @@ enum {
- EDCCA_JAPAN = 3
- };
-
-+enum {
-+ UNI_EVENT_SR_CFG_SR_ENABLE = 0x1,
-+ UNI_EVENT_SR_SW_SD = 0x83,
-+ UNI_EVENT_SR_HW_IND = 0xC9,
-+ UNI_EVENT_SR_HW_ESR_ENABLE = 0xD8,
-+};
-+enum {
-+ UNI_CMD_SR_CFG_SR_ENABLE = 0x1,
-+ UNI_CMD_SR_SW_SD = 0x84,
-+ UNI_CMD_SR_HW_IND = 0xCB,
-+ UNI_CMD_SR_HW_ENHANCE_SR_ENABLE = 0xDA,
-+};
-+
-+struct mt7996_mcu_sr_basic_event {
-+ struct mt7996_mcu_rxd rxd;
-+
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+};
-+
-+struct sr_sd_tlv {
-+ u8 _rsv[16];
-+ __le32 sr_tx_airtime;
-+ __le32 obss_airtime;
-+ __le32 my_tx_airtime;
-+ __le32 my_rx_airtime;
-+ __le32 channel_busy_time;
-+ __le32 total_airtime;
-+ __le32 total_airtime_ratio;
-+ __le32 obss_airtime_ratio;
-+ u8 rule;
-+ u8 _rsv2[59];
-+} __packed;
-+
-+struct mt7996_mcu_sr_swsd_event {
-+ struct mt7996_mcu_sr_basic_event basic;
-+ struct sr_sd_tlv tlv[3];
-+} __packed;
-+
-+struct mt7996_mcu_sr_common_event {
-+ struct mt7996_mcu_sr_basic_event basic;
-+ __le32 value;
-+};
-+
-+struct mt7996_mcu_sr_hw_ind_event {
-+ struct mt7996_mcu_sr_basic_event basic;
-+ __le16 non_srg_valid_cnt;
-+ u8 _rsv[4];
-+ __le16 inter_bss_ppdu_cnt;
-+ u8 _rsv2[4];
-+ __le32 sr_ampdu_mpdu_cnt;
-+ __le32 sr_ampdu_mpdu_acked_cnt;
-+};
- #endif
-
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1021-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1021-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch
deleted file mode 100644
index bb58282..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1021-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 424dc1cf297e6bf20ed5c430d215f64517178270 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 1 Aug 2023 16:02:28 +0800
-Subject: [PATCH 1021/1044] mtk: wifi: mt76: mt7996: Establish BA in VO queue
-
----
- mt7996/mac.c | 2 --
- 1 file changed, 2 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 1c1b3eb5..4e52aa1b 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1032,8 +1032,6 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
- return;
-
- tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-- if (tid >= 6) /* skip VO queue */
-- return;
-
- if (is_8023) {
- fc = IEEE80211_FTYPE_DATA |
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1022-mtk-wifi-mt76-mt7996-add-eagle-iFEM-HWITS-ZWDFS-SW-w.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1022-mtk-wifi-mt76-mt7996-add-eagle-iFEM-HWITS-ZWDFS-SW-w.patch
deleted file mode 100644
index ebd565c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1022-mtk-wifi-mt76-mt7996-add-eagle-iFEM-HWITS-ZWDFS-SW-w.patch
+++ /dev/null
@@ -1,155 +0,0 @@
-From b0a949cc4ac49a2a276949ebbd0cbecc859c8576 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:00:17 +0800
-Subject: [PATCH 1022/1044] mtk: wifi: mt76: mt7996: add eagle iFEM HWITS ZWDFS
- SW workaround
-
-Fix the case that control channel is not first chan during first
-interface setup.
-Refactor ifem adie logic (if/else to switch, use sku_type & fem_type)
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/main.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++---
- mt7996/mcu.c | 6 +++--
- mt7996/mt7996.h | 1 +
- 3 files changed, 67 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 9ca37e1e..dbe3d33f 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1431,6 +1431,61 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
- mutex_unlock(&dev->mt76.mutex);
- }
-
-+static void
-+mt7996_background_radar_handle_7975_ifem(struct ieee80211_hw *hw,
-+ struct cfg80211_chan_def *user_chandef,
-+ struct cfg80211_chan_def *fw_chandef)
-+{
-+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+ struct cfg80211_chan_def *c = user_chandef;
-+ struct ieee80211_channel *first_chan;
-+ bool is_ifem_adie, expand = false;
-+
-+ switch (mt76_chip(&dev->mt76)) {
-+ case 0x7990:
-+ is_ifem_adie = dev->fem_type == MT7996_FEM_INT &&
-+ dev->chip_sku != MT7996_SKU_233;
-+ break;
-+ case 0x7992:
-+ is_ifem_adie = dev->chip_sku == MT7992_SKU_44 &&
-+ dev->fem_type != MT7996_FEM_EXT;
-+ break;
-+ default:
-+ return;
-+ }
-+
-+ if (!user_chandef || !is_ifem_adie)
-+ goto out;
-+
-+ if (user_chandef->width == NL80211_CHAN_WIDTH_160) {
-+ first_chan = ieee80211_get_channel(hw->wiphy, user_chandef->center_freq1 - 70);
-+ if (dev->bg_nxt_freq)
-+ goto out;
-+
-+ if (first_chan->flags & IEEE80211_CHAN_RADAR)
-+ dev->bg_nxt_freq = first_chan->center_freq;
-+ else
-+ c = fw_chandef;
-+
-+ c->chan = ieee80211_get_channel(hw->wiphy, first_chan->center_freq + 80);
-+ } else {
-+ if (!dev->bg_nxt_freq)
-+ goto out;
-+
-+ c->chan = ieee80211_get_channel(hw->wiphy, dev->bg_nxt_freq);
-+ dev->bg_nxt_freq = 0;
-+ expand = true;
-+ }
-+ c->width = NL80211_CHAN_WIDTH_80;
-+ c->center_freq1 = c->chan->center_freq + 30;
-+
-+ if (c == user_chandef)
-+ cfg80211_background_radar_update_channel(hw->wiphy, c, expand);
-+ return;
-+out:
-+ dev->bg_nxt_freq = 0;
-+}
-+
- static int
- mt7996_set_radar_background(struct ieee80211_hw *hw,
- struct cfg80211_chan_def *chandef)
-@@ -1439,6 +1494,7 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
- struct mt7996_dev *dev = phy->dev;
- int ret = -EINVAL;
- bool running;
-+ struct cfg80211_chan_def ifem_chandef = {};
-
- mutex_lock(&dev->mt76.mutex);
-
-@@ -1451,13 +1507,14 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
- goto out;
- }
-
-+ mt7996_background_radar_handle_7975_ifem(hw, chandef, &ifem_chandef);
-+
- /* rdd2 already configured on a radar channel */
- running = dev->rdd2_phy &&
- cfg80211_chandef_valid(&dev->rdd2_chandef) &&
- !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR);
-
-- if (!chandef || running ||
-- !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) {
-+ if (!chandef || running) {
- ret = mt7996_mcu_rdd_background_enable(phy, NULL);
- if (ret)
- goto out;
-@@ -1466,7 +1523,9 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
- goto update_phy;
- }
-
-- ret = mt7996_mcu_rdd_background_enable(phy, chandef);
-+ ret = mt7996_mcu_rdd_background_enable(phy,
-+ ifem_chandef.chan ?
-+ &ifem_chandef : chandef);
- if (ret)
- goto out;
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 16341a41..bc4c3a94 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -369,12 +369,14 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
- if (!mphy)
- return;
-
-- if (r->band_idx == MT_RX_SEL2)
-+ if (r->band_idx == MT_RX_SEL2) {
-+ dev->bg_nxt_freq = 0;
- cfg80211_background_radar_event(mphy->hw->wiphy,
- &dev->rdd2_chandef,
- GFP_ATOMIC);
-- else
-+ } else {
- ieee80211_radar_detected(mphy->hw);
-+ }
- dev->hw_pattern++;
- }
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index adb0cdd1..b2fb68c3 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -441,6 +441,7 @@ struct mt7996_dev {
- bool testmode_enable;
- bool bin_file_mode;
- u8 eeprom_mode;
-+ u32 bg_nxt_freq;
-
- bool ibf;
- u8 fw_debug_wm;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1023-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1023-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch
deleted file mode 100644
index aa995e4..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1023-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 6bca319efea2666854a49851f7e85aa01e2046ed Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Sat, 12 Aug 2023 04:17:22 +0800
-Subject: [PATCH 1023/1044] mtk: wifi: mt76: mt7996: report tx and rx byte to
- tpt_led
-
----
- mt7996/mcu.c | 15 +++++++++++----
- 1 file changed, 11 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index bc4c3a94..03a2402d 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -522,6 +522,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- u8 ac;
- u16 wlan_idx;
- struct mt76_wcid *wcid;
-+ struct mt76_phy *mphy;
-+ u32 tx_bytes, rx_bytes;
-
- switch (le16_to_cpu(res->tag)) {
- case UNI_ALL_STA_TXRX_RATE:
-@@ -541,11 +543,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- if (!wcid)
- break;
-
-+ mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- wcid->stats.tx_bytes +=
-- le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
-- wcid->stats.rx_bytes +=
-- le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
-+ tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
-+ rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
-+
-+ wcid->stats.tx_bytes += tx_bytes;
-+ wcid->stats.rx_bytes += rx_bytes;
-+
-+ ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
-+ ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
- }
- break;
- case UNI_ALL_STA_TXRX_MSDU_COUNT:
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1024-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1024-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch
deleted file mode 100644
index de8c46e..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1024-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From ab4e26243d8bbccfe1e8ba69c1af4e3457909f32 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 21 Sep 2023 00:52:46 +0800
-Subject: [PATCH 1024/1044] mtk: wifi: mt76: mt7996: support dup wtbl
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/init.c | 1 +
- mt7996/mt7996.h | 1 +
- mt7996/mtk_mcu.c | 23 +++++++++++++++++++++++
- 3 files changed, 25 insertions(+)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 1498787f..30879ec3 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -675,6 +675,7 @@ static void mt7996_init_work(struct work_struct *work)
- mt7996_mcu_set_eeprom(dev);
- mt7996_mac_init(dev);
- mt7996_txbf_init(dev);
-+ mt7996_mcu_set_dup_wtbl(dev);
- }
-
- void mt7996_wfsys_reset(struct mt7996_dev *dev)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b2fb68c3..23497b46 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -807,6 +807,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
- void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
-+int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index dbdf8d80..ea4e5bf2 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -257,4 +257,27 @@ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
- le16_to_cpu(event->basic.tag));
- }
- }
-+
-+int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
-+{
-+#define CHIP_CONFIG_DUP_WTBL 4
-+#define DUP_WTBL_NUM 80
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ __le16 base;
-+ __le16 num;
-+ u8 _rsv2[4];
-+ } __packed req = {
-+ .tag = cpu_to_le16(CHIP_CONFIG_DUP_WTBL),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .base = cpu_to_le16(MT7996_WTBL_STA - DUP_WTBL_NUM + 1),
-+ .num = cpu_to_le16(DUP_WTBL_NUM),
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
-+ sizeof(req), true);
-+}
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1025-mtk-wifi-mt76-mt7996-add-ibf-control-vendor-cmd.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1025-mtk-wifi-mt76-mt7996-add-ibf-control-vendor-cmd.patch
deleted file mode 100644
index 5fc86f1..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1025-mtk-wifi-mt76-mt7996-add-ibf-control-vendor-cmd.patch
+++ /dev/null
@@ -1,143 +0,0 @@
-From 4c1c8c14c01399c1851eefde7e56d6dcd7f9f2d3 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Fri, 22 Sep 2023 09:54:49 +0800
-Subject: [PATCH 1025/1044] mtk: wifi: mt76: mt7996: add ibf control vendor cmd
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- mt7996/vendor.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 23 +++++++++++++++++
- 2 files changed, 88 insertions(+)
-
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 9f333d0e..dae3260a 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -60,6 +60,11 @@ edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
- [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL] = { .type = NLA_U8 },
- };
-
-+static const struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -556,6 +561,54 @@ mt7996_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- return EDCCA_MAX_BW_NUM;
- }
-
-+static int mt7996_vendor_ibf_ctrl(struct wiphy *wiphy,
-+ struct wireless_dev *wdev,
-+ const void *data,
-+ int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct mt7996_dev *dev = phy->dev;
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_IBF_CTRL];
-+ int err;
-+ u8 val;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_IBF_CTRL_MAX, data, data_len,
-+ ibf_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]) {
-+ val = nla_get_u8(tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]);
-+
-+ dev->ibf = !!val;
-+
-+ err = mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
-+ if (err)
-+ return err;
-+ }
-+ return 0;
-+}
-+
-+static int
-+mt7996_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ struct sk_buff *skb, const void *data, int data_len,
-+ unsigned long *storage)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct mt7996_dev *dev = phy->dev;
-+
-+ if (*storage == 1)
-+ return -ENOENT;
-+ *storage = 1;
-+
-+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_IBF_DUMP_ENABLE, dev->ibf))
-+ return -ENOMEM;
-+
-+ return 1;
-+}
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
- .info = {
-@@ -604,6 +657,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = edcca_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_ibf_ctrl,
-+ .dumpit = mt7996_vendor_ibf_ctrl_dump,
-+ .policy = ibf_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_IBF_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 4465bc9d..49f46f25 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -7,6 +7,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
-
-@@ -102,4 +103,26 @@ enum mtk_vendor_attr_bss_color_ctrl {
- NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
- };
-
-+enum mtk_vendor_attr_ibf_ctrl {
-+ MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
-+ MTK_VENDOR_ATTR_IBF_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_ibf_dump {
-+ MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
-+ MTK_VENDOR_ATTR_IBF_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
-+};
-+
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1026-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1026-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch
deleted file mode 100644
index 27628ed..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1026-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch
+++ /dev/null
@@ -1,229 +0,0 @@
-From e41cd653fb95b60acd3f7c0ee1690ffa4ba995db Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 6 Nov 2023 11:10:10 +0800
-Subject: [PATCH 1026/1044] mtk: wifi: mt76: try more times when send message
- timeout.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- dma.c | 7 ++++--
- mcu.c | 65 ++++++++++++++++++++++++++++++++++++----------------
- mt7996/mac.c | 45 +++++++++++++++---------------------
- 3 files changed, 68 insertions(+), 49 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 56044639..66c000ef 100644
---- a/dma.c
-+++ b/dma.c
-@@ -504,9 +504,12 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
- {
- struct mt76_queue_buf buf = {};
- dma_addr_t addr;
-+ int ret = -ENOMEM;
-
-- if (test_bit(MT76_MCU_RESET, &dev->phy.state))
-+ if (test_bit(MT76_MCU_RESET, &dev->phy.state)) {
-+ ret = -EAGAIN;
- goto error;
-+ }
-
- if (q->queued + 1 >= q->ndesc - 1)
- goto error;
-@@ -528,7 +531,7 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
-
- error:
- dev_kfree_skb(skb);
-- return -ENOMEM;
-+ return ret;
- }
-
- static int
-diff --git a/mcu.c b/mcu.c
-index fa4b0544..2926f715 100644
---- a/mcu.c
-+++ b/mcu.c
-@@ -4,6 +4,7 @@
- */
-
- #include "mt76.h"
-+#include "mt76_connac.h"
- #include <linux/moduleparam.h>
-
- struct sk_buff *
-@@ -74,35 +75,59 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
- int cmd, bool wait_resp,
- struct sk_buff **ret_skb)
- {
-+#define MT76_MSG_MAX_RETRY_CNT 3
- unsigned long expires;
-- int ret, seq;
-+ int ret, seq, retry_cnt;
-+ struct sk_buff *skb_tmp;
-+ bool retry = wait_resp && is_mt7996(dev);
-
- if (ret_skb)
- *ret_skb = NULL;
-
- mutex_lock(&dev->mcu.mutex);
--
-- ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq);
-- if (ret < 0)
-- goto out;
--
-- if (!wait_resp) {
-- ret = 0;
-- goto out;
-+ retry_cnt = retry ? MT76_MSG_MAX_RETRY_CNT : 1;
-+ while (retry_cnt) {
-+ skb_tmp = mt76_mcu_msg_alloc(dev, skb->data, skb->len);
-+ if (!skb_tmp)
-+ goto out;
-+
-+ if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
-+ if (test_bit(MT76_MCU_RESET, &dev->phy.state))
-+ usleep_range(200000, 500000);
-+ dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
-+ }
-+
-+ ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
-+ if (ret < 0 && ret != -EAGAIN)
-+ goto out;
-+
-+ if (!wait_resp) {
-+ ret = 0;
-+ goto out;
-+ }
-+
-+ expires = jiffies + dev->mcu.timeout;
-+
-+ do {
-+ skb_tmp = mt76_mcu_get_response(dev, expires);
-+ ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb_tmp, seq);
-+ if (ret == -ETIMEDOUT)
-+ break;
-+
-+ if (!ret && ret_skb)
-+ *ret_skb = skb_tmp;
-+ else
-+ dev_kfree_skb(skb_tmp);
-+
-+ if (ret != -EAGAIN)
-+ goto out;
-+ } while (ret == -EAGAIN);
-+
-+ retry_cnt--;
- }
-
-- expires = jiffies + dev->mcu.timeout;
--
-- do {
-- skb = mt76_mcu_get_response(dev, expires);
-- ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb, seq);
-- if (!ret && ret_skb)
-- *ret_skb = skb;
-- else
-- dev_kfree_skb(skb);
-- } while (ret == -EAGAIN);
--
- out:
-+ dev_kfree_skb(skb);
- mutex_unlock(&dev->mcu.mutex);
-
- return ret;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 4e52aa1b..c6d79989 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1666,10 +1666,14 @@ mt7996_mac_restart(struct mt7996_dev *dev)
- set_bit(MT76_RESET, &dev->mphy.state);
- set_bit(MT76_MCU_RESET, &dev->mphy.state);
- wake_up(&dev->mt76.mcu.wait);
-- if (phy2)
-+ if (phy2) {
- set_bit(MT76_RESET, &phy2->mt76->state);
-- if (phy3)
-+ set_bit(MT76_MCU_RESET, &phy2->mt76->state);
-+ }
-+ if (phy3) {
- set_bit(MT76_RESET, &phy3->mt76->state);
-+ set_bit(MT76_MCU_RESET, &phy3->mt76->state);
-+ }
-
- /* lock/unlock all queues to ensure that no tx is pending */
- mt76_txq_schedule_all(&dev->mphy);
-@@ -1784,13 +1788,24 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
- phy3 = mt7996_phy3(dev);
- dev->recovery.hw_full_reset = true;
-
-- wake_up(&dev->mt76.mcu.wait);
- ieee80211_stop_queues(mt76_hw(dev));
- if (phy2)
- ieee80211_stop_queues(phy2->mt76->hw);
- if (phy3)
- ieee80211_stop_queues(phy3->mt76->hw);
-
-+ set_bit(MT76_RESET, &dev->mphy.state);
-+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
-+ wake_up(&dev->mt76.mcu.wait);
-+ if (phy2) {
-+ set_bit(MT76_RESET, &phy2->mt76->state);
-+ set_bit(MT76_MCU_RESET, &phy2->mt76->state);
-+ }
-+ if (phy3) {
-+ set_bit(MT76_RESET, &phy3->mt76->state);
-+ set_bit(MT76_MCU_RESET, &phy3->mt76->state);
-+ }
-+
- cancel_work_sync(&dev->wed_rro.work);
- cancel_delayed_work_sync(&dev->mphy.mac_work);
- if (phy2)
-@@ -1893,16 +1908,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
- set_bit(MT76_MCU_RESET, &dev->mphy.state);
- wake_up(&dev->mt76.mcu.wait);
-
-- cancel_work_sync(&dev->wed_rro.work);
-- cancel_delayed_work_sync(&dev->mphy.mac_work);
-- if (phy2) {
-- set_bit(MT76_RESET, &phy2->mt76->state);
-- cancel_delayed_work_sync(&phy2->mt76->mac_work);
-- }
-- if (phy3) {
-- set_bit(MT76_RESET, &phy3->mt76->state);
-- cancel_delayed_work_sync(&phy3->mt76->mac_work);
-- }
- mt76_worker_disable(&dev->mt76.tx_worker);
- mt76_for_each_q_rx(&dev->mt76, i) {
- if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
-@@ -1913,8 +1918,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
- }
- napi_disable(&dev->mt76.tx_napi);
-
-- mutex_lock(&dev->mt76.mutex);
--
- mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
-
- if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
-@@ -1987,20 +1990,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
- if (phy3)
- ieee80211_wake_queues(phy3->mt76->hw);
-
-- mutex_unlock(&dev->mt76.mutex);
--
- mt7996_update_beacons(dev);
-
-- ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
-- MT7996_WATCHDOG_TIME);
-- if (phy2)
-- ieee80211_queue_delayed_work(phy2->mt76->hw,
-- &phy2->mt76->mac_work,
-- MT7996_WATCHDOG_TIME);
-- if (phy3)
-- ieee80211_queue_delayed_work(phy3->mt76->hw,
-- &phy3->mt76->mac_work,
-- MT7996_WATCHDOG_TIME);
- dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.",
- wiphy_name(dev->mt76.hw->wiphy));
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1027-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1027-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch
deleted file mode 100644
index 54ee436..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1027-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-From a86c3c6d2be2e115540d0a6ce5166943fd6657ba Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Tue, 21 Nov 2023 09:55:46 +0800
-Subject: [PATCH 1027/1044] mtk: wifi: mt76: mt7996: add SER overlap handle
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- mcu.c | 3 ++-
- mt7996/mac.c | 8 ++++++++
- mt7996/mcu.c | 8 ++++++++
- mt7996/mt7996.h | 2 ++
- 4 files changed, 20 insertions(+), 1 deletion(-)
-
-diff --git a/mcu.c b/mcu.c
-index 2926f715..a7afa2d7 100644
---- a/mcu.c
-+++ b/mcu.c
-@@ -94,7 +94,8 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
- if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
- if (test_bit(MT76_MCU_RESET, &dev->phy.state))
- usleep_range(200000, 500000);
-- dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
-+ dev_err(dev->dev, "send message %08x timeout, try again(%d).\n",
-+ cmd, (MT76_MSG_MAX_RETRY_CNT - retry_cnt));
- }
-
- ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index c6d79989..2e0fb5d9 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1889,6 +1889,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
- if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
- return;
-
-+ dev->recovery.l1_reset_last = dev->recovery.l1_reset;
- dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
- wiphy_name(dev->mt76.hw->wiphy));
-
-@@ -1906,6 +1907,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
-
- set_bit(MT76_RESET, &dev->mphy.state);
- set_bit(MT76_MCU_RESET, &dev->mphy.state);
-+ if (phy2)
-+ set_bit(MT76_RESET, &phy2->mt76->state);
-+ if (phy3)
-+ set_bit(MT76_RESET, &phy3->mt76->state);
- wake_up(&dev->mt76.mcu.wait);
-
- mt76_worker_disable(&dev->mt76.tx_worker);
-@@ -2120,6 +2125,9 @@ void mt7996_reset(struct mt7996_dev *dev)
- return;
- }
-
-+ if ((READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
-+ dev->recovery.l1_reset++;
-+
- queue_work(dev->mt76.wq, &dev->reset_work);
- wake_up(&dev->reset_wait);
- }
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 03a2402d..c427ea20 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -246,6 +246,14 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
- u32 val;
- u8 seq;
-
-+ if (dev->recovery.l1_reset_last != dev->recovery.l1_reset) {
-+ dev_info(dev->mt76.dev,"\n%s L1 SER recovery overlap, drop message %08x.",
-+ wiphy_name(dev->mt76.hw->wiphy), cmd);
-+
-+ dev_kfree_skb(skb);
-+ return -EPERM;
-+ }
-+
- mdev->mcu.timeout = 20 * HZ;
-
- seq = ++dev->mt76.mcu.msg_seq & 0xf;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 23497b46..c590a8b8 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -392,6 +392,8 @@ struct mt7996_dev {
- wait_queue_head_t reset_wait;
- struct {
- u32 state;
-+ u32 l1_reset;
-+ u32 l1_reset_last;
- u32 wa_reset_count;
- u32 wm_reset_count;
- bool hw_full_reset:1;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1028-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1028-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch
deleted file mode 100644
index 578e073..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1028-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From b1e9d2e1fd1b0b788bcb42c468b72d7e0fe1d583 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 13 Jul 2023 16:36:36 +0800
-Subject: [PATCH 1028/1044] mtk: wifi: mt76: mt7996: kite default 1-pcie
- setting
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/pci.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index 04056181..05830c01 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -11,6 +11,9 @@
- #include "mac.h"
- #include "../trace.h"
-
-+static bool hif2_enable = false;
-+module_param(hif2_enable, bool, 0644);
-+
- static LIST_HEAD(hif_list);
- static DEFINE_SPINLOCK(hif_lock);
- static u32 hif_idx;
-@@ -63,6 +66,9 @@ static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
- {
- hif_idx++;
-
-+ if (!hif2_enable)
-+ return NULL;
-+
- if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL) &&
- !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x799a, NULL))
- return NULL;
-@@ -77,6 +83,9 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
- {
- struct mt7996_hif *hif;
-
-+ if (!hif2_enable)
-+ return 0;
-+
- hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL);
- if (!hif)
- return -ENOMEM;
-@@ -101,6 +110,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- int irq, hif2_irq, ret;
- struct mt76_dev *mdev;
-
-+ hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
-+
- ret = pcim_enable_device(pdev);
- if (ret)
- return ret;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1029-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1029-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch
deleted file mode 100644
index 665f489..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1029-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch
+++ /dev/null
@@ -1,291 +0,0 @@
-From 649e775c56c37b314cf69767bbf845237a2674cc Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Fri, 28 Apr 2023 10:39:58 +0800
-Subject: [PATCH 1029/1044] mtk: wifi: mt76: mt7996: add debugfs knob for
- rx_counters
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- agg-rx.c | 8 ++++++++
- mac80211.c | 16 ++++++++++++++--
- mt76.h | 15 +++++++++++++++
- mt7996/mac.c | 18 +++++++++++++++---
- mt7996/mtk_debugfs.c | 42 ++++++++++++++++++++++++++++++++++++++++++
- 5 files changed, 94 insertions(+), 5 deletions(-)
-
-diff --git a/agg-rx.c b/agg-rx.c
-index 07c386c7..37588ac2 100644
---- a/agg-rx.c
-+++ b/agg-rx.c
-@@ -33,10 +33,13 @@ mt76_rx_aggr_release_frames(struct mt76_rx_tid *tid,
- struct sk_buff_head *frames,
- u16 head)
- {
-+ struct mt76_phy *phy = mt76_dev_phy(tid->dev, tid->band_idx);
- int idx;
-
- while (ieee80211_sn_less(tid->head, head)) {
- idx = tid->head % tid->size;
-+ if (!tid->reorder_buf[idx])
-+ phy->rx_stats.rx_agg_miss++;
- mt76_aggr_release(tid, frames, idx);
- }
- }
-@@ -151,6 +154,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
- struct mt76_wcid *wcid = status->wcid;
- struct ieee80211_sta *sta;
- struct mt76_rx_tid *tid;
-+ struct mt76_phy *phy;
- bool sn_less;
- u16 seqno, head, size, idx;
- u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
-@@ -186,6 +190,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
- head = tid->head;
- seqno = status->seqno;
- size = tid->size;
-+ phy = mt76_dev_phy(tid->dev, tid->band_idx);
- sn_less = ieee80211_sn_less(seqno, head);
-
- if (!tid->started) {
-@@ -197,6 +202,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
-
- if (sn_less) {
- __skb_unlink(skb, frames);
-+ phy->rx_stats.rx_dup_drop++;
- dev_kfree_skb(skb);
- goto out;
- }
-@@ -223,6 +229,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
-
- /* Discard if the current slot is already in use */
- if (tid->reorder_buf[idx]) {
-+ phy->rx_stats.rx_dup_drop++;
- dev_kfree_skb(skb);
- goto out;
- }
-@@ -254,6 +261,7 @@ int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno,
- tid->head = ssn;
- tid->size = size;
- tid->num = tidno;
-+ tid->band_idx = wcid->phy_idx;
- INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work);
- spin_lock_init(&tid->lock);
-
-diff --git a/mac80211.c b/mac80211.c
-index e7d02d15..ae040ec4 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -784,6 +784,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
- }
-
- if (ether_addr_equal(skb->data + offset, rfc1042_header)) {
-+ phy->rx_stats.rx_drop++;
- dev_kfree_skb(skb);
- return;
- }
-@@ -1100,10 +1101,16 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
-
- *sta = wcid_to_sta(mstat.wcid);
- *hw = mt76_phy_hw(dev, mstat.phy_idx);
-+
-+ if ((mstat.flag & RX_FLAG_8023) || ieee80211_is_data_qos(hdr->frame_control)) {
-+ struct mt76_phy *phy = mt76_dev_phy(dev, mstat.phy_idx);
-+
-+ phy->rx_stats.rx_mac80211++;
-+ }
- }
-
- static void
--mt76_check_ccmp_pn(struct sk_buff *skb)
-+mt76_check_ccmp_pn(struct mt76_dev *dev, struct sk_buff *skb)
- {
- struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
- struct mt76_wcid *wcid = status->wcid;
-@@ -1150,7 +1157,11 @@ skip_hdr_check:
- ret = memcmp(status->iv, wcid->rx_key_pn[security_idx],
- sizeof(status->iv));
- if (ret <= 0) {
-+ struct mt76_phy *phy = mt76_dev_phy(dev, status->phy_idx);
-+
-+ phy->rx_stats.rx_pn_iv_error++;
- status->flag |= RX_FLAG_ONLY_MONITOR;
-+
- return;
- }
-
-@@ -1331,7 +1342,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
- while ((skb = __skb_dequeue(frames)) != NULL) {
- struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
-
-- mt76_check_ccmp_pn(skb);
-+ mt76_check_ccmp_pn(dev, skb);
- skb_shinfo(skb)->frag_list = NULL;
- mt76_rx_convert(dev, skb, &hw, &sta);
- ieee80211_rx_list(hw, sta, skb, &list);
-@@ -1354,6 +1365,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
- }
-
- list_for_each_entry_safe(skb, tmp, &list, list) {
-+ dev->rx_kernel++;
- skb_list_del_init(skb);
- napi_gro_receive(napi, skb);
- }
-diff --git a/mt76.h b/mt76.h
-index a75277fe..58fd55b1 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -423,6 +423,7 @@ struct mt76_rx_tid {
- struct rcu_head rcu_head;
-
- struct mt76_dev *dev;
-+ u8 band_idx;
-
- spinlock_t lock;
- struct delayed_work reorder_work;
-@@ -854,6 +855,19 @@ struct mt76_phy {
- bool al;
- u8 pin;
- } leds;
-+
-+ struct {
-+ u32 rx_mac80211;
-+
-+ u32 rx_drop;
-+ u32 rx_rxd_drop;
-+ u32 rx_dup_drop;
-+ u32 rx_agg_miss;
-+ u32 rx_icv_error;
-+ u32 rx_fcs_error;
-+ u32 rx_tkip_mic_error;
-+ u32 rx_pn_iv_error;
-+ } rx_stats;
- };
-
- struct mt76_dev {
-@@ -959,6 +973,7 @@ struct mt76_dev {
- };
-
- const char *bin_file_name;
-+ u32 rx_kernel;
- };
-
- /* per-phy stats. */
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 2e0fb5d9..7885bc4c 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -469,8 +469,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- return -EINVAL;
-
- /* ICV error or CCMP/BIP/WPI MIC error */
-- if (rxd1 & MT_RXD1_NORMAL_ICV_ERR)
-+ if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) {
-+ mphy->rx_stats.rx_icv_error++;
- status->flag |= RX_FLAG_ONLY_MONITOR;
-+ }
-
- unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
- idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
-@@ -501,11 +503,15 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- !(csum_status & (BIT(0) | BIT(2) | BIT(3))))
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-
-- if (rxd1 & MT_RXD3_NORMAL_FCS_ERR)
-+ if (rxd1 & MT_RXD3_NORMAL_FCS_ERR) {
-+ mphy->rx_stats.rx_fcs_error++;
- status->flag |= RX_FLAG_FAILED_FCS_CRC;
-+ }
-
-- if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR)
-+ if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) {
-+ mphy->rx_stats.rx_tkip_mic_error++;
- status->flag |= RX_FLAG_MMIC_ERROR;
-+ }
-
- if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 &&
- !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) {
-@@ -1415,8 +1421,10 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb, u32 *info)
- {
- struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
-+ struct mt76_phy *phy;
- __le32 *rxd = (__le32 *)skb->data;
- __le32 *end = (__le32 *)&skb->data[skb->len];
-+ u8 band_idx;
- enum rx_pkt_type type;
-
- type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
-@@ -1458,6 +1466,10 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- }
- fallthrough;
- default:
-+ band_idx = le32_get_bits(rxd[1], MT_RXD1_NORMAL_BAND_IDX);
-+ phy = mt76_dev_phy(mdev, band_idx);
-+ if (likely(phy))
-+ phy->rx_stats.rx_rxd_drop++;
- dev_kfree_skb(skb);
- break;
- }
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index ed01c089..d290ad4d 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2854,6 +2854,46 @@ mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
- }
- DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
-
-+static int mt7996_rx_counters(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u32 rx_mac80211 = 0;
-+ int i = 0;
-+
-+ for (i = 0; i < __MT_MAX_BAND; i++) {
-+ struct mt76_phy *phy = mt76_dev_phy(&dev->mt76, i);
-+
-+ if (!phy)
-+ continue;
-+
-+ seq_printf(s, "\n==========PHY%d==========\n", i);
-+
-+#define SEQ_PRINT(_str, _rx_param) do { \
-+ seq_printf(s, _str"\n", phy->rx_stats._rx_param); \
-+ } while (0)
-+
-+ SEQ_PRINT("Rx to mac80211: %u", rx_mac80211);
-+ SEQ_PRINT("Rx drop: %u", rx_drop);
-+ SEQ_PRINT("Rx drop due to RXD type error: %u", rx_rxd_drop);
-+ SEQ_PRINT("Rx duplicated drop: %u", rx_dup_drop);
-+ SEQ_PRINT("Rx agg miss: %u", rx_agg_miss);
-+ SEQ_PRINT("Rx ICV error: %u", rx_icv_error);
-+ SEQ_PRINT("Rx FCS error: %u", rx_fcs_error);
-+ SEQ_PRINT("Rx TKIP MIC error: %u", rx_tkip_mic_error);
-+ SEQ_PRINT("Rx PN/IV error: %u", rx_pn_iv_error);
-+#undef SEQ_PRINT
-+
-+ rx_mac80211 += phy->rx_stats.rx_mac80211;
-+ }
-+
-+ seq_printf(s, "\n==========SUM==========\n");
-+ seq_printf(s, "Rx to kernel: %u\n", dev->mt76.rx_kernel);
-+ seq_printf(s, "Rx to mac80211: %u\n", rx_mac80211);
-+
-+
-+ return 0;
-+}
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -2917,6 +2957,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
-
- debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
- mt7996_trinfo_read);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "rx_counters", dir,
-+ mt7996_rx_counters);
- debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
- debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
- debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1030-mtk-wifi-mt76-mt7996-add-three-wire-pta-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1030-mtk-wifi-mt76-mt7996-add-three-wire-pta-support.patch
deleted file mode 100644
index 53f6642..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1030-mtk-wifi-mt76-mt7996-add-three-wire-pta-support.patch
+++ /dev/null
@@ -1,133 +0,0 @@
-From 311174c13b07be59176f82594b6704a64acae40c Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 24 Oct 2023 15:59:18 +0800
-Subject: [PATCH 1030/1044] mtk: wifi: mt76: mt7996: add three wire pta support
-
-three wire enable bit 0 & 1 for EXT0 & EXT1, respectively
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76_connac_mcu.h | 1 +
- mt7996/vendor.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 12 ++++++++++++
- 3 files changed, 62 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 9edb580c..a59e5a0b 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1285,6 +1285,7 @@ enum {
- MCU_UNI_CMD_PER_STA_INFO = 0x6d,
- MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
- MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
-+ MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
- };
-
- enum {
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index dae3260a..9ba6f00a 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -60,6 +60,11 @@ edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
- [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL] = { .type = NLA_U8 },
- };
-
-+static const struct nla_policy
-+three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
- static const struct nla_policy
- ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
- [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-@@ -561,6 +566,39 @@ mt7996_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- return EDCCA_MAX_BW_NUM;
- }
-
-+static int mt7996_vendor_3wire_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ const void *data, int data_len)
-+{
-+#define UNI_3WIRE_EXT_EN 0
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL];
-+ struct {
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 three_wire_mode;
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_3WIRE_EXT_EN),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ };
-+ int err;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_3WIRE_CTRL_MAX, data, data_len,
-+ three_wire_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (!tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE])
-+ return -EINVAL;
-+
-+ req.three_wire_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE]);
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PTA_3WIRE_CTRL), &req,
-+ sizeof(req), false);
-+}
-+
- static int mt7996_vendor_ibf_ctrl(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- const void *data,
-@@ -657,6 +695,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = edcca_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_3wire_ctrl,
-+ .policy = three_wire_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_3WIRE_CTRL_MAX,
-+ },
- {
- .info = {
- .vendor_id = MTK_NL80211_VENDOR_ID,
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 49f46f25..29ccc050 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -7,6 +7,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
-@@ -43,6 +44,17 @@ enum mtk_vendor_attr_edcca_dump {
- NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
-
-+enum mtk_vendor_attr_3wire_ctrl {
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
-+};
-+
- enum mtk_vendor_attr_mu_ctrl {
- MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1031-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1031-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch
deleted file mode 100644
index af11090..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1031-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch
+++ /dev/null
@@ -1,1204 +0,0 @@
-From 3eac9c285c13a00891a81064bb346b0da307a9e8 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 3 Jan 2023 09:42:07 +0800
-Subject: [PATCH 1031/1044] mtk: wifi: mt76: mt7996: support BF/MIMO debug
- commands
-
-This commit includes the following commands:
-1. starec_bf_read
-2. txbf_snd_info: start/stop sounding and set sounding period
-3. fbkRptInfo
-4. fix muru rate
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-fix the wrong wlan_idx for user3
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/mcu.c | 5 +
- mt7996/mcu.h | 4 +
- mt7996/mt7996.h | 5 +
- mt7996/mtk_debugfs.c | 120 +++++++++
- mt7996/mtk_mcu.c | 626 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h | 342 +++++++++++++++++++++++
- 6 files changed, 1102 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index c427ea20..d4cd83c6 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -741,6 +741,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- case MCU_UNI_EVENT_TESTMODE_CTRL:
- mt7996_tm_rf_test_event(dev, skb);
- break;
-+#endif
-+#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
-+ case MCU_UNI_EVENT_BF:
-+ mt7996_mcu_rx_bf_event(dev, skb);
-+ break;
- #endif
- default:
- break;
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 34fdfb26..347893c8 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -770,8 +770,12 @@ enum {
-
- enum {
- BF_SOUNDING_ON = 1,
-+ BF_PFMU_TAG_READ = 5,
-+ BF_STA_REC_READ = 11,
- BF_HW_EN_UPDATE = 17,
- BF_MOD_EN_CTRL = 20,
-+ BF_FBRPT_DBG_INFO_READ = 23,
-+ BF_TXSND_INFO = 24,
- };
-
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index c590a8b8..33936f90 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -810,6 +810,11 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
- void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
-+void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
-+int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
-+int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
-+int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index d290ad4d..2104c889 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2894,6 +2894,117 @@ static int mt7996_rx_counters(struct seq_file *s, void *data)
- return 0;
- }
-
-+static int
-+mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
-+ mt7996_starec_bf_read_set, "%lld\n");
-+
-+static ssize_t
-+mt7996_bf_txsnd_info_set(struct file *file,
-+ const char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mt7996_phy *phy = file->private_data;
-+ char buf[40];
-+ int ret;
-+
-+ if (count >= sizeof(buf))
-+ return -EINVAL;
-+
-+ if (copy_from_user(buf, user_buf, count))
-+ return -EFAULT;
-+
-+ if (count && buf[count - 1] == '\n')
-+ buf[count - 1] = '\0';
-+ else
-+ buf[count] = '\0';
-+
-+ ret = mt7996_mcu_set_txbf_snd_info(phy, buf);
-+
-+ if (ret) return -EFAULT;
-+
-+ return count;
-+}
-+
-+static const struct file_operations fops_bf_txsnd_info = {
-+ .write = mt7996_bf_txsnd_info_set,
-+ .read = NULL,
-+ .open = simple_open,
-+ .llseek = default_llseek,
-+};
-+
-+static int
-+mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
-+ mt7996_bf_fbk_rpt_set, "%lld\n");
-+
-+static int
-+mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
-+ mt7996_bf_pfmu_tag_read_set, "%lld\n");
-+
-+static int
-+mt7996_muru_fixed_rate_set(void *data, u64 val)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ return mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL,
-+ val);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_fixed_rate_enable, NULL,
-+ mt7996_muru_fixed_rate_set, "%lld\n");
-+
-+static ssize_t
-+mt7996_muru_fixed_rate_parameter_set(struct file *file,
-+ const char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mt7996_dev *dev = file->private_data;
-+ char buf[40];
-+ int ret;
-+
-+ if (count >= sizeof(buf))
-+ return -EINVAL;
-+
-+ if (copy_from_user(buf, user_buf, count))
-+ return -EFAULT;
-+
-+ if (count && buf[count - 1] == '\n')
-+ buf[count - 1] = '\0';
-+ else
-+ buf[count] = '\0';
-+
-+
-+ ret = mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+ buf);
-+
-+ if (ret) return -EFAULT;
-+
-+ return count;
-+}
-+
-+static const struct file_operations fops_muru_fixed_group_rate = {
-+ .write = mt7996_muru_fixed_rate_parameter_set,
-+ .read = NULL,
-+ .open = simple_open,
-+ .llseek = default_llseek,
-+};
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -2980,6 +3091,15 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
- debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
-
-+ debugfs_create_file("muru_fixed_rate_enable", 0600, dir, dev,
-+ &fops_muru_fixed_rate_enable);
-+ debugfs_create_file("muru_fixed_group_rate", 0600, dir, dev,
-+ &fops_muru_fixed_group_rate);
-+ debugfs_create_file("bf_txsnd_info", 0600, dir, phy, &fops_bf_txsnd_info);
-+ debugfs_create_file("bf_starec_read", 0600, dir, phy, &fops_starec_bf_read);
-+ debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
-+ debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
-+
- return 0;
- }
-
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index ea4e5bf2..67419cd9 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -280,4 +280,630 @@ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
- sizeof(req), true);
- }
-+
-+static struct tlv *
-+__mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
-+{
-+ struct tlv *ptlv, tlv = {
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(len),
-+ };
-+
-+ ptlv = skb_put(skb, len);
-+ memcpy(ptlv, &tlv, sizeof(tlv));
-+
-+ return ptlv;
-+}
-+
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+#define MT7996_MTK_BF_MAX_SIZE sizeof(struct bf_starec_read)
-+ struct uni_header hdr;
-+ struct sk_buff *skb;
-+ struct tlv *tlv;
-+ int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
-+
-+ memset(&hdr, 0, sizeof(hdr));
-+
-+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
-+ if (!skb)
-+ return -ENOMEM;
-+
-+ skb_put_data(skb, &hdr, sizeof(hdr));
-+
-+ switch (action) {
-+ case BF_PFMU_TAG_READ: {
-+ struct bf_pfmu_tag *req;
-+
-+ tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
-+ req = (struct bf_pfmu_tag *)tlv;
-+#define BFER 1
-+ req->pfmu_id = idx;
-+ req->bfer = BFER;
-+ req->band_idx = phy->mt76->band_idx;
-+ break;
-+ }
-+ case BF_STA_REC_READ: {
-+ struct bf_starec_read *req;
-+
-+ tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
-+ req = (struct bf_starec_read *)tlv;
-+ req->wlan_idx = idx;
-+ break;
-+ }
-+ case BF_FBRPT_DBG_INFO_READ: {
-+ struct bf_fbk_rpt_info *req;
-+
-+ if (idx != 0) {
-+ dev_info(dev->mt76.dev, "Invalid input");
-+ return 0;
-+ }
-+
-+ tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
-+ req = (struct bf_fbk_rpt_info *)tlv;
-+ req->action = idx;
-+ req->band_idx = phy->mt76->band_idx;
-+ break;
-+ }
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
-+}
-+
-+int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
-+{
-+ char *buf = (char *)para;
-+ __le16 input[5] = {0};
-+ u8 recv_arg = 0;
-+ struct bf_txsnd_info *req;
-+ struct uni_header hdr;
-+ struct sk_buff *skb;
-+ struct tlv *tlv;
-+ int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
-+
-+ memset(&hdr, 0, sizeof(hdr));
-+
-+ skb = mt76_mcu_msg_alloc(&phy->dev->mt76, NULL, len);
-+ if (!skb)
-+ return -ENOMEM;
-+
-+ skb_put_data(skb, &hdr, sizeof(hdr));
-+
-+ recv_arg = sscanf(buf, "%hx:%hx:%hx:%hx:%hx", &input[0], &input[1], &input[2],
-+ &input[3], &input[4]);
-+
-+ if (!recv_arg)
-+ return -EINVAL;
-+
-+ tlv = __mt7996_mcu_add_uni_tlv(skb, BF_TXSND_INFO, sizeof(*req));
-+ req = (struct bf_txsnd_info *)tlv;
-+ req->action = input[0];
-+
-+ switch (req->action) {
-+ case BF_SND_READ_INFO: {
-+ req->read_clr = input[1];
-+ break;
-+ }
-+ case BF_SND_CFG_OPT: {
-+ req->vht_opt = input[1];
-+ req->he_opt = input[2];
-+ req->glo_opt = input[3];
-+ break;
-+ }
-+ case BF_SND_CFG_INTV: {
-+ req->wlan_idx = input[1];
-+ req->snd_intv = input[2];
-+ break;
-+ }
-+ case BF_SND_STA_STOP: {
-+ req->wlan_idx = input[1];
-+ req->snd_stop = input[2];
-+ break;
-+ }
-+ case BF_SND_CFG_MAX_STA: {
-+ req->max_snd_stas = input[1];
-+ break;
-+ }
-+ case BF_SND_CFG_BFRP: {
-+ req->man = input[1];
-+ req->tx_time = input[2];
-+ req->mcs = input[3];
-+ req->ldpc = input[4];
-+ break;
-+ }
-+ case BF_SND_CFG_INF: {
-+ req->inf = input[1];
-+ break;
-+ }
-+ case BF_SND_CFG_TXOP_SND: {
-+ req->man = input[1];
-+ req->ac_queue = input[2];
-+ req->sxn_protect = input[3];
-+ req->direct_fbk = input[4];
-+ break;
-+ }
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
-+}
-+
-+void
-+mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+#define HE_MODE 3
-+ struct mt7996_mcu_bf_basic_event *event;
-+
-+ event = (struct mt7996_mcu_bf_basic_event *)skb->data;
-+
-+ dev_info(dev->mt76.dev, " bf_event tag = %d\n", event->tag);
-+
-+ switch (event->tag) {
-+ case UNI_EVENT_BF_PFMU_TAG: {
-+
-+ struct mt7996_pfmu_tag_event *tag;
-+ u32 *raw_t1, *raw_t2;
-+
-+ tag = (struct mt7996_pfmu_tag_event *) skb->data;
-+
-+ raw_t1 = (u32 *)&tag->t1;
-+ raw_t2 = (u32 *)&tag->t2;
-+
-+ dev_info(dev->mt76.dev, "=================== TXBf Profile Tag1 Info ==================\n");
-+ dev_info(dev->mt76.dev,
-+ "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
-+ raw_t1[0], raw_t1[1], raw_t1[2]);
-+ dev_info(dev->mt76.dev,
-+ "DW4 = 0x%08x, DW5 = 0x%08x, DW6 = 0x%08x\n\n",
-+ raw_t1[3], raw_t1[4], raw_t1[5]);
-+ dev_info(dev->mt76.dev, "PFMU ID = %d Invalid status = %d\n",
-+ tag->t1.pfmu_idx, tag->t1.invalid_prof);
-+ dev_info(dev->mt76.dev, "iBf/eBf = %d\n\n", tag->t1.ebf);
-+ dev_info(dev->mt76.dev, "DBW = %d\n", tag->t1.data_bw);
-+ dev_info(dev->mt76.dev, "SU/MU = %d\n", tag->t1.is_mu);
-+ dev_info(dev->mt76.dev,
-+ "nrow = %d, ncol = %d, ng = %d, LM = %d, CodeBook = %d MobCalEn = %d\n",
-+ tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
-+ tag->t1.mob_cal_en);
-+
-+ if (tag->t1.lm <= HE_MODE) {
-+ dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
-+ tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
-+ } else {
-+ dev_info(dev->mt76.dev, "PartialBW = %d\n",
-+ tag->t1.bw_info.partial_bw_info);
-+ }
-+
-+ dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
-+ tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
-+ dev_info(dev->mt76.dev, "Mem Col3 = %d, Mem Row3 = %d, Mem Col4 = %d, Mem Row4 = %d\n\n",
-+ tag->t1.col_id3, tag->t1.row_id3, tag->t1.col_id4, tag->t1.row_id4);
-+ dev_info(dev->mt76.dev,
-+ "STS0_SNR = 0x%02x, STS1_SNR = 0x%02x, STS2_SNR = 0x%02x, STS3_SNR = 0x%02x\n",
-+ tag->t1.snr_sts0, tag->t1.snr_sts1, tag->t1.snr_sts2, tag->t1.snr_sts3);
-+ dev_info(dev->mt76.dev,
-+ "STS4_SNR = 0x%02x, STS5_SNR = 0x%02x, STS6_SNR = 0x%02x, STS7_SNR = 0x%02x\n",
-+ tag->t1.snr_sts4, tag->t1.snr_sts5, tag->t1.snr_sts6, tag->t1.snr_sts7);
-+ dev_info(dev->mt76.dev, "=============================================================\n");
-+
-+ dev_info(dev->mt76.dev, "=================== TXBf Profile Tag2 Info ==================\n");
-+ dev_info(dev->mt76.dev,
-+ "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
-+ raw_t2[0], raw_t2[1], raw_t2[2]);
-+ dev_info(dev->mt76.dev,
-+ "DW3 = 0x%08x, DW4 = 0x%08x, DW5 = 0x%08x\n\n",
-+ raw_t2[3], raw_t2[4], raw_t2[5]);
-+ dev_info(dev->mt76.dev, "Smart antenna ID = 0x%x, SE index = %d\n",
-+ tag->t2.smart_ant, tag->t2.se_idx);
-+ dev_info(dev->mt76.dev, "Timeout = 0x%x\n", tag->t2.ibf_timeout);
-+ dev_info(dev->mt76.dev, "Desired BW = %d, Desired Ncol = %d, Desired Nrow = %d\n",
-+ tag->t2.ibf_data_bw, tag->t2.ibf_nc, tag->t2.ibf_nr);
-+ dev_info(dev->mt76.dev, "Desired RU Allocation = %d\n", tag->t2.ibf_ru);
-+ dev_info(dev->mt76.dev, "Mobility DeltaT = %d, Mobility LQ = %d\n",
-+ tag->t2.mob_delta_t, tag->t2.mob_lq_result);
-+ dev_info(dev->mt76.dev, "=============================================================\n");
-+ break;
-+ }
-+ case UNI_EVENT_BF_STAREC: {
-+
-+ struct mt7996_mcu_bf_starec_read *r;
-+
-+ r = (struct mt7996_mcu_bf_starec_read *)skb->data;
-+ dev_info(dev->mt76.dev, "=================== BF StaRec ===================\n"
-+ "rStaRecBf.u2PfmuId = %d\n"
-+ "rStaRecBf.fgSU_MU = %d\n"
-+ "rStaRecBf.u1TxBfCap = %d\n"
-+ "rStaRecBf.ucSoundingPhy = %d\n"
-+ "rStaRecBf.ucNdpaRate = %d\n"
-+ "rStaRecBf.ucNdpRate = %d\n"
-+ "rStaRecBf.ucReptPollRate= %d\n"
-+ "rStaRecBf.ucTxMode = %d\n"
-+ "rStaRecBf.ucNc = %d\n"
-+ "rStaRecBf.ucNr = %d\n"
-+ "rStaRecBf.ucCBW = %d\n"
-+ "rStaRecBf.ucMemRequire20M = %d\n"
-+ "rStaRecBf.ucMemRow0 = %d\n"
-+ "rStaRecBf.ucMemCol0 = %d\n"
-+ "rStaRecBf.ucMemRow1 = %d\n"
-+ "rStaRecBf.ucMemCol1 = %d\n"
-+ "rStaRecBf.ucMemRow2 = %d\n"
-+ "rStaRecBf.ucMemCol2 = %d\n"
-+ "rStaRecBf.ucMemRow3 = %d\n"
-+ "rStaRecBf.ucMemCol3 = %d\n",
-+ r->pfmu_id,
-+ r->is_su_mu,
-+ r->txbf_cap,
-+ r->sounding_phy,
-+ r->ndpa_rate,
-+ r->ndp_rate,
-+ r->rpt_poll_rate,
-+ r->tx_mode,
-+ r->nc,
-+ r->nr,
-+ r->bw,
-+ r->mem_require_20m,
-+ r->mem_row0,
-+ r->mem_col0,
-+ r->mem_row1,
-+ r->mem_col1,
-+ r->mem_row2,
-+ r->mem_col2,
-+ r->mem_row3,
-+ r->mem_col3);
-+
-+ dev_info(dev->mt76.dev, "rStaRecBf.u2SmartAnt = 0x%x\n"
-+ "rStaRecBf.ucSEIdx = %d\n"
-+ "rStaRecBf.uciBfTimeOut = 0x%x\n"
-+ "rStaRecBf.uciBfDBW = %d\n"
-+ "rStaRecBf.uciBfNcol = %d\n"
-+ "rStaRecBf.uciBfNrow = %d\n"
-+ "rStaRecBf.nr_bw160 = %d\n"
-+ "rStaRecBf.nc_bw160 = %d\n"
-+ "rStaRecBf.ru_start_idx = %d\n"
-+ "rStaRecBf.ru_end_idx = %d\n"
-+ "rStaRecBf.trigger_su = %d\n"
-+ "rStaRecBf.trigger_mu = %d\n"
-+ "rStaRecBf.ng16_su = %d\n"
-+ "rStaRecBf.ng16_mu = %d\n"
-+ "rStaRecBf.codebook42_su = %d\n"
-+ "rStaRecBf.codebook75_mu = %d\n"
-+ "rStaRecBf.he_ltf = %d\n"
-+ "rStaRecBf.pp_fd_val = %d\n"
-+ "======================================\n",
-+ r->smart_ant,
-+ r->se_idx,
-+ r->bf_timeout,
-+ r->bf_dbw,
-+ r->bf_ncol,
-+ r->bf_nrow,
-+ r->nr_lt_bw80,
-+ r->nc_lt_bw80,
-+ r->ru_start_idx,
-+ r->ru_end_idx,
-+ r->trigger_su,
-+ r->trigger_mu,
-+ r->ng16_su,
-+ r->ng16_mu,
-+ r->codebook42_su,
-+ r->codebook75_mu,
-+ r->he_ltf,
-+ r->pp_fd_val);
-+ break;
-+ }
-+ case UNI_EVENT_BF_FBK_INFO: {
-+ struct mt7996_mcu_txbf_fbk_info *info;
-+ __le32 total, i;
-+
-+ info = (struct mt7996_mcu_txbf_fbk_info *)skb->data;
-+
-+ total = info->u4PFMUWRDoneCnt + info->u4PFMUWRFailCnt;
-+ total += info->u4PFMUWRTimeoutFreeCnt + info->u4FbRptPktDropCnt;
-+
-+ dev_info(dev->mt76.dev, "\n");
-+ dev_info(dev->mt76.dev, "\x1b[32m =================================\x1b[m\n");
-+ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRDoneCnt = %u\x1b[m\n",
-+ info->u4PFMUWRDoneCnt);
-+ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRFailCnt = %u\x1b[m\n",
-+ info->u4PFMUWRFailCnt);
-+ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeOutCnt = %u\x1b[m\n",
-+ info->u4PFMUWRTimeOutCnt);
-+ dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeoutFreeCnt = %u\x1b[m\n",
-+ info->u4PFMUWRTimeoutFreeCnt);
-+ dev_info(dev->mt76.dev, "\x1b[32m FbRptPktDropCnt = %u\x1b[m\n",
-+ info->u4FbRptPktDropCnt);
-+ dev_info(dev->mt76.dev, "\x1b[32m TotalFbRptPkt = %u\x1b[m\n", total);
-+ dev_info(dev->mt76.dev, "\x1b[32m PollPFMUIntrStatTimeOut = %u(micro-sec)\x1b[m\n",
-+ info->u4PollPFMUIntrStatTimeOut);
-+ dev_info(dev->mt76.dev, "\x1b[32m FbRptDeQInterval = %u(milli-sec)\x1b[m\n",
-+ info->u4DeQInterval);
-+ dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptTimeOutQ = %u\x1b[m\n",
-+ info->u4RptPktTimeOutListNum);
-+ dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptQ = %u\x1b[m\n",
-+ info->u4RptPktListNum);
-+
-+ // [ToDo] Check if it is valid entry
-+ for (i = 0; ((i < 5) && (i < CFG_BF_STA_REC_NUM)); i++) {
-+
-+ // [ToDo] AID needs to be refined
-+ dev_info(dev->mt76.dev,"\x1b[32m AID%u RxFbRptCnt = %u\x1b[m\n"
-+ , i, info->au4RxPerStaFbRptCnt[i]);
-+ }
-+
-+ break;
-+ }
-+ case UNI_EVENT_BF_TXSND_INFO: {
-+ struct mt7996_mcu_tx_snd_info *info;
-+ struct uni_event_bf_txsnd_sta_info *snd_sta_info;
-+ int Idx;
-+ int max_wtbl_size = mt7996_wtbl_size(dev);
-+
-+ info = (struct mt7996_mcu_tx_snd_info *)skb->data;
-+ dev_info(dev->mt76.dev, "=================== Global Setting ===================\n");
-+
-+ dev_info(dev->mt76.dev, "VhtOpt = 0x%02X, HeOpt = 0x%02X, GloOpt = 0x%02X\n",
-+ info->vht_opt, info->he_opt, info->glo_opt);
-+
-+ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+ dev_info(dev->mt76.dev, "SuSta[%d] = 0x%08X,", Idx,
-+ info->snd_rec_su_sta[Idx]);
-+ if ((Idx & 0x03) == 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+ }
-+
-+ if ((Idx & 0x03) != 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+
-+
-+ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+ dev_info(dev->mt76.dev, "VhtMuSta[%d] = 0x%08X,", Idx, info->snd_rec_vht_mu_sta[Idx]);
-+ if ((Idx & 0x03) == 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+ }
-+
-+ if ((Idx & 0x03) != 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+
-+ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+ dev_info(dev->mt76.dev, "HeTBSta[%d] = 0x%08X,", Idx, info->snd_rec_he_tb_sta[Idx]);
-+ if ((Idx & 0x03) == 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+ }
-+
-+ if ((Idx & 0x03) != 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+
-+ for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+ dev_info(dev->mt76.dev, "EhtTBSta[%d] = 0x%08X,", Idx, info->snd_rec_eht_tb_sta[Idx]);
-+ if ((Idx & 0x03) == 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+ }
-+
-+ if ((Idx & 0x03) != 0x03)
-+ dev_info(dev->mt76.dev, "\n");
-+
-+ for (Idx = 0; Idx < CFG_WIFI_RAM_BAND_NUM; Idx++) {
-+ dev_info(dev->mt76.dev, "Band%u:\n", Idx);
-+ dev_info(dev->mt76.dev, " Wlan Idx For VHT MC Sounding = %u\n", info->wlan_idx_for_mc_snd[Idx]);
-+ dev_info(dev->mt76.dev, " Wlan Idx For HE TB Sounding = %u\n", info->wlan_idx_for_he_tb_snd[Idx]);
-+ dev_info(dev->mt76.dev, " Wlan Idx For EHT TB Sounding = %u\n", info->wlan_idx_for_eht_tb_snd[Idx]);
-+ }
-+
-+ dev_info(dev->mt76.dev, "ULLen = %d, ULMcs = %d, ULLDCP = %d\n",
-+ info->ul_length, info->mcs, info->ldpc);
-+
-+ dev_info(dev->mt76.dev, "=================== STA Info ===================\n");
-+
-+ for (Idx = 1; (Idx < 5 && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
-+ snd_sta_info = &info->snd_sta_info[Idx];
-+ dev_info(dev->mt76.dev, "Idx%2u Interval = %d, interval counter = %d, TxCnt = %d, StopReason = 0x%02X\n",
-+ Idx,
-+ snd_sta_info->snd_intv,
-+ snd_sta_info->snd_intv_cnt,
-+ snd_sta_info->snd_tx_cnt,
-+ snd_sta_info->snd_stop_reason);
-+ }
-+
-+ dev_info(dev->mt76.dev, "=================== STA Info Connected ===================\n");
-+ // [ToDo] How to iterate and get AID info of station
-+ // Check UniEventBFCtrlTxSndHandle() on Logan
-+
-+ //hardcode max_wtbl_size as 5
-+ max_wtbl_size = 5;
-+ for (Idx = 1; ((Idx < max_wtbl_size) && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
-+
-+ // [ToDo] We do not show AID info here
-+ snd_sta_info = &info->snd_sta_info[Idx];
-+ dev_info(dev->mt76.dev, " Interval = %d (%u ms), interval counter = %d (%u ms), TxCnt = %d, StopReason = 0x%02X\n",
-+ snd_sta_info->snd_intv,
-+ snd_sta_info->snd_intv * 10,
-+ snd_sta_info->snd_intv_cnt,
-+ snd_sta_info->snd_intv_cnt * 10,
-+ snd_sta_info->snd_tx_cnt,
-+ snd_sta_info->snd_stop_reason);
-+ }
-+
-+ dev_info(dev->mt76.dev, "======================================\n");
-+
-+ break;
-+ }
-+ default:
-+ dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
-+ __func__, event->tag);
-+ }
-+
-+}
-+
-+
-+int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val)
-+{
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 value;
-+ __le16 rsv;
-+ } __packed data = {
-+ .tag = cpu_to_le16(action),
-+ .len = cpu_to_le16(sizeof(data) - 4),
-+ .value = cpu_to_le16(!!val),
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
-+ false);
-+}
-+
-+int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para)
-+{
-+ char *buf = (char *)para;
-+ u8 num_user = 0, recv_arg = 0, max_mcs = 0, usr_mcs[4] = {0};
-+ __le16 bw;
-+ int i;
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 cmd_version;
-+ u8 cmd_revision;
-+ __le16 rsv;
-+
-+ struct uni_muru_mum_set_group_tbl_entry entry;
-+ } __packed data = {
-+ .tag = cpu_to_le16(action),
-+ .len = cpu_to_le16(sizeof(data) - 4),
-+ };
-+
-+#define __RUALLOC_TYPE_CHECK_HE(BW) ((BW == RUALLOC_BW20) || (BW == RUALLOC_BW40) || (BW == RUALLOC_BW80) || (BW == RUALLOC_BW160))
-+#define __RUALLOC_TYPE_CHECK_EHT(BW) (__RUALLOC_TYPE_CHECK_HE(BW) || (BW == RUALLOC_BW320))
-+ /* [Num of user] - 1~4
-+ * [RUAlloc] - BW320: 395, BW160: 137, BW80: 134, BW40: 130, BW20: 122
-+ * [LTF/GI] - For VHT, short GI: 0, Long GI: 1; *
-+ * For HE/EHT, 4xLTF+3.2us: 0, 4xLTF+0.8us: 1, 2xLTF+0.8us:2
-+ * [Phy/FullBW] - VHT: 0 / HEFullBw: 1 / HEPartialBw: 2 / EHTFullBW: 3, EHTPartialBW: 4
-+ * [DL/UL] DL: 0, UL: 1, DL_UL: 2
-+ * [Wcid User0] - WCID 0
-+ * [MCS of WCID0] - For HE/VHT, 0-11: 1ss MCS0-MCS11, 12-23: 2SS MCS0-MCS11
-+ * For EHT, 0-13: 1ss MCS0-MCS13, 14-27: 2SS MCS0-MCS13
-+ * [WCID 1]
-+ * [MCS of WCID1]
-+ * [WCID 2]
-+ * [MCS of WCID2]
-+ * [WCID 3]
-+ * [MCS of WCID3]
-+ */
-+
-+ recv_arg = sscanf(buf, "%hhu %hu %hhu %hhu %hhu %hu %hhu %hu %hhu %hu %hhu %hu %hhu",
-+ &num_user, &bw, &data.entry.gi, &data.entry.capa, &data.entry.dl_ul,
-+ &data.entry.wlan_idx0, &usr_mcs[0],
-+ &data.entry.wlan_idx1, &usr_mcs[1],
-+ &data.entry.wlan_idx2, &usr_mcs[2],
-+ &data.entry.wlan_idx3, &usr_mcs[3]);
-+
-+ if (recv_arg != (5 + (2 * num_user))) {
-+ dev_err(dev->mt76.dev, "The number of argument is invalid\n");
-+ goto error;
-+ }
-+
-+ if (num_user > 0 && num_user < 5)
-+ data.entry.num_user = num_user - 1;
-+ else {
-+ dev_err(dev->mt76.dev, "The number of user count is invalid\n");
-+ goto error;
-+ }
-+
-+ /**
-+ * Older chip shall be set as HE. Refer to getHWSupportByChip() in Logan
-+ * driver to know the value for differnt chips
-+ */
-+ data.cmd_version = UNI_CMD_MURU_VER_EHT;
-+
-+ if (data.cmd_version == UNI_CMD_MURU_VER_EHT)
-+ max_mcs = UNI_MAX_MCS_SUPPORT_EHT;
-+ else
-+ max_mcs = UNI_MAX_MCS_SUPPORT_HE;
-+
-+
-+ // Parameter Check
-+ if (data.cmd_version != UNI_CMD_MURU_VER_EHT) {
-+ if ((data.entry.capa > MAX_MODBF_HE) || (bw == RUALLOC_BW320))
-+ goto error;
-+ } else {
-+ if ((data.entry.capa <= MAX_MODBF_HE) && (bw == RUALLOC_BW320))
-+ goto error;
-+ }
-+
-+ if (data.entry.capa <= MAX_MODBF_HE)
-+ max_mcs = UNI_MAX_MCS_SUPPORT_HE;
-+
-+ if (__RUALLOC_TYPE_CHECK_EHT(bw)) {
-+ data.entry.ru_alloc = (u8)(bw & 0xFF);
-+ if (bw == RUALLOC_BW320)
-+ data.entry.ru_alloc_ext = (u8)(bw >> 8);
-+ } else {
-+ dev_err(dev->mt76.dev, "RU_ALLOC argument is invalid\n");
-+ goto error;
-+ }
-+
-+ if ((data.entry.gi > 2) ||
-+ ((data.entry.gi > 1) && (data.entry.capa == MAX_MODBF_VHT))) {
-+ dev_err(dev->mt76.dev, "GI argument is invalid\n");
-+ goto error;
-+ }
-+
-+ if (data.entry.dl_ul > 2) {
-+ dev_err(dev->mt76.dev, "DL_UL argument is invalid\n");
-+ goto error;
-+ }
-+
-+#define __mcs_handler(_n) \
-+ do { \
-+ if (usr_mcs[_n] > max_mcs) { \
-+ usr_mcs[_n] -= (max_mcs + 1); \
-+ data.entry.nss##_n = 1; \
-+ if (usr_mcs[_n] > max_mcs) \
-+ usr_mcs[_n] = max_mcs; \
-+ } \
-+ if ((data.entry.dl_ul & 0x1) == 0) \
-+ data.entry.dl_mcs_user##_n = usr_mcs[_n]; \
-+ if ((data.entry.dl_ul & 0x3) > 0) \
-+ data.entry.ul_mcs_user##_n = usr_mcs[_n]; \
-+ } \
-+ while (0)
-+
-+ for (i=0; i<= data.entry.num_user; i++) {
-+ switch (i) {
-+ case 0:
-+ __mcs_handler(0);
-+ break;
-+ case 1:
-+ __mcs_handler(1);
-+ break;
-+ case 2:
-+ __mcs_handler(2);
-+ break;
-+ case 3:
-+ __mcs_handler(3);
-+ break;
-+ default:
-+ break;
-+ }
-+ }
-+#undef __mcs_handler
-+
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data,
-+ sizeof(data), false);
-+
-+error:
-+ dev_err(dev->mt76.dev, "Command failed!\n");
-+ return -EINVAL;
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 098e63ae..71c6684a 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -119,6 +119,348 @@ enum {
- EDCCA_FCC = 1,
- EDCCA_ETSI = 2,
- EDCCA_JAPAN = 3
-+
-+struct bf_pfmu_tag {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 pfmu_id;
-+ bool bfer;
-+ u8 band_idx;
-+ u8 __rsv[5];
-+ u8 buf[56];
-+} __packed;
-+
-+struct bf_starec_read {
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 wlan_idx;
-+ u8 __rsv[2];
-+} __packed;
-+
-+struct bf_fbk_rpt_info {
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 wlan_idx; // Only need for dynamic_pfmu_update 0x4
-+ u8 action;
-+ u8 band_idx;
-+ u8 __rsv[4];
-+
-+} __packed;
-+
-+struct bf_txsnd_info {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 action;
-+ u8 read_clr;
-+ u8 vht_opt;
-+ u8 he_opt;
-+ __le16 wlan_idx;
-+ u8 glo_opt;
-+ u8 snd_intv;
-+ u8 snd_stop;
-+ u8 max_snd_stas;
-+ u8 tx_time;
-+ u8 mcs;
-+ u8 ldpc;
-+ u8 inf;
-+ u8 man;
-+ u8 ac_queue;
-+ u8 sxn_protect;
-+ u8 direct_fbk;
-+ u8 __rsv[2];
-+} __packed;
-+
-+struct mt7996_mcu_bf_basic_event {
-+ struct mt7996_mcu_rxd rxd;
-+
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+};
-+
-+struct mt7996_mcu_bf_starec_read {
-+
-+ struct mt7996_mcu_bf_basic_event event;
-+
-+ __le16 pfmu_id;
-+ bool is_su_mu;
-+ u8 txbf_cap;
-+ u8 sounding_phy;
-+ u8 ndpa_rate;
-+ u8 ndp_rate;
-+ u8 rpt_poll_rate;
-+ u8 tx_mode;
-+ u8 nc;
-+ u8 nr;
-+ u8 bw;
-+ u8 total_mem_require;
-+ u8 mem_require_20m;
-+ u8 mem_row0;
-+ u8 mem_col0:6;
-+ u8 mem_row0_msb:2;
-+ u8 mem_row1;
-+ u8 mem_col1:6;
-+ u8 mem_row1_msb:2;
-+ u8 mem_row2;
-+ u8 mem_col2:6;
-+ u8 mem_row2_msb:2;
-+ u8 mem_row3;
-+ u8 mem_col3:6;
-+ u8 mem_row3_msb:2;
-+
-+ __le16 smart_ant;
-+ u8 se_idx;
-+ u8 auto_sounding_ctrl;
-+
-+ u8 bf_timeout;
-+ u8 bf_dbw;
-+ u8 bf_ncol;
-+ u8 bf_nrow;
-+
-+ u8 nr_lt_bw80;
-+ u8 nc_lt_bw80;
-+ u8 ru_start_idx;
-+ u8 ru_end_idx;
-+
-+ bool trigger_su;
-+ bool trigger_mu;
-+
-+ bool ng16_su;
-+ bool ng16_mu;
-+
-+ bool codebook42_su;
-+ bool codebook75_mu;
-+
-+ u8 he_ltf;
-+ u8 pp_fd_val;
-+};
-+
-+#define TXBF_PFMU_ID_NUM_MAX 48
-+
-+#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 TXBF_PFMU_ID_NUM_MAX
-+#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 TXBF_PFMU_ID_NUM_MAX
-+#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2 TXBF_PFMU_ID_NUM_MAX
-+
-+/* CFG_BF_STA_REC shall be varied based on BAND Num */
-+#define CFG_BF_STA_REC_NUM (TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2)
-+
-+#define BF_SND_CTRL_STA_DWORD_CNT ((CFG_BF_STA_REC_NUM + 0x1F) >> 5)
-+
-+#ifndef ALIGN_4
-+ #define ALIGN_4(_value) (((_value) + 3) & ~3u)
-+#endif /* ALIGN_4 */
-+
-+#define CFG_WIFI_RAM_BAND_NUM 3
-+
-+struct uni_event_bf_txsnd_sta_info {
-+ u8 snd_intv; /* Sounding interval upper bound, unit:15ms */
-+ u8 snd_intv_cnt; /* Sounding interval counter */
-+ u8 snd_tx_cnt; /* Tx sounding count for debug */
-+ u8 snd_stop_reason; /* Bitwise reason to put in Stop Queue */
-+};
-+
-+struct mt7996_mcu_tx_snd_info {
-+
-+ struct mt7996_mcu_bf_basic_event event;
-+
-+ u8 vht_opt;
-+ u8 he_opt;
-+ u8 glo_opt;
-+ u8 __rsv;
-+ __le32 snd_rec_su_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+ __le32 snd_rec_vht_mu_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+ __le32 snd_rec_he_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+ __le32 snd_rec_eht_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+ __le16 wlan_idx_for_mc_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
-+ __le16 wlan_idx_for_he_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
-+ __le16 wlan_idx_for_eht_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
-+ __le16 ul_length;
-+ u8 mcs;
-+ u8 ldpc;
-+ struct uni_event_bf_txsnd_sta_info snd_sta_info[CFG_BF_STA_REC_NUM];
-+};
-+
-+struct mt7996_mcu_txbf_fbk_info {
-+
-+ struct mt7996_mcu_bf_basic_event event;
-+
-+ __le32 u4DeQInterval; /* By ms */
-+ __le32 u4PollPFMUIntrStatTimeOut; /* micro-sec */
-+ __le32 u4RptPktTimeOutListNum;
-+ __le32 u4RptPktListNum;
-+ __le32 u4PFMUWRTimeOutCnt;
-+ __le32 u4PFMUWRFailCnt;
-+ __le32 u4PFMUWRDoneCnt;
-+ __le32 u4PFMUWRTimeoutFreeCnt;
-+ __le32 u4FbRptPktDropCnt;
-+ __le32 au4RxPerStaFbRptCnt[CFG_BF_STA_REC_NUM];
-+};
-+
-+struct pfmu_ru_field {
-+ __le32 ru_start_id:7;
-+ __le32 _rsv1:1;
-+ __le32 ru_end_id:7;
-+ __le32 _rsv2:1;
-+} __packed;
-+
-+struct pfmu_partial_bw_info {
-+ __le32 partial_bw_info:9;
-+ __le32 _rsv1:7;
-+} __packed;
-+
-+struct mt7996_pfmu_tag1 {
-+ __le32 pfmu_idx:10;
-+ __le32 ebf:1;
-+ __le32 data_bw:3;
-+ __le32 lm:3;
-+ __le32 is_mu:1;
-+ __le32 nr:3;
-+ __le32 nc:3;
-+ __le32 codebook:2;
-+ __le32 ngroup:2;
-+ __le32 invalid_prof:1;
-+ __le32 _rsv:3;
-+
-+ __le32 col_id1:7, row_id1:9;
-+ __le32 col_id2:7, row_id2:9;
-+ __le32 col_id3:7, row_id3:9;
-+ __le32 col_id4:7, row_id4:9;
-+
-+ union {
-+ struct pfmu_ru_field field;
-+ struct pfmu_partial_bw_info bw_info;
-+ };
-+ __le32 mob_cal_en:1;
-+ __le32 _rsv2:3;
-+ __le32 mob_ru_alloc:9; /* EHT profile uses full 9 bit */
-+ __le32 _rsv3:3;
-+
-+ __le32 snr_sts0:8, snr_sts1:8, snr_sts2:8, snr_sts3:8;
-+ __le32 snr_sts4:8, snr_sts5:8, snr_sts6:8, snr_sts7:8;
-+
-+ __le32 _rsv4;
-+} __packed;
-+
-+struct mt7996_pfmu_tag2 {
-+ __le32 smart_ant:24;
-+ __le32 se_idx:5;
-+ __le32 _rsv:3;
-+
-+ __le32 _rsv1:16;
-+ __le32 ibf_timeout:8;
-+ __le32 _rsv2:8;
-+
-+ __le32 ibf_data_bw:3;
-+ __le32 ibf_nc:3;
-+ __le32 ibf_nr:3;
-+ __le32 ibf_ru:9;
-+ __le32 _rsv3:14;
-+
-+ __le32 mob_delta_t:8;
-+ __le32 mob_lq_result:7;
-+ __le32 _rsv5:1;
-+ __le32 _rsv6:16;
-+
-+ __le32 _rsv7;
-+} __packed;
-+
-+struct mt7996_pfmu_tag_event {
-+ struct mt7996_mcu_bf_basic_event event;
-+
-+ u8 bfer;
-+ u8 __rsv[3];
-+
-+ struct mt7996_pfmu_tag1 t1;
-+ struct mt7996_pfmu_tag2 t2;
-+};
-+
-+enum {
-+ UNI_EVENT_BF_PFMU_TAG = 0x5,
-+ UNI_EVENT_BF_PFMU_DATA = 0x7,
-+ UNI_EVENT_BF_STAREC = 0xB,
-+ UNI_EVENT_BF_CAL_PHASE = 0xC,
-+ UNI_EVENT_BF_FBK_INFO = 0x17,
-+ UNI_EVENT_BF_TXSND_INFO = 0x18,
-+ UNI_EVENT_BF_PLY_INFO = 0x19,
-+ UNI_EVENT_BF_METRIC_INFO = 0x1A,
-+ UNI_EVENT_BF_TXCMD_CFG_INFO = 0x1B,
-+ UNI_EVENT_BF_SND_CNT_INFO = 0x1D,
-+ UNI_EVENT_BF_MAX_NUM
-+};
-+
-+enum {
-+ UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
-+ UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+};
-+
-+struct uni_muru_mum_set_group_tbl_entry {
-+ __le16 wlan_idx0;
-+ __le16 wlan_idx1;
-+ __le16 wlan_idx2;
-+ __le16 wlan_idx3;
-+
-+ u8 dl_mcs_user0:4;
-+ u8 dl_mcs_user1:4;
-+ u8 dl_mcs_user2:4;
-+ u8 dl_mcs_user3:4;
-+ u8 ul_mcs_user0:4;
-+ u8 ul_mcs_user1:4;
-+ u8 ul_mcs_user2:4;
-+ u8 ul_mcs_user3:4;
-+
-+ u8 num_user:2;
-+ u8 rsv:6;
-+ u8 nss0:2;
-+ u8 nss1:2;
-+ u8 nss2:2;
-+ u8 nss3:2;
-+ u8 ru_alloc;
-+ u8 ru_alloc_ext;
-+
-+ u8 capa;
-+ u8 gi;
-+ u8 dl_ul;
-+ u8 _rsv2;
-+};
-+
-+enum UNI_CMD_MURU_VER_T {
-+ UNI_CMD_MURU_VER_LEG = 0,
-+ UNI_CMD_MURU_VER_HE,
-+ UNI_CMD_MURU_VER_EHT,
-+ UNI_CMD_MURU_VER_MAX
-+};
-+
-+#define UNI_MAX_MCS_SUPPORT_HE 11
-+#define UNI_MAX_MCS_SUPPORT_EHT 13
-+
-+enum {
-+ RUALLOC_BW20 = 122,
-+ RUALLOC_BW40 = 130,
-+ RUALLOC_BW80 = 134,
-+ RUALLOC_BW160 = 137,
-+ RUALLOC_BW320 = 395,
-+};
-+
-+enum {
-+ MAX_MODBF_VHT = 0,
-+ MAX_MODBF_HE = 2,
-+ MAX_MODBF_EHT = 4,
-+};
-+
-+enum {
-+ BF_SND_READ_INFO = 0,
-+ BF_SND_CFG_OPT,
-+ BF_SND_CFG_INTV,
-+ BF_SND_STA_STOP,
-+ BF_SND_CFG_MAX_STA,
-+ BF_SND_CFG_BFRP,
-+ BF_SND_CFG_INF,
-+ BF_SND_CFG_TXOP_SND
- };
-
- enum {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1032-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1032-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch
deleted file mode 100644
index f6e1f07..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1032-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From e6ce0b945f7cfe7b181223b5e5db4a213c3c4433 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 13 Jun 2023 14:49:02 +0800
-Subject: [PATCH 1032/1044] mtk: wifi: mt76: mt7996: add build the following
- MURU mcu command tlvs
-
-It includes the following tlvs:
-1. MURU tlv id 0x10, 0x33, 0xC8, 0xC9, 0xCA, 0xCC, 0xCD
-2. BF tlv id 0x1c
-
----
- mt7996/mcu.h | 1 +
- mt7996/mt7996.h | 3 ++
- mt7996/mtk_debugfs.c | 12 +++++++
- mt7996/mtk_mcu.c | 78 ++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h | 14 ++++++++
- 5 files changed, 108 insertions(+)
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 347893c8..527c9c79 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -776,6 +776,7 @@ enum {
- BF_MOD_EN_CTRL = 20,
- BF_FBRPT_DBG_INFO_READ = 23,
- BF_TXSND_INFO = 24,
-+ BF_CFG_PHY = 28,
- };
-
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 33936f90..4a268a4e 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -815,6 +815,9 @@ void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
- int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
- int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
-+int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
-+int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
-+int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 2104c889..9e7acd4b 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3005,6 +3005,16 @@ static const struct file_operations fops_muru_fixed_group_rate = {
- .llseek = default_llseek,
- };
-
-+static int mt7996_muru_prot_thr_set(void *data, u64 val)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ return mt7996_mcu_muru_set_prot_frame_thr(phy->dev, (u32)val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
-+ mt7996_muru_prot_thr_set, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -3100,6 +3110,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
- debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
-
-+ debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
-+
- return 0;
- }
-
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index 67419cd9..b7d86384 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -906,4 +906,82 @@ error:
- return -EINVAL;
- }
-
-+/**
-+ * This function can be used to build the following commands
-+ * MURU_SUTX_CTRL (0x10)
-+ * SET_FORCE_MU (0x33)
-+ * SET_MUDL_ACK_POLICY (0xC8)
-+ * SET_TRIG_TYPE (0xC9)
-+ * SET_20M_DYN_ALGO (0xCA)
-+ * SET_CERT_MU_EDCA_OVERRIDE (0xCD)
-+ */
-+int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val)
-+{
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 config;
-+ u8 rsv[3];
-+ } __packed data = {
-+ .tag = cpu_to_le16(action),
-+ .len = cpu_to_le16(sizeof(data) - 4),
-+ .config = (u8) val,
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
-+ false);
-+}
-+
-+int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val)
-+{
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le32 prot_frame_thr;
-+ } __packed data = {
-+ .tag = cpu_to_le16(UNI_CMD_MURU_PROT_FRAME_THR),
-+ .len = cpu_to_le16(sizeof(data) - 4),
-+ .prot_frame_thr = cpu_to_le32(val),
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
-+ false);
-+}
-+
-+int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
-+{
-+#define BF_PHY_SMTH_INT_BYPASS 0
-+#define BYPASS_VAL 1
-+ struct mt7996_dev *dev = phy->dev;
-+ struct {
-+ u8 _rsv[4];
-+
-+ u16 tag;
-+ u16 len;
-+
-+ u8 action;
-+ u8 band_idx;
-+ u8 smthintbypass;
-+ u8 __rsv2[5];
-+ } __packed data = {
-+ .tag = cpu_to_le16(BF_CFG_PHY),
-+ .len = cpu_to_le16(sizeof(data) - 4),
-+ .action = BF_PHY_SMTH_INT_BYPASS,
-+ .band_idx = phy->mt76->band_idx,
-+ .smthintbypass = val,
-+ };
-+
-+ if (val != BYPASS_VAL)
-+ return -EINVAL;
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &data, sizeof(data),
-+ true);
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 71c6684a..b061950c 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -119,6 +119,20 @@ enum {
- EDCCA_FCC = 1,
- EDCCA_ETSI = 2,
- EDCCA_JAPAN = 3
-+};
-+
-+enum {
-+ UNI_CMD_MURU_SUTX_CTRL = 0x10,
-+ UNI_CMD_MURU_FIXED_RATE_CTRL,
-+ UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+ UNI_CMD_MURU_SET_FORCE_MU = 0x33,
-+ UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
-+ UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
-+ UNI_CMD_MURU_SET_TRIG_TYPE,
-+ UNI_CMD_MURU_SET_20M_DYN_ALGO,
-+ UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
-+ UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
-+};
-
- struct bf_pfmu_tag {
- __le16 tag;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1033-mtk-wifi-mt76-mt7996-add-cert-patch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1033-mtk-wifi-mt76-mt7996-add-cert-patch.patch
deleted file mode 100644
index 951e868..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1033-mtk-wifi-mt76-mt7996-add-cert-patch.patch
+++ /dev/null
@@ -1,1094 +0,0 @@
-From 775ed42df549473e6e9e966ac2d9abcfed74a73b Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Mon, 14 Aug 2023 13:36:58 +0800
-Subject: [PATCH 1033/1044] mtk: wifi: mt76: mt7996: add cert patch
-
-This patch includes TGac and TGax
-
-Commit histroy:
-
-Add vendor cmd set ap wireless rts_sigta support
-
-Signed-off-by: ye he <ye.he@mediatek.com>
----
- mt7996/mac.c | 9 ++
- mt7996/main.c | 31 ++++++-
- mt7996/mcu.c | 40 +++++++++
- mt7996/mcu.h | 6 ++
- mt7996/mt7996.h | 13 +++
- mt7996/mtk_mcu.c | 205 ++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h | 184 +++++++++++++++++++++++++++++++++++--
- mt7996/vendor.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++-
- mt7996/vendor.h | 67 ++++++++++++++
- 9 files changed, 778 insertions(+), 7 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 7885bc4c..bf42ae07 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -10,6 +10,7 @@
- #include "../dma.h"
- #include "mac.h"
- #include "mcu.h"
-+#include "vendor.h"
-
- #define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
-
-@@ -2285,6 +2286,14 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
- }
- }
-
-+void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en)
-+{
-+ if (en)
-+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
-+ else
-+ ieee80211_hw_clear(hw, SUPPORTS_AMSDU_IN_AMPDU);
-+}
-+
- void mt7996_mac_sta_rc_work(struct work_struct *work)
- {
- struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index dbe3d33f..d314d9fb 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -588,6 +588,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- bool beacon, bool mcast)
- {
- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
-+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt76_phy *mphy = hw->priv;
- u16 rate;
- u8 i, idx;
-@@ -597,6 +598,9 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- if (beacon) {
- struct mt7996_phy *phy = mphy->priv;
-
-+ if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
-+ rate = 0x0200;
-+
- /* odd index for driver, even index for firmware */
- idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
- if (phy->beacon_rate != rate)
-@@ -726,6 +730,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- u8 band_idx = mvif->phy->mt76->band_idx;
- int ret, idx;
-
-+#ifdef CONFIG_MTK_VENDOR
-+ struct mt7996_phy *phy = &dev->phy;
-+#endif
-+
- idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
- if (idx < 0)
- return -ENOSPC;
-@@ -751,7 +759,28 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- if (ret)
- return ret;
-
-- return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
-+ ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
-+ if (ret)
-+ return ret;
-+
-+#ifdef CONFIG_MTK_VENDOR
-+ switch (band_idx) {
-+ case MT_BAND1:
-+ phy = mt7996_phy2(dev);
-+ break;
-+ case MT_BAND2:
-+ phy = mt7996_phy3(dev);
-+ break;
-+ case MT_BAND0:
-+ default:
-+ break;
-+ }
-+
-+ if (phy && phy->muru_onoff & MUMIMO_DL_CERT)
-+ mt7996_mcu_set_mimo(phy);
-+#endif
-+
-+ return 0;
- }
-
- void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index d4cd83c6..3a34afd9 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1349,6 +1349,10 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- {
- struct sta_rec_vht *vht;
- struct tlv *tlv;
-+#ifdef CONFIG_MTK_VENDOR
-+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+ struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->phy;
-+#endif
-
- /* For 6G band, this tlv is necessary to let hw work normally */
- if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
-@@ -1360,6 +1364,9 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
- vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
- vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
-+#ifdef CONFIG_MTK_VENDOR
-+ vht->rts_bw_sig = phy->rts_bw_sig;
-+#endif
- }
-
- static void
-@@ -4450,6 +4457,27 @@ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val)
- &req, sizeof(req), true);
- }
-
-+int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable)
-+{
-+ struct {
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ bool enable;
-+ u8 _rsv2[3];
-+ } __packed req = {
-+ .band_idx = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(option),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .enable = enable,
-+ };
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
-+ &req, sizeof(req), true);
-+}
-+
- int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
- {
- struct {
-@@ -4965,6 +4993,18 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
-
- switch (mode) {
-+ case RATE_PARAM_FIXED_OFDMA:
-+ if (val == 3)
-+ phy->muru_onoff = OFDMA_DL;
-+ else
-+ phy->muru_onoff = val;
-+ break;
-+ case RATE_PARAM_FIXED_MIMO:
-+ if (val == 0)
-+ phy->muru_onoff = MUMIMO_DL_CERT | MUMIMO_DL;
-+ else
-+ phy->muru_onoff = MUMIMO_UL;
-+ break;
- case RATE_PARAM_AUTO_MU:
- if (val < 0 || val > 15) {
- printk("Wrong value! The value is between 0-15.\n");
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 527c9c79..af078edd 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -755,6 +755,8 @@ enum {
- RATE_PARAM_FIXED_GI = 11,
- RATE_PARAM_AUTO = 20,
- #ifdef CONFIG_MTK_VENDOR
-+ RATE_PARAM_FIXED_MIMO = 30,
-+ RATE_PARAM_FIXED_OFDMA = 31,
- RATE_PARAM_AUTO_MU = 32,
- #endif
- };
-@@ -767,6 +769,7 @@ enum {
- #define OFDMA_UL BIT(1)
- #define MUMIMO_DL BIT(2)
- #define MUMIMO_UL BIT(3)
-+#define MUMIMO_DL_CERT BIT(4)
-
- enum {
- BF_SOUNDING_ON = 1,
-@@ -853,11 +856,14 @@ enum {
- UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
- UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
- UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
-+ UNI_BAND_CONFIG_RTS_SIGTA_EN = 0x09,
-+ UNI_BAND_CONFIG_DIS_SECCH_CCA_DET = 0x0a,
- };
-
- enum {
- UNI_WSYS_CONFIG_FW_LOG_CTRL,
- UNI_WSYS_CONFIG_FW_DBG_CTRL,
-+ UNI_CMD_CERT_CFG = 6,
- };
-
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 4a268a4e..b9c37612 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -349,6 +349,7 @@ struct mt7996_phy {
- } test;
- #endif
- #ifdef CONFIG_MTK_VENDOR
-+ u8 rts_bw_sig;
- spinlock_t amnt_lock;
- struct mt7996_air_monitor_ctrl amnt_ctrl;
- #endif
-@@ -476,6 +477,9 @@ struct mt7996_dev {
- } dbg;
- const struct mt7996_dbg_reg_desc *dbg_reg;
- #endif
-+#ifdef CONFIG_MTK_VENDOR
-+ bool cert_mode;
-+#endif
- };
-
- enum {
-@@ -673,6 +677,7 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
- int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
- void mt7996_mcu_scs_sta_poll(struct work_struct *work);
-+int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
-
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-@@ -791,6 +796,10 @@ void mt7996_vendor_register(struct mt7996_phy *phy);
- void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
- int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
- struct ieee80211_sta *sta);
-+void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
-+void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
-+int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
-+int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
- #endif
-
- int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-@@ -818,6 +827,10 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
- int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
- int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
- int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
-+int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type);
-+void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
-+void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
-+void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index b7d86384..cacca449 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -984,4 +984,209 @@ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
- true);
- }
-
-+int mt7996_mcu_set_bsrp_ctrl(struct mt7996_phy *phy, u16 interval,
-+ u16 ru_alloc, u32 trig_type, u8 trig_flow, u8 ext_cmd)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 interval;
-+ __le16 ru_alloc;
-+ __le32 trigger_type;
-+ u8 trigger_flow;
-+ u8 ext_cmd_bsrp;
-+ u8 band_bitmap;
-+ u8 _rsv2;
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_CMD_MURU_BSRP_CTRL),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .interval = cpu_to_le16(interval),
-+ .ru_alloc = cpu_to_le16(ru_alloc),
-+ .trigger_type = cpu_to_le32(trig_type),
-+ .trigger_flow = trig_flow,
-+ .ext_cmd_bsrp = ext_cmd,
-+ .band_bitmap = BIT(phy->mt76->band_idx),
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
-+ sizeof(req), false);
-+}
-+
-+int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ int ret = 0;
-+ char buf[] = "01:00:00:1B";
-+
-+ if (enable) {
-+ ret = mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_TRIG_TYPE, trig_type);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ switch (trig_type) {
-+ case CAPI_BASIC:
-+ return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 0, 0, enable);
-+ case CAPI_BRP:
-+ return mt7996_mcu_set_txbf_snd_info(phy, buf);
-+ case CAPI_MU_BAR:
-+ return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
-+ MU_DL_ACK_POLICY_MU_BAR);
-+ case CAPI_BSRP:
-+ return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 4, 0, enable);
-+ default:
-+ return 0;
-+ }
-+}
-+
-+int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_muru *muru;
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 version;
-+ u8 revision;
-+ u8 _rsv2[2];
-+
-+ struct mt7996_muru muru;
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_CMD_MURU_MUNUAL_CONFIG),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .version = UNI_CMD_MURU_VER_EHT,
-+ };
-+
-+ muru = (struct mt7996_muru *) data;
-+ memcpy(&req.muru, muru, sizeof(struct mt7996_muru));
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
-+ sizeof(req), false);
-+}
-+
-+int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
-+{
-+ struct mt7996_muru *muru;
-+ struct mt7996_muru_dl *dl;
-+ struct mt7996_muru_ul *ul;
-+ struct mt7996_muru_comm *comm;
-+ int ret = 0;
-+
-+ muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
-+ dl = &muru->dl;
-+ ul = &muru->ul;
-+ comm = &muru->comm;
-+
-+ switch (action) {
-+ case MU_CTRL_DL_USER_CNT:
-+ dl->user_num = val;
-+ comm->ppdu_format = MURU_PPDU_HE_MU;
-+ comm->sch_type = MURU_OFDMA_SCH_TYPE_DL;
-+ muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
-+ muru->cfg_dl = cpu_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
-+ ret = mt7996_mcu_set_muru_cfg(phy, muru);
-+ break;
-+ case MU_CTRL_UL_USER_CNT:
-+ ul->user_num = val;
-+ comm->ppdu_format = MURU_PPDU_HE_TRIG;
-+ comm->sch_type = MURU_OFDMA_SCH_TYPE_UL;
-+ muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
-+ muru->cfg_ul = cpu_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
-+ ret = mt7996_mcu_set_muru_cfg(phy, muru);
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ kfree(muru);
-+ return ret;
-+}
-+
-+void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ int enable_su;
-+
-+ switch (ppdu_type) {
-+ case CAPI_SU:
-+ enable_su = 1;
-+ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
-+ mt7996_set_muru_cfg(phy, MU_CTRL_DL_USER_CNT, 0);
-+ break;
-+ case CAPI_MU:
-+ enable_su = 0;
-+ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
-+ break;
-+ default:
-+ break;
-+ }
-+}
-+
-+void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 user_cnt)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ int enable_su = 0;
-+
-+ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
-+ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
-+ mt7996_mcu_muru_set_prot_frame_thr(dev, 9999);
-+
-+ mt7996_set_muru_cfg(phy, type, user_cnt);
-+}
-+
-+void mt7996_mcu_set_mimo(struct mt7996_phy *phy)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+ int disable_ra = 1;
-+ char buf[] = "2 134 0 1 0 1 2 2 2";
-+ int force_mu = 1;
-+
-+ switch (chandef->width) {
-+ case NL80211_CHAN_WIDTH_20_NOHT:
-+ case NL80211_CHAN_WIDTH_20:
-+ strscpy(buf, "2 122 0 1 0 1 2 2 2", sizeof(buf));
-+ break;
-+ case NL80211_CHAN_WIDTH_80:
-+ break;
-+ case NL80211_CHAN_WIDTH_160:
-+ strscpy(buf, "2 137 0 1 0 1 2 2 2", sizeof(buf));
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
-+ mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL, disable_ra);
-+ mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL, buf);
-+ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_FORCE_MU, force_mu);
-+}
-+
-+void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 action;
-+ u8 _rsv2[3];
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_CMD_CERT_CFG),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .action = type, /* 1: CAPI Enable */
-+ };
-+
-+ mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &req,
-+ sizeof(req), false);
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index b061950c..98c9660a 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -122,14 +122,15 @@ enum {
- };
-
- enum {
-+ UNI_CMD_MURU_BSRP_CTRL = 0x01,
- UNI_CMD_MURU_SUTX_CTRL = 0x10,
-- UNI_CMD_MURU_FIXED_RATE_CTRL,
-- UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+ UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
-+ UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL = 0x12,
- UNI_CMD_MURU_SET_FORCE_MU = 0x33,
- UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
-- UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
-- UNI_CMD_MURU_SET_TRIG_TYPE,
-- UNI_CMD_MURU_SET_20M_DYN_ALGO,
-+ UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC8,
-+ UNI_CMD_MURU_SET_TRIG_TYPE = 0xC9,
-+ UNI_CMD_MURU_SET_20M_DYN_ALGO = 0xCA,
- UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
- UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
- };
-@@ -533,6 +534,179 @@ struct mt7996_mcu_sr_hw_ind_event {
- __le32 sr_ampdu_mpdu_cnt;
- __le32 sr_ampdu_mpdu_acked_cnt;
- };
-+
-+struct mt7996_muru_comm {
-+ u8 pda_pol;
-+ u8 band;
-+ u8 spe_idx;
-+ u8 proc_type;
-+
-+ __le16 mlo_ctrl;
-+ u8 sch_type;
-+ u8 ppdu_format;
-+ u8 ac;
-+ u8 _rsv[3];
-+};
-+
-+struct mt7996_muru_dl {
-+ u8 user_num;
-+ u8 tx_mode;
-+ u8 bw;
-+ u8 gi;
-+
-+ u8 ltf;
-+ u8 mcs;
-+ u8 dcm;
-+ u8 cmprs;
-+
-+ __le16 ru[16];
-+
-+ u8 c26[2];
-+ u8 ack_policy;
-+ u8 tx_power;
-+
-+ __le16 mu_ppdu_duration;
-+ u8 agc_disp_order;
-+ u8 _rsv1;
-+
-+ u8 agc_disp_pol;
-+ u8 agc_disp_ratio;
-+ __le16 agc_disp_linkMFG;
-+
-+ __le16 prmbl_punc_bmp;
-+ u8 _rsv2[2];
-+
-+ struct {
-+ __le16 wlan_idx;
-+ u8 ru_alloc_seg;
-+ u8 ru_idx;
-+ u8 ldpc;
-+ u8 nss;
-+ u8 mcs;
-+ u8 mu_group_idx;
-+ u8 vht_groud_id;
-+ u8 vht_up;
-+ u8 he_start_stream;
-+ u8 he_mu_spatial;
-+ __le16 tx_power_alpha;
-+ u8 ack_policy;
-+ u8 ru_allo_ps160;
-+ } usr[16];
-+};
-+
-+struct mt7996_muru_ul {
-+ u8 user_num;
-+ u8 tx_mode;
-+
-+ u8 ba_type;
-+ u8 _rsv;
-+
-+ u8 bw;
-+ u8 gi_ltf;
-+ __le16 ul_len;
-+
-+ __le16 trig_cnt;
-+ u8 pad;
-+ u8 trig_type;
-+
-+ __le16 trig_intv;
-+ u8 trig_ta[ETH_ALEN];
-+ __le16 ul_ru[16];
-+
-+ u8 c26[2];
-+ __le16 agc_disp_linkMFG;
-+
-+ u8 agc_disp_mu_len;
-+ u8 agc_disp_pol;
-+ u8 agc_disp_ratio;
-+ u8 agc_disp_pu_idx;
-+
-+ struct {
-+ __le16 wlan_idx;
-+ u8 ru_alloc_seg;
-+ u8 ru_idx;
-+ u8 ldpc;
-+ u8 nss;
-+ u8 mcs;
-+ u8 target_rssi;
-+ __le32 trig_pkt_size;
-+ u8 ru_allo_ps160;
-+ u8 _rsv2[3];
-+ } usr[16];
-+};
-+
-+struct mt7996_muru_dbg {
-+ /* HE TB RX Debug */
-+ __le32 rx_hetb_nonsf_en_bitmap;
-+ __le32 rx_hetb_cfg[2];
-+};
-+
-+struct mt7996_muru {
-+ __le32 cfg_comm;
-+ __le32 cfg_dl;
-+ __le32 cfg_ul;
-+ __le32 cfg_dbg;
-+
-+ struct mt7996_muru_comm comm;
-+ struct mt7996_muru_dl dl;
-+ struct mt7996_muru_ul ul;
-+ struct mt7996_muru_dbg dbg;
-+};
-+
-+
-+#define MURU_PPDU_HE_TRIG BIT(2)
-+#define MURU_PPDU_HE_MU BIT(3)
-+
-+#define MURU_OFDMA_SCH_TYPE_DL BIT(0)
-+#define MURU_OFDMA_SCH_TYPE_UL BIT(1)
-+
-+/* Common Config */
-+#define MURU_COMM_PPDU_FMT BIT(0)
-+#define MURU_COMM_SCH_TYPE BIT(1)
-+#define MURU_COMM_BAND BIT(2)
-+#define MURU_COMM_WMM BIT(3)
-+#define MURU_COMM_SPE_IDX BIT(4)
-+#define MURU_COMM_PROC_TYPE BIT(5)
-+#define MURU_COMM_SET (MURU_COMM_PPDU_FMT | MURU_COMM_SCH_TYPE)
-+#define MURU_COMM_SET_TM (MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
-+ MURU_COMM_WMM | MURU_COMM_SPE_IDX)
-+
-+/* DL Common config */
-+#define MURU_FIXED_DL_TOTAL_USER_CNT BIT(4)
-+
-+/* UL Common Config */
-+#define MURU_FIXED_UL_TOTAL_USER_CNT BIT(4)
-+
-+enum {
-+ CAPI_SU,
-+ CAPI_MU,
-+ CAPI_ER_SU,
-+ CAPI_TB,
-+ CAPI_LEGACY
-+};
-+
-+enum {
-+ CAPI_BASIC,
-+ CAPI_BRP,
-+ CAPI_MU_BAR,
-+ CAPI_MU_RTS,
-+ CAPI_BSRP,
-+ CAPI_GCR_MU_BAR,
-+ CAPI_BQRP,
-+ CAPI_NDP_FRP,
-+};
-+
-+enum {
-+ MU_DL_ACK_POLICY_MU_BAR = 3,
-+ MU_DL_ACK_POLICY_TF_FOR_ACK = 4,
-+ MU_DL_ACK_POLICY_SU_BAR = 5,
-+};
-+
-+enum muru_vendor_ctrl {
-+ MU_CTRL_UPDATE,
-+ MU_CTRL_DL_USER_CNT,
-+ MU_CTRL_UL_USER_CNT,
-+};
- #endif
-
- #endif
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 9ba6f00a..477c5c42 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -10,10 +10,31 @@
- #include "vendor.h"
- #include "mtk_mcu.h"
-
-+#ifdef CONFIG_MTK_VENDOR
- static const struct nla_policy
- mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
- [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
- [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_MU_CTRL_STRUCT] = {.type = NLA_BINARY },
-+};
-+
-+static const struct nla_policy
-+wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
- };
-
- static const struct nla_policy
-@@ -70,6 +91,17 @@ ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
- [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
- };
-
-+static const struct nla_policy
-+rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -84,6 +116,8 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
- {
- struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
- struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct mt7996_muru *muru;
- int err;
- u8 val8;
- u32 val32 = 0;
-@@ -99,9 +133,17 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
- FIELD_PREP(RATE_CFG_VAL, val8);
- ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7996_set_wireless_vif, &val32);
-+ } else if (tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT]) {
-+ muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
-+
-+ nla_memcpy(muru, tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT],
-+ sizeof(struct mt7996_muru));
-+
-+ err = mt7996_mcu_set_muru_cfg(phy, muru);
-+ kfree(muru);
- }
-
-- return 0;
-+ return err;
- }
-
- static int
-@@ -124,6 +166,48 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- return len;
- }
-
-+void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+
-+ switch (value) {
-+ case BW_SIGNALING_STATIC:
-+ case BW_SIGNALING_DYNAMIC:
-+ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, true);
-+ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, false);
-+ break;
-+ default:
-+ value = BW_SIGNALING_DISABLE;
-+ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, false);
-+ mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, true);
-+ break;
-+ }
-+
-+ phy->rts_bw_sig = value;
-+
-+ /* Set RTS Threshold to a lower Value */
-+ mt7996_mcu_set_rts_thresh(phy, 500);
-+}
-+
-+static int
-+mt7996_vendor_wireless_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ struct sk_buff *skb, const void *data, int data_len,
-+ unsigned long *storage)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ int len = 0;
-+
-+ if (*storage == 1)
-+ return -ENOENT;
-+ *storage = 1;
-+
-+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+ ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU)))
-+ return -ENOMEM;
-+ len += 1;
-+
-+ return len;
-+ }
-+
- void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
- {
- struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
-@@ -647,6 +731,126 @@ mt7996_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- return 1;
- }
-
-+static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
-+ struct wireless_dev *wdev,
-+ const void *data,
-+ int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct mt7996_dev *dev = phy->dev;
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
-+ int err;
-+ u32 val;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
-+ rfeature_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ val = CAPI_RFEATURE_CHANGED;
-+
-+ if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
-+ u8 enable, trig_type;
-+ int rem;
-+ struct nlattr *cur;
-+
-+ nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG], rem) {
-+ switch (nla_type(cur)) {
-+ case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN:
-+ enable = nla_get_u8(cur);
-+ break;
-+ case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE:
-+ trig_type = nla_get_u8(cur);
-+ break;
-+ default:
-+ return -EINVAL;
-+ };
-+ }
-+
-+ err = mt7996_mcu_set_rfeature_trig_type(phy, enable, trig_type);
-+ if (err)
-+ return err;
-+ } else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]) {
-+ u8 ack_policy;
-+
-+ ack_policy = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]);
-+ switch (ack_policy) {
-+ case MU_DL_ACK_POLICY_TF_FOR_ACK:
-+ return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
-+ ack_policy);
-+ default:
-+ return 0;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
-+ struct wireless_dev *wdev,
-+ const void *data,
-+ int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct mt7996_dev *dev = phy->dev;
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
-+ int err;
-+ u8 val8;
-+ u16 val16;
-+ u32 val32;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX, data, data_len,
-+ wireless_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ val32 = CAPI_WIRELESS_CHANGED;
-+
-+ if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]);
-+ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
-+ FIELD_PREP(RATE_CFG_VAL, val8);
-+ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+ mt7996_set_wireless_vif, &val32);
-+ if (val8 == 3) /* DL20and80 */
-+ mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_20M_DYN_ALGO, 1);
-+ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]) {
-+ val16 = nla_get_u16(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]);
-+ hw->max_tx_aggregation_subframes = val16;
-+ hw->max_rx_aggregation_subframes = val16;
-+ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]);
-+ mt7996_mcu_set_ppdu_tx_type(phy, val8);
-+ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]);
-+ if (phy->muru_onoff & OFDMA_UL)
-+ mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_UL_USER_CNT, val8);
-+ else
-+ mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_DL_USER_CNT, val8);
-+ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]);
-+ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
-+ FIELD_PREP(RATE_CFG_VAL, val8);
-+ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+ mt7996_set_wireless_vif, &val32);
-+ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
-+ dev->cert_mode = val8;
-+ mt7996_mcu_set_cert(phy, val8);
-+ mt7996_mcu_set_bypass_smthint(phy, val8);
-+ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
-+ mt7996_set_wireless_amsdu(hw, val8);
-+ } else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]);
-+ mt7996_set_wireless_rts_sigta(hw, val8);
-+ }
-+
-+ return 0;
-+}
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
- .info = {
-@@ -660,6 +864,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = mu_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_wireless_ctrl,
-+ .dumpit = mt7996_vendor_wireless_ctrl_dump,
-+ .policy = wireless_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX,
-+ },
- {
- .info = {
- .vendor_id = MTK_NL80211_VENDOR_ID,
-@@ -718,6 +934,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = ibf_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_IBF_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_rfeature_ctrl,
-+ .policy = rfeature_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-@@ -727,3 +954,4 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
-
- spin_lock_init(&phy->amnt_lock);
- }
-+#endif
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 29ccc050..7011914b 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -3,8 +3,12 @@
-
- #define MTK_NL80211_VENDOR_ID 0x0ce7
-
-+#ifdef CONFIG_MTK_VENDOR
-+
- enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
-+ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
-@@ -60,6 +64,7 @@ enum mtk_vendor_attr_mu_ctrl {
-
- MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -67,6 +72,66 @@ enum mtk_vendor_attr_mu_ctrl {
- NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
- };
-
-+enum mtk_capi_control_changed {
-+ CAPI_RFEATURE_CHANGED = BIT(16),
-+ CAPI_WIRELESS_CHANGED = BIT(17),
-+};
-+
-+enum mtk_vendor_attr_rfeature_ctrl {
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_ctrl {
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA, /* reserve */
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_dump {
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
-+};
-+
-+enum bw_sig {
-+ BW_SIGNALING_DISABLE,
-+ BW_SIGNALING_STATIC,
-+ BW_SIGNALING_DYNAMIC
-+};
-+
- enum mtk_vendor_attr_mnt_ctrl {
- MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
-
-@@ -138,3 +203,5 @@ enum mtk_vendor_attr_ibf_dump {
- };
-
- #endif
-+
-+#endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1034-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1034-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch
deleted file mode 100644
index c0cf121..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1034-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch
+++ /dev/null
@@ -1,1833 +0,0 @@
-From e5de8b93a62c62b5c44b10022e53d410f93c70d6 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 6 Apr 2023 16:40:28 +0800
-Subject: [PATCH 1034/1044] mtk: wifi: mt76: testmode: add testmode bf support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add iTest additional bf command
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-fw return Gx (5g band) ibf cal struct for both 2G and 5G ibf.
-Therefore, memcpy cannot be used for 2G ibf cal.
-https://gerrit.mediatek.inc/c/neptune/wlan_driver/logan/+/8206056
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76.h | 5 +
- mt76_connac_mcu.h | 4 +-
- mt7996/mcu.c | 8 +-
- mt7996/mcu.h | 45 ++-
- mt7996/mt7996.h | 12 +-
- mt7996/mtk_debugfs.c | 6 +-
- mt7996/mtk_mcu.c | 79 ++++-
- mt7996/mtk_mcu.h | 338 +++++++++++++++++++-
- mt7996/regs.h | 3 +
- mt7996/testmode.c | 744 +++++++++++++++++++++++++++++++++++++++++--
- mt7996/testmode.h | 19 ++
- testmode.c | 60 ++++
- testmode.h | 53 +++
- tools/fields.c | 37 +++
- 14 files changed, 1354 insertions(+), 59 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index 58fd55b1..543d9de5 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -752,6 +752,11 @@ struct mt76_testmode_data {
- u32 tx_time;
- u32 tx_ipg;
-
-+ u8 txbf_act;
-+ u16 txbf_param[8];
-+ bool is_txbf_dut;
-+ bool bf_en;
-+ bool bf_ever_en;
- bool ibf;
- bool ebf;
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index a59e5a0b..266ee711 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -488,7 +488,8 @@ struct sta_rec_bf {
- bool codebook75_mu;
-
- u8 he_ltf;
-- u8 rsv[3];
-+ u8 pp_fd_val;
-+ u8 rsv[2];
- } __packed;
-
- struct sta_rec_bfee {
-@@ -1278,6 +1279,7 @@ enum {
- MCU_UNI_CMD_VOW = 0x37,
- MCU_UNI_CMD_PP = 0x38,
- MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
-+ MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
- MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- MCU_UNI_CMD_PRECAL_RESULT = 0x47,
- MCU_UNI_CMD_RRO = 0x57,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 3a34afd9..6e3047ba 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1070,7 +1070,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
- bss->hw_bss_idx = idx;
-
- if (vif->type == NL80211_IFTYPE_MONITOR) {
-- memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
-+ struct mt76_testmode_data *td = &phy->test;
-+
-+ if (!td->bf_en)
-+ memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
-+ else
-+ memcpy(bss->bssid, td->addr[2], ETH_ALEN);
- return 0;
- }
-
-@@ -4108,7 +4113,6 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
- int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
- {
- #define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv)
--#define BF_PROCESSING 4
- struct uni_header hdr;
- struct sk_buff *skb;
- struct tlv *tlv;
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index af078edd..054a616b 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -685,6 +685,22 @@ struct bf_sounding_on {
- __le32 snd_period;
- } __packed;
-
-+enum sounding_mode {
-+ SU_SOUNDING,
-+ MU_SOUNDING,
-+ SU_PERIODIC_SOUNDING,
-+ MU_PERIODIC_SOUNDING,
-+ BF_PROCESSING,
-+ TXCMD_NONTB_SU_SOUNDING,
-+ TXCMD_VHT_MU_SOUNDING,
-+ TXCMD_TB_PER_BRP_SOUNDING,
-+ TXCMD_TB_SOUNDING,
-+
-+ /* keep last */
-+ NUM_SOUNDING_MODE,
-+ SOUNDING_MODE_MAX = NUM_SOUNDING_MODE - 1,
-+};
-+
- struct bf_hw_en_status_update {
- __le16 tag;
- __le16 len;
-@@ -710,6 +726,24 @@ union bf_tag_tlv {
- struct bf_mod_en_ctrl bf_mod_en;
- };
-
-+enum {
-+ BF_SOUNDING_OFF = 0,
-+ BF_SOUNDING_ON = 1,
-+ BF_DATA_PACKET_APPLY = 2,
-+ BF_PFMU_TAG_READ = 5,
-+ BF_PFMU_TAG_WRITE = 6,
-+ BF_STA_REC_READ = 11,
-+ BF_PHASE_CALIBRATION = 12,
-+ BF_IBF_PHASE_COMP = 13,
-+ BF_PROFILE_WRITE_20M_ALL = 15,
-+ BF_HW_EN_UPDATE = 17,
-+ BF_MOD_EN_CTRL = 20,
-+ BF_FBRPT_DBG_INFO_READ = 23,
-+ BF_TXSND_INFO = 24,
-+ BF_CMD_TXCMD = 27,
-+ BF_CFG_PHY = 28,
-+};
-+
- struct ra_rate {
- __le16 wlan_idx;
- u8 mode;
-@@ -771,17 +805,6 @@ enum {
- #define MUMIMO_UL BIT(3)
- #define MUMIMO_DL_CERT BIT(4)
-
--enum {
-- BF_SOUNDING_ON = 1,
-- BF_PFMU_TAG_READ = 5,
-- BF_STA_REC_READ = 11,
-- BF_HW_EN_UPDATE = 17,
-- BF_MOD_EN_CTRL = 20,
-- BF_FBRPT_DBG_INFO_READ = 23,
-- BF_TXSND_INFO = 24,
-- BF_CFG_PHY = 28,
--};
--
- enum {
- CMD_BAND_NONE,
- CMD_BAND_24G,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b9c37612..2f76a0af 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -480,6 +480,14 @@ struct mt7996_dev {
- #ifdef CONFIG_MTK_VENDOR
- bool cert_mode;
- #endif
-+
-+#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
-+ struct {
-+ void *txbf_phase_cal;
-+ void *txbf_pfmu_data;
-+ void *txbf_pfmu_tag;
-+ } test;
-+#endif
- };
-
- enum {
-@@ -819,7 +827,7 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
- void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
--int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer);
- void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
- int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
-@@ -831,10 +839,12 @@ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig
- void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
- void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
- void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
-+void mt7996_tm_update_channel(struct mt7996_phy *phy);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- int mt7996_dma_rro_init(struct mt7996_dev *dev);
- #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
-
-+
- #endif
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 9e7acd4b..c4cdbcdc 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2899,7 +2899,7 @@ mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
- {
- struct mt7996_phy *phy = data;
-
-- return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
-+ return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx, 0);
- }
- DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
- mt7996_starec_bf_read_set, "%lld\n");
-@@ -2943,7 +2943,7 @@ mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
- {
- struct mt7996_phy *phy = data;
-
-- return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
-+ return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx, 0);
- }
- DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
- mt7996_bf_fbk_rpt_set, "%lld\n");
-@@ -2953,7 +2953,7 @@ mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
- {
- struct mt7996_phy *phy = data;
-
-- return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
-+ return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx, 1);
- }
- DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
- mt7996_bf_pfmu_tag_read_set, "%lld\n");
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index cacca449..f70bd0be 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -295,7 +295,7 @@ __mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
- return ptlv;
- }
-
--int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer)
- {
- struct mt7996_dev *dev = phy->dev;
- #define MT7996_MTK_BF_MAX_SIZE sizeof(struct bf_starec_read)
-@@ -318,9 +318,8 @@ int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
-
- tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
- req = (struct bf_pfmu_tag *)tlv;
--#define BFER 1
- req->pfmu_id = idx;
-- req->bfer = BFER;
-+ req->bfer = bfer;
- req->band_idx = phy->mt76->band_idx;
- break;
- }
-@@ -432,10 +431,36 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
- return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
- }
-
-+static inline void
-+mt7996_ibf_phase_assign(struct mt7996_dev *dev,
-+ struct mt7996_ibf_cal_info *cal,
-+ struct mt7996_txbf_phase *phase)
-+{
-+ /* fw return ibf calibrated data with
-+ * the mt7996_txbf_phase_info_5g struct for both 2G and 5G.
-+ * Therefore, memcpy cannot be used here.
-+ */
-+ phase_assign(cal->group, m_t0_h, true);
-+ phase_assign(cal->group, m_t1_h, true);
-+ phase_assign(cal->group, m_t2_h, true);
-+ phase_assign(cal->group, m_t2_h_sx2, false);
-+ phase_assign_rx(cal->group, r0);
-+ phase_assign_rx(cal->group, r1);
-+ phase_assign_rx(cal->group, r2);
-+ phase_assign_rx(cal->group, r3);
-+ phase_assign_rx_g0(cal->group, r2_sx2);
-+ phase_assign_rx_g0(cal->group, r3_sx2);
-+ phase_assign(cal->group, r0_reserved, false);
-+ phase_assign(cal->group, r1_reserved, false);
-+ phase_assign(cal->group, r2_reserved, false);
-+ phase_assign(cal->group, r3_reserved, false);
-+ phase_assign(cal->group, r2_sx2_reserved, false);
-+ phase_assign(cal->group, r3_sx2_reserved, false);
-+}
-+
- void
- mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
- {
--#define HE_MODE 3
- struct mt7996_mcu_bf_basic_event *event;
-
- event = (struct mt7996_mcu_bf_basic_event *)skb->data;
-@@ -470,13 +495,12 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
- tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
- tag->t1.mob_cal_en);
-
-- if (tag->t1.lm <= HE_MODE) {
-+ if (tag->t1.lm <= BF_LM_HE)
- dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
- tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
-- } else {
-+ else
- dev_info(dev->mt76.dev, "PartialBW = %d\n",
- tag->t1.bw_info.partial_bw_info);
-- }
-
- dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
- tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
-@@ -730,6 +754,47 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
-
- break;
- }
-+ case UNI_EVENT_BF_CAL_PHASE: {
-+ struct mt7996_ibf_cal_info *cal;
-+ struct mt7996_txbf_phase_out phase_out;
-+ struct mt7996_txbf_phase *phase;
-+
-+ cal = (struct mt7996_ibf_cal_info *)skb->data;
-+ phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+ memcpy(&phase_out, &cal->phase_out, sizeof(phase_out));
-+ switch (cal->cal_type) {
-+ case IBF_PHASE_CAL_NORMAL:
-+ case IBF_PHASE_CAL_NORMAL_INSTRUMENT:
-+ /* Only calibrate group M */
-+ if (cal->group_l_m_n != GROUP_M)
-+ break;
-+ phase = &phase[cal->group];
-+ phase->status = cal->status;
-+ dev_info(dev->mt76.dev, "Calibrated result = %d\n", phase->status);
-+ dev_info(dev->mt76.dev, "Group %d and Group M\n", cal->group);
-+ mt7996_ibf_phase_assign(dev, cal, phase);
-+ break;
-+ case IBF_PHASE_CAL_VERIFY:
-+ case IBF_PHASE_CAL_VERIFY_INSTRUMENT:
-+ dev_info(dev->mt76.dev, "Verification result = %d\n", cal->status);
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ dev_info(dev->mt76.dev, "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d\n",
-+ phase_out.c0_uh, phase_out.c1_uh, phase_out.c2_uh, phase_out.c3_uh);
-+ dev_info(dev->mt76.dev, "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d\n",
-+ phase_out.c0_h, phase_out.c1_h, phase_out.c2_h, phase_out.c3_h);
-+ dev_info(dev->mt76.dev, "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d\n",
-+ phase_out.c0_mh, phase_out.c1_mh, phase_out.c2_mh, phase_out.c3_mh);
-+ dev_info(dev->mt76.dev, "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d\n",
-+ phase_out.c0_m, phase_out.c1_m, phase_out.c2_m, phase_out.c3_m);
-+ dev_info(dev->mt76.dev, "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d\n",
-+ phase_out.c0_l, phase_out.c1_l, phase_out.c2_l, phase_out.c3_l);
-+
-+ break;
-+ }
- default:
- dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
- __func__, event->tag);
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 98c9660a..252ae98e 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -189,6 +189,164 @@ struct bf_txsnd_info {
- u8 __rsv[2];
- } __packed;
-
-+#define MAX_PHASE_GROUP_NUM 9
-+
-+struct bf_phase_comp {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 bw;
-+ u8 jp_band;
-+ u8 band_idx;
-+ bool read_from_e2p;
-+ bool disable;
-+ u8 group;
-+ u8 rsv[2];
-+ u8 buf[44];
-+} __packed;
-+
-+struct bf_tx_apply {
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 wlan_idx;
-+ bool ebf;
-+ bool ibf;
-+ bool mu_txbf;
-+ bool phase_cal;
-+ u8 rsv[2];
-+} __packed;
-+
-+struct bf_phase_cal {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 group_l_m_n;
-+ u8 group;
-+ u8 sx2;
-+ u8 cal_type;
-+ u8 lna_gain_level;
-+ u8 band_idx;
-+ u8 rsv[2];
-+} __packed;
-+
-+struct bf_txcmd {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 action;
-+ u8 bf_manual;
-+ u8 bf_bit;
-+ u8 rsv[5];
-+} __packed;
-+
-+struct bf_pfmu_data_all {
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 pfmu_id;
-+ u8 band_idx;
-+ u8 rsv[2];
-+
-+ u8 buf[512];
-+} __packed;
-+
-+#define TXBF_DUT_MAC_SUBADDR 0x22
-+#define TXBF_GOLDEN_MAC_SUBADDR 0x11
-+
-+struct mt7996_tm_bf_req {
-+ u8 _rsv[4];
-+
-+ union {
-+ struct bf_sounding_on sounding;
-+ struct bf_tx_apply tx_apply;
-+ struct bf_pfmu_tag pfmu_tag;
-+ struct bf_pfmu_data_all pfmu_data_all;
-+ struct bf_phase_cal phase_cal;
-+ struct bf_phase_comp phase_comp;
-+ struct bf_txcmd txcmd;
-+ };
-+} __packed;
-+
-+enum tm_trx_mac_type {
-+ TM_TRX_MAC_TX = 1,
-+ TM_TRX_MAC_RX,
-+ TM_TRX_MAC_TXRX,
-+ TM_TRX_MAC_TXRX_RXV,
-+ TM_TRX_MAC_RXV,
-+ TM_TRX_MAC_RX_RXV,
-+};
-+
-+enum tm_trx_param_idx {
-+ TM_TRX_PARAM_RSV,
-+ /* MAC */
-+ TM_TRX_PARAM_SET_TRX,
-+ TM_TRX_PARAM_RX_FILTER,
-+ TM_TRX_PARAM_RX_FILTER_PKT_LEN,
-+ TM_TRX_PARAM_SLOT_TIME,
-+ TM_TRX_PARAM_CLEAN_PERSTA_TXQUEUE,
-+ TM_TRX_PARAM_AMPDU_WTBL,
-+ TM_TRX_PARAM_MU_RX_AID,
-+ TM_TRX_PARAM_PHY_MANUAL_TX,
-+
-+ /* PHY */
-+ TM_TRX_PARAM_RX_PATH,
-+ TM_TRX_PARAM_TX_STREAM,
-+ TM_TRX_PARAM_TSSI_STATUS,
-+ TM_TRX_PARAM_DPD_STATUS,
-+ TM_TRX_PARAM_RATE_POWER_OFFSET_ON_OFF,
-+ TM_TRX_PARAM_THERMO_COMP_STATUS,
-+ TM_TRX_PARAM_FREQ_OFFSET,
-+ TM_TRX_PARAM_FAGC_RSSI_PATH,
-+ TM_TRX_PARAM_PHY_STATUS_COUNT,
-+ TM_TRX_PARAM_RXV_INDEX,
-+
-+ TM_TRX_PARAM_ANTENNA_PORT,
-+ TM_TRX_PARAM_THERMAL_ONOFF,
-+ TM_TRX_PARAM_TX_POWER_CONTROL_ALL_RF,
-+ TM_TRX_PARAM_RATE_POWER_OFFSET,
-+ TM_TRX_PARAM_SLT_CMD_TEST,
-+ TM_TRX_PARAM_SKU,
-+ TM_TRX_PARAM_POWER_PERCENTAGE_ON_OFF,
-+ TM_TRX_PARAM_BF_BACKOFF_ON_OFF,
-+ TM_TRX_PARAM_POWER_PERCENTAGE_LEVEL,
-+ TM_TRX_PARAM_FRTBL_CFG,
-+ TM_TRX_PARAM_PREAMBLE_PUNC_ON_OFF,
-+
-+ TM_TRX_PARAM_MAX_NUM,
-+};
-+
-+enum trx_action {
-+ TM_TRX_ACTION_SET,
-+ TM_TRX_ACTION_GET,
-+};
-+
-+struct tm_trx_set {
-+ u8 type;
-+ u8 enable;
-+ u8 band_idx;
-+ u8 rsv;
-+} __packed;
-+
-+struct mt7996_tm_trx_req {
-+ u8 param_num;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 param_idx;
-+ u8 band_idx;
-+ u8 testmode_en;
-+ u8 action;
-+ u8 rsv[3];
-+
-+ u32 data;
-+ struct tm_trx_set set_trx;
-+
-+ u8 buf[220];
-+} __packed;
-+
- struct mt7996_mcu_bf_basic_event {
- struct mt7996_mcu_rxd rxd;
-
-@@ -394,6 +552,181 @@ struct mt7996_pfmu_tag_event {
- struct mt7996_pfmu_tag2 t2;
- };
-
-+struct mt7996_pfmu_tag {
-+ struct mt7996_pfmu_tag1 t1;
-+ struct mt7996_pfmu_tag2 t2;
-+};
-+
-+enum bf_lm_type {
-+ BF_LM_LEGACY,
-+ BF_LM_HT,
-+ BF_LM_VHT,
-+ BF_LM_HE,
-+ BF_LM_EHT,
-+};
-+
-+struct mt7996_txbf_phase_out {
-+ u8 c0_l;
-+ u8 c1_l;
-+ u8 c2_l;
-+ u8 c3_l;
-+ u8 c0_m;
-+ u8 c1_m;
-+ u8 c2_m;
-+ u8 c3_m;
-+ u8 c0_mh;
-+ u8 c1_mh;
-+ u8 c2_mh;
-+ u8 c3_mh;
-+ u8 c0_h;
-+ u8 c1_h;
-+ u8 c2_h;
-+ u8 c3_h;
-+ u8 c0_uh;
-+ u8 c1_uh;
-+ u8 c2_uh;
-+ u8 c3_uh;
-+};
-+
-+struct mt7996_txbf_rx_phase_2g {
-+ u8 rx_uh;
-+ u8 rx_h;
-+ u8 rx_m;
-+ u8 rx_l;
-+ u8 rx_ul;
-+};
-+
-+struct mt7996_txbf_rx_phase_5g {
-+ u8 rx_uh;
-+ u8 rx_h;
-+ u8 rx_mh;
-+ u8 rx_m;
-+ u8 rx_l;
-+ u8 rx_ul;
-+};
-+
-+struct mt7996_txbf_phase_info_2g {
-+ struct mt7996_txbf_rx_phase_2g r0;
-+ struct mt7996_txbf_rx_phase_2g r1;
-+ struct mt7996_txbf_rx_phase_2g r2;
-+ struct mt7996_txbf_rx_phase_2g r3;
-+ struct mt7996_txbf_rx_phase_2g r2_sx2;
-+ struct mt7996_txbf_rx_phase_2g r3_sx2;
-+ u8 m_t0_h;
-+ u8 m_t1_h;
-+ u8 m_t2_h;
-+ u8 m_t2_h_sx2;
-+ u8 r0_reserved;
-+ u8 r1_reserved;
-+ u8 r2_reserved;
-+ u8 r3_reserved;
-+ u8 r2_sx2_reserved;
-+ u8 r3_sx2_reserved;
-+};
-+
-+struct mt7996_txbf_phase_info_5g {
-+ struct mt7996_txbf_rx_phase_5g r0;
-+ struct mt7996_txbf_rx_phase_5g r1;
-+ struct mt7996_txbf_rx_phase_5g r2;
-+ struct mt7996_txbf_rx_phase_5g r3;
-+ struct mt7996_txbf_rx_phase_2g r2_sx2; /* no middle-high in r2_sx2 */
-+ struct mt7996_txbf_rx_phase_2g r3_sx2; /* no middle-high in r3_sx2 */
-+ u8 m_t0_h;
-+ u8 m_t1_h;
-+ u8 m_t2_h;
-+ u8 m_t2_h_sx2;
-+ u8 r0_reserved;
-+ u8 r1_reserved;
-+ u8 r2_reserved;
-+ u8 r3_reserved;
-+ u8 r2_sx2_reserved;
-+ u8 r3_sx2_reserved;
-+};
-+
-+struct mt7996_txbf_phase {
-+ u8 status;
-+ union {
-+ struct mt7996_txbf_phase_info_2g phase_2g;
-+ struct mt7996_txbf_phase_info_5g phase_5g;
-+ };
-+};
-+
-+#define phase_assign(group, field, dump, ...) ({ \
-+ if (group) { \
-+ phase->phase_5g.field = cal->phase_5g.field; \
-+ if (dump) \
-+ dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->phase_5g.field); \
-+ } else { \
-+ phase->phase_2g.field = cal->phase_5g.field; \
-+ if (dump) \
-+ dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->phase_2g.field); \
-+ } \
-+})
-+
-+#define phase_assign_rx_g0(group, rx, ...) ({ \
-+ phase_assign(group, rx.rx_uh, false); \
-+ phase_assign(group, rx.rx_h, false); \
-+ phase_assign(group, rx.rx_m, false); \
-+ phase_assign(group, rx.rx_l, false); \
-+ phase_assign(group, rx.rx_ul, false); \
-+})
-+
-+#define phase_assign_rx(group, rx, ...) ({ \
-+ if (group) { \
-+ phase_assign(group, rx.rx_uh, true); \
-+ phase_assign(group, rx.rx_h, true); \
-+ phase->phase_5g.rx.rx_mh = cal->phase_5g.rx.rx_mh; \
-+ dev_info(dev->mt76.dev, "%s.rx_mh = %d\n", #rx, phase->phase_5g.rx.rx_mh); \
-+ phase_assign(group, rx.rx_m, true); \
-+ phase_assign(group, rx.rx_l, true); \
-+ phase_assign(group, rx.rx_ul, true); \
-+ } else { \
-+ phase_assign(group, rx.rx_uh, true); \
-+ phase_assign(group, rx.rx_h, true); \
-+ phase_assign(group, rx.rx_m, true); \
-+ phase_assign(group, rx.rx_l, true); \
-+ phase_assign(group, rx.rx_ul, true); \
-+ } \
-+})
-+
-+#define GROUP_L 0
-+#define GROUP_M 1
-+#define GROUP_H 2
-+
-+struct mt7996_pfmu_data {
-+ __le16 subc_idx;
-+ __le16 phi11;
-+ __le16 phi21;
-+ __le16 phi31;
-+};
-+
-+struct mt7996_ibf_cal_info {
-+ struct mt7996_mcu_bf_basic_event event;
-+
-+ u8 category_id;
-+ u8 group_l_m_n;
-+ u8 group;
-+ bool sx2;
-+ u8 status;
-+ u8 cal_type;
-+ u8 _rsv[2];
-+ struct mt7996_txbf_phase_out phase_out;
-+ union {
-+ struct mt7996_txbf_phase_info_2g phase_2g;
-+ struct mt7996_txbf_phase_info_5g phase_5g;
-+ };
-+} __packed;
-+
-+enum {
-+ IBF_PHASE_CAL_UNSPEC,
-+ IBF_PHASE_CAL_NORMAL,
-+ IBF_PHASE_CAL_VERIFY,
-+ IBF_PHASE_CAL_NORMAL_INSTRUMENT,
-+ IBF_PHASE_CAL_VERIFY_INSTRUMENT,
-+};
-+
-+#define MT7996_TXBF_SUBCAR_NUM 64
-+
- enum {
- UNI_EVENT_BF_PFMU_TAG = 0x5,
- UNI_EVENT_BF_PFMU_DATA = 0x7,
-@@ -408,11 +741,6 @@ enum {
- UNI_EVENT_BF_MAX_NUM
- };
-
--enum {
-- UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
-- UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
--};
--
- struct uni_muru_mum_set_group_tbl_entry {
- __le16 wlan_idx0;
- __le16 wlan_idx1;
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index e94f9a90..aa04d8d2 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -326,6 +326,9 @@ enum offs_rev {
- #define MT_ARB_SCR_TX_DISABLE BIT(8)
- #define MT_ARB_SCR_RX_DISABLE BIT(9)
-
-+#define MT_ARB_TQSAXM0(_band) MT_WF_ARB(_band, 0x180)
-+#define MT_ARB_TQSAXM_ALTX_START_MASK GENMASK(12, 8)
-+
- /* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */
- #define MT_WF_RMAC_BASE(_band) __BASE(WF_RMAC_BASE, (_band))
- #define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs))
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 26ae5827..2fb36a97 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -23,6 +23,7 @@ enum {
- TM_CHANGED_IPI_THRESHOLD,
- TM_CHANGED_IPI_PERIOD,
- TM_CHANGED_IPI_RESET,
-+ TM_CHANGED_TXBF_ACT,
-
- /* must be last */
- NUM_TM_CHANGED
-@@ -41,25 +42,31 @@ static const u8 tm_change_map[] = {
- [TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
- [TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
- [TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
-+ [TM_CHANGED_TXBF_ACT] = MT76_TM_ATTR_TXBF_ACT,
- };
-
- static void mt7996_tm_ipi_work(struct work_struct *work);
-+static int mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx,
-+ bool ebf, bool ibf, bool phase_cal);
-
- static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
- {
- static const u32 width_to_bw[][NUM_BW_MAP] = {
-- [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
-+ [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, BF_CDBW_40MHZ, 40,
- FIRST_CONTROL_CHAN_BITMAP_BW40},
-- [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
-+ [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, BF_CDBW_80MHZ, 80,
- FIRST_CONTROL_CHAN_BITMAP_BW80},
-- [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
-- [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
-+ [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, BF_CDBW_8080MHZ,
-+ 80, 0x0},
-+ [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, BF_CDBW_160MHZ, 160,
- FIRST_CONTROL_CHAN_BITMAP_BW160},
-- [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
-- [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
-- [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
-- [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
-- [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
-+ [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, BF_CDBW_5MHZ, 5, 0x0},
-+ [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, BF_CDBW_10MHZ, 10, 0x0},
-+ [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ, 20, 0x0},
-+ [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ,
-+ 20, 0x0},
-+ [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, BF_CDBW_320MHZ,
-+ 320, 0x0},
- };
-
- if (width >= ARRAY_SIZE(width_to_bw))
-@@ -68,26 +75,26 @@ static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_m
- return width_to_bw[width][method];
- }
-
--static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
-+static u8 mt7996_tm_rate_mapping(u8 tx_rate_mode, enum rate_mapping_type type)
- {
-- static const u8 rate_to_phy[] = {
-- [MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
-- [MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
-- [MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
-- [MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
-- [MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
-- [MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
-- [MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
-- [MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
-- [MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
-- [MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
-- [MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
-+ static const u8 rate_to_phy[][NUM_RATE_MAP] = {
-+ [MT76_TM_TX_MODE_CCK] = {MT_PHY_TYPE_CCK, BF_LM_LEGACY},
-+ [MT76_TM_TX_MODE_OFDM] = {MT_PHY_TYPE_OFDM, BF_LM_LEGACY},
-+ [MT76_TM_TX_MODE_HT] = {MT_PHY_TYPE_HT, BF_LM_HT},
-+ [MT76_TM_TX_MODE_VHT] = {MT_PHY_TYPE_VHT, BF_LM_VHT},
-+ [MT76_TM_TX_MODE_HE_SU] = {MT_PHY_TYPE_HE_SU, BF_LM_HE},
-+ [MT76_TM_TX_MODE_HE_EXT_SU] = {MT_PHY_TYPE_HE_EXT_SU, BF_LM_HE},
-+ [MT76_TM_TX_MODE_HE_TB] = {MT_PHY_TYPE_HE_TB, BF_LM_HE},
-+ [MT76_TM_TX_MODE_HE_MU] = {MT_PHY_TYPE_HE_MU, BF_LM_HE},
-+ [MT76_TM_TX_MODE_EHT_SU] = {MT_PHY_TYPE_EHT_SU, BF_LM_EHT},
-+ [MT76_TM_TX_MODE_EHT_TRIG] = {MT_PHY_TYPE_EHT_TRIG, BF_LM_EHT},
-+ [MT76_TM_TX_MODE_EHT_MU] = {MT_PHY_TYPE_EHT_MU, BF_LM_EHT},
- };
-
- if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
- return -EINVAL;
-
-- return rate_to_phy[tx_rate_mode];
-+ return rate_to_phy[tx_rate_mode][type];
- }
-
- static int
-@@ -239,7 +246,7 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
- INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
- }
-
--static void
-+void
- mt7996_tm_update_channel(struct mt7996_phy *phy)
- {
- #define CHAN_FREQ_BW_80P80_TAG (SET_ID(CHAN_FREQ) | BIT(16))
-@@ -303,7 +310,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
- mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
- mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
- mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
-- mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+ mt7996_tm_set(dev, SET_ID(TX_MODE),
-+ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
- mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
-
- if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
-@@ -331,7 +339,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
-
- mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
- mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
-- mt7996_tm_update_channel(phy);
-+ if (!td->bf_en)
-+ mt7996_tm_update_channel(phy);
-
- /* trigger firmware to start TX */
- mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
-@@ -373,7 +382,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
- return;
- }
-
-- mt7996_tm_update_channel(phy);
-+ if (!td->bf_en)
-+ mt7996_tm_update_channel(phy);
-
- if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
- if (td->aid)
-@@ -381,7 +391,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
- else
- ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
- }
-- mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+ mt7996_tm_set(dev, SET_ID(TX_MODE),
-+ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
- mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
- mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
- mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
-@@ -405,7 +416,8 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
-
- if (en) {
- mt7996_tm_update_channel(phy);
-- mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+ mt7996_tm_set(dev, SET_ID(TX_MODE),
-+ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
- mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
- /* fix payload is OFDM */
- mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
-@@ -1047,6 +1059,678 @@ mt7996_tm_set_ipi(struct mt7996_phy *phy)
- return 0;
- }
-
-+static int
-+mt7996_tm_set_trx_mac(struct mt7996_phy *phy, u8 type, bool en)
-+{
-+#define UNI_TM_TRX_CTRL 0
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_trx_req req = {
-+ .param_num = 1,
-+ .tag = cpu_to_le16(UNI_TM_TRX_CTRL),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .param_idx = cpu_to_le16(TM_TRX_PARAM_SET_TRX),
-+ .band_idx = phy->mt76->band_idx,
-+ .testmode_en = 1,
-+ .action = TM_TRX_ACTION_SET,
-+ .set_trx = {
-+ .type = type,
-+ .enable = en,
-+ .band_idx = phy->mt76->band_idx,
-+ }
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_TRX_PARAM),
-+ &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
-+{
-+#define EBF_BBP_RX_OFFSET 0x10280
-+#define EBF_BBP_RX_ENABLE (BIT(0) | BIT(15))
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ bool enable = val[0];
-+ void *phase_cal, *pfmu_data, *pfmu_tag;
-+ u8 nss, band_idx = phy->mt76->band_idx;
-+ enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
-+ u8 sub_addr = td->is_txbf_dut ? TXBF_DUT_MAC_SUBADDR : TXBF_GOLDEN_MAC_SUBADDR;
-+ u8 peer_addr = td->is_txbf_dut ? TXBF_GOLDEN_MAC_SUBADDR : TXBF_DUT_MAC_SUBADDR;
-+ u8 bss_addr = TXBF_DUT_MAC_SUBADDR;
-+ u8 addr[ETH_ALEN] = {0x00, sub_addr, sub_addr, sub_addr, sub_addr, sub_addr};
-+ u8 bssid[ETH_ALEN] = {0x00, bss_addr, bss_addr, bss_addr, bss_addr, bss_addr};
-+ u8 peer_addrs[ETH_ALEN] = {0x00, peer_addr, peer_addr, peer_addr, peer_addr, peer_addr};
-+ struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv;
-+
-+ if (!enable) {
-+ td->bf_en = false;
-+ return 0;
-+ }
-+
-+ if (!dev->test.txbf_phase_cal) {
-+ phase_cal = devm_kzalloc(dev->mt76.dev,
-+ sizeof(struct mt7996_txbf_phase) *
-+ MAX_PHASE_GROUP_NUM,
-+ GFP_KERNEL);
-+ if (!phase_cal)
-+ return -ENOMEM;
-+
-+ dev->test.txbf_phase_cal = phase_cal;
-+ }
-+
-+ if (!dev->test.txbf_pfmu_data) {
-+ pfmu_data = devm_kzalloc(dev->mt76.dev,
-+ sizeof(struct mt7996_pfmu_data) *
-+ MT7996_TXBF_SUBCAR_NUM,
-+ GFP_KERNEL);
-+ if (!pfmu_data)
-+ return -ENOMEM;
-+
-+ dev->test.txbf_pfmu_data = pfmu_data;
-+ }
-+
-+ if (!dev->test.txbf_pfmu_tag) {
-+ pfmu_tag = devm_kzalloc(dev->mt76.dev,
-+ sizeof(struct mt7996_pfmu_tag), GFP_KERNEL);
-+ if (!pfmu_tag)
-+ return -ENOMEM;
-+
-+ dev->test.txbf_pfmu_tag = pfmu_tag;
-+ }
-+
-+ td->bf_en = true;
-+ dev->ibf = td->ibf;
-+ memcpy(td->addr[0], peer_addrs, ETH_ALEN);
-+ memcpy(td->addr[1], addr, ETH_ALEN);
-+ memcpy(td->addr[2], bssid, ETH_ALEN);
-+ memcpy(phy->monitor_vif->addr, addr, ETH_ALEN);
-+ mt7996_tm_set_mac_addr(dev, td->addr[0], SET_ID(DA));
-+ mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
-+ mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
-+
-+ /* bss idx & omac idx should be set to band idx for ibf cal */
-+ mvif->mt76.idx = band_idx;
-+ dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
-+ mvif->mt76.omac_idx = band_idx;
-+ phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
-+
-+ mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true);
-+ mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true);
-+
-+ if (td->ibf) {
-+ if (td->is_txbf_dut) {
-+ /* Enable ITxBF Capability */
-+ mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
-+ mt7996_tm_set_trx_mac(phy, TM_TRX_MAC_TX, true);
-+
-+ td->tx_ipg = 999;
-+ td->tx_mpdu_len = 1024;
-+ td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
-+ nss = hweight8(td->tx_antenna_mask);
-+ if (nss > 1 && nss <= 4)
-+ td->tx_rate_idx = 15 + 8 * (nss - 2);
-+ else
-+ td->tx_rate_idx = 31;
-+ } else {
-+ td->tx_antenna_mask = 1;
-+ td->tx_mpdu_len = 1024;
-+ td->tx_rate_idx = 0;
-+ mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
-+ dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
-+ mt76_rr(dev, EBF_BBP_RX_OFFSET));
-+ }
-+
-+ td->tx_rate_mode = MT76_TM_TX_MODE_HT;
-+ td->tx_rate_sgi = 0;
-+ } else {
-+ if (td->is_txbf_dut) {
-+ /* Enable ETxBF Capability */
-+ mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
-+ td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
-+ td->tx_spe_idx = 24 + phy->mt76->band_idx;
-+ if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT ||
-+ td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU)
-+ mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
-+
-+ mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
-+ mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
-+ } else {
-+ /* Turn On BBP CR for RX */
-+ mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
-+ dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
-+ mt76_rr(dev, EBF_BBP_RX_OFFSET));
-+
-+ td->tx_antenna_mask = 1;
-+ }
-+ width = phy->mt76->chandef.width;
-+
-+ if (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_MU)
-+ td->tx_rate_mode = MT76_TM_TX_MODE_EHT_SU;
-+ }
-+ mt76_testmode_param_set(td, MT76_TM_ATTR_TX_ANTENNA);
-+
-+ mt7996_tm_set(dev, SET_ID(TX_MODE),
-+ mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
-+ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
-+ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
-+ mt7996_tm_set(dev, SET_ID(CBW),
-+ mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+ mt7996_tm_set(dev, SET_ID(DBW),
-+ mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+ mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
-+ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
-+ mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
-+ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
-+ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
-+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(TX_COMMIT));
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_txbf_phase_comp(struct mt7996_phy *phy, u16 *val)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_bf_req req = {
-+ .phase_comp = {
-+ .tag = cpu_to_le16(BF_IBF_PHASE_COMP),
-+ .len = cpu_to_le16(sizeof(req.phase_comp)),
-+ .bw = val[0],
-+ .jp_band = (val[2] == 1) ? 1 : 0,
-+ .band_idx = phy->mt76->band_idx,
-+ .read_from_e2p = val[3],
-+ .disable = val[4],
-+ .group = val[2],
-+ }
-+ };
-+ struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+
-+ wait_event_timeout(dev->mt76.tx_wait, phase[val[2]].status != 0, HZ);
-+ if (val[2])
-+ memcpy(req.phase_comp.buf, &phase[val[2]].phase_5g, sizeof(req.phase_comp.buf));
-+ else
-+ memcpy(req.phase_comp.buf, &phase[val[2]].phase_2g, sizeof(req.phase_comp.buf));
-+
-+ pr_info("ibf cal process: phase comp info\n");
-+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
-+ &req, sizeof(req), 0);
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
-+ sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_profile_tag_write(struct mt7996_phy *phy, u8 pfmu_idx, struct mt7996_pfmu_tag *tag)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_bf_req req = {
-+ .pfmu_tag = {
-+ .tag = cpu_to_le16(BF_PFMU_TAG_WRITE),
-+ .len = cpu_to_le16(sizeof(req.pfmu_tag)),
-+ .pfmu_id = pfmu_idx,
-+ .bfer = true,
-+ .band_idx = phy->mt76->band_idx,
-+ }
-+ };
-+
-+ memcpy(req.pfmu_tag.buf, tag, sizeof(*tag));
-+ wait_event_timeout(dev->mt76.tx_wait, tag->t1.pfmu_idx != 0, HZ);
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
-+ sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_add_txbf_sta(struct mt7996_phy *phy, u8 pfmu_idx, u8 nr, u8 nc, bool ebf)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct {
-+ struct sta_req_hdr hdr;
-+ struct sta_rec_bf bf;
-+ } __packed req = {
-+ .hdr = {
-+ .bss_idx = phy->mt76->band_idx,
-+ .wlan_idx_lo = to_wcid_lo(phy->mt76->band_idx + 1),
-+ .tlv_num = 1,
-+ .is_tlv_append = 1,
-+ .muar_idx = 0,
-+ .wlan_idx_hi = to_wcid_hi(phy->mt76->band_idx + 1),
-+ },
-+ .bf = {
-+ .tag = cpu_to_le16(STA_REC_BF),
-+ .len = cpu_to_le16(sizeof(req.bf)),
-+ .pfmu = cpu_to_le16(pfmu_idx),
-+ .sounding_phy = 1,
-+ .bf_cap = ebf,
-+ .ncol = nc,
-+ .nrow = nr,
-+ .ibf_timeout = 0xff,
-+ .tx_mode = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY),
-+ },
-+ };
-+ u8 ndp_rate, ndpa_rate, rept_poll_rate, bf_bw;
-+
-+ if (td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU ||
-+ td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) {
-+ rept_poll_rate = 0x49;
-+ ndpa_rate = 0x49;
-+ ndp_rate = 0;
-+ } else if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT) {
-+ rept_poll_rate = 0x9;
-+ ndpa_rate = 0x9;
-+ ndp_rate = 0;
-+ } else {
-+ rept_poll_rate = 0;
-+ ndpa_rate = 0;
-+ if (nr == 1)
-+ ndp_rate = 8;
-+ else if (nr == 2)
-+ ndp_rate = 16;
-+ else
-+ ndp_rate = 24;
-+ }
-+
-+ bf_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
-+ req.bf.ndp_rate = ndp_rate;
-+ req.bf.ndpa_rate = ndpa_rate;
-+ req.bf.rept_poll_rate = rept_poll_rate;
-+ req.bf.bw = bf_bw;
-+ req.bf.tx_mode = (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) ? 0xf : req.bf.tx_mode;
-+
-+ if (ebf) {
-+ req.bf.mem[0].row = 0;
-+ req.bf.mem[1].row = 1;
-+ req.bf.mem[2].row = 2;
-+ req.bf.mem[3].row = 3;
-+ } else {
-+ req.bf.mem[0].row = 4;
-+ req.bf.mem[1].row = 5;
-+ req.bf.mem[2].row = 6;
-+ req.bf.mem[3].row = 7;
-+ }
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), &req,
-+ sizeof(req), true);
-+}
-+
-+static int
-+mt7996_tm_txbf_profile_update(struct mt7996_phy *phy, u16 *val, bool ebf)
-+{
-+#define MT_ARB_IBF_ENABLE (BIT(0) | GENMASK(9, 8))
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
-+ u8 pfmu_idx = val[0], nc = val[2], nr;
-+ int ret;
-+ bool is_atenl = val[5];
-+
-+ if (td->tx_antenna_mask == 3)
-+ nr = 1;
-+ else if (td->tx_antenna_mask == 7)
-+ nr = 2;
-+ else
-+ nr = 3;
-+
-+ memset(tag, 0, sizeof(*tag));
-+ tag->t1.pfmu_idx = pfmu_idx;
-+ tag->t1.ebf = ebf;
-+ tag->t1.nr = nr;
-+ tag->t1.nc = nc;
-+ tag->t1.invalid_prof = true;
-+ tag->t1.data_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
-+ tag->t2.se_idx = td->tx_spe_idx;
-+
-+ if (ebf) {
-+ tag->t1.row_id1 = 0;
-+ tag->t1.row_id2 = 1;
-+ tag->t1.row_id3 = 2;
-+ tag->t1.row_id4 = 3;
-+ tag->t1.lm = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_LM);
-+ } else {
-+ tag->t1.row_id1 = 4;
-+ tag->t1.row_id2 = 5;
-+ tag->t1.row_id3 = 6;
-+ tag->t1.row_id4 = 7;
-+ tag->t1.lm = mt7996_tm_rate_mapping(MT76_TM_TX_MODE_OFDM, RATE_MODE_TO_LM);
-+
-+ tag->t2.ibf_timeout = 0xff;
-+ tag->t2.ibf_nr = nr;
-+ tag->t2.ibf_nc = nc;
-+ }
-+
-+ ret = mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
-+ if (ret)
-+ return ret;
-+
-+ ret = mt7996_tm_add_txbf_sta(phy, pfmu_idx, nr, nc, ebf);
-+ if (ret)
-+ return ret;
-+
-+ if (!is_atenl && !td->ibf) {
-+ mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_TQSAXM_ALTX_START_MASK);
-+ dev_info(dev->mt76.dev, "Set TX queue start CR for AX management (0x%x) = 0x%x\n",
-+ MT_ARB_TQSAXM0(phy->mt76->band_idx),
-+ mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx)));
-+ } else if (!is_atenl && td->ibf && ebf) {
-+ /* iBF's ebf profile update */
-+ mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_IBF_ENABLE);
-+ dev_info(dev->mt76.dev, "Set TX queue start CR for AX management (0x%x) = 0x%x\n",
-+ MT_ARB_TQSAXM0(phy->mt76->band_idx),
-+ mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx)));
-+ }
-+
-+ if (!ebf && is_atenl)
-+ return mt7996_tm_txbf_apply_tx(phy, 1, false, true, true);
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_txbf_phase_cal(struct mt7996_phy *phy, u16 *val)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_bf_req req = {
-+ .phase_cal = {
-+ .tag = cpu_to_le16(BF_PHASE_CALIBRATION),
-+ .len = cpu_to_le16(sizeof(req.phase_cal)),
-+ .group = val[0],
-+ .group_l_m_n = val[1],
-+ .sx2 = val[2],
-+ .cal_type = val[3],
-+ .lna_gain_level = val[4],
-+ .band_idx = phy->mt76->band_idx,
-+ },
-+ };
-+ struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+
-+ /* reset phase status before update phase cal data */
-+ phase[req.phase_cal.group].status = 0;
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
-+ sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_profile_update_all(struct mt7996_phy *phy, u16 *val)
-+{
-+#define MT7996_TXBF_PFMU_DATA_LEN (MT7996_TXBF_SUBCAR_NUM * sizeof(struct mt7996_pfmu_data))
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ u8 nss = hweight8(td->tx_antenna_mask);
-+ u16 pfmu_idx = val[0];
-+ u16 subc_id = val[1];
-+ u16 angle11 = val[2];
-+ u16 angle21 = val[3];
-+ u16 angle31 = val[4];
-+ u16 angle41 = val[5];
-+ s16 phi11 = 0, phi21 = 0, phi31 = 0;
-+ struct mt7996_pfmu_data *pfmu_data;
-+
-+ if (subc_id > MT7996_TXBF_SUBCAR_NUM - 1)
-+ return -EINVAL;
-+
-+ if (nss == 2) {
-+ phi11 = (s16)(angle21 - angle11);
-+ } else if (nss == 3) {
-+ phi11 = (s16)(angle31 - angle11);
-+ phi21 = (s16)(angle31 - angle21);
-+ } else {
-+ phi11 = (s16)(angle41 - angle11);
-+ phi21 = (s16)(angle41 - angle21);
-+ phi31 = (s16)(angle41 - angle31);
-+ }
-+
-+ pfmu_data = (struct mt7996_pfmu_data *)phy->dev->test.txbf_pfmu_data;
-+ pfmu_data = &pfmu_data[subc_id];
-+
-+ if (subc_id < 32)
-+ pfmu_data->subc_idx = cpu_to_le16(subc_id + 224);
-+ else
-+ pfmu_data->subc_idx = cpu_to_le16(subc_id - 32);
-+
-+ pfmu_data->phi11 = cpu_to_le16(phi11);
-+ pfmu_data->phi21 = cpu_to_le16(phi21);
-+ pfmu_data->phi31 = cpu_to_le16(phi31);
-+ if (subc_id == MT7996_TXBF_SUBCAR_NUM - 1) {
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_bf_req req = {
-+ .pfmu_data_all = {
-+ .tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL),
-+ .len = cpu_to_le16(sizeof(req.pfmu_data_all)),
-+ .pfmu_id = pfmu_idx,
-+ .band_idx = phy->mt76->band_idx,
-+ },
-+ };
-+
-+ memcpy(req.pfmu_data_all.buf, dev->test.txbf_pfmu_data, MT7996_TXBF_PFMU_DATA_LEN);
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
-+ &req, sizeof(req), true);
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_txbf_e2p_update(struct mt7996_phy *phy)
-+{
-+#define TXBF_PHASE_EEPROM_START_OFFSET 0xc00
-+#define TXBF_PHASE_GROUP_EEPROM_OFFSET 0x2e
-+ struct mt7996_txbf_phase *phase, *p;
-+ struct mt7996_dev *dev = phy->dev;
-+ u8 *eeprom = dev->mt76.eeprom.data;
-+ u16 offset;
-+ int i;
-+
-+ offset = TXBF_PHASE_EEPROM_START_OFFSET;
-+ phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+ for (i = 0; i < MAX_PHASE_GROUP_NUM; i++) {
-+ p = &phase[i];
-+
-+ if (!p->status)
-+ continue;
-+
-+ /* copy phase cal data to eeprom */
-+ if (i)
-+ memcpy(eeprom + offset, &p->phase_5g, sizeof(p->phase_5g));
-+ else
-+ memcpy(eeprom + offset, &p->phase_2g, sizeof(p->phase_2g));
-+ offset += TXBF_PHASE_GROUP_EEPROM_OFFSET;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx, bool ebf,
-+ bool ibf, bool phase_cal)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_bf_req req = {
-+ .tx_apply = {
-+ .tag = cpu_to_le16(BF_DATA_PACKET_APPLY),
-+ .len = cpu_to_le16(sizeof(req.tx_apply)),
-+ .wlan_idx = cpu_to_le16(wlan_idx),
-+ .ebf = ebf,
-+ .ibf = ibf,
-+ .phase_cal = phase_cal,
-+ },
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_set_tx(struct mt7996_phy *phy, u16 *val)
-+{
-+ bool bf_on = val[0], update = val[3];
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+
-+ if (bf_on) {
-+ mt7996_tm_set_rx_frames(phy, false);
-+ mt7996_tm_set_tx_frames(phy, false);
-+ mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
-+ tag->t1.invalid_prof = false;
-+ mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
-+ td->bf_ever_en = true;
-+
-+ if (update)
-+ mt7996_tm_txbf_apply_tx(phy, 1, 0, 1, 1);
-+ } else {
-+ if (!td->bf_ever_en) {
-+ mt7996_tm_set_rx_frames(phy, false);
-+ mt7996_tm_set_tx_frames(phy, false);
-+ td->ibf = false;
-+ td->ebf = false;
-+
-+ if (update)
-+ mt7996_tm_txbf_apply_tx(phy, 1, 0, 0, 0);
-+ } else {
-+ td->bf_ever_en = false;
-+
-+ mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
-+ tag->t1.invalid_prof = true;
-+ mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_tm_trigger_sounding(struct mt7996_phy *phy, u16 *val, bool en)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ u8 sounding_mode = val[0];
-+ u8 sta_num = val[1];
-+ u32 sounding_interval = (u32)val[2] << 2; /* input unit: 4ms */
-+ u16 tag = en ? BF_SOUNDING_ON : BF_SOUNDING_OFF;
-+ struct mt7996_tm_bf_req req = {
-+ .sounding = {
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(sizeof(req.sounding)),
-+ .snd_mode = sounding_mode,
-+ .sta_num = sta_num,
-+ .wlan_id = {
-+ cpu_to_le16(val[3]),
-+ cpu_to_le16(val[4]),
-+ cpu_to_le16(val[5]),
-+ cpu_to_le16(val[6])
-+ },
-+ .snd_period = cpu_to_le32(sounding_interval),
-+ },
-+ };
-+
-+ if (sounding_mode > SOUNDING_MODE_MAX)
-+ return -EINVAL;
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
-+ &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_txcmd(struct mt7996_phy *phy, u16 *val)
-+{
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_tm_bf_req req = {
-+ .txcmd = {
-+ .tag = cpu_to_le16(BF_CMD_TXCMD),
-+ .len = cpu_to_le16(sizeof(req.txcmd)),
-+ .action = val[0],
-+ .bf_manual = val[1],
-+ .bf_bit = val[2],
-+ },
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_set_txbf(struct mt7996_phy *phy)
-+{
-+#define TXBF_IS_DUT_MASK BIT(0)
-+#define TXBF_IBF_MASK BIT(1)
-+ struct mt76_testmode_data *td = &phy->mt76->test;
-+ u16 *val = td->txbf_param;
-+
-+ dev_info(phy->dev->mt76.dev,
-+ "ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u, %u, %u\n",
-+ td->txbf_act, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
-+
-+ switch (td->txbf_act) {
-+ case MT76_TM_TXBF_ACT_GOLDEN_INIT:
-+ case MT76_TM_TXBF_ACT_INIT:
-+ case MT76_TM_TX_EBF_ACT_GOLDEN_INIT:
-+ case MT76_TM_TX_EBF_ACT_INIT:
-+ td->ibf = !u32_get_bits(td->txbf_act, TXBF_IBF_MASK);
-+ td->ebf = true;
-+ td->is_txbf_dut = !!u32_get_bits(td->txbf_act, TXBF_IS_DUT_MASK);
-+ return mt7996_tm_txbf_init(phy, val);
-+ case MT76_TM_TXBF_ACT_UPDATE_CH:
-+ mt7996_tm_update_channel(phy);
-+ break;
-+ case MT76_TM_TXBF_ACT_PHASE_COMP:
-+ return mt7996_tm_txbf_phase_comp(phy, val);
-+ case MT76_TM_TXBF_ACT_TX_PREP:
-+ return mt7996_tm_txbf_set_tx(phy, val);
-+ case MT76_TM_TXBF_ACT_IBF_PROF_UPDATE:
-+ return mt7996_tm_txbf_profile_update(phy, val, false);
-+ case MT76_TM_TXBF_ACT_EBF_PROF_UPDATE:
-+ return mt7996_tm_txbf_profile_update(phy, val, true);
-+ case MT76_TM_TXBF_ACT_PHASE_CAL:
-+ return mt7996_tm_txbf_phase_cal(phy, val);
-+ case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD:
-+ case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL:
-+ return mt7996_tm_txbf_profile_update_all(phy, val);
-+ case MT76_TM_TXBF_ACT_E2P_UPDATE:
-+ return mt7996_tm_txbf_e2p_update(phy);
-+ case MT76_TM_TXBF_ACT_APPLY_TX: {
-+ u16 wlan_idx = val[0];
-+ bool ebf = !!val[1], ibf = !!val[2], phase_cal = !!val[4];
-+
-+ return mt7996_tm_txbf_apply_tx(phy, wlan_idx, ebf, ibf, phase_cal);
-+ }
-+ case MT76_TM_TXBF_ACT_TRIGGER_SOUNDING:
-+ return mt7996_tm_trigger_sounding(phy, val, true);
-+ case MT76_TM_TXBF_ACT_STOP_SOUNDING:
-+ memset(val, 0, sizeof(td->txbf_param));
-+ return mt7996_tm_trigger_sounding(phy, val, false);
-+ case MT76_TM_TXBF_ACT_PROFILE_TAG_READ:
-+ case MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE:
-+ case MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: {
-+ u8 pfmu_idx = val[0];
-+ bool bfer = !!val[1];
-+ struct mt7996_dev *dev = phy->dev;
-+ struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
-+
-+ if (!tag) {
-+ dev_err(dev->mt76.dev,
-+ "pfmu tag is not initialized!\n");
-+ return 0;
-+ }
-+
-+ if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE)
-+ return mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
-+ else if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_READ)
-+ return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, pfmu_idx, bfer);
-+
-+ tag->t1.invalid_prof = !!val[0];
-+
-+ return 0;
-+ }
-+ case MT76_TM_TXBF_ACT_STA_REC_READ:
-+ return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, val[0], 0);
-+ case MT76_TM_TXBF_ACT_TXCMD:
-+ return mt7996_tm_txbf_txcmd(phy, val);
-+ default:
-+ break;
-+ };
-+
-+ return 0;
-+}
-+
- static void
- mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- {
-@@ -1086,6 +1770,8 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- mt7996_tm_set_ipi(phy);
- if (changed & BIT(TM_CHANGED_IPI_RESET))
- mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
-+ if (changed & BIT(TM_CHANGED_TXBF_ACT))
-+ mt7996_tm_set_txbf(phy);
- }
-
- static int
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index 78662b2e..f97ccb26 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -27,6 +27,17 @@ enum {
- FW_CDBW_8080MHZ,
- };
-
-+enum {
-+ BF_CDBW_20MHZ,
-+ BF_CDBW_40MHZ,
-+ BF_CDBW_80MHZ,
-+ BF_CDBW_160MHZ,
-+ BF_CDBW_320MHZ,
-+ BF_CDBW_10MHZ = BF_CDBW_320MHZ,
-+ BF_CDBW_5MHZ,
-+ BF_CDBW_8080MHZ,
-+};
-+
- #define FIRST_CONTROL_CHAN_BITMAP_BW40 0x5555555
- #define FIRST_CONTROL_CHAN_BITMAP_BW80 0x111111
- #define FIRST_CONTROL_CHAN_BITMAP_BW160 0x100101
-@@ -34,12 +45,20 @@ enum {
- enum bw_mapping_method {
- BW_MAP_NL_TO_FW,
- BW_MAP_NL_TO_TM,
-+ BW_MAP_NL_TO_BF,
- BW_MAP_NL_TO_MHZ,
- BW_MAP_NL_TO_CONTROL_BITMAP_5G,
-
- NUM_BW_MAP,
- };
-
-+enum rate_mapping_type {
-+ RATE_MODE_TO_PHY,
-+ RATE_MODE_TO_LM,
-+
-+ NUM_RATE_MAP,
-+};
-+
- struct tm_cal_param {
- __le32 func_data;
- u8 band_idx;
-diff --git a/testmode.c b/testmode.c
-index 69147f86..a5c07f8d 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -462,6 +462,42 @@ out:
- return err;
- }
-
-+static int
-+mt76_testmode_txbf_profile_update_all_cmd(struct mt76_phy *phy, struct nlattr **tb, u32 state)
-+{
-+#define PARAM_UNIT 5
-+ static u8 pfmu_idx;
-+ struct mt76_testmode_data *td = &phy->test;
-+ struct mt76_dev *dev = phy->dev;
-+ struct nlattr *cur;
-+ u16 tmp_val[PARAM_UNIT], *val = td->txbf_param;
-+ int idx, rem, ret, i = 0;
-+
-+ memset(td->txbf_param, 0, sizeof(td->txbf_param));
-+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
-+ if (nla_len(cur) != 2)
-+ return -EINVAL;
-+ idx = i % PARAM_UNIT;
-+ tmp_val[idx] = nla_get_u16(cur);
-+ if (idx == 1 && (tmp_val[idx] == 0xf0 || tmp_val[idx] == 0xff)) {
-+ pfmu_idx = tmp_val[0];
-+ return 0;
-+ }
-+ if (idx == PARAM_UNIT - 1) {
-+ val[0] = pfmu_idx;
-+ memcpy(val + 1, tmp_val, sizeof(tmp_val));
-+ if (dev->test_ops->set_params) {
-+ ret = dev->test_ops->set_params(phy, tb, state);
-+ if (ret)
-+ return ret;
-+ }
-+ }
-+ i++;
-+ }
-+
-+ return 0;
-+}
-+
- int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- void *data, int len)
- {
-@@ -607,6 +643,30 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- }
- }
-
-+ if (tb[MT76_TM_ATTR_TXBF_ACT]) {
-+ struct nlattr *cur;
-+ int rem, idx = 0;
-+
-+ if (!tb[MT76_TM_ATTR_TXBF_PARAM] ||
-+ mt76_tm_get_u8(tb[MT76_TM_ATTR_TXBF_ACT], &td->txbf_act,
-+ 0, MT76_TM_TXBF_ACT_MAX))
-+ goto out;
-+
-+ if (td->txbf_act == MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD) {
-+ err = mt76_testmode_txbf_profile_update_all_cmd(phy, tb, state);
-+ goto out;
-+ }
-+
-+ memset(td->txbf_param, 0, sizeof(td->txbf_param));
-+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
-+ if (nla_len(cur) != 2 ||
-+ idx >= ARRAY_SIZE(td->txbf_param))
-+ goto out;
-+
-+ td->txbf_param[idx++] = nla_get_u16(cur);
-+ }
-+ }
-+
- if (dev->test_ops->set_params) {
- err = dev->test_ops->set_params(phy, tb, state);
- if (err)
-diff --git a/testmode.h b/testmode.h
-index 5d677f8c..bda7624a 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -286,6 +286,59 @@ enum mt76_testmode_eeprom_action {
- MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
- };
-
-+/**
-+ * enum mt76_testmode_txbf_act - txbf action
-+ *
-+ * @MT76_TM_TXBF_ACT_GOLDEN_INIT: init ibf setting for golden device
-+ * @MT76_TM_TXBF_ACT_INIT: init ibf setting for DUT
-+ * @MT76_TM_TX_EBF_ACT_GOLDEN_INIT: init ebf setting for golden device
-+ * @MT76_TM_TX_EBF_ACT_INIT: init ebf setting for DUT
-+ * @MT76_TM_TXBF_ACT_UPDATE_CH: update channel info
-+ * @MT76_TM_TXBF_ACT_PHASE_COMP: txbf phase compensation
-+ * @MT76_TM_TXBF_ACT_TX_PREP: TX preparation for txbf
-+ * @MT76_TM_TXBF_ACT_IBF_PROF_UPDATE: update ibf profile (pfmu tag, bf sta record)
-+ * @MT76_TM_TXBF_ACT_EBF_PROF_UPDATE: update ebf profile
-+ * @MT76_TM_TXBF_ACT_APPLY_TX: apply TX setting for txbf
-+ * @MT76_TM_TXBF_ACT_PHASE_CAL: perform txbf phase calibration
-+ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL: update bf profile via instrument
-+ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD: update bf profile via instrument
-+ * @MT76_TM_TXBF_ACT_E2P_UPDATE: write back txbf calibration result to eeprom
-+ * @MT76_TM_TXBF_ACT_TRIGGER_SOUNDING: trigger beamformer to send sounding packet
-+ * @MT76_TM_TXBF_ACT_STOP_SOUNDING: stop sending sounding packet
-+ * @MT76_TM_TXBF_ACT_PROFILE_TAG_READ: read pfmu tag
-+ * @MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE: update pfmu tag
-+ * @MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: invalidate pfmu tag
-+ * @MT76_TM_TXBF_ACT_STA_REC_READ: read bf sta record
-+ * @MT76_TM_TXBF_ACT_TXCMD: configure txcmd bf bit manually
-+ */
-+enum mt76_testmode_txbf_act {
-+ MT76_TM_TXBF_ACT_GOLDEN_INIT,
-+ MT76_TM_TXBF_ACT_INIT,
-+ MT76_TM_TX_EBF_ACT_GOLDEN_INIT,
-+ MT76_TM_TX_EBF_ACT_INIT,
-+ MT76_TM_TXBF_ACT_UPDATE_CH,
-+ MT76_TM_TXBF_ACT_PHASE_COMP,
-+ MT76_TM_TXBF_ACT_TX_PREP,
-+ MT76_TM_TXBF_ACT_IBF_PROF_UPDATE,
-+ MT76_TM_TXBF_ACT_EBF_PROF_UPDATE,
-+ MT76_TM_TXBF_ACT_APPLY_TX,
-+ MT76_TM_TXBF_ACT_PHASE_CAL,
-+ MT76_TM_TXBF_ACT_PROF_UPDATE_ALL,
-+ MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD,
-+ MT76_TM_TXBF_ACT_E2P_UPDATE,
-+ MT76_TM_TXBF_ACT_TRIGGER_SOUNDING,
-+ MT76_TM_TXBF_ACT_STOP_SOUNDING,
-+ MT76_TM_TXBF_ACT_PROFILE_TAG_READ,
-+ MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE,
-+ MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID,
-+ MT76_TM_TXBF_ACT_STA_REC_READ,
-+ MT76_TM_TXBF_ACT_TXCMD,
-+
-+ /* keep last */
-+ NUM_MT76_TM_TXBF_ACT,
-+ MT76_TM_TXBF_ACT_MAX = NUM_MT76_TM_TXBF_ACT - 1,
-+};
-+
- extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
-
- #endif
-diff --git a/tools/fields.c b/tools/fields.c
-index 77696ce7..f793d1a5 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -44,6 +44,30 @@ static const char * const testmode_offchan_bw[] = {
- [NL80211_CHAN_WIDTH_160] = "160",
- };
-
-+static const char * const testmode_txbf_act[] = {
-+ [MT76_TM_TXBF_ACT_GOLDEN_INIT] = "golden_init",
-+ [MT76_TM_TXBF_ACT_INIT] = "init",
-+ [MT76_TM_TX_EBF_ACT_GOLDEN_INIT] = "ebf_golden_init",
-+ [MT76_TM_TX_EBF_ACT_INIT] = "ebf_init",
-+ [MT76_TM_TXBF_ACT_UPDATE_CH] = "update_ch",
-+ [MT76_TM_TXBF_ACT_PHASE_COMP] = "phase_comp",
-+ [MT76_TM_TXBF_ACT_TX_PREP] = "tx_prep",
-+ [MT76_TM_TXBF_ACT_IBF_PROF_UPDATE] = "ibf_prof_update",
-+ [MT76_TM_TXBF_ACT_EBF_PROF_UPDATE] = "ebf_prof_update",
-+ [MT76_TM_TXBF_ACT_APPLY_TX] = "apply_tx",
-+ [MT76_TM_TXBF_ACT_PHASE_CAL] = "phase_cal",
-+ [MT76_TM_TXBF_ACT_PROF_UPDATE_ALL] = "prof_update",
-+ [MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD] = "prof_update_all",
-+ [MT76_TM_TXBF_ACT_E2P_UPDATE] = "e2p_update",
-+ [MT76_TM_TXBF_ACT_TRIGGER_SOUNDING] = "trigger_sounding",
-+ [MT76_TM_TXBF_ACT_STOP_SOUNDING] = "stop_sounding",
-+ [MT76_TM_TXBF_ACT_PROFILE_TAG_READ] = "pfmu_tag_read",
-+ [MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE] = "pfmu_tag_write",
-+ [MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID] = "set_invalid_prof",
-+ [MT76_TM_TXBF_ACT_STA_REC_READ] = "sta_rec_read",
-+ [MT76_TM_TXBF_ACT_TXCMD] = "txcmd",
-+};
-+
- static void print_enum(const struct tm_field *field, struct nlattr *attr)
- {
- unsigned int i = nla_get_u8(attr);
-@@ -94,6 +118,17 @@ static void print_s8(const struct tm_field *field, struct nlattr *attr)
- printf("%d", (int8_t)nla_get_u8(attr));
- }
-
-+static bool parse_u16_hex(const struct tm_field *field, int idx,
-+ struct nl_msg *msg, const char *val)
-+{
-+ return !nla_put_u16(msg, idx, strtoul(val, NULL, 16));
-+}
-+
-+static void print_u16_hex(const struct tm_field *field, struct nlattr *attr)
-+{
-+ printf("%d", nla_get_u16(attr));
-+}
-+
- static bool parse_u32(const struct tm_field *field, int idx,
- struct nl_msg *msg, const char *val)
- {
-@@ -399,6 +434,8 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- FIELD(u8, AID, "aid"),
- FIELD(u8, RU_ALLOC, "ru_alloc"),
- FIELD(u8, RU_IDX, "ru_idx"),
-+ FIELD_ENUM(TXBF_ACT, "txbf_act", testmode_txbf_act),
-+ FIELD_ARRAY(u16_hex, TXBF_PARAM, "txbf_param"),
- FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
- FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
- FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1035-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1035-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch
deleted file mode 100644
index 326198c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1035-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch
+++ /dev/null
@@ -1,221 +0,0 @@
-From 68abdcf3519187da5d7fe324b8aa5cbb64ea13a3 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 22 Sep 2023 12:33:06 +0800
-Subject: [PATCH 1035/1044] mtk: wifi: mt76: mt7996: add zwdfs cert mode
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/mcu.c | 44 ++++++++++++++++++++++++++++++++------------
- mt7996/mcu.h | 14 ++++++++++++++
- mt7996/mt7996.h | 5 +++++
- mt7996/vendor.c | 37 +++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 12 ++++++++++++
- 5 files changed, 100 insertions(+), 12 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6e3047ba..ded28e9a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4506,18 +4506,7 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
- int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- u8 rx_sel, u8 val)
- {
-- struct {
-- u8 _rsv[4];
--
-- __le16 tag;
-- __le16 len;
--
-- u8 ctrl;
-- u8 rdd_idx;
-- u8 rdd_rx_sel;
-- u8 val;
-- u8 rsv[4];
-- } __packed req = {
-+ struct mt7996_rdd_ctrl req = {
- .tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
- .len = cpu_to_le16(sizeof(req) - 4),
- .ctrl = cmd,
-@@ -4530,6 +4519,37 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- &req, sizeof(req), true);
- }
-
-+int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable_timer)
-+{
-+ struct mt7996_rdd_ctrl req = {
-+ .tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .ctrl = RDD_DISABLE_ZW_TIMER,
-+ .rdd_idx = MT_RX_SEL2,
-+ .disable_timer = disable_timer,
-+ };
-+
-+ if (!is_mt7996(&dev->mt76) ||
-+ (mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) % 2))
-+ return 0;
-+
-+ switch (dev->mt76.region) {
-+ case NL80211_DFS_ETSI:
-+ req.val = 0;
-+ break;
-+ case NL80211_DFS_JP:
-+ req.val = 2;
-+ break;
-+ case NL80211_DFS_FCC:
-+ default:
-+ req.val = 1;
-+ break;
-+ }
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
-+ &req, sizeof(req), true);
-+}
-+
- int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 054a616b..398bf3d2 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -119,6 +119,20 @@ struct mt7996_mcu_rdd_report {
- } hw_pulse[32];
- } __packed;
-
-+struct mt7996_rdd_ctrl {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 ctrl;
-+ u8 rdd_idx;
-+ u8 rdd_rx_sel;
-+ u8 val;
-+ u8 disable_timer;
-+ u8 rsv[3];
-+} __packed;
-+
- struct mt7996_mcu_background_chain_ctrl {
- u8 _rsv[4];
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 2f76a0af..f937008f 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -517,8 +517,11 @@ enum mt7996_rdd_cmd {
- RDD_READ_PULSE,
- RDD_RESUME_BF,
- RDD_IRQ_OFF,
-+ RDD_DISABLE_ZW_TIMER,
- };
-
-+#define RDD_ZW_TIMER_OFF BIT(31)
-+
- static inline struct mt7996_phy *
- mt7996_hw_phy(struct ieee80211_hw *hw)
- {
-@@ -660,6 +663,8 @@ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
- int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
- int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- u8 rx_sel, u8 val);
-+int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
-+ bool disable_timer);
- int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
- struct cfg80211_chan_def *chandef);
- int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 477c5c42..c7fd3278 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -102,6 +102,11 @@ rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
- [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
- };
-
-+static const struct nla_policy
-+background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
-+ [MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -851,6 +856,27 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
- return 0;
- }
-
-+static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
-+ struct wireless_dev *wdev,
-+ const void *data,
-+ int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL];
-+ int err;
-+ u8 background_radar_mode;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX, data, data_len,
-+ background_radar_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ background_radar_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE]);
-+
-+ return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
-+}
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
- .info = {
-@@ -945,6 +971,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = rfeature_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_background_radar_mode_ctrl,
-+ .policy = background_radar_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 7011914b..920b6e6a 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -14,6 +14,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
-+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -126,6 +127,17 @@ enum mtk_vendor_attr_wireless_dump {
- NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
- };
-
-+enum mtk_vendor_attr_background_radar_ctrl {
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
-+};
-+
- enum bw_sig {
- BW_SIGNALING_DISABLE,
- BW_SIGNALING_STATIC,
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1036-mtk-wifi-mt76-testmode-add-channel-68-96.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1036-mtk-wifi-mt76-testmode-add-channel-68-96.patch
deleted file mode 100644
index e3e2aa7..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1036-mtk-wifi-mt76-testmode-add-channel-68-96.patch
+++ /dev/null
@@ -1,248 +0,0 @@
-From b159ab04d85f593ca0785404469f2ea23eababef Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 11 Sep 2023 14:43:07 +0800
-Subject: [PATCH 1036/1044] mtk: wifi: mt76: testmode: add channel 68 & 96
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add all the channel between 68 & 96 since ibf 5g channel group 3 will use channel 84.
-Also, "mtk: wifi: mt76: testmode: add channel 68 & 96" can be
-merged into to "mtk: wifi: mt76: testmode: add basic testmode support"
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Fix 5g channel list size
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mac80211.c | 9 +++++++++
- mt7996/eeprom.c | 49 +++++++++++++++++++++++++++++++++++++++++++++--
- mt7996/eeprom.h | 2 ++
- mt7996/mcu.c | 10 +++++++++-
- mt7996/testmode.c | 15 ++++++++++++---
- mt7996/testmode.h | 6 +++---
- 6 files changed, 82 insertions(+), 9 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index ae040ec4..f7cd47f9 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -35,6 +35,15 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
- CHAN5G(60, 5300),
- CHAN5G(64, 5320),
-
-+ CHAN5G(68, 5340),
-+ CHAN5G(72, 5360),
-+ CHAN5G(76, 5380),
-+ CHAN5G(80, 5400),
-+ CHAN5G(84, 5420),
-+ CHAN5G(88, 5440),
-+ CHAN5G(92, 5460),
-+ CHAN5G(96, 5480),
-+
- CHAN5G(100, 5500),
- CHAN5G(104, 5520),
- CHAN5G(108, 5540),
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index d93b1558..1eb292c0 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -18,6 +18,17 @@ const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
- CHAN2G(11, 2462)
- };
-
-+const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
-+ CHAN5G(68, 5340),
-+ CHAN5G(72, 5360),
-+ CHAN5G(76, 5380),
-+ CHAN5G(80, 5400),
-+ CHAN5G(84, 5420),
-+ CHAN5G(88, 5440),
-+ CHAN5G(92, 5460),
-+ CHAN5G(96, 5480)
-+};
-+
- const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
- CHAN5G(50, 5250),
- CHAN5G(114, 5570),
-@@ -44,6 +55,7 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
- };
-
- const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
-+const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
- const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
- const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
- const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
-@@ -138,8 +150,8 @@ mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
- if (band == NL80211_BAND_2GHZ)
- dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
- else if (band == NL80211_BAND_5GHZ)
-- dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-- dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+ dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-+ DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
- else
- dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
- (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
-@@ -399,6 +411,39 @@ out:
- return ret;
- }
-
-+static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
-+{
-+#define MT76_CHANNELS_5GHZ_SIZE 36 /* ARRAY_SIZE(mt76_channels_5ghz) */
-+#define MT76_CHANNELS_6GHZ_SIZE 59 /* ARRAY_SIZE(mt76_channels_6ghz) */
-+
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_2G] = ARRAY_SIZE(dpd_2g_ch_list_bw20);
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G_SKIP] = ARRAY_SIZE(dpd_5g_skip_ch_list);
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G] = MT76_CHANNELS_5GHZ_SIZE -
-+ DPD_CH_NUM(BW20_5G_SKIP);
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw160);
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = MT76_CHANNELS_6GHZ_SIZE;
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw160);
-+
-+ switch (mt76_chip(&dev->mt76)) {
-+ case 0x7990:
-+ dev->prek.rev = mt7996_prek_rev;
-+ /* 5g & 6g bw 80 dpd channel list is not used */
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
-+ break;
-+ case 0x7992:
-+ dev->prek.rev = mt7992_prek_rev;
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw80);
-+ /* 6g is not used in current sku */
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = 0;
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_6G] = 0;
-+ dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = 0;
-+ break;
-+ default:
-+ dev->prek.rev = mt7996_prek_rev;
-+ break;
-+ }
-+}
-+
- static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
- {
- struct mt76_dev *mdev = &dev->mt76;
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 8f0f87b6..3e9992a3 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -67,6 +67,8 @@ enum mt7996_eeprom_field {
-
- extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
- extern const u32 dpd_2g_bw20_ch_num;
-+extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
-+extern const u32 dpd_5g_skip_ch_num;
- extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
- extern const u32 dpd_5g_bw160_ch_num;
- extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index ded28e9a..cf46fee4 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3780,7 +3780,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- chan_list_size = mphy->sband_5g.sband.n_channels;
- base_offset += dpd_size_2g;
- if (bw == NL80211_CHAN_WIDTH_160) {
-- base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+ base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-+ DPD_PER_CH_BW20_SIZE;
- per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- cal_id = RF_DPD_FLAT_5G_MEM_CAL;
- chan_list = dpd_5g_ch_list_bw160;
-@@ -3789,6 +3790,9 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
- channel -= 2;
- }
-+ if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
-+ channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+ return 0;
- break;
- case NL80211_BAND_6GHZ:
- dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
-@@ -3828,6 +3832,10 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- if (idx == chan_list_size)
- return -EINVAL;
-
-+ if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
-+ channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+ idx -= dpd_5g_skip_ch_num;
-+
- cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
-
- for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 2fb36a97..0dc6629d 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -531,6 +531,11 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
- memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
-
- for (i = 0; i < channel_size; i++) {
-+ if (chan_list[i].band == NL80211_BAND_5GHZ &&
-+ chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
-+ chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+ continue;
-+
- memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
- chandef->width = width;
-
-@@ -612,7 +617,8 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
- if (ret)
- return ret;
-- wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+ wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-+ DPD_PER_CH_BW20_SIZE;
- wait_event_timeout(mdev->mcu.wait,
- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-
-@@ -868,6 +874,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
- const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
- u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
- u16 first_control = 0, control_chan = chandef->chan->hw_value;
-+ bool not_first;
-
- bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
- if (!bitmap)
-@@ -877,7 +884,9 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
- offset = width_mhz / 10 - 2;
-
- for (i = 0; i < size; i++) {
-- if (!((1 << i) & bitmap))
-+ not_first = (chandef->width != NL80211_CHAN_WIDTH_160) ?
-+ (i % bitmap) : (i >= 32) || !((1 << i) & bitmap);
-+ if (not_first)
- continue;
-
- if (control_chan >= chan[i].hw_value)
-@@ -886,7 +895,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
- break;
- }
-
-- if (i == size || first_control == 0)
-+ if (first_control == 0)
- return control_chan;
-
- return first_control + offset;
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index f97ccb26..ba1767ae 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -38,9 +38,9 @@ enum {
- BF_CDBW_8080MHZ,
- };
-
--#define FIRST_CONTROL_CHAN_BITMAP_BW40 0x5555555
--#define FIRST_CONTROL_CHAN_BITMAP_BW80 0x111111
--#define FIRST_CONTROL_CHAN_BITMAP_BW160 0x100101
-+#define FIRST_CONTROL_CHAN_BITMAP_BW40 2
-+#define FIRST_CONTROL_CHAN_BITMAP_BW80 4
-+#define FIRST_CONTROL_CHAN_BITMAP_BW160 0x10010101
-
- enum bw_mapping_method {
- BW_MAP_NL_TO_FW,
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1037-mtk-wifi-mt76-mt7996-support-enable-disable-pp-featu.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1037-mtk-wifi-mt76-mt7996-support-enable-disable-pp-featu.patch
deleted file mode 100644
index a914829..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1037-mtk-wifi-mt76-mt7996-support-enable-disable-pp-featu.patch
+++ /dev/null
@@ -1,112 +0,0 @@
-From 8548624e0e3323cf0e14ce21ab988cd5ed5167cf Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 25 Sep 2023 19:20:49 +0800
-Subject: [PATCH 1037/1044] mtk: wifi: mt76: mt7996: support enable/disable pp
- feature by nl80211 vendor commands
-
-User can enable/disable preamble puncture feature through hostapd
-configuration and hostapd_cli. Driver can receive the nl80211 vendor
-message and convert it to mcu commands.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/vendor.c | 38 ++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 12 ++++++++++++
- 2 files changed, 50 insertions(+)
-
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index c7fd3278..9732ed28 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -107,6 +107,11 @@ background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
- [MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
- };
-
-+static struct nla_policy
-+pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
-+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -877,6 +882,28 @@ static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
- return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
- }
-
-+static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ const void *data, int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_PP_CTRL];
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ int err;
-+ u8 val8;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX, data, data_len,
-+ pp_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (tb[MTK_VENDOR_ATTR_PP_MODE]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_MODE]);
-+ err = mt7996_mcu_set_pp_en(phy, !!val8, 0, 0);
-+ }
-+
-+ return err;
-+}
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
- .info = {
-@@ -982,6 +1009,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = background_radar_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_PP_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_pp_ctrl,
-+ .policy = pp_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 920b6e6a..98128965 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
-+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -214,6 +215,17 @@ enum mtk_vendor_attr_ibf_dump {
- NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
- };
-
-+enum mtk_vendor_attr_pp_ctrl {
-+ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_PP_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-+ MTK_VENDOR_ATTR_PP_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
-+};
-+
- #endif
-
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1038-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1038-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch
deleted file mode 100644
index b005bee..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1038-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch
+++ /dev/null
@@ -1,598 +0,0 @@
-From 911e50206b1155bb240eaa7670fa6715ac6cb500 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 12 Oct 2023 16:17:33 +0800
-Subject: [PATCH 1038/1044] mtk: wifi: mt76: testmode: add kite testmode
- support
-
-Add Kite testmode support
-1. avoid entering connac 2 testmode flow in kite
-2. refactor prek implementation for handling chip difference
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 63 +++++++++++++-----------------
- mt7996/eeprom.h | 81 +++++++++++++++++++++++++++------------
- mt7996/mcu.c | 48 ++++++++++++++---------
- mt7996/mt7996.h | 18 ++++++++-
- mt7996/testmode.c | 97 ++++++++++++++++++++++++++++-------------------
- testmode.c | 11 ++++--
- 6 files changed, 198 insertions(+), 120 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 1eb292c0..39e65010 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -29,12 +29,39 @@ const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
- CHAN5G(96, 5480)
- };
-
-+const struct ieee80211_channel dpd_5g_ch_list_bw80[] = {
-+ CHAN5G(42, 5210),
-+ CHAN5G(58, 5290),
-+ CHAN5G(106, 5530),
-+ CHAN5G(122, 5610),
-+ CHAN5G(138, 5690),
-+ CHAN5G(155, 5775),
-+ CHAN5G(171, 5855)
-+};
-+
- const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
- CHAN5G(50, 5250),
- CHAN5G(114, 5570),
- CHAN5G(163, 5815)
- };
-
-+const struct ieee80211_channel dpd_6g_ch_list_bw80[] = {
-+ CHAN6G(7, 5985),
-+ CHAN6G(23, 6065),
-+ CHAN6G(39, 6145),
-+ CHAN6G(55, 6225),
-+ CHAN6G(71, 6305),
-+ CHAN6G(87, 6385),
-+ CHAN6G(103, 6465),
-+ CHAN6G(119, 6545),
-+ CHAN6G(135, 6625),
-+ CHAN6G(151, 6705),
-+ CHAN6G(167, 6785),
-+ CHAN6G(183, 6865),
-+ CHAN6G(199, 6945),
-+ CHAN6G(215, 7025)
-+};
-+
- const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
- CHAN6G(15, 6025),
- CHAN6G(47, 6185),
-@@ -54,12 +81,6 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
- CHAN6G(191, 6905)
- };
-
--const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
--const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
--const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
--const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
--const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
--
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
- #define FEM_INT 0
-@@ -129,36 +150,6 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
- }
- }
-
--int
--mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
--{
-- /* handle different sku */
-- static const u8 band_to_idx[] = {
-- [NL80211_BAND_2GHZ] = MT_BAND0,
-- [NL80211_BAND_5GHZ] = MT_BAND1,
-- [NL80211_BAND_6GHZ] = MT_BAND2,
-- };
-- struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
-- struct mt76_phy *mphy;
-- int dpd_size;
--
-- if (!phy)
-- return 0;
--
-- mphy = phy->mt76;
--
-- if (band == NL80211_BAND_2GHZ)
-- dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
-- else if (band == NL80211_BAND_5GHZ)
-- dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-- DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-- else
-- dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-- (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
--
-- return dpd_size;
--}
--
- static int
- mt7996_eeprom_load_default(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 3e9992a3..0d05e75e 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -45,36 +45,69 @@ enum mt7996_eeprom_field {
- #define MT_EE_WIFI_CAL_DPD GENMASK(5, 3)
-
- #define MT_EE_CAL_UNIT 1024
--#define MT_EE_CAL_GROUP_SIZE_2G (4 * MT_EE_CAL_UNIT)
--#define MT_EE_CAL_GROUP_SIZE_5G (45 * MT_EE_CAL_UNIT)
--#define MT_EE_CAL_GROUP_SIZE_6G (125 * MT_EE_CAL_UNIT)
--#define MT_EE_CAL_ADCDCOC_SIZE_2G (4 * 4)
--#define MT_EE_CAL_ADCDCOC_SIZE_5G (4 * 4)
--#define MT_EE_CAL_ADCDCOC_SIZE_6G (4 * 5)
--#define MT_EE_CAL_GROUP_SIZE (MT_EE_CAL_GROUP_SIZE_2G + \
-- MT_EE_CAL_GROUP_SIZE_5G + \
-- MT_EE_CAL_GROUP_SIZE_6G + \
-- MT_EE_CAL_ADCDCOC_SIZE_2G + \
-- MT_EE_CAL_ADCDCOC_SIZE_5G + \
-- MT_EE_CAL_ADCDCOC_SIZE_6G)
--
--#define DPD_PER_CH_LEGACY_SIZE (4 * MT_EE_CAL_UNIT)
--#define DPD_PER_CH_MEM_SIZE (13 * MT_EE_CAL_UNIT)
--#define DPD_PER_CH_OTFG0_SIZE (2 * MT_EE_CAL_UNIT)
--#define DPD_PER_CH_BW20_SIZE (DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
--#define DPD_PER_CH_GT_BW20_SIZE (DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
--#define MT_EE_CAL_DPD_SIZE (780 * MT_EE_CAL_UNIT)
-+
-+enum mt7996_prek_rev {
-+ GROUP_SIZE_2G,
-+ GROUP_SIZE_5G,
-+ GROUP_SIZE_6G,
-+ ADCDCOC_SIZE_2G,
-+ ADCDCOC_SIZE_5G,
-+ ADCDCOC_SIZE_6G,
-+ DPD_LEGACY_SIZE,
-+ DPD_MEM_SIZE,
-+ DPD_OTFG0_SIZE,
-+};
-+
-+static const u32 mt7996_prek_rev[] = {
-+ [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
-+ [GROUP_SIZE_5G] = 45 * MT_EE_CAL_UNIT,
-+ [GROUP_SIZE_6G] = 125 * MT_EE_CAL_UNIT,
-+ [ADCDCOC_SIZE_2G] = 4 * 4,
-+ [ADCDCOC_SIZE_5G] = 4 * 4,
-+ [ADCDCOC_SIZE_6G] = 4 * 5,
-+ [DPD_LEGACY_SIZE] = 4 * MT_EE_CAL_UNIT,
-+ [DPD_MEM_SIZE] = 13 * MT_EE_CAL_UNIT,
-+ [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
-+};
-+
-+/* kite 2/5g config */
-+static const u32 mt7992_prek_rev[] = {
-+ [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
-+ [GROUP_SIZE_5G] = 110 * MT_EE_CAL_UNIT,
-+ [GROUP_SIZE_6G] = 0,
-+ [ADCDCOC_SIZE_2G] = 4 * 4,
-+ [ADCDCOC_SIZE_5G] = 4 * 5,
-+ [ADCDCOC_SIZE_6G] = 0,
-+ [DPD_LEGACY_SIZE] = 5 * MT_EE_CAL_UNIT,
-+ [DPD_MEM_SIZE] = 16 * MT_EE_CAL_UNIT,
-+ [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
-+};
-
- extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
--extern const u32 dpd_2g_bw20_ch_num;
- extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
--extern const u32 dpd_5g_skip_ch_num;
-+extern const struct ieee80211_channel dpd_5g_ch_list_bw80[];
- extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
--extern const u32 dpd_5g_bw160_ch_num;
-+extern const struct ieee80211_channel dpd_6g_ch_list_bw80[];
- extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
--extern const u32 dpd_6g_bw160_ch_num;
- extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
--extern const u32 dpd_6g_bw320_ch_num;
-+
-+#define PREK(id) (dev->prek.rev[(id)])
-+#define DPD_CH_NUM(_type) (dev->prek.dpd_ch_num[DPD_CH_NUM_##_type])
-+#define MT_EE_CAL_GROUP_SIZE (PREK(GROUP_SIZE_2G) + PREK(GROUP_SIZE_5G) + \
-+ PREK(GROUP_SIZE_6G) + PREK(ADCDCOC_SIZE_2G) + \
-+ PREK(ADCDCOC_SIZE_5G) + PREK(ADCDCOC_SIZE_6G))
-+#define DPD_PER_CH_BW20_SIZE (PREK(DPD_LEGACY_SIZE) + PREK(DPD_OTFG0_SIZE))
-+#define DPD_PER_CH_GT_BW20_SIZE (PREK(DPD_MEM_SIZE) + PREK(DPD_OTFG0_SIZE))
-+#define MT_EE_CAL_DPD_SIZE_2G (DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE)
-+#define MT_EE_CAL_DPD_SIZE_5G (DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE + \
-+ DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE + \
-+ DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE)
-+#define MT_EE_CAL_DPD_SIZE_6G (DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE + \
-+ DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE + \
-+ DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE + \
-+ DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE)
-+#define MT_EE_CAL_DPD_SIZE (MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G + \
-+ MT_EE_CAL_DPD_SIZE_6G)
-
- #define RF_DPD_FLAT_CAL BIT(28)
- #define RF_PRE_CAL BIT(29)
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index cf46fee4..a39b8bab 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3749,13 +3749,11 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- enum nl80211_chan_width bw = chandef->width;
- const struct ieee80211_channel *chan_list;
- u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
-- u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
-+ u32 per_chan_size = DPD_PER_CH_BW20_SIZE;
- u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
- u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
- int idx, i, ret;
--
-- dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-- dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+ bool has_skip_ch = (band == NL80211_BAND_5GHZ);
-
- switch (band) {
- case NL80211_BAND_2GHZ:
-@@ -3771,27 +3769,35 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- return 0;
- cal_id = RF_DPD_FLAT_CAL;
- chan_list = dpd_2g_ch_list_bw20;
-- chan_list_size = dpd_2g_bw20_ch_num;
-+ chan_list_size = DPD_CH_NUM(BW20_2G);
- break;
- case NL80211_BAND_5GHZ:
- dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
- cal_id = RF_DPD_FLAT_5G_CAL;
- chan_list = mphy->sband_5g.sband.channels;
- chan_list_size = mphy->sband_5g.sband.n_channels;
-- base_offset += dpd_size_2g;
-+ base_offset += MT_EE_CAL_DPD_SIZE_2G;
- if (bw == NL80211_CHAN_WIDTH_160) {
-- base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-- DPD_PER_CH_BW20_SIZE;
-+ base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE +
-+ DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
- per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- cal_id = RF_DPD_FLAT_5G_MEM_CAL;
- chan_list = dpd_5g_ch_list_bw160;
-- chan_list_size = dpd_5g_bw160_ch_num;
-+ chan_list_size = DPD_CH_NUM(BW160_5G);
-+ has_skip_ch = false;
-+ } else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
-+ base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
-+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+ cal_id = RF_DPD_FLAT_5G_MEM_CAL;
-+ chan_list = dpd_5g_ch_list_bw80;
-+ chan_list_size = DPD_CH_NUM(BW80_5G);
-+ has_skip_ch = false;
- } else if (bw > NL80211_CHAN_WIDTH_20) {
- /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
- channel -= 2;
- }
- if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
-- channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+ channel <= dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
- return 0;
- break;
- case NL80211_BAND_6GHZ:
-@@ -3799,20 +3805,27 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- cal_id = RF_DPD_FLAT_6G_CAL;
- chan_list = mphy->sband_6g.sband.channels;
- chan_list_size = mphy->sband_6g.sband.n_channels;
-- base_offset += dpd_size_2g + dpd_size_5g;
-+ base_offset += MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G;
- if (bw == NL80211_CHAN_WIDTH_160) {
- base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
- per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- cal_id = RF_DPD_FLAT_6G_MEM_CAL;
- chan_list = dpd_6g_ch_list_bw160;
-- chan_list_size = dpd_6g_bw160_ch_num;
-- } else if (bw == NL80211_CHAN_WIDTH_320) {
-+ chan_list_size = DPD_CH_NUM(BW160_6G);
-+ } else if (is_mt7996(&dev->mt76) && bw == NL80211_CHAN_WIDTH_320) {
- base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-- dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+ DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE +
-+ DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
- per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- cal_id = RF_DPD_FLAT_6G_MEM_CAL;
- chan_list = dpd_6g_ch_list_bw320;
-- chan_list_size = dpd_6g_bw320_ch_num;
-+ chan_list_size = DPD_CH_NUM(BW320_6G);
-+ } else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
-+ base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+ per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+ cal_id = RF_DPD_FLAT_6G_MEM_CAL;
-+ chan_list = dpd_6g_ch_list_bw80;
-+ chan_list_size = DPD_CH_NUM(BW80_6G);
- } else if (bw > NL80211_CHAN_WIDTH_20) {
- /* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
- channel -= 2;
-@@ -3832,9 +3845,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- if (idx == chan_list_size)
- return -EINVAL;
-
-- if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
-- channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-- idx -= dpd_5g_skip_ch_num;
-+ if (has_skip_ch && channel > dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
-+ idx -= DPD_CH_NUM(BW20_5G_SKIP);
-
- cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index f937008f..881328be 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -195,6 +195,19 @@ struct mt7996_twt_flow {
-
- DECLARE_EWMA(avg_signal, 10, 8)
-
-+enum mt7996_dpd_ch_num {
-+ DPD_CH_NUM_BW20_2G,
-+ DPD_CH_NUM_BW20_5G,
-+ DPD_CH_NUM_BW20_5G_SKIP,
-+ DPD_CH_NUM_BW80_5G,
-+ DPD_CH_NUM_BW160_5G,
-+ DPD_CH_NUM_BW20_6G,
-+ DPD_CH_NUM_BW80_6G,
-+ DPD_CH_NUM_BW160_6G,
-+ DPD_CH_NUM_BW320_6G,
-+ DPD_CH_NUM_TYPE_MAX,
-+};
-+
- struct mt7996_sta {
- struct mt76_wcid wcid; /* must be first */
-
-@@ -457,6 +470,10 @@ struct mt7996_dev {
-
- void *cal;
- u32 cur_prek_offset;
-+ struct {
-+ const u32 *rev;
-+ u8 dpd_ch_num[DPD_CH_NUM_TYPE_MAX];
-+ } prek;
-
- struct {
- u16 table_mask;
-@@ -593,7 +610,6 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
- int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
- struct ieee80211_channel *chan);
- s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
--int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
- int mt7996_dma_init(struct mt7996_dev *dev);
- void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
- void mt7996_dma_prefetch(struct mt7996_dev *dev);
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 0dc6629d..44ec84cc 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -434,7 +434,7 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
- static int
- mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- {
-- u8 *eeprom;
-+ u8 *eeprom, do_precal;
- u32 i, group_size, dpd_size, size, offs, *pre_cal;
- int ret = 0;
- struct mt7996_dev *dev = phy->dev;
-@@ -462,6 +462,9 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- dpd_size = MT_EE_CAL_DPD_SIZE;
- size = group_size + dpd_size;
- offs = MT_EE_DO_PRE_CAL;
-+ do_precal = (MT_EE_WIFI_CAL_GROUP_2G * !!PREK(GROUP_SIZE_2G)) |
-+ (MT_EE_WIFI_CAL_GROUP_5G * !!PREK(GROUP_SIZE_5G)) |
-+ (MT_EE_WIFI_CAL_GROUP_6G * !!PREK(GROUP_SIZE_6G));
-
- switch (state) {
- case MT76_TM_STATE_GROUP_PREK:
-@@ -476,13 +479,10 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
- 30 * HZ);
-
-- if (ret) {
-+ if (ret)
- dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
-- return ret;
-- }
--
-- if (!ret)
-- eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
-+ else
-+ eeprom[offs] |= do_precal;
- break;
- case MT76_TM_STATE_GROUP_PREK_DUMP:
- pre_cal = (u32 *)dev->cal;
-@@ -520,10 +520,12 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
- struct mt76_phy *mphy = phy->mt76;
- struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
- struct ieee80211_channel chan_backup;
-- int i, ret;
-+ int i, ret, skip_ch_num = DPD_CH_NUM(BW20_5G_SKIP);
-
- if (!chan_list)
- return -EOPNOTSUPP;
-+ if (!channel_size)
-+ return 0;
-
- req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
-
-@@ -533,7 +535,7 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
- for (i = 0; i < channel_size; i++) {
- if (chan_list[i].band == NL80211_BAND_5GHZ &&
- chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
-- chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+ chan_list[i].hw_value <= dpd_5g_skip_ch_list[skip_ch_num - 1].hw_value)
- continue;
-
- memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
-@@ -602,11 +604,11 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- switch (state) {
- case MT76_TM_STATE_DPD_2G:
- ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
-- dpd_2g_bw20_ch_num,
-+ DPD_CH_NUM(BW20_2G),
- NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
-- wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
-- wait_event_timeout(mdev->mcu.wait,
-- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+ wait_on_prek_offset += DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-
- do_precal = MT_EE_WIFI_CAL_DPD_2G;
- break;
-@@ -617,18 +619,27 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
- if (ret)
- return ret;
-- wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-- DPD_PER_CH_BW20_SIZE;
-- wait_event_timeout(mdev->mcu.wait,
-- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+ wait_on_prek_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-+
-+ /* 5g channel bw80 calibration */
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw80,
-+ DPD_CH_NUM(BW80_5G),
-+ NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_5G_MEM_CAL);
-+ if (ret)
-+ return ret;
-+ wait_on_prek_offset += DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-
- /* 5g channel bw160 calibration */
- ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
-- dpd_5g_bw160_ch_num,
-+ DPD_CH_NUM(BW160_5G),
- NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
-- wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-- wait_event_timeout(mdev->mcu.wait,
-- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+ wait_on_prek_offset += DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-
- do_precal = MT_EE_WIFI_CAL_DPD_5G;
- break;
-@@ -639,27 +650,37 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
- if (ret)
- return ret;
-- wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-- wait_event_timeout(mdev->mcu.wait,
-- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+ wait_on_prek_offset += DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-+
-+ /* 6g channel bw80 calibration */
-+ ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw80,
-+ DPD_CH_NUM(BW80_6G),
-+ NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_6G_MEM_CAL);
-+ if (ret)
-+ return ret;
-+ wait_on_prek_offset += DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-
- /* 6g channel bw160 calibration */
- ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
-- dpd_6g_bw160_ch_num,
-+ DPD_CH_NUM(BW160_6G),
- NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
- if (ret)
- return ret;
-- wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-- wait_event_timeout(mdev->mcu.wait,
-- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+ wait_on_prek_offset += DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-
- /* 6g channel bw320 calibration */
- ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
-- dpd_6g_bw320_ch_num,
-+ DPD_CH_NUM(BW320_6G),
- NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
-- wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-- wait_event_timeout(mdev->mcu.wait,
-- dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+ wait_on_prek_offset += DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE;
-+ wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+ 30 * HZ);
-
- do_precal = MT_EE_WIFI_CAL_DPD_6G;
- break;
-@@ -732,9 +753,9 @@ mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int
- eeprom = dev->mt76.eeprom.data;
- offs = MT_EE_DO_PRE_CAL;
-
-- dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-- dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-- dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+ dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
-+ dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
-+ dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
-
- switch (type) {
- case PREK_SYNC_ALL:
-@@ -810,9 +831,9 @@ mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *
- u8 *pre_cal;
-
- pre_cal = dev->cal;
-- dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-- dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-- dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+ dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
-+ dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
-+ dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
-
- cal_idx = le32_to_cpu(data->cal_idx);
- cal_type = le32_to_cpu(data->cal_type);
-diff --git a/testmode.c b/testmode.c
-index a5c07f8d..09ab68ce 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -37,6 +37,11 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
- };
- EXPORT_SYMBOL_GPL(mt76_tm_policy);
-
-+static inline bool mt76_testmode_offload(struct mt76_dev *dev)
-+{
-+ return is_mt7996(dev) || is_mt7992(dev);
-+}
-+
- void mt76_testmode_tx_pending(struct mt76_phy *phy)
- {
- struct mt76_testmode_data *td = &phy->test;
-@@ -197,7 +202,7 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
- u8 max_nss = hweight8(phy->antenna_mask);
- int ret;
-
-- if (is_mt7996(phy->dev))
-+ if (mt76_testmode_offload(phy->dev))
- return 0;
-
- ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
-@@ -293,7 +298,7 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
- td->tx_done = 0;
- td->tx_pending = td->tx_count;
-
-- if (!is_mt7996(dev))
-+ if (!mt76_testmode_offload(dev))
- mt76_worker_schedule(&dev->tx_worker);
- }
-
-@@ -303,7 +308,7 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
- struct mt76_testmode_data *td = &phy->test;
- struct mt76_dev *dev = phy->dev;
-
-- if (is_mt7996(dev) && dev->test_ops->tx_stop) {
-+ if (mt76_testmode_offload(dev) && dev->test_ops->tx_stop) {
- dev->test_ops->tx_stop(phy);
- return;
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1039-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1039-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch
deleted file mode 100644
index 744e679..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1039-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 9c7e1159e0daa47df31d51c73b26a12b7c3c0a42 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 14 Nov 2023 11:27:06 +0800
-Subject: [PATCH 1039/1044] mtk: wifi: mt76: mt7996: assign DEAUTH to ALTX
- queue for CERT
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- mt7996/mac.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index bf42ae07..6fa46e46 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -753,6 +753,8 @@ static void
- mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
- struct sk_buff *skb, struct ieee80211_key_conf *key)
- {
-+ struct mt76_phy *mphy =
-+ mt76_dev_phy(&dev->mt76, le32_get_bits(txwi[1], MT_TXD1_TGID));
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-@@ -762,6 +764,14 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
- u8 fc_type, fc_stype;
- u32 val;
-
-+ if (ieee80211_is_cert_mode(mphy->hw) && ieee80211_is_deauth(fc)) {
-+ /* In WPA3 cert TC-4.8.1, the deauth must be transmitted without
-+ * considering PSM bit
-+ */
-+ txwi[0] &= ~cpu_to_le32(MT_TXD0_Q_IDX);
-+ txwi[0] |= cpu_to_le32(FIELD_PREP(MT_TXD0_Q_IDX, MT_LMAC_ALTX0));
-+ }
-+
- if (ieee80211_is_action(fc) &&
- mgmt->u.action.category == WLAN_CATEGORY_BACK &&
- mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1040-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1040-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch
deleted file mode 100644
index b1cd4c6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1040-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch
+++ /dev/null
@@ -1,153 +0,0 @@
-From ba07e0c01317f0d39fb56aceef557c04c0f770c2 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 22 Nov 2023 22:42:09 +0800
-Subject: [PATCH 1040/1044] mtk: wifi: mt76: mt7996: add no_beacon vendor
- command for cert
-
-Add the vendor command to disable/enable beacon
-
-[Usage]
-hostapd_cli -i <interface> no_beacon <value>
-<value>
-0: enable beacon
-1: disable beacon
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- mt7996/mcu.c | 11 +++++++++++
- mt7996/mt7996.h | 1 +
- mt7996/vendor.c | 41 +++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 12 ++++++++++++
- 4 files changed, 65 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index a39b8bab..c97a3204 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5058,4 +5058,15 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- break;
- }
- }
-+
-+void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
-+{
-+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+ struct ieee80211_hw *hw = mvif->phy->mt76->hw;
-+ u8 val = *((u8 *)data);
-+
-+ vif->bss_conf.enable_beacon = val;
-+
-+ mt7996_mcu_add_beacon(hw, vif, val);
-+}
- #endif
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 881328be..bb2cee92 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -829,6 +829,7 @@ void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
- void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
- int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
- int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
-+void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
- #endif
-
- int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 9732ed28..c87cc5c1 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -112,6 +112,11 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
- [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
- };
-
-+static const struct nla_policy
-+beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
-+ [MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -904,6 +909,31 @@ static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
- return err;
- }
-
-+static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
-+ struct wireless_dev *wdev,
-+ const void *data,
-+ int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL];
-+ int err;
-+ u8 val8;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_BEACON_CTRL_MAX, data, data_len,
-+ beacon_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]) {
-+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]);
-+ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+ mt7996_set_beacon_vif, &val8);
-+ }
-+
-+ return 0;
-+}
-+
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
- .info = {
-@@ -1020,6 +1050,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = pp_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_beacon_ctrl,
-+ .policy = beacon_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 98128965..e7d88828 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
-+ MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -226,6 +227,17 @@ enum mtk_vendor_attr_pp_ctrl {
- NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
- };
-
-+enum mtk_vendor_attr_beacon_ctrl {
-+ MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
-+ MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
-+};
-+
- #endif
-
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1041-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1041-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch
deleted file mode 100644
index e104a02..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1041-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch
+++ /dev/null
@@ -1,270 +0,0 @@
-From 5e847c789f6a3b7e234067e33171207fb32f46ea Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 24 Nov 2023 09:49:08 +0800
-Subject: [PATCH 1041/1044] mtk: wifi: mt76: mt7996: add adie efuse merge
- support
-
-Merge adie-dependent parameters in efuse into eeprom after FT.
-Note that Eagle BE14000 is not considered yet.
-Add efuse dump command.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/debugfs.c | 41 ++++++++++++++
- mt7996/eeprom.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.c | 6 +-
- 3 files changed, 190 insertions(+), 2 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 7a03de12..50ac6d4d 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -862,6 +862,46 @@ mt7996_rf_regval_set(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get,
- mt7996_rf_regval_set, "0x%08llx\n");
-
-+static ssize_t
-+mt7996_efuse_get(struct file *file, char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mt7996_dev *dev = file->private_data;
-+ struct mt76_dev *mdev = &dev->mt76;
-+ u8 *buff = mdev->otp.data;
-+ int i;
-+ ssize_t ret;
-+ u32 block_num;
-+
-+ mdev->otp.size = MT7996_EEPROM_SIZE;
-+ if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444)
-+ mdev->otp.size += 3 * MT_EE_CAL_UNIT;
-+
-+ if (!mdev->otp.data) {
-+ mdev->otp.data = devm_kzalloc(mdev->dev, mdev->otp.size, GFP_KERNEL);
-+ if (!mdev->otp.data)
-+ return -ENOMEM;
-+
-+ block_num = DIV_ROUND_UP(mdev->otp.size, MT7996_EEPROM_BLOCK_SIZE);
-+ for (i = 0; i < block_num; i++) {
-+ buff = mdev->otp.data + i * MT7996_EEPROM_BLOCK_SIZE;
-+ ret = mt7996_mcu_get_eeprom(dev, i * MT7996_EEPROM_BLOCK_SIZE, buff);
-+ if (ret)
-+ continue;
-+ }
-+ }
-+
-+ ret = simple_read_from_buffer(user_buf, count, ppos, mdev->otp.data, mdev->otp.size);
-+
-+ return ret;
-+}
-+
-+static const struct file_operations mt7996_efuse_ops = {
-+ .read = mt7996_efuse_get,
-+ .open = simple_open,
-+ .llseek = default_llseek,
-+};
-+
- int mt7996_init_debugfs(struct mt7996_phy *phy)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -888,6 +928,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
- mt7996_twt_stats);
- debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
-+ debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
-
- if (phy->mt76->cap.has_5ghz) {
- debugfs_create_u32("dfs_hw_pattern", 0400, dir,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 39e65010..45cbd03d 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -464,6 +464,147 @@ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
- return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
- }
-
-+static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
-+{
-+#define MT_EE_CAL_FREE_MAX_SIZE 30
-+#define MT_EE_7977BN_OFFSET (0x1200 - 0x500)
-+#define MT_EE_END_OFFSET 0xffff
-+ enum adie_type {
-+ ADIE_7975,
-+ ADIE_7976,
-+ ADIE_7977,
-+ ADIE_7978,
-+ ADIE_7979,
-+ };
-+ static const u16 adie_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
-+ [ADIE_7975] = {0x5cd, 0x5cf, 0x5d1, 0x5d3, 0x6c0, 0x6c1, 0x6c2, 0x6c3,
-+ 0x7a1, 0x7a6, 0x7a8, 0x7aa, -1},
-+ [ADIE_7976] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
-+ 0x70, 0x71, 0x790, 0x791, 0x794, 0x795, 0x7a6, 0x7a8, 0x7aa, -1},
-+ [ADIE_7977] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
-+ 0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, -1},
-+ [ADIE_7978] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
-+ 0x90, 0x91, 0x94, 0x95, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
-+ 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, -1},
-+ [ADIE_7979] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
-+ 0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, -1},
-+ };
-+ static const u16 eep_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
-+ [ADIE_7975] = {0x451, 0x453, 0x455, 0x457, 0x44c, 0x44d, 0x44e, 0x44f,
-+ 0xba1, 0xba6, 0xba8, 0xbaa, -1},
-+ [ADIE_7976] = {0x44c, 0x44d, 0x44e, 0x44f, 0x450,
-+ 0x451, 0x453, 0x455, 0x457, 0x459,
-+ 0x470, 0x471, 0xb90, 0xb91, 0xb94, 0xb95,
-+ 0xba6, 0xba8, 0xbaa, -1},
-+ [ADIE_7977] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250,
-+ 0x1251, 0x1253, 0x1255, 0x1257, 0x1259,
-+ 0x1269, 0x126a, 0x127a, 0x127b, 0x127c, 0x127d, 0x127e, -1},
-+ [ADIE_7978] = {0x44c, 0x44d, 0x44e, 0x44f, 0x450,
-+ 0x451, 0x453, 0x455, 0x457, 0x459,
-+ 0xb90, 0xb91, 0xb94, 0xb95,
-+ 0xba6, 0xba7, 0xba8, 0xba9, 0xbaa,
-+ 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, -1},
-+ [ADIE_7979] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250,
-+ 0x1251, 0x1253, 0x1255, 0x1257, 0x1259,
-+ 0x1269, 0x126a, 0x127a, 0x127b, 0x127c, 0x127d, 0x127e, -1},
-+ };
-+ static const u16 adie_base_7996[] = {
-+ 0x400, 0x1e00, 0x1200
-+ };
-+ static const u16 adie_base_7992[] = {
-+ 0x400, 0x1200, 0x0
-+ };
-+ static const u16 *adie_offs[__MT_MAX_BAND];
-+ static const u16 *eep_offs[__MT_MAX_BAND];
-+ static const u16 *adie_base;
-+ u8 *eeprom = dev->mt76.eeprom.data;
-+ u8 buf[MT7996_EEPROM_BLOCK_SIZE];
-+ int adie_id, band, i, ret;
-+
-+ switch (mt76_chip(&dev->mt76)) {
-+ case 0x7990:
-+ adie_base = adie_base_7996;
-+ /* adie 0 */
-+ if (dev->fem_type == MT7996_FEM_INT)
-+ adie_id = ADIE_7975;
-+ else
-+ adie_id = ADIE_7976;
-+ adie_offs[0] = adie_offs_list[adie_id];
-+ eep_offs[0] = eep_offs_list[adie_id];
-+
-+ /* adie 1 */
-+ if (dev->chip_sku != MT7996_SKU_404) {
-+ adie_offs[1] = adie_offs_list[ADIE_7977];
-+ eep_offs[1] = eep_offs_list[ADIE_7977];
-+ }
-+
-+ /* adie 2 */
-+ adie_offs[2] = adie_offs_list[ADIE_7977];
-+ eep_offs[2] = eep_offs_list[ADIE_7977];
-+ break;
-+ case 0x7992:
-+ adie_base = adie_base_7992;
-+ /* adie 0 */
-+ if (dev->chip_sku == MT7992_SKU_44 &&
-+ dev->fem_type != MT7996_FEM_EXT)
-+ adie_id = ADIE_7975;
-+ else if (dev->chip_sku == MT7992_SKU_24)
-+ adie_id = ADIE_7978;
-+ else
-+ adie_id = ADIE_7976;
-+ adie_offs[0] = adie_offs_list[adie_id];
-+ eep_offs[0] = eep_offs_list[adie_id];
-+
-+ /* adie 1 */
-+ if (dev->chip_sku == MT7992_SKU_44 &&
-+ dev->fem_type != MT7996_FEM_INT)
-+ adie_id = ADIE_7977;
-+ else if (dev->chip_sku != MT7992_SKU_23)
-+ adie_id = ADIE_7979;
-+ else
-+ break;
-+ adie_offs[1] = adie_offs_list[adie_id];
-+ eep_offs[1] = eep_offs_list[adie_id];
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ for (band = 0; band < __MT_MAX_BAND; band++) {
-+ u16 adie_offset, eep_offset;
-+ u32 block_num, prev_block_num = -1;
-+
-+ if (!adie_offs[band])
-+ continue;
-+
-+ for (i = 0; i < MT_EE_CAL_FREE_MAX_SIZE; i++) {
-+ adie_offset = adie_offs[band][i] + adie_base[band];
-+ eep_offset = eep_offs[band][i];
-+ block_num = adie_offset / MT7996_EEPROM_BLOCK_SIZE;
-+
-+ if (adie_offs[band][i] == MT_EE_END_OFFSET)
-+ break;
-+
-+ if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444 &&
-+ band == MT_BAND1)
-+ eep_offset -= MT_EE_7977BN_OFFSET;
-+
-+ if (prev_block_num != block_num) {
-+ ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf);
-+ if (ret) {
-+ prev_block_num = -1;
-+ continue;
-+ }
-+ }
-+
-+ eeprom[eep_offset] = buf[adie_offset % MT7996_EEPROM_BLOCK_SIZE];
-+ prev_block_num = block_num;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
- int mt7996_eeprom_init(struct mt7996_dev *dev)
- {
- int ret;
-@@ -489,6 +630,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- if (ret)
- return ret;
-
-+ ret = mt7996_apply_cal_free_data(dev);
-+ if (ret)
-+ return ret;
-+
- ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
- if (ret < 0)
- return ret;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index c97a3204..26d0a1c1 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3620,7 +3620,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
- };
- struct sk_buff *skb;
- bool valid;
-- int ret;
-+ int ret = 0;
- u8 *buf = read_buf;
-
- ret = mt76_mcu_send_and_get_msg(&dev->mt76,
-@@ -3638,11 +3638,13 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
-
- skb_pull(skb, 48);
- memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
-+ } else {
-+ ret = -EINVAL;
- }
-
- dev_kfree_skb(skb);
-
-- return 0;
-+ return ret;
- }
-
- int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1042-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1042-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
deleted file mode 100644
index f515e7f..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1042-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
+++ /dev/null
@@ -1,167 +0,0 @@
-From cbc1ac7f49105b788c535d71885fecd5c64cffee Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 5 Dec 2023 16:48:33 +0800
-Subject: [PATCH 1042/1044] mtk: wifi: mt7996: add Eagle 2adie TBTC (BE14000)
- support
-
-Add fwdl/default eeprom load support for Eagle 2 adie TBTC
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add Eagle 2adie TBTC efuse merge
-Add Eagle 2adie TBTC group prek size
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 8 ++++++--
- mt7996/eeprom.h | 12 ++++++++++++
- mt7996/init.c | 6 ++++++
- mt7996/mcu.c | 5 +++++
- mt7996/mt7996.h | 8 ++++++++
- mt7996/regs.h | 1 +
- 6 files changed, 38 insertions(+), 2 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 45cbd03d..3d422b0b 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -131,6 +131,8 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
- case 0x7990:
- if (dev->chip_sku == MT7996_SKU_404)
- return MT7996_EEPROM_DEFAULT_404;
-+ else if (dev->chip_sku == MT7996_SKU_233)
-+ return MT7996_EEPROM_DEFAULT_233;
- return MT7996_EEPROM_DEFAULT;
- case 0x7992:
- if (dev->chip_sku == MT7992_SKU_23) {
-@@ -418,6 +420,8 @@ static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
- switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
- dev->prek.rev = mt7996_prek_rev;
-+ if (dev->chip_sku == MT7996_SKU_233)
-+ dev->prek.rev = mt7996_prek_rev_233;
- /* 5g & 6g bw 80 dpd channel list is not used */
- dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
- break;
-@@ -525,7 +529,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
- case 0x7990:
- adie_base = adie_base_7996;
- /* adie 0 */
-- if (dev->fem_type == MT7996_FEM_INT)
-+ if (dev->fem_type == MT7996_FEM_INT && dev->chip_sku != MT7996_SKU_233)
- adie_id = ADIE_7975;
- else
- adie_id = ADIE_7976;
-@@ -533,7 +537,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
- eep_offs[0] = eep_offs_list[adie_id];
-
- /* adie 1 */
-- if (dev->chip_sku != MT7996_SKU_404) {
-+ if (dev->chip_sku == MT7996_SKU_444) {
- adie_offs[1] = adie_offs_list[ADIE_7977];
- eep_offs[1] = eep_offs_list[ADIE_7977];
- }
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 0d05e75e..cd866123 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -70,6 +70,18 @@ static const u32 mt7996_prek_rev[] = {
- [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
- };
-
-+static const u32 mt7996_prek_rev_233[] = {
-+ [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
-+ [GROUP_SIZE_5G] = 44 * MT_EE_CAL_UNIT,
-+ [GROUP_SIZE_6G] = 100 * MT_EE_CAL_UNIT,
-+ [ADCDCOC_SIZE_2G] = 4 * 4,
-+ [ADCDCOC_SIZE_5G] = 4 * 4,
-+ [ADCDCOC_SIZE_6G] = 4 * 5,
-+ [DPD_LEGACY_SIZE] = 4 * MT_EE_CAL_UNIT,
-+ [DPD_MEM_SIZE] = 13 * MT_EE_CAL_UNIT,
-+ [DPD_OTFG0_SIZE] = 2 * MT_EE_CAL_UNIT,
-+};
-+
- /* kite 2/5g config */
- static const u32 mt7992_prek_rev[] = {
- [GROUP_SIZE_2G] = 4 * MT_EE_CAL_UNIT,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 30879ec3..ec90cdc7 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -905,6 +905,12 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
-
- switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
-+ if (FIELD_GET(MT_PAD_GPIO_2ADIE_TBTC, val)) {
-+ dev->chip_sku = MT7996_SKU_233;
-+ dev->fem_type = MT7996_FEM_INT;
-+ return 0;
-+ }
-+
- adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
- if (adie_comb <= 1)
- dev->chip_sku = MT7996_SKU_444;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 26d0a1c1..2cdaf845 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -23,6 +23,11 @@
- _fw = MT7992_##name; \
- break; \
- case 0x7990: \
-+ if ((_dev)->chip_sku == MT7996_SKU_233) \
-+ _fw = MT7996_##name##_233; \
-+ else \
-+ _fw = MT7996_##name; \
-+ break; \
- default: \
- _fw = MT7996_##name; \
- break; \
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index bb2cee92..bde0c086 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -35,6 +35,12 @@
- #define MT7996_FIRMWARE_WM_TM "mediatek/mt7996/mt7996_wm_tm.bin"
- #define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin"
-
-+#define MT7996_FIRMWARE_WA_233 "mediatek/mt7996/mt7996_wa_233.bin"
-+#define MT7996_FIRMWARE_WM_233 "mediatek/mt7996/mt7996_wm_233.bin"
-+#define MT7996_FIRMWARE_DSP_233 MT7996_FIRMWARE_DSP
-+#define MT7996_FIRMWARE_WM_TM_233 "mediatek/mt7996/mt7996_wm_tm_233.bin"
-+#define MT7996_ROM_PATCH_233 "mediatek/mt7996/mt7996_rom_patch_233.bin"
-+
- #define MT7992_FIRMWARE_WA "mediatek/mt7996/mt7992_wa.bin"
- #define MT7992_FIRMWARE_WM "mediatek/mt7996/mt7992_wm.bin"
- #define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin"
-@@ -54,6 +60,7 @@
- #define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
-
- #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
-+#define MT7996_EEPROM_DEFAULT_233 "mediatek/mt7996/mt7996_eeprom_233.bin"
- #define MT7996_EEPROM_DEFAULT_404 "mediatek/mt7996/mt7996_eeprom_dual_404.bin"
- #define MT7996_EEPROM_DEFAULT_TM "mediatek/mt7996/mt7996_eeprom_tm.bin"
- #define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom_2i5i.bin"
-@@ -123,6 +130,7 @@ enum mt7996_fem_type {
- enum mt7996_sku_type {
- MT7996_SKU_404,
- MT7996_SKU_444,
-+ MT7996_SKU_233,
- };
-
- enum mt7992_sku_type {
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index aa04d8d2..8d1462a7 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -666,6 +666,7 @@ enum offs_rev {
-
- #define MT_PAD_GPIO 0x700056f0
- #define MT_PAD_GPIO_ADIE_COMB GENMASK(16, 15)
-+#define MT_PAD_GPIO_2ADIE_TBTC BIT(19)
- #define MT_PAD_GPIO_ADIE_COMB_7992 GENMASK(17, 16)
- #define MT_PAD_GPIO_ADIE_NUM_7992 BIT(15)
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1043-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1043-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch
deleted file mode 100644
index 5aefd99..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1043-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From e38dd3530b0bce868b754b9fb02648974a036ab6 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 22 Dec 2023 17:27:10 +0800
-Subject: [PATCH 1043/1044] mtk: wifi: mt76: mt7996: add background radar hw
- cap check
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/debugfs.c | 5 +++++
- mt7996/init.c | 7 ++++---
- mt7996/mt7996.h | 20 ++++++++++++++++++++
- 3 files changed, 29 insertions(+), 3 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 50ac6d4d..2a5f82da 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -257,6 +257,11 @@ mt7996_rdd_monitor(struct seq_file *s, void *data)
-
- mutex_lock(&dev->mt76.mutex);
-
-+ if (!mt7996_get_background_radar_cap(dev)) {
-+ seq_puts(s, "no background radar capability\n");
-+ goto out;
-+ }
-+
- if (!cfg80211_chandef_valid(chandef)) {
- ret = -EINVAL;
- goto out;
-diff --git a/mt7996/init.c b/mt7996/init.c
-index ec90cdc7..20415e3c 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -393,9 +393,10 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
-
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
-- if (!mdev->dev->of_node ||
-- !of_property_read_bool(mdev->dev->of_node,
-- "mediatek,disable-radar-background"))
-+ if (mt7996_get_background_radar_cap(phy->dev) &&
-+ (!mdev->dev->of_node ||
-+ !of_property_read_bool(mdev->dev->of_node,
-+ "mediatek,disable-radar-background")))
- wiphy_ext_feature_set(wiphy,
- NL80211_EXT_FEATURE_RADAR_BACKGROUND);
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index bde0c086..95db69ca 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -599,6 +599,26 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- return band == MT_BAND0 || band == MT_BAND2;
- }
-
-+static inline bool
-+mt7996_get_background_radar_cap(struct mt7996_dev *dev)
-+{
-+ switch (mt76_chip(&dev->mt76)) {
-+ case 0x7990:
-+ if (dev->chip_sku == MT7996_SKU_233)
-+ return 0;
-+ break;
-+ case 0x7992:
-+ if (dev->chip_sku == MT7992_SKU_23 ||
-+ dev->chip_sku == MT7992_SKU_24)
-+ return 0;
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ return 1;
-+}
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1044-mtk-wifi-mt76-mt7996-support-disable-muru-debug-info.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1044-mtk-wifi-mt76-mt7996-support-disable-muru-debug-info.patch
deleted file mode 100644
index 1d601d3..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1044-mtk-wifi-mt76-mt7996-support-disable-muru-debug-info.patch
+++ /dev/null
@@ -1,98 +0,0 @@
-From f3eec1dbbbf7cb3096017f968e616d3311172c1e Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Fri, 22 Dec 2023 10:53:00 +0800
-Subject: [PATCH 1044/1044] mtk: wifi: mt76: mt7996: support disable muru debug
- info when recording fwlog
-
-When we record fwlog, we will also enable recording muru debug info log by
-default. However, in certain test scenarios, this can result in
-recording too many logs, causing inconvenience during issue analysis.
-Therefore, this commit adds an debug option, fw_debug_muru_disable, in
-debugfs. User can modify this option to enable/disable recording muru
-debug info log.
-
-[Usage]
-Set:
-$ echo val > debugfs/fw_debug_muru_disable
-Get:
-$ cat debugfs/fw_debug_muru_disable
-
-val can be the following values:
-0 = enable recording muru debug info (Default value)
-1 = disable recording muru debug info
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/debugfs.c | 29 +++++++++++++++++++++++++++++
- mt7996/mt7996.h | 1 +
- 2 files changed, 30 insertions(+)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 2a5f82da..dff9e467 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -463,6 +463,9 @@ mt7996_fw_debug_muru_set(void *data)
- } debug;
- int ret;
-
-+ if (dev->fw_debug_muru_disable)
-+ return 0;
-+
- for (debug = DEBUG_BSRP_STATUS; debug <= DEBUG_MEC_UPDATE_AMSDU; debug++) {
- ret = mt7996_mcu_muru_dbg_info(dev, debug,
- dev->fw_debug_bin & BIT(0));
-@@ -907,6 +910,30 @@ static const struct file_operations mt7996_efuse_ops = {
- .llseek = default_llseek,
- };
-
-+static int
-+mt7996_fw_debug_muru_disable_set(void *data, u64 val)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ dev->fw_debug_muru_disable = !!val;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_fw_debug_muru_disable_get(void *data, u64 *val)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ *val = dev->fw_debug_muru_disable;
-+
-+ return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
-+ mt7996_fw_debug_muru_disable_get,
-+ mt7996_fw_debug_muru_disable_set, "%lld\n");
-+
- int mt7996_init_debugfs(struct mt7996_phy *phy)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -943,6 +970,8 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
- mt7996_rdd_monitor);
- }
-+ debugfs_create_file("fw_debug_muru_disable", 0600, dir, dev,
-+ &fops_fw_debug_muru_disable);
-
- if (phy == &dev->phy)
- dev->debugfs_dir = dir;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 95db69ca..2227c08a 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -472,6 +472,7 @@ struct mt7996_dev {
- u8 fw_debug_wa;
- u8 fw_debug_bin;
- u16 fw_debug_seq;
-+ bool fw_debug_muru_disable;
-
- struct dentry *debugfs_dir;
- struct rchan *relay_fwlog;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2000-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2000-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
deleted file mode 100644
index f17bdc5..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2000-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
+++ /dev/null
@@ -1,626 +0,0 @@
-From 531fa0a7bd4865ee9e631c6cd1d5655c8e8995a6 Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 6 Feb 2023 19:49:22 +0800
-Subject: [PATCH 2000/2032] mtk: wifi: mt76: revert page_poll for kernel 5.4
-
-This reverts commit e8c10835cf062c577ddf426913788c39d30b4bd7.
-
----
- dma.c | 75 ++++++++++++++++++++++++++-------------------------
- mac80211.c | 57 ---------------------------------------
- mt76.h | 22 +--------------
- mt7915/main.c | 26 +++++++-----------
- usb.c | 43 ++++++++++++++---------------
- wed.c | 50 ++++++++++++++++++++++------------
- 6 files changed, 104 insertions(+), 169 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 66c000ef..33a84f5f 100644
---- a/dma.c
-+++ b/dma.c
-@@ -178,7 +178,7 @@ mt76_free_pending_rxwi(struct mt76_dev *dev)
- local_bh_disable();
- while ((t = __mt76_get_rxwi(dev)) != NULL) {
- if (t->ptr)
-- mt76_put_page_pool_buf(t->ptr, false);
-+ skb_free_frag(t->ptr);
- kfree(t);
- }
- local_bh_enable();
-@@ -450,9 +450,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- if (!t)
- return NULL;
-
-- dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr,
-- SKB_WITH_OVERHEAD(q->buf_size),
-- page_pool_get_dma_dir(q->page_pool));
-+ dma_unmap_single(dev->dma_dev, t->dma_addr,
-+ SKB_WITH_OVERHEAD(q->buf_size),
-+ DMA_FROM_DEVICE);
-
- buf = t->ptr;
- t->dma_addr = 0;
-@@ -462,9 +462,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- if (drop)
- *drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
- } else {
-- dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
-- SKB_WITH_OVERHEAD(q->buf_size),
-- page_pool_get_dma_dir(q->page_pool));
-+ dma_unmap_single(dev->dma_dev, e->dma_addr[0],
-+ SKB_WITH_OVERHEAD(q->buf_size),
-+ DMA_FROM_DEVICE);
- }
-
- done:
-@@ -638,7 +638,8 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- bool allow_direct)
- {
- int len = SKB_WITH_OVERHEAD(q->buf_size);
-- int frames = 0;
-+ int frames = 0, offset = q->buf_offset;
-+ dma_addr_t addr;
-
- if (!q->ndesc)
- return 0;
-@@ -647,28 +648,29 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
-
- while (q->queued < q->ndesc - 1) {
- struct mt76_queue_buf qbuf = {};
-- enum dma_data_direction dir;
-- dma_addr_t addr;
-- int offset;
- void *buf = NULL;
-
- if (mt76_queue_is_wed_rro_ind(q))
- goto done;
-
-- buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
-+ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
- if (!buf)
- break;
-
-- addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
-- dir = page_pool_get_dma_dir(q->page_pool);
-- dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
-+ addr = dma_map_single(dev->dma_dev, buf, len, DMA_FROM_DEVICE);
-+ if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
-+ skb_free_frag(buf);
-+ break;
-+ }
-
-- qbuf.addr = addr + q->buf_offset;
-+ qbuf.addr = addr + offset;
- done:
-- qbuf.len = len - q->buf_offset;
-+ qbuf.len = len - offset;
- qbuf.skip_unmap = false;
- if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
-- mt76_put_page_pool_buf(buf, allow_direct);
-+ dma_unmap_single(dev->dma_dev, addr, len,
-+ DMA_FROM_DEVICE);
-+ skb_free_frag(buf);
- break;
- }
- frames++;
-@@ -722,10 +724,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
- if (!q->entry)
- return -ENOMEM;
-
-- ret = mt76_create_page_pool(dev, q);
-- if (ret)
-- return ret;
--
- ret = mt76_wed_dma_setup(dev, q, false);
- if (ret)
- return ret;
-@@ -744,6 +742,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
- static void
- mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
- {
-+ struct page *page;
- void *buf;
- bool more;
-
-@@ -759,7 +758,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
- break;
-
- if (!mt76_queue_is_wed_rro(q))
-- mt76_put_page_pool_buf(buf, false);
-+ skb_free_frag(buf);
- } while (1);
-
- spin_lock_bh(&q->lock);
-@@ -769,6 +768,16 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
- }
-
- spin_unlock_bh(&q->lock);
-+
-+ if (mt76_queue_is_wed_rx(q))
-+ return;
-+
-+ if (!q->rx_page.va)
-+ return;
-+
-+ page = virt_to_page(q->rx_page.va);
-+ __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
-+ memset(&q->rx_page, 0, sizeof(q->rx_page));
- }
-
- static void
-@@ -791,15 +800,10 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
- /* reset WED rx queues */
- mt76_wed_dma_setup(dev, q, true);
-
-- if (mt76_queue_is_wed_tx_free(q))
-- return;
--
-- if (mtk_wed_device_active(&dev->mmio.wed) &&
-- mt76_queue_is_wed_rro(q))
-- return;
--
-- mt76_dma_sync_idx(dev, q);
-- mt76_dma_rx_fill(dev, q, false);
-+ if (!mt76_queue_is_wed_tx_free(q)) {
-+ mt76_dma_sync_idx(dev, q);
-+ mt76_dma_rx_fill(dev, q, false);
-+ }
- }
-
- static void
-@@ -816,7 +820,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
-
- skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size);
- } else {
-- mt76_put_page_pool_buf(data, allow_direct);
-+ skb_free_frag(data);
- }
-
- if (more)
-@@ -891,7 +895,6 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- goto free_frag;
-
- skb_reserve(skb, q->buf_offset);
-- skb_mark_for_recycle(skb);
-
- *(u32 *)skb->cb = info;
-
-@@ -907,7 +910,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- continue;
-
- free_frag:
-- mt76_put_page_pool_buf(data, allow_direct);
-+ skb_free_frag(data);
- }
-
- mt76_dma_rx_fill(dev, q, true);
-@@ -1010,8 +1013,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
-
- netif_napi_del(&dev->napi[i]);
- mt76_dma_rx_cleanup(dev, q);
--
-- page_pool_destroy(q->page_pool);
- }
-
- if (mtk_wed_device_active(&dev->mmio.wed))
-diff --git a/mac80211.c b/mac80211.c
-index f7cd47f9..380a74e4 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -4,7 +4,6 @@
- */
- #include <linux/sched.h>
- #include <linux/of.h>
--#include <net/page_pool.h>
- #include "mt76.h"
-
- static const struct ieee80211_channel mt76_channels_2ghz[] = {
-@@ -566,47 +565,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
- }
- EXPORT_SYMBOL_GPL(mt76_unregister_phy);
-
--int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q)
--{
-- struct page_pool_params pp_params = {
-- .order = 0,
-- .flags = PP_FLAG_PAGE_FRAG,
-- .nid = NUMA_NO_NODE,
-- .dev = dev->dma_dev,
-- };
-- int idx = q - dev->q_rx;
--
-- switch (idx) {
-- case MT_RXQ_MAIN:
-- case MT_RXQ_BAND1:
-- case MT_RXQ_BAND2:
-- pp_params.pool_size = 256;
-- break;
-- default:
-- pp_params.pool_size = 16;
-- break;
-- }
--
-- if (mt76_is_mmio(dev)) {
-- /* rely on page_pool for DMA mapping */
-- pp_params.flags |= PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
-- pp_params.dma_dir = DMA_FROM_DEVICE;
-- pp_params.max_len = PAGE_SIZE;
-- pp_params.offset = 0;
-- }
--
-- q->page_pool = page_pool_create(&pp_params);
-- if (IS_ERR(q->page_pool)) {
-- int err = PTR_ERR(q->page_pool);
--
-- q->page_pool = NULL;
-- return err;
-- }
--
-- return 0;
--}
--EXPORT_SYMBOL_GPL(mt76_create_page_pool);
--
- struct mt76_dev *
- mt76_alloc_device(struct device *pdev, unsigned int size,
- const struct ieee80211_ops *ops,
-@@ -1819,21 +1777,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
- }
- EXPORT_SYMBOL_GPL(mt76_ethtool_worker);
-
--void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index)
--{
--#ifdef CONFIG_PAGE_POOL_STATS
-- struct page_pool_stats stats = {};
-- int i;
--
-- mt76_for_each_q_rx(dev, i)
-- page_pool_get_stats(dev->q_rx[i].page_pool, &stats);
--
-- page_pool_ethtool_stats_get(data, &stats);
-- *index += page_pool_ethtool_stats_get_count();
--#endif
--}
--EXPORT_SYMBOL_GPL(mt76_ethtool_page_pool_stats);
--
- enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
- {
- struct ieee80211_hw *hw = phy->hw;
-diff --git a/mt76.h b/mt76.h
-index 543d9de5..540814f1 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -246,7 +246,7 @@ struct mt76_queue {
-
- dma_addr_t desc_dma;
- struct sk_buff *rx_head;
-- struct page_pool *page_pool;
-+ struct page_frag_cache rx_page;
- };
-
- struct mt76_mcu_ops {
-@@ -1601,7 +1601,6 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
- return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout);
- }
-
--void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index);
- void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
- struct mt76_sta_stats *stats, bool eht);
- int mt76_skb_adjust_pad(struct sk_buff *skb, int pad);
-@@ -1747,25 +1746,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
- struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
- int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
- struct mt76_txwi_cache *r, dma_addr_t phys);
--int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q);
--static inline void mt76_put_page_pool_buf(void *buf, bool allow_direct)
--{
-- struct page *page = virt_to_head_page(buf);
--
-- page_pool_put_full_page(page->pp, page, allow_direct);
--}
--
--static inline void *
--mt76_get_page_pool_buf(struct mt76_queue *q, u32 *offset, u32 size)
--{
-- struct page *page;
--
-- page = page_pool_dev_alloc_frag(q->page_pool, offset, size);
-- if (!page)
-- return NULL;
--
-- return page_address(page) + *offset;
--}
-
- static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
- {
-diff --git a/mt7915/main.c b/mt7915/main.c
-index 49d5b459..103a0709 100644
---- a/mt7915/main.c
-+++ b/mt7915/main.c
-@@ -1402,22 +1402,19 @@ void mt7915_get_et_strings(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- u32 sset, u8 *data)
- {
-- if (sset != ETH_SS_STATS)
-- return;
--
-- memcpy(data, mt7915_gstrings_stats, sizeof(mt7915_gstrings_stats));
-- data += sizeof(mt7915_gstrings_stats);
-- page_pool_ethtool_stats_get_strings(data);
-+ if (sset == ETH_SS_STATS)
-+ memcpy(data, mt7915_gstrings_stats,
-+ sizeof(mt7915_gstrings_stats));
- }
-
- static
- int mt7915_get_et_sset_count(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, int sset)
- {
-- if (sset != ETH_SS_STATS)
-- return 0;
-+ if (sset == ETH_SS_STATS)
-+ return MT7915_SSTATS_LEN;
-
-- return MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
-+ return 0;
- }
-
- static void mt7915_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
-@@ -1445,7 +1442,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
- .idx = mvif->mt76.idx,
- };
- /* See mt7915_ampdu_stat_read_phy, etc */
-- int i, ei = 0, stats_size;
-+ int i, ei = 0;
-
- mutex_lock(&dev->mt76.mutex);
-
-@@ -1557,12 +1554,9 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
- return;
-
- ei += wi.worker_stat_count;
--
-- mt76_ethtool_page_pool_stats(&dev->mt76, &data[ei], &ei);
--
-- stats_size = MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
-- if (ei != stats_size)
-- dev_err(dev->mt76.dev, "ei: %d size: %d", ei, stats_size);
-+ if (ei != MT7915_SSTATS_LEN)
-+ dev_err(dev->mt76.dev, "ei: %d MT7915_SSTATS_LEN: %d",
-+ ei, (int)MT7915_SSTATS_LEN);
- }
-
- static void
-diff --git a/usb.c b/usb.c
-index dc690d1c..058f2d12 100644
---- a/usb.c
-+++ b/usb.c
-@@ -319,27 +319,29 @@ mt76u_set_endpoints(struct usb_interface *intf,
-
- static int
- mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
-- int nsgs)
-+ int nsgs, gfp_t gfp)
- {
- int i;
-
- for (i = 0; i < nsgs; i++) {
-+ struct page *page;
- void *data;
- int offset;
-
-- data = mt76_get_page_pool_buf(q, &offset, q->buf_size);
-+ data = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
- if (!data)
- break;
-
-- sg_set_page(&urb->sg[i], virt_to_head_page(data), q->buf_size,
-- offset);
-+ page = virt_to_head_page(data);
-+ offset = data - page_address(page);
-+ sg_set_page(&urb->sg[i], page, q->buf_size, offset);
- }
-
- if (i < nsgs) {
- int j;
-
- for (j = nsgs; j < urb->num_sgs; j++)
-- mt76_put_page_pool_buf(sg_virt(&urb->sg[j]), false);
-+ skb_free_frag(sg_virt(&urb->sg[j]));
- urb->num_sgs = i;
- }
-
-@@ -352,16 +354,15 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
-
- static int
- mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
-- struct urb *urb, int nsgs)
-+ struct urb *urb, int nsgs, gfp_t gfp)
- {
- enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
-- int offset;
-
- if (qid == MT_RXQ_MAIN && dev->usb.sg_en)
-- return mt76u_fill_rx_sg(dev, q, urb, nsgs);
-+ return mt76u_fill_rx_sg(dev, q, urb, nsgs, gfp);
-
- urb->transfer_buffer_length = q->buf_size;
-- urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size);
-+ urb->transfer_buffer = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
-
- return urb->transfer_buffer ? 0 : -ENOMEM;
- }
-@@ -399,7 +400,7 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
- if (err)
- return err;
-
-- return mt76u_refill_rx(dev, q, e->urb, sg_size);
-+ return mt76u_refill_rx(dev, q, e->urb, sg_size, GFP_KERNEL);
- }
-
- static void mt76u_urb_free(struct urb *urb)
-@@ -407,10 +408,10 @@ static void mt76u_urb_free(struct urb *urb)
- int i;
-
- for (i = 0; i < urb->num_sgs; i++)
-- mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
-+ skb_free_frag(sg_virt(&urb->sg[i]));
-
- if (urb->transfer_buffer)
-- mt76_put_page_pool_buf(urb->transfer_buffer, false);
-+ skb_free_frag(urb->transfer_buffer);
-
- usb_free_urb(urb);
- }
-@@ -546,8 +547,6 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
- len -= data_len;
- nsgs++;
- }
--
-- skb_mark_for_recycle(skb);
- dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
-
- return nsgs;
-@@ -613,7 +612,7 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
-
- count = mt76u_process_rx_entry(dev, urb, q->buf_size);
- if (count > 0) {
-- err = mt76u_refill_rx(dev, q, urb, count);
-+ err = mt76u_refill_rx(dev, q, urb, count, GFP_ATOMIC);
- if (err < 0)
- break;
- }
-@@ -664,10 +663,6 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
- struct mt76_queue *q = &dev->q_rx[qid];
- int i, err;
-
-- err = mt76_create_page_pool(dev, q);
-- if (err)
-- return err;
--
- spin_lock_init(&q->lock);
- q->entry = devm_kcalloc(dev->dev,
- MT_NUM_RX_ENTRIES, sizeof(*q->entry),
-@@ -696,6 +691,7 @@ EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue);
- static void
- mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
- {
-+ struct page *page;
- int i;
-
- for (i = 0; i < q->ndesc; i++) {
-@@ -705,8 +701,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
- mt76u_urb_free(q->entry[i].urb);
- q->entry[i].urb = NULL;
- }
-- page_pool_destroy(q->page_pool);
-- q->page_pool = NULL;
-+
-+ if (!q->rx_page.va)
-+ return;
-+
-+ page = virt_to_page(q->rx_page.va);
-+ __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
-+ memset(&q->rx_page, 0, sizeof(q->rx_page));
- }
-
- static void mt76u_free_rx(struct mt76_dev *dev)
-diff --git a/wed.c b/wed.c
-index f89e4537..8eca4d81 100644
---- a/wed.c
-+++ b/wed.c
-@@ -9,8 +9,12 @@
- void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
- {
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
-+ u32 length;
- int i;
-
-+ length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
-+ sizeof(struct skb_shared_info));
-+
- for (i = 0; i < dev->rx_token_size; i++) {
- struct mt76_txwi_cache *t;
-
-@@ -18,7 +22,9 @@ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
- if (!t || !t->ptr)
- continue;
-
-- mt76_put_page_pool_buf(t->ptr, false);
-+ dma_unmap_single(dev->dma_dev, t->dma_addr,
-+ wed->wlan.rx_size, DMA_FROM_DEVICE);
-+ __free_pages(virt_to_page(t->ptr), get_order(length));
- t->ptr = NULL;
-
- mt76_put_rxwi(dev, t);
-@@ -33,33 +39,45 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- {
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
- struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc;
-- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
-- int i, len = SKB_WITH_OVERHEAD(q->buf_size);
-- struct mt76_txwi_cache *t = NULL;
-+ u32 length;
-+ int i;
-+
-+ length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
-+ sizeof(struct skb_shared_info));
-
- for (i = 0; i < size; i++) {
-- enum dma_data_direction dir;
-+ struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
- dma_addr_t addr;
-- u32 offset;
-+ struct page *page;
- int token;
-- void *buf;
-+ void *ptr;
-
-- t = mt76_get_rxwi(dev);
- if (!t)
- goto unmap;
-
-- buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
-- if (!buf)
-+ page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
-+ if (!page) {
-+ mt76_put_rxwi(dev, t);
- goto unmap;
-+ }
-
-- addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
-- dir = page_pool_get_dma_dir(q->page_pool);
-- dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
-+ addr = dma_map_single(dev->dma_dev, ptr,
-+ wed->wlan.rx_size,
-+ DMA_TO_DEVICE);
-+
-+ if (unlikely(dma_mapping_error(dev->dev, addr))) {
-+ skb_free_frag(ptr);
-+ mt76_put_rxwi(dev, t);
-+ goto unmap;
-+ }
-
- desc->buf0 = cpu_to_le32(addr);
-- token = mt76_rx_token_consume(dev, buf, t, addr);
-+ token = mt76_rx_token_consume(dev, ptr, t, addr);
- if (token < 0) {
-- mt76_put_page_pool_buf(buf, false);
-+ dma_unmap_single(dev->dma_dev, addr,
-+ wed->wlan.rx_size, DMA_TO_DEVICE);
-+ __free_pages(page, get_order(length));
-+ mt76_put_rxwi(dev, t);
- goto unmap;
- }
-
-@@ -74,8 +92,6 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- return 0;
-
- unmap:
-- if (t)
-- mt76_put_rxwi(dev, t);
- mt76_wed_release_rx_buf(wed);
-
- return -ENOMEM;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2001-mtk-wifi-mt76-rework-wed-rx-flow.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2001-mtk-wifi-mt76-rework-wed-rx-flow.patch
deleted file mode 100644
index 1259a33..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2001-mtk-wifi-mt76-rework-wed-rx-flow.patch
+++ /dev/null
@@ -1,541 +0,0 @@
-From c6914b48e41c17245715400f8733ed9a38a4d5da Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 6 Feb 2023 13:37:23 +0800
-Subject: [PATCH 2001/2032] mtk: wifi: mt76: rework wed rx flow
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- dma.c | 125 +++++++++++++++++++++++++++++++-----------------
- mac80211.c | 2 +-
- mt76.h | 25 ++++++----
- mt7915/mmio.c | 3 +-
- mt7915/mt7915.h | 1 +
- tx.c | 16 +++----
- wed.c | 57 ++++++++++++++--------
- 7 files changed, 144 insertions(+), 85 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 33a84f5f..c54187bd 100644
---- a/dma.c
-+++ b/dma.c
-@@ -64,17 +64,17 @@ mt76_alloc_txwi(struct mt76_dev *dev)
- return t;
- }
-
--static struct mt76_txwi_cache *
-+static struct mt76_rxwi_cache *
- mt76_alloc_rxwi(struct mt76_dev *dev)
- {
-- struct mt76_txwi_cache *t;
-+ struct mt76_rxwi_cache *r;
-
-- t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC);
-- if (!t)
-+ r = kzalloc(L1_CACHE_ALIGN(sizeof(*r)), GFP_ATOMIC);
-+ if (!r)
- return NULL;
-
-- t->ptr = NULL;
-- return t;
-+ r->ptr = NULL;
-+ return r;
- }
-
- static struct mt76_txwi_cache *
-@@ -93,20 +93,20 @@ __mt76_get_txwi(struct mt76_dev *dev)
- return t;
- }
-
--static struct mt76_txwi_cache *
-+static struct mt76_rxwi_cache *
- __mt76_get_rxwi(struct mt76_dev *dev)
- {
-- struct mt76_txwi_cache *t = NULL;
-+ struct mt76_rxwi_cache *r = NULL;
-
-- spin_lock_bh(&dev->wed_lock);
-+ spin_lock_bh(&dev->lock);
- if (!list_empty(&dev->rxwi_cache)) {
-- t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache,
-+ r = list_first_entry(&dev->rxwi_cache, struct mt76_rxwi_cache,
- list);
-- list_del(&t->list);
-+ list_del(&r->list);
- }
-- spin_unlock_bh(&dev->wed_lock);
-+ spin_unlock_bh(&dev->lock);
-
-- return t;
-+ return r;
- }
-
- static struct mt76_txwi_cache *
-@@ -120,13 +120,13 @@ mt76_get_txwi(struct mt76_dev *dev)
- return mt76_alloc_txwi(dev);
- }
-
--struct mt76_txwi_cache *
-+struct mt76_rxwi_cache *
- mt76_get_rxwi(struct mt76_dev *dev)
- {
-- struct mt76_txwi_cache *t = __mt76_get_rxwi(dev);
-+ struct mt76_rxwi_cache *r = __mt76_get_rxwi(dev);
-
-- if (t)
-- return t;
-+ if (r)
-+ return r;
-
- return mt76_alloc_rxwi(dev);
- }
-@@ -145,14 +145,14 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
- EXPORT_SYMBOL_GPL(mt76_put_txwi);
-
- void
--mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
-+mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r)
- {
-- if (!t)
-+ if (!r)
- return;
-
-- spin_lock_bh(&dev->wed_lock);
-- list_add(&t->list, &dev->rxwi_cache);
-- spin_unlock_bh(&dev->wed_lock);
-+ spin_lock_bh(&dev->lock);
-+ list_add(&r->list, &dev->rxwi_cache);
-+ spin_unlock_bh(&dev->lock);
- }
- EXPORT_SYMBOL_GPL(mt76_put_rxwi);
-
-@@ -173,13 +173,13 @@ mt76_free_pending_txwi(struct mt76_dev *dev)
- void
- mt76_free_pending_rxwi(struct mt76_dev *dev)
- {
-- struct mt76_txwi_cache *t;
-+ struct mt76_rxwi_cache *r;
-
- local_bh_disable();
-- while ((t = __mt76_get_rxwi(dev)) != NULL) {
-- if (t->ptr)
-- skb_free_frag(t->ptr);
-- kfree(t);
-+ while ((r = __mt76_get_rxwi(dev)) != NULL) {
-+ if (r->ptr)
-+ skb_free_frag(r->ptr);
-+ kfree(r);
- }
- local_bh_enable();
- }
-@@ -225,10 +225,10 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
-
- static int
- mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
-- struct mt76_queue_buf *buf, void *data)
-+ struct mt76_queue_buf *buf, void *data,
-+ struct mt76_rxwi_cache *rxwi)
- {
- struct mt76_queue_entry *entry = &q->entry[q->head];
-- struct mt76_txwi_cache *txwi = NULL;
- struct mt76_desc *desc;
- int idx = q->head;
- u32 buf1 = 0, ctrl;
-@@ -249,13 +249,15 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
- #endif
-
- if (mt76_queue_is_wed_rx(q)) {
-- txwi = mt76_get_rxwi(dev);
-- if (!txwi)
-- return -ENOMEM;
-+ if (!rxwi) {
-+ rxwi = mt76_get_rxwi(dev);
-+ if (!rxwi)
-+ return -ENOMEM;
-+ }
-
-- rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr);
-+ rx_token = mt76_rx_token_consume(dev, data, rxwi, buf->addr);
- if (rx_token < 0) {
-- mt76_put_rxwi(dev, txwi);
-+ mt76_put_rxwi(dev, rxwi);
- return -ENOMEM;
- }
-
-@@ -271,7 +273,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
- done:
- entry->dma_addr[0] = buf->addr;
- entry->dma_len[0] = buf->len;
-- entry->txwi = txwi;
-+ entry->rxwi = rxwi;
- entry->buf = data;
- entry->wcid = 0xffff;
- entry->skip_buf1 = true;
-@@ -420,7 +422,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
-
- static void *
- mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
-- int *len, u32 *info, bool *more, bool *drop)
-+ int *len, u32 *info, bool *more, bool *drop, bool flush)
- {
- struct mt76_queue_entry *e = &q->entry[idx];
- struct mt76_desc *desc = &q->desc[idx];
-@@ -445,20 +447,53 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
-
- if (mt76_queue_is_wed_rx(q)) {
- u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
-- struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token);
-+ struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
-
-- if (!t)
-+ if (!r)
- return NULL;
-
-- dma_unmap_single(dev->dma_dev, t->dma_addr,
-+ dma_unmap_single(dev->dma_dev, r->dma_addr,
- SKB_WITH_OVERHEAD(q->buf_size),
- DMA_FROM_DEVICE);
-
-- buf = t->ptr;
-- t->dma_addr = 0;
-- t->ptr = NULL;
-+ if (flush) {
-+ buf = r->ptr;
-+ r->dma_addr = 0;
-+ r->ptr = NULL;
-+
-+ mt76_put_rxwi(dev, r);
-+ } else {
-+ struct mt76_queue_buf qbuf;
-+
-+ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
-+ if (!buf)
-+ return NULL;
-+
-+ memcpy(buf, r->ptr, SKB_WITH_OVERHEAD(q->buf_size));
-+
-+ r->dma_addr = dma_map_single(dev->dma_dev, r->ptr,
-+ SKB_WITH_OVERHEAD(q->buf_size),
-+ DMA_FROM_DEVICE);
-+ if (unlikely(dma_mapping_error(dev->dma_dev, r->dma_addr))) {
-+ skb_free_frag(r->ptr);
-+ mt76_put_rxwi(dev, r);
-+ return NULL;
-+ }
-+
-+ qbuf.addr = r->dma_addr;
-+ qbuf.len = SKB_WITH_OVERHEAD(q->buf_size);
-+ qbuf.skip_unmap = false;
-+
-+ if (mt76_dma_add_rx_buf(dev, q, &qbuf, r->ptr, r) < 0) {
-+ dma_unmap_single(dev->dma_dev, r->dma_addr,
-+ SKB_WITH_OVERHEAD(q->buf_size),
-+ DMA_FROM_DEVICE);
-+ skb_free_frag(r->ptr);
-+ mt76_put_rxwi(dev, r);
-+ return NULL;
-+ }
-+ }
-
-- mt76_put_rxwi(dev, t);
- if (drop)
- *drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
- } else {
-@@ -495,7 +530,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
- q->tail = (q->tail + 1) % q->ndesc;
- q->queued--;
-
-- return mt76_dma_get_buf(dev, q, idx, len, info, more, drop);
-+ return mt76_dma_get_buf(dev, q, idx, len, info, more, drop, flush);
- }
-
- static int
-@@ -667,7 +702,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- done:
- qbuf.len = len - offset;
- qbuf.skip_unmap = false;
-- if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
-+ if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf, NULL) < 0) {
- dma_unmap_single(dev->dma_dev, addr, len,
- DMA_FROM_DEVICE);
- skb_free_frag(buf);
-diff --git a/mac80211.c b/mac80211.c
-index 380a74e4..91e771d3 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -595,7 +595,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
- spin_lock_init(&dev->lock);
- spin_lock_init(&dev->cc_lock);
- spin_lock_init(&dev->status_lock);
-- spin_lock_init(&dev->wed_lock);
- mutex_init(&dev->mutex);
- init_waitqueue_head(&dev->tx_wait);
-
-@@ -628,6 +627,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
- INIT_LIST_HEAD(&dev->txwi_cache);
- INIT_LIST_HEAD(&dev->rxwi_cache);
- dev->token_size = dev->drv->token_size;
-+ dev->rx_token_size = dev->drv->rx_token_size;
-
- for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
- skb_queue_head_init(&dev->rx_skb[i]);
-diff --git a/mt76.h b/mt76.h
-index 540814f1..878553a2 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -200,6 +200,7 @@ struct mt76_queue_entry {
- };
- union {
- struct mt76_txwi_cache *txwi;
-+ struct mt76_rxwi_cache *rxwi;
- struct urb *urb;
- int buf_sz;
- };
-@@ -411,12 +412,16 @@ struct mt76_txwi_cache {
- struct list_head list;
- dma_addr_t dma_addr;
-
-- union {
-- struct sk_buff *skb;
-- void *ptr;
-- };
--
- unsigned long jiffies;
-+
-+ struct sk_buff *skb;
-+};
-+
-+struct mt76_rxwi_cache {
-+ struct list_head list;
-+ dma_addr_t dma_addr;
-+
-+ void *ptr;
- };
-
- struct mt76_rx_tid {
-@@ -504,6 +509,7 @@ struct mt76_driver_ops {
- u16 txwi_size;
- u16 token_size;
- u8 mcs_rates;
-+ u16 rx_token_size;
-
- void (*update_survey)(struct mt76_phy *phy);
-
-@@ -881,7 +887,6 @@ struct mt76_dev {
-
- struct ieee80211_hw *hw;
-
-- spinlock_t wed_lock;
- spinlock_t lock;
- spinlock_t cc_lock;
-
-@@ -1563,8 +1568,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
- }
-
- void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
--void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
--struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
-+void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r);
-+struct mt76_rxwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
- void mt76_free_pending_rxwi(struct mt76_dev *dev);
- void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
- struct napi_struct *napi);
-@@ -1743,9 +1748,9 @@ struct mt76_txwi_cache *
- mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
- int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
- void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
--struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
-+struct mt76_rxwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
- int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
-- struct mt76_txwi_cache *r, dma_addr_t phys);
-+ struct mt76_rxwi_cache *r, dma_addr_t phys);
-
- static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
- {
-diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 6004d64f..5938bd9f 100644
---- a/mt7915/mmio.c
-+++ b/mt7915/mmio.c
-@@ -714,7 +714,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
- wed->wlan.reset = mt7915_mmio_wed_reset;
- wed->wlan.reset_complete = mt76_wed_reset_complete;
-
-- dev->mt76.rx_token_size = wed->wlan.rx_npkt;
-+ dev->mt76.rx_token_size += wed->wlan.rx_npkt;
-
- if (mtk_wed_device_attach(wed))
- return 0;
-@@ -921,6 +921,7 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev,
- SURVEY_INFO_TIME_RX |
- SURVEY_INFO_TIME_BSS_RX,
- .token_size = MT7915_TOKEN_SIZE,
-+ .rx_token_size = MT7915_RX_TOKEN_SIZE;
- .tx_prepare_skb = mt7915_tx_prepare_skb,
- .tx_complete_skb = mt76_connac_tx_complete_skb,
- .rx_skb = mt7915_queue_rx_skb,
-diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index a30d08eb..f1e2c93a 100644
---- a/mt7915/mt7915.h
-+++ b/mt7915/mt7915.h
-@@ -62,6 +62,7 @@
- #define MT7915_EEPROM_BLOCK_SIZE 16
- #define MT7915_HW_TOKEN_SIZE 4096
- #define MT7915_TOKEN_SIZE 8192
-+#define MT7915_RX_TOKEN_SIZE 4096
-
- #define MT7915_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */
- #define MT7915_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
-diff --git a/tx.c b/tx.c
-index ab42f69b..46dae6e0 100644
---- a/tx.c
-+++ b/tx.c
-@@ -851,16 +851,16 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
- EXPORT_SYMBOL_GPL(mt76_token_consume);
-
- int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
-- struct mt76_txwi_cache *t, dma_addr_t phys)
-+ struct mt76_rxwi_cache *r, dma_addr_t phys)
- {
- int token;
-
- spin_lock_bh(&dev->rx_token_lock);
-- token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size,
-+ token = idr_alloc(&dev->rx_token, r, 0, dev->rx_token_size,
- GFP_ATOMIC);
- if (token >= 0) {
-- t->ptr = ptr;
-- t->dma_addr = phys;
-+ r->ptr = ptr;
-+ r->dma_addr = phys;
- }
- spin_unlock_bh(&dev->rx_token_lock);
-
-@@ -897,15 +897,15 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
- }
- EXPORT_SYMBOL_GPL(mt76_token_release);
-
--struct mt76_txwi_cache *
-+struct mt76_rxwi_cache *
- mt76_rx_token_release(struct mt76_dev *dev, int token)
- {
-- struct mt76_txwi_cache *t;
-+ struct mt76_rxwi_cache *r;
-
- spin_lock_bh(&dev->rx_token_lock);
-- t = idr_remove(&dev->rx_token, token);
-+ r = idr_remove(&dev->rx_token, token);
- spin_unlock_bh(&dev->rx_token_lock);
-
-- return t;
-+ return r;
- }
- EXPORT_SYMBOL_GPL(mt76_rx_token_release);
-diff --git a/wed.c b/wed.c
-index 8eca4d81..0a0b5c05 100644
---- a/wed.c
-+++ b/wed.c
-@@ -9,28 +9,45 @@
- void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
- {
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
-- u32 length;
-+ struct page *page;
- int i;
-
-- length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
-- sizeof(struct skb_shared_info));
--
- for (i = 0; i < dev->rx_token_size; i++) {
-- struct mt76_txwi_cache *t;
-+ struct mt76_rxwi_cache *r;
-
-- t = mt76_rx_token_release(dev, i);
-- if (!t || !t->ptr)
-+ r = mt76_rx_token_release(dev, i);
-+ if (!r || !r->ptr)
- continue;
-
-- dma_unmap_single(dev->dma_dev, t->dma_addr,
-+ dma_unmap_single(dev->dma_dev, r->dma_addr,
- wed->wlan.rx_size, DMA_FROM_DEVICE);
-- __free_pages(virt_to_page(t->ptr), get_order(length));
-- t->ptr = NULL;
-+ skb_free_frag(r->ptr);
-+ r->ptr = NULL;
-
-- mt76_put_rxwi(dev, t);
-+ mt76_put_rxwi(dev, r);
- }
-
- mt76_free_pending_rxwi(dev);
-+
-+ mt76_for_each_q_rx(dev, i) {
-+ struct mt76_queue *q = &dev->q_rx[i];
-+
-+ if (mt76_queue_is_wed_rx(q)) {
-+ if (!q->rx_page.va)
-+ continue;
-+
-+ page = virt_to_page(q->rx_page.va);
-+ __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
-+ memset(&q->rx_page, 0, sizeof(q->rx_page));
-+ }
-+ }
-+
-+ if (!wed->rx_buf_ring.rx_page.va)
-+ return;
-+
-+ page = virt_to_page(wed->rx_buf_ring.rx_page.va);
-+ __page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias);
-+ memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page));
- }
- EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf);
-
-@@ -46,18 +63,18 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- sizeof(struct skb_shared_info));
-
- for (i = 0; i < size; i++) {
-- struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
-+ struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
- dma_addr_t addr;
- struct page *page;
- int token;
- void *ptr;
-
-- if (!t)
-+ if (!r)
- goto unmap;
-
-- page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
-- if (!page) {
-- mt76_put_rxwi(dev, t);
-+ ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
-+ if (!ptr) {
-+ mt76_put_rxwi(dev, r);
- goto unmap;
- }
-
-@@ -67,17 +84,17 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
-
- if (unlikely(dma_mapping_error(dev->dev, addr))) {
- skb_free_frag(ptr);
-- mt76_put_rxwi(dev, t);
-+ mt76_put_rxwi(dev, r);
- goto unmap;
- }
-
- desc->buf0 = cpu_to_le32(addr);
-- token = mt76_rx_token_consume(dev, ptr, t, addr);
-+ token = mt76_rx_token_consume(dev, ptr, r, addr);
- if (token < 0) {
- dma_unmap_single(dev->dma_dev, addr,
- wed->wlan.rx_size, DMA_TO_DEVICE);
-- __free_pages(page, get_order(length));
-- mt76_put_rxwi(dev, t);
-+ skb_free_frag(ptr);
-+ mt76_put_rxwi(dev, r);
- goto unmap;
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2002-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2002-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch
deleted file mode 100644
index c1d7f8f..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2002-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 397754d5ae1770a66b1a1cb05ba93360f3b3b533 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Wed, 19 Apr 2023 17:13:41 +0800
-Subject: [PATCH 2002/2032] mtk: wifi: mt76: wed: change wed token init size to
- adapt wed3.0
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- tx.c | 10 +++++++---
- 1 file changed, 7 insertions(+), 3 deletions(-)
-
-diff --git a/tx.c b/tx.c
-index 46dae6e0..e2795067 100644
---- a/tx.c
-+++ b/tx.c
-@@ -827,12 +827,16 @@ EXPORT_SYMBOL_GPL(__mt76_set_tx_blocked);
-
- int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
- {
-- int token;
-+ int token, start = 0;
-+
-+ if (mtk_wed_device_active(&dev->mmio.wed))
-+ start = dev->mmio.wed.wlan.nbuf;
-
- spin_lock_bh(&dev->token_lock);
-
-- token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
-- if (token >= 0)
-+ token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
-+ GFP_ATOMIC);
-+ if (token >= start)
- dev->token_count++;
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2003-mtk-wifi-mt76-add-random-early-drop-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2003-mtk-wifi-mt76-add-random-early-drop-support.patch
deleted file mode 100644
index 547c268..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2003-mtk-wifi-mt76-add-random-early-drop-support.patch
+++ /dev/null
@@ -1,316 +0,0 @@
-From 5a8454a960379567f438f55eda39635a54a7bfbf Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Wed, 19 Apr 2023 18:32:41 +0800
-Subject: [PATCH 2003/2032] mtk: wifi: mt76: add random early drop support
-
----
- mt7996/debugfs.c | 1 +
- mt7996/mac.c | 7 ++++
- mt7996/mcu.c | 81 ++++++++++++++++++++++++++++++++++++++++++--
- mt7996/mcu.h | 4 ++-
- mt7996/mt7996.h | 5 ++-
- mt7996/mtk_debugfs.c | 23 +++++++++++++
- mt7996/mtk_mcu.c | 26 ++++++++++++++
- mt7996/mtk_mcu.h | 24 +++++++++++++
- 8 files changed, 167 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index dff9e467..01f98b2e 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -629,6 +629,7 @@ mt7996_tx_stats_show(struct seq_file *file, void *data)
- seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts);
- seq_printf(file, "Tx success: %8u (MPDUs)\n", success);
- seq_printf(file, "Tx PER: %u%%\n", per);
-+ seq_printf(file, "Tx RED drop: %8u\n", phy->red_drop);
-
- mt7996_txbf_stat_read_phy(phy, file);
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 6fa46e46..64cec164 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1176,6 +1176,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
-
- wcid->stats.tx_retries += tx_retries;
- wcid->stats.tx_failed += tx_failed;
-+
-+ if (FIELD_GET(MT_TXFREE_INFO_STAT, info) == 2) {
-+ struct mt7996_phy *mphy =
-+ __mt7996_phy(dev, wcid->phy_idx);
-+
-+ mphy->red_drop++;
-+ }
- continue;
- }
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 2cdaf845..be21dd62 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3145,8 +3145,8 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
- if (ret)
- return ret;
-
-- return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
-- MCU_WA_PARAM_RED, 0, 0);
-+ return mt7996_mcu_red_config(dev,
-+ mtk_wed_device_active(&dev->mt76.mmio.wed));
- }
-
- int mt7996_mcu_init(struct mt7996_dev *dev)
-@@ -3178,6 +3178,83 @@ out:
- skb_queue_purge(&dev->mt76.mcu.res_q);
- }
-
-+static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
-+{
-+#define RED_TOKEN_SRC_CNT 4
-+#define RED_TOKEN_CONFIG 2
-+ struct {
-+ __le32 arg0;
-+ __le32 arg1;
-+ __le32 arg2;
-+
-+ u8 mode;
-+ u8 version;
-+ u8 _rsv[4];
-+ __le16 len;
-+
-+ __le16 tcp_offset;
-+ __le16 priority_offset;
-+ __le16 token_per_src[RED_TOKEN_SRC_CNT];
-+ __le16 token_thr_per_src[RED_TOKEN_SRC_CNT];
-+
-+ u8 _rsv2[604];
-+ } __packed req = {
-+ .arg0 = cpu_to_le32(MCU_WA_PARAM_RED_CONFIG),
-+
-+ .mode = RED_TOKEN_CONFIG,
-+ .len = cpu_to_le16(sizeof(req) - sizeof(__le32) * 3),
-+
-+ .tcp_offset = cpu_to_le16(200),
-+ .priority_offset = cpu_to_le16(255),
-+ };
-+ u8 i;
-+
-+ for (i = 0; i < RED_TOKEN_SRC_CNT; i++) {
-+ req.token_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
-+ req.token_thr_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
-+ }
-+
-+ if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
-+ req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
-+ cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
-+ &req, sizeof(req), false);
-+}
-+
-+int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable)
-+{
-+#define RED_DISABLE 0
-+#define RED_BY_WA_ENABLE 2
-+ struct {
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 enable;
-+ u8 __rsv2[3];
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_VOW_RED_ENABLE),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .enable = enable ? RED_BY_WA_ENABLE : RED_DISABLE,
-+ };
-+ int ret;
-+
-+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
-+ sizeof(req), true);
-+
-+ if (ret)
-+ return ret;
-+
-+ ret = mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
-+ MCU_WA_PARAM_RED_EN, enable, 0);
-+
-+ if (ret || !enable)
-+ return ret;
-+
-+ return mt7996_mcu_wa_red_config(dev);
-+}
-+
- int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
- {
- struct {
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 398bf3d2..4fa399bc 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -346,8 +346,9 @@ enum {
- enum {
- MCU_WA_PARAM_PDMA_RX = 0x04,
- MCU_WA_PARAM_CPU_UTIL = 0x0b,
-- MCU_WA_PARAM_RED = 0x0e,
-+ MCU_WA_PARAM_RED_EN = 0x0e,
- MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
-+ MCU_WA_PARAM_RED_CONFIG = 0x40,
- };
-
- enum mcu_mmps_mode {
-@@ -919,6 +920,7 @@ enum {
- UNI_VOW_DRR_CTRL,
- UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
- UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
-+ UNI_VOW_RED_ENABLE = 0x18,
- };
-
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 2227c08a..b0eb5d91 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -352,6 +352,7 @@ struct mt7996_phy {
- bool has_aux_rx;
-
- struct mt7996_scs_ctrl scs_ctrl;
-+ u32 red_drop;
-
- u8 muru_onoff;
-
-@@ -718,6 +719,7 @@ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)
- int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
- int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val);
- int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
-+int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
- int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
- int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
- int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
-@@ -891,11 +893,12 @@ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
- void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
- void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
- void mt7996_tm_update_channel(struct mt7996_phy *phy);
-+
-+int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- int mt7996_dma_rro_init(struct mt7996_dev *dev);
- #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
-
--
- #endif
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index c4cdbcdc..03f88780 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3015,6 +3015,27 @@ static int mt7996_muru_prot_thr_set(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
- mt7996_muru_prot_thr_set, "%lld\n");
-
-+static int
-+mt7996_red_config_set(void *data, u64 val)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ return mt7996_mcu_red_config(dev, !!val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_red_config, NULL,
-+ mt7996_red_config_set, "%lld\n");
-+
-+static int
-+mt7996_vow_drr_dbg(void *data, u64 val)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ return mt7996_mcu_set_vow_drr_dbg(dev, (u32)val);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
-+ mt7996_vow_drr_dbg, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -3092,6 +3113,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- mt7996_wtbl_read);
-
- debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
-+ debugfs_create_file("red", 0200, dir, dev, &fops_red_config);
-+ debugfs_create_file("vow_drr_dbg", 0200, dir, dev, &fops_vow_drr_dbg);
-
- debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
- debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index f70bd0be..9a6636fd 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -1254,4 +1254,30 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
- sizeof(req), false);
- }
-
-+int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
-+{
-+#define MT7996_VOW_DEBUG_MODE 0xe
-+ struct {
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 __rsv2[4];
-+ __le32 action;
-+ __le32 val;
-+ u8 __rsv3[8];
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .action = cpu_to_le32(MT7996_VOW_DEBUG_MODE),
-+ .val = cpu_to_le32(val),
-+ };
-+
-+ if (val & ~VOW_DRR_DBG_FLAGS)
-+ return -EINVAL;
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
-+ sizeof(req), true);
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 252ae98e..1568f10e 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -1035,6 +1035,30 @@ enum muru_vendor_ctrl {
- MU_CTRL_DL_USER_CNT,
- MU_CTRL_UL_USER_CNT,
- };
-+
-+enum {
-+ VOW_DRR_DBG_DUMP_BMP = BIT(0),
-+ VOW_DRR_DBG_EST_AT_PRINT = BIT(1),
-+ VOW_DRR_DBG_ADJ_GLOBAL_THLD = BIT(21),
-+ VOW_DRR_DBG_PRN_LOUD = BIT(22),
-+ VOW_DRR_DBG_PRN_ADJ_STA = BIT(23),
-+ VOW_DRR_DBG_FIX_CR = GENMASK(27, 24),
-+ VOW_DRR_DBG_CLR_FIX_CR = BIT(28),
-+ VOW_DRR_DBG_DISABLE = BIT(29),
-+ VOW_DRR_DBG_DUMP_CR = BIT(30),
-+ VOW_DRR_DBG_PRN = BIT(31)
-+};
-+
-+#define VOW_DRR_DBG_FLAGS (VOW_DRR_DBG_DUMP_BMP | \
-+ VOW_DRR_DBG_EST_AT_PRINT | \
-+ VOW_DRR_DBG_ADJ_GLOBAL_THLD | \
-+ VOW_DRR_DBG_PRN_LOUD | \
-+ VOW_DRR_DBG_PRN_ADJ_STA | \
-+ VOW_DRR_DBG_FIX_CR | \
-+ VOW_DRR_DBG_CLR_FIX_CR | \
-+ VOW_DRR_DBG_DISABLE | \
-+ VOW_DRR_DBG_DUMP_CR | \
-+ VOW_DRR_DBG_PRN)
- #endif
-
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2004-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2004-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
deleted file mode 100644
index 4f0a133..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2004-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From 240627635f05c66751095e95a1265feb99be8224 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Thu, 18 May 2023 15:01:47 +0800
-Subject: [PATCH 2004/2032] mtk: wifi: mt76: mt7996: reset addr_elem when
- delete ba
-
-The old addr element info may be used when the signature is not equel to
-0xff, and sta will find error SDP cause the SDP/SDL=0 issue.
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
-
-1. without this patch will delete wrong session id when delete ba.
-Due to fw change the cmd format.
-https://gerrit.mediatek.inc/c/neptune/firmware/bora/wifi/custom/+/7969193
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
----
- mt76.h | 1 +
- mt7996/mcu.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 47 insertions(+)
-
-diff --git a/mt76.h b/mt76.h
-index 878553a2..17418e86 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -439,6 +439,7 @@ struct mt76_rx_tid {
- u16 nframes;
-
- u8 num;
-+ u16 session_id;
-
- u8 started:1, stopped:1, timer_pending:1;
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 4fa399bc..a2604192 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -298,6 +298,52 @@ struct mt7996_mcu_thermal_notify {
- u8 __rsv2[4];
- } __packed;
-
-+struct mt7996_mcu_rro_event {
-+ struct mt7996_mcu_rxd rxd;
-+
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+} __packed;
-+
-+struct mt7996_mcu_rro_ba {
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 wlan_id;
-+ u8 tid;
-+ u8 __rsv1;
-+ __le32 status;
-+ __le16 session_id;
-+ u8 __rsv2[2];
-+} __packed;
-+
-+struct mt7996_mcu_rro_ba_del_chk_done {
-+ __le16 tag;
-+ __le16 len;
-+
-+ __le16 session_id;
-+ __le16 mld_id;
-+ u8 tid;
-+ u8 __rsv[3];
-+} __packed;
-+
-+enum {
-+ UNI_RRO_BA_SESSION_STATUS = 0,
-+ UNI_RRO_BA_SESSION_TBL = 1,
-+ UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
-+ UNI_RRO_BA_SESSION_MAX_NUM
-+};
-+
-+struct mt7996_mcu_rro_del_ba {
-+ struct mt7996_mcu_rro_event event;
-+
-+ u8 wlan_idx;
-+ u8 tid;
-+ u8 __rsv2[2];
-+};
-+
- enum mt7996_chan_mib_offs {
- UNI_MIB_OBSS_AIRTIME = 26,
- UNI_MIB_NON_WIFI_TIME = 27,
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2005-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2005-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch
deleted file mode 100644
index 14c8084..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2005-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 57ec71ee33dacaf5050f535fc83ce0497c141b33 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Fri, 6 Oct 2023 14:01:41 +0800
-Subject: [PATCH 2005/2032] mtk: wifi: mt76: wed: change pcie0 R5 to pcie1 to
- get 6G ICS
-
----
- mt7996/dma.c | 4 ++++
- mt7996/init.c | 6 ++----
- mt7996/mmio.c | 5 ++++-
- 3 files changed, 10 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 759a58e8..5d85e9ea 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -538,6 +538,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- if (mt7996_band_valid(dev, MT_BAND2)) {
- /* rx data queue for mt7996 band2 */
- rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
-+ if (mtk_wed_device_active(wed_hif2) && mtk_wed_get_rx_capa(wed_hif2)) {
-+ dev->mt76.q_rx[MT_RXQ_BAND2].flags = MT_WED_Q_RX(0);
-+ dev->mt76.q_rx[MT_RXQ_BAND2].wed = wed_hif2;
-+ }
- ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
- MT_RXQ_ID(MT_RXQ_BAND2),
- MT7996_RX_RING_SIZE,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 20415e3c..aedf4edc 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -638,10 +638,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- goto error;
-
- if (wed == &dev->mt76.mmio.wed_hif2 && mtk_wed_device_active(wed)) {
-- u32 irq_mask = dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2;
--
-- mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask);
-- mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, irq_mask);
-+ mt76_wr(dev, MT_INT_PCIE1_MASK_CSR, MT_INT_TX_RX_DONE_EXT);
-+ mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
- }
-
- return 0;
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 367a204d..44e64f86 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -527,12 +527,15 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
- dev->mt76.mmio.irqmask);
- if (intr1 & MT_INT_RX_TXFREE_EXT)
- napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]);
-+
-+ if (intr1 & MT_INT_RX_DONE_BAND2_EXT)
-+ napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]);
- }
-
- if (mtk_wed_device_active(wed)) {
- mtk_wed_device_irq_set_mask(wed, 0);
- intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask);
-- intr |= (intr1 & ~MT_INT_RX_TXFREE_EXT);
-+ intr |= (intr1 & ~MT_INT_TX_RX_DONE_EXT);
- } else {
- mt76_wr(dev, MT_INT_MASK_CSR, 0);
- if (dev->hif2)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2006-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2006-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch
deleted file mode 100644
index 99ac2f6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2006-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From 6cf957f7acc9124acdb7b231b54e98d305b968f3 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Tue, 23 May 2023 12:06:29 +0800
-Subject: [PATCH 2006/2032] mtk: wifi: mt76: add SER support for wed3.0
-
----
- dma.c | 5 +++--
- mt7996/mmio.c | 1 +
- 2 files changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index c54187bd..e5be891c 100644
---- a/dma.c
-+++ b/dma.c
-@@ -834,8 +834,9 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
-
- /* reset WED rx queues */
- mt76_wed_dma_setup(dev, q, true);
--
-- if (!mt76_queue_is_wed_tx_free(q)) {
-+ if (!mt76_queue_is_wed_tx_free(q) &&
-+ !(mt76_queue_is_wed_rro(q) &&
-+ mtk_wed_device_active(&dev->mmio.wed))) {
- mt76_dma_sync_idx(dev, q);
- mt76_dma_rx_fill(dev, q, false);
- }
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 44e64f86..92ae5138 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -297,6 +297,7 @@ out:
-
- return ret;
- }
-+
- #endif
-
- int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2007-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2007-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch
deleted file mode 100644
index daaace2..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2007-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From cbe204fc70c0aebce3a42cfc314ef0c5bd8a16cf Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Wed, 19 Jul 2023 10:55:09 +0800
-Subject: [PATCH 2007/2032] mtk: wifi: mt76: mt7915: wed: find rx token by
- physical address
-
-The token id in RxDMAD may be incorrect when it is not the last frame due to
-WED HW bug. Lookup correct token id by physical address in sdp0.
-Add len == 0 check to drop garbage frames
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- dma.c | 27 +++++++++++++++++++++++++--
- 1 file changed, 25 insertions(+), 2 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index e5be891c..1021b3e5 100644
---- a/dma.c
-+++ b/dma.c
-@@ -446,9 +446,32 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- mt76_dma_should_drop_buf(drop, ctrl, buf1, desc_info);
-
- if (mt76_queue_is_wed_rx(q)) {
-+ u32 id, find = 0;
- u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
-- struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
-+ struct mt76_rxwi_cache *r;
-+
-+ if (*more) {
-+ spin_lock_bh(&dev->rx_token_lock);
-+
-+ idr_for_each_entry(&dev->rx_token, r, id) {
-+ if (r->dma_addr == le32_to_cpu(desc->buf0)) {
-+ find = 1;
-+ token = id;
-+
-+ /* Write correct id back to DMA*/
-+ u32p_replace_bits(&buf1, id,
-+ MT_DMA_CTL_TOKEN);
-+ WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
-+ break;
-+ }
-+ }
-
-+ spin_unlock_bh(&dev->rx_token_lock);
-+ if (!find)
-+ return NULL;
-+ }
-+
-+ r = mt76_rx_token_release(dev, token);
- if (!r)
- return NULL;
-
-@@ -902,7 +925,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- if (!data)
- break;
-
-- if (drop)
-+ if (drop || (len == 0))
- goto free_frag;
-
- if (q->rx_head)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2008-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2008-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch
deleted file mode 100644
index 8075e12..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2008-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From 51238985cbb77e8955cc727e6f3853b53c9c088d Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Thu, 20 Jul 2023 10:25:50 +0800
-Subject: [PATCH 2008/2032] mtk: wifi: mt76: mt7996: add dma mask limitation
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- dma.c | 4 ++--
- wed.c | 4 ++--
- 2 files changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 1021b3e5..da21f641 100644
---- a/dma.c
-+++ b/dma.c
-@@ -488,7 +488,7 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- } else {
- struct mt76_queue_buf qbuf;
-
-- buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
-+ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
- if (!buf)
- return NULL;
-
-@@ -711,7 +711,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- if (mt76_queue_is_wed_rro_ind(q))
- goto done;
-
-- buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
-+ buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
- if (!buf)
- break;
-
-diff --git a/wed.c b/wed.c
-index 0a0b5c05..1c6d53c8 100644
---- a/wed.c
-+++ b/wed.c
-@@ -65,14 +65,14 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- for (i = 0; i < size; i++) {
- struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
- dma_addr_t addr;
-- struct page *page;
- int token;
- void *ptr;
-
- if (!r)
- goto unmap;
-
-- ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
-+ ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length,
-+ GFP_ATOMIC | GFP_DMA32);
- if (!ptr) {
- mt76_put_rxwi(dev, r);
- goto unmap;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2009-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2009-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch
deleted file mode 100644
index d38058c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2009-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch
+++ /dev/null
@@ -1,122 +0,0 @@
-From f2dfba0451106438a4cb312552b95da9a0857c68 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Fri, 18 Aug 2023 10:17:08 +0800
-Subject: [PATCH 2009/2032] mtk: wifi: mt76: mt7996: add per bss statistic info
-
-Whenever WED is enabled, unicast traffic might run through HW path.
-As a result, we need to count them using WM event.
-Broadcast and multicast traffic on the other hand, will be counted in mac80211
-as they always go through SW path and thus mac80211 can always see and count them.
-
-| | Tx | Rx |
-|---------|--------------------------------|---------------------------|
-| Unicast | mt76 | mt76 |
-| | __mt7996_stat_to_netdev() | __mt7996_stat_to_netdev() |
-|---------|--------------------------------|---------------------------|
-| BMCast | mac80211 | mac80211 |
-| | __ieee80211_subif_start_xmit() | ieee80211_deliver_skb() |
----
- mt7996/init.c | 1 +
- mt7996/main.c | 1 +
- mt7996/mcu.c | 40 +++++++++++++++++++++++++++++++++++-----
- 3 files changed, 37 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index aedf4edc..518f70e4 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -390,6 +390,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
-+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STAS_COUNT);
-
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index d314d9fb..e1c107fb 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -251,6 +251,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- mvif->sta.wcid.phy_idx = band_idx;
- mvif->sta.wcid.hw_key_idx = -1;
- mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+ mvif->sta.vif = mvif;
- mt76_wcid_init(&mvif->sta.wcid);
-
- mt7996_mac_wtbl_update(dev, idx,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index be21dd62..479eee0e 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -521,6 +521,27 @@ mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rat
- return 0;
- }
-
-+static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
-+ struct mt76_wcid *wcid,
-+ u32 tx_bytes, u32 rx_bytes,
-+ u32 tx_packets, u32 rx_packets)
-+{
-+ struct mt7996_sta *msta;
-+ struct ieee80211_vif *vif;
-+ struct wireless_dev *wdev;
-+
-+ if (wiphy_ext_feature_isset(mphy->hw->wiphy,
-+ NL80211_EXT_FEATURE_STAS_COUNT)) {
-+ msta = container_of(wcid, struct mt7996_sta, wcid);
-+ vif = container_of((void *)msta->vif, struct ieee80211_vif,
-+ drv_priv);
-+ wdev = ieee80211_vif_to_wdev(vif);
-+
-+ dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
-+ dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
-+ }
-+}
-+
- static void
- mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- {
-@@ -536,7 +557,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- u16 wlan_idx;
- struct mt76_wcid *wcid;
- struct mt76_phy *mphy;
-- u32 tx_bytes, rx_bytes;
-+ u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
-
- switch (le16_to_cpu(res->tag)) {
- case UNI_ALL_STA_TXRX_RATE:
-@@ -564,6 +585,9 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- wcid->stats.tx_bytes += tx_bytes;
- wcid->stats.rx_bytes += rx_bytes;
-
-+ __mt7996_stat_to_netdev(mphy, wcid,
-+ tx_bytes, rx_bytes, 0, 0);
-+
- ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
- ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
- }
-@@ -575,10 +599,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- if (!wcid)
- break;
-
-- wcid->stats.tx_packets +=
-- le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
-- wcid->stats.rx_packets +=
-- le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
-+ mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
-+
-+ tx_packets = le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
-+ rx_packets = le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
-+
-+ wcid->stats.tx_packets += tx_packets;
-+ wcid->stats.rx_packets += rx_packets;
-+
-+ __mt7996_stat_to_netdev(mphy, wcid, 0, 0,
-+ tx_packets, rx_packets);
- break;
- default:
- break;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2010-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2010-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch
deleted file mode 100644
index 26dc702..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2010-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 1b648b7fe779a9862e966a27d274ac42c24e4bcd Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 26 Oct 2023 17:27:43 +0800
-Subject: [PATCH 2010/2032] mtk: wifi: mt76: mt7996: do not report netdev stats
- on monitor vif
-
-This fixes the following NULL pointer crash when enabling monitor mode:
-[ 205.593158] Call trace:
-[ 205.595597] mt7996_mcu_rx_event+0x4a0/0x6e8 [mt7996e]
-[ 205.600725] mt7996_queue_rx_skb+0x6e4/0xfa0 [mt7996e]
-[ 205.605851] mt76_dma_rx_poll+0x384/0x420 [mt76]
-[ 205.610459] __napi_poll+0x38/0x1c0
-[ 205.613935] napi_threaded_poll+0x80/0xe8
-[ 205.617934] kthread+0x124/0x128
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mcu.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 479eee0e..15644029 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -537,6 +537,9 @@ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
- drv_priv);
- wdev = ieee80211_vif_to_wdev(vif);
-
-+ if (vif->type == NL80211_IFTYPE_MONITOR)
-+ return;
-+
- dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
- dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
- }
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2011-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2011-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch
deleted file mode 100644
index 2db4015..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2011-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch
+++ /dev/null
@@ -1,675 +0,0 @@
-From 655711c39cac51c4b63ec8e32dd79ec362081293 Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Mon, 11 Sep 2023 16:35:15 +0800
-Subject: [PATCH 2011/2032] mtk: wifi: mt76: mt7996: add support for HW-ATF
-
----
- mt7996/debugfs.c | 90 ++++++++++++++++
- mt7996/init.c | 43 ++++++++
- mt7996/mac.c | 6 ++
- mt7996/mcu.c | 265 ++++++++++++++++++++++++++++++++++++++++++-----
- mt7996/mcu.h | 1 +
- mt7996/mt7996.h | 96 ++++++++++++++++-
- 6 files changed, 475 insertions(+), 26 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 01f98b2e..8e4ceeeb 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -935,6 +935,91 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
- mt7996_fw_debug_muru_disable_get,
- mt7996_fw_debug_muru_disable_set, "%lld\n");
-
-+static int
-+mt7996_vow_info_read(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ struct mt7996_vow_ctrl *vow = &dev->vow;
-+ int i;
-+
-+ seq_printf(s, "VoW ATF Configuration:\n");
-+ seq_printf(s, "ATF: %s\n", vow->atf_enable ? "enabled" : "disabled");
-+ seq_printf(s, "WATF: %s\n", vow->watf_enable ? "enabled" : "disabled");
-+ seq_printf(s, "Airtime Quantums (unit: 256 us)\n");
-+ for (i = 0; i < VOW_DRR_QUANTUM_NUM; ++i)
-+ seq_printf(s, "\tL%d: %hhu\n", i, vow->drr_quantum[i]);
-+ seq_printf(s, "Max Airtime Deficit: %hhu (unit: 256 us)\n", vow->max_deficit);
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_atf_enable_get(void *data, u64 *val)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ *val = phy->dev->vow.atf_enable;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_atf_enable_set(void *data, u64 val)
-+{
-+ struct mt7996_phy *phy = data;
-+ struct mt7996_vow_ctrl *vow = &phy->dev->vow;
-+ int ret;
-+
-+ vow->max_deficit = val ? 64 : 1;
-+ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
-+ if (ret)
-+ return ret;
-+
-+ vow->atf_enable = !!val;
-+ return mt7996_mcu_set_vow_feature_ctrl(phy);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_atf_enable, mt7996_atf_enable_get,
-+ mt7996_atf_enable_set, "%llu\n");
-+
-+static int
-+mt7996_airtime_read(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ struct mt76_dev *mdev = &dev->mt76;
-+ struct mt7996_vow_sta_ctrl *vow;
-+ struct ieee80211_sta *sta;
-+ struct mt7996_sta *msta;
-+ struct mt76_wcid *wcid;
-+ struct mt76_vif *vif;
-+ u64 airtime;
-+ u16 i;
-+
-+ seq_printf(s, "VoW Airtime Information:\n");
-+ rcu_read_lock();
-+ for (i = 1; i < MT7996_WTBL_STA; ++i) {
-+ wcid = rcu_dereference(mdev->wcid[i]);
-+ if (!wcid || !wcid->sta)
-+ continue;
-+
-+ msta = container_of(wcid, struct mt7996_sta, wcid);
-+ sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
-+ vow = &msta->vow;
-+ vif = &msta->vif->mt76;
-+
-+ spin_lock_bh(&vow->lock);
-+ airtime = vow->tx_airtime;
-+ vow->tx_airtime = 0;
-+ spin_unlock_bh(&vow->lock);
-+
-+ seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
-+ sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
-+ }
-+ rcu_read_unlock();
-+
-+ return 0;
-+}
-+
- int mt7996_init_debugfs(struct mt7996_phy *phy)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -962,6 +1047,11 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- mt7996_twt_stats);
- debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
- debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "vow_info", dir,
-+ mt7996_vow_info_read);
-+ debugfs_create_file("atf_enable", 0600, dir, phy, &fops_atf_enable);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "airtime", dir,
-+ mt7996_airtime_read);
-
- if (phy->mt76->cap.has_5ghz) {
- debugfs_create_u32("dfs_hw_pattern", 0400, dir,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 518f70e4..b902bcc5 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -562,6 +562,37 @@ int mt7996_txbf_init(struct mt7996_dev *dev)
- return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
- }
-
-+static int mt7996_vow_init(struct mt7996_phy *phy)
-+{
-+ struct mt7996_vow_ctrl *vow = &phy->dev->vow;
-+ int ret;
-+
-+ vow->atf_enable = true;
-+ vow->watf_enable = false;
-+ vow->max_deficit = 64;
-+ vow->sch_type = VOW_SCH_TYPE_FOLLOW_POLICY;
-+ vow->sch_policy = VOW_SCH_POLICY_SRR;
-+
-+ vow->drr_quantum[0] = VOW_DRR_QUANTUM_L0;
-+ vow->drr_quantum[1] = VOW_DRR_QUANTUM_L1;
-+ vow->drr_quantum[2] = VOW_DRR_QUANTUM_L2;
-+ vow->drr_quantum[3] = VOW_DRR_QUANTUM_L3;
-+ vow->drr_quantum[4] = VOW_DRR_QUANTUM_L4;
-+ vow->drr_quantum[5] = VOW_DRR_QUANTUM_L5;
-+ vow->drr_quantum[6] = VOW_DRR_QUANTUM_L6;
-+ vow->drr_quantum[7] = VOW_DRR_QUANTUM_L7;
-+
-+ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
-+ if (ret)
-+ return ret;
-+
-+ ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
-+ if (ret)
-+ return ret;
-+
-+ return mt7996_mcu_set_vow_feature_ctrl(phy);
-+}
-+
- static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- enum mt76_band_id band)
- {
-@@ -634,6 +665,12 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- if (ret)
- goto error;
-
-+ if (mt7996_vow_should_enable(dev)) {
-+ ret = mt7996_vow_init(phy);
-+ if (ret)
-+ goto error;
-+ }
-+
- ret = mt7996_init_debugfs(phy);
- if (ret)
- goto error;
-@@ -1440,6 +1477,12 @@ int mt7996_register_device(struct mt7996_dev *dev)
-
- dev->recovery.hw_init_done = true;
-
-+ if (mt7996_vow_should_enable(dev)) {
-+ ret = mt7996_vow_init(&dev->phy);
-+ if (ret)
-+ goto error;
-+ }
-+
- ret = mt7996_init_debugfs(&dev->phy);
- if (ret)
- goto error;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 64cec164..8de4ab9a 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -103,6 +103,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
- };
- struct ieee80211_sta *sta;
- struct mt7996_sta *msta;
-+ struct mt7996_vow_sta_ctrl *vow;
- u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
- LIST_HEAD(sta_poll_list);
- int i;
-@@ -161,6 +162,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
-
- sta = container_of((void *)msta, struct ieee80211_sta,
- drv_priv);
-+ vow = &msta->vow;
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- u8 q = mt76_connac_lmac_mapping(i);
- u32 tx_cur = tx_time[q];
-@@ -171,6 +173,10 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
- continue;
-
- ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
-+
-+ spin_lock_bh(&vow->lock);
-+ vow->tx_airtime += tx_cur;
-+ spin_unlock_bh(&vow->lock);
- }
-
- /* get signal strength of resp frames (CTS/BA/ACK) */
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 15644029..a907e667 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2223,34 +2223,37 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- }
-
- static int
--mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-- struct ieee80211_sta *sta)
-+mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
- {
--#define MT_STA_BSS_GROUP 1
-- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-- struct mt7996_sta *msta;
-- struct {
-- u8 __rsv1[4];
-+ struct mt7996_vow_sta_ctrl *vow = &msta->vow;
-+ u8 omac_idx = msta->vif->mt76.omac_idx;
-+ int ret;
-
-- __le16 tag;
-- __le16 len;
-- __le16 wlan_idx;
-- u8 __rsv2[2];
-- __le32 action;
-- __le32 val;
-- u8 __rsv3[8];
-- } __packed req = {
-- .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
-- .len = cpu_to_le16(sizeof(req) - 4),
-- .action = cpu_to_le32(MT_STA_BSS_GROUP),
-- .val = cpu_to_le32(mvif->mt76.idx % 16),
-- };
-+ /* Assignment of STA BSS group index aligns FW.
-+ * Each band has its own BSS group bitmap space.
-+ * 0: BSS 0
-+ * 4..18: BSS 0x11..0x1f
-+ */
-+ vow->bss_grp_idx = (omac_idx <= HW_BSSID_MAX)
-+ ? omac_idx
-+ : HW_BSSID_MAX + omac_idx - EXT_BSSID_START;
-+ vow->paused = false;
-+ vow->drr_quantum[IEEE80211_AC_VO] = VOW_DRR_QUANTUM_IDX0;
-+ vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
-+ vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
-+ vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
-+ vow->tx_airtime = 0;
-+ spin_lock_init(&vow->lock);
-+
-+ ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
-+ if (ret)
-+ return ret;
-
-- msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
-- req.wlan_idx = cpu_to_le16(msta->wcid.idx);
-+ ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_PAUSE);
-+ if (ret)
-+ return ret;
-
-- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
-- sizeof(req), true);
-+ return mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_ALL);
- }
-
- int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-@@ -2306,7 +2309,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
- }
-
-- ret = mt7996_mcu_add_group(dev, vif, sta);
-+ ret = mt7996_mcu_sta_init_vow(mvif->phy, msta);
- if (ret) {
- dev_kfree_skb(skb);
- return ret;
-@@ -5143,6 +5146,218 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
- &req, sizeof(req), false);
- }
-
-+int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
-+ enum vow_drr_ctrl_id id)
-+{
-+ struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
-+ u32 val = 0;
-+ struct {
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ __le16 wlan_idx;
-+ u8 band_idx;
-+ u8 wmm_idx;
-+ __le32 ctrl_id;
-+
-+ union {
-+ __le32 val;
-+ u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
-+ };
-+
-+ u8 __rsv2[3];
-+ u8 omac_idx;
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
-+ .band_idx = phy->mt76->band_idx,
-+ .wmm_idx = msta ? msta->vif->mt76.wmm_idx : 0,
-+ .ctrl_id = cpu_to_le32(id),
-+ .omac_idx = msta ? msta->vif->mt76.omac_idx : 0
-+ };
-+
-+ switch (id) {
-+ case VOW_DRR_CTRL_STA_ALL:
-+ val |= FIELD_PREP(MT7996_DRR_STA_BSS_GRP_MASK, vow->bss_grp_idx);
-+ val |= FIELD_PREP(MT7996_DRR_STA_AC0_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BK]);
-+ val |= FIELD_PREP(MT7996_DRR_STA_AC1_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BE]);
-+ val |= FIELD_PREP(MT7996_DRR_STA_AC2_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VI]);
-+ val |= FIELD_PREP(MT7996_DRR_STA_AC3_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VO]);
-+ req.val = cpu_to_le32(val);
-+ break;
-+ case VOW_DRR_CTRL_STA_BSS_GROUP:
-+ req.val = cpu_to_le32(vow->bss_grp_idx);
-+ break;
-+ case VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND:
-+ req.val = cpu_to_le32(phy->dev->vow.max_deficit);
-+ break;
-+ case VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL:
-+ memcpy(req.drr_quantum, phy->dev->vow.drr_quantum, VOW_DRR_QUANTUM_NUM);
-+ break;
-+ case VOW_DRR_CTRL_STA_PAUSE:
-+ req.val = cpu_to_le32(vow->paused);
-+ break;
-+ default:
-+ dev_err(phy->dev->mt76.dev, "Unknown VoW DRR Control ID: %u\n", id);
-+ return -EINVAL;
-+ }
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
-+ &req, sizeof(req), true);
-+}
-+
-+int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
-+{
-+ struct mt7996_vow_ctrl *vow = &phy->dev->vow;
-+ struct {
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ /* DW0 */
-+ __le16 apply_bwc_enable_per_grp;
-+ __le16 apply_bwc_refill_period : 1;
-+ __le16 __rsv2 : 3;
-+ __le16 apply_band1_search_rule : 1;
-+ __le16 apply_band0_search_rule : 1;
-+ __le16 __rsv3 : 3;
-+ __le16 apply_watf_enable : 1;
-+ __le16 __rsv4 : 2;
-+ __le16 apply_grp_no_change_in_txop : 1;
-+ __le16 apply_atf_enable : 1;
-+ __le16 apply_bwc_token_refill_enable : 1;
-+ __le16 apply_bwc_enable : 1;
-+
-+ /* DW1 */
-+ __le16 apply_bwc_check_time_token_per_grp;
-+ __le16 __rsv5;
-+
-+ /* DW2 */
-+ __le16 apply_bwc_check_len_token_per_grp;
-+ __le16 __rsv6;
-+
-+ /* DW3 */
-+ u8 band_idx;
-+ u8 __rsv7[3];
-+
-+ /* DW4 */
-+ __le32 __rsv8;
-+
-+ /* DW5 */
-+ __le16 bwc_enable_per_grp;
-+ __le16 bwc_refill_period : 3;
-+ __le16 __rsv9 : 1;
-+ __le16 band1_search_rule : 1;
-+ __le16 band0_search_rule : 1;
-+ __le16 __rsv10 : 3;
-+ __le16 watf_enable : 1;
-+ __le16 __rsv11 : 2;
-+ __le16 grp_no_change_in_txop : 1;
-+ __le16 atf_enable : 1;
-+ __le16 bwc_token_refill_enable : 1;
-+ __le16 bwc_enable : 1;
-+
-+ /* DW6 */
-+ __le16 bwc_check_time_token_per_grp;
-+ __le16 __rsv12;
-+
-+ /* DW7 */
-+ __le16 bwc_check_len_token_per_grp;
-+ __le16 __rsv13;
-+
-+ /* DW8 */
-+ __le32 apply_atf_rts_sta_lock : 1;
-+ __le32 atf_rts_sta_lock : 1;
-+ __le32 apply_atf_keep_quantum : 1;
-+ __le32 atf_keep_quantum : 1;
-+ __le32 apply_tx_cnt_mode_ctrl : 1;
-+ __le32 tx_cnt_mode_ctrl : 4;
-+ __le32 apply_tx_measure_mode_enable : 1;
-+ __le32 tx_measure_mode_enable : 1;
-+ __le32 apply_backoff_ctrl : 1;
-+ __le32 backoff_bound_enable : 1;
-+ __le32 backoff_bound : 5;
-+ __le32 apply_atf_rts_fail_charge : 1;
-+ __le32 atf_rts_fail_charge : 1;
-+ __le32 apply_zero_eifs : 1;
-+ __le32 zero_eifs : 1;
-+ __le32 apply_rx_rifs_enable : 1;
-+ __le32 rx_rifs_enable : 1;
-+ __le32 apply_vow_ctrl : 1;
-+ __le32 vow_ctrl_val : 1;
-+ __le32 vow_ctrl_bit : 5;
-+ __le32 __rsv14 : 1;
-+
-+ /* DW9 */
-+ __le32 apply_spl_sta_num : 1;
-+ __le32 spl_sta_num : 3;
-+ __le32 dbg_lvl : 2;
-+ __le32 apply_atf_sch_ctrl : 1;
-+ __le32 atf_sch_type : 2;
-+ __le32 atf_sch_policy : 2;
-+ __le32 __rsv15 : 21;
-+ } __packed req = {
-+ .tag = cpu_to_le16(UNI_VOW_FEATURE_CTRL),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ /* DW0 */
-+ .apply_bwc_enable_per_grp = cpu_to_le16(0xffff),
-+ .apply_bwc_refill_period = true,
-+ .apply_band1_search_rule = true,
-+ .apply_band0_search_rule = true,
-+ .apply_watf_enable = true,
-+ .apply_grp_no_change_in_txop = true,
-+ .apply_atf_enable = true,
-+ .apply_bwc_token_refill_enable = true,
-+ .apply_bwc_enable = true,
-+ /* DW1 */
-+ .apply_bwc_check_time_token_per_grp = cpu_to_le16(0xffff),
-+ /* DW2 */
-+ .apply_bwc_check_len_token_per_grp = cpu_to_le16(0xffff),
-+ /* DW3 */
-+ .band_idx = phy->mt76->band_idx,
-+ /* DW5 */
-+ .bwc_enable_per_grp = cpu_to_le16(0xffff),
-+ .bwc_refill_period = VOW_REFILL_PERIOD_32US,
-+ .band1_search_rule = VOW_SEARCH_WMM_FIRST,
-+ .band0_search_rule = VOW_SEARCH_WMM_FIRST,
-+ .watf_enable = vow->watf_enable,
-+ .grp_no_change_in_txop = true,
-+ .atf_enable = vow->atf_enable,
-+ .bwc_token_refill_enable = true,
-+ .bwc_enable = false,
-+ /* DW6 */
-+ .bwc_check_time_token_per_grp = cpu_to_le16(0x0),
-+ /* DW7 */
-+ .bwc_check_len_token_per_grp = cpu_to_le16(0x0),
-+ /* DW8 */
-+ .apply_atf_rts_sta_lock = false,
-+ .apply_atf_keep_quantum = true,
-+ .atf_keep_quantum = true,
-+ .apply_tx_cnt_mode_ctrl = false,
-+ .apply_tx_measure_mode_enable = false,
-+ .apply_backoff_ctrl = false,
-+ .apply_atf_rts_fail_charge = false,
-+ .apply_zero_eifs = false,
-+ .apply_rx_rifs_enable = false,
-+ .apply_vow_ctrl = true,
-+ .vow_ctrl_val = true,
-+ /* Reset DRR table when SER occurs. */
-+ .vow_ctrl_bit = 26,
-+ /* DW9 */
-+ .apply_spl_sta_num = false,
-+ .dbg_lvl = 0,
-+ .apply_atf_sch_ctrl = true,
-+ .atf_sch_type = vow->sch_type,
-+ .atf_sch_policy = vow->sch_policy
-+ };
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
-+ &req, sizeof(req), true);
-+}
-+
- #ifdef CONFIG_MTK_VENDOR
- void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- {
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index a2604192..7b8540f6 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -964,6 +964,7 @@ enum {
-
- enum {
- UNI_VOW_DRR_CTRL,
-+ UNI_VOW_FEATURE_CTRL,
- UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
- UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
- UNI_VOW_RED_ENABLE = 0x18,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b0eb5d91..b1abe42b 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -115,6 +115,12 @@
- #define MT7996_RX_MSDU_PAGE_SIZE (128 + \
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
-
-+#define MT7996_DRR_STA_BSS_GRP_MASK GENMASK(5, 0)
-+#define MT7996_DRR_STA_AC0_QNTM_MASK GENMASK(10, 8)
-+#define MT7996_DRR_STA_AC1_QNTM_MASK GENMASK(14, 12)
-+#define MT7996_DRR_STA_AC2_QNTM_MASK GENMASK(18, 16)
-+#define MT7996_DRR_STA_AC3_QNTM_MASK GENMASK(22, 20)
-+
- struct mt7996_vif;
- struct mt7996_sta;
- struct mt7996_dfs_pulse;
-@@ -216,6 +222,81 @@ enum mt7996_dpd_ch_num {
- DPD_CH_NUM_TYPE_MAX,
- };
-
-+enum {
-+ VOW_SEARCH_AC_FIRST,
-+ VOW_SEARCH_WMM_FIRST
-+};
-+
-+enum {
-+ VOW_REFILL_PERIOD_1US,
-+ VOW_REFILL_PERIOD_2US,
-+ VOW_REFILL_PERIOD_4US,
-+ VOW_REFILL_PERIOD_8US,
-+ VOW_REFILL_PERIOD_16US,
-+ VOW_REFILL_PERIOD_32US,
-+ VOW_REFILL_PERIOD_64US,
-+ VOW_REFILL_PERIOD_128US
-+};
-+
-+/* Default DRR airtime quantum of each level */
-+enum {
-+ VOW_DRR_QUANTUM_L0 = 6,
-+ VOW_DRR_QUANTUM_L1 = 12,
-+ VOW_DRR_QUANTUM_L2 = 16,
-+ VOW_DRR_QUANTUM_L3 = 20,
-+ VOW_DRR_QUANTUM_L4 = 24,
-+ VOW_DRR_QUANTUM_L5 = 28,
-+ VOW_DRR_QUANTUM_L6 = 32,
-+ VOW_DRR_QUANTUM_L7 = 36
-+};
-+
-+enum {
-+ VOW_DRR_QUANTUM_IDX0,
-+ VOW_DRR_QUANTUM_IDX1,
-+ VOW_DRR_QUANTUM_IDX2,
-+ VOW_DRR_QUANTUM_IDX3,
-+ VOW_DRR_QUANTUM_IDX4,
-+ VOW_DRR_QUANTUM_IDX5,
-+ VOW_DRR_QUANTUM_IDX6,
-+ VOW_DRR_QUANTUM_IDX7,
-+ VOW_DRR_QUANTUM_NUM
-+};
-+
-+enum {
-+ VOW_SCH_TYPE_FOLLOW_POLICY,
-+ VOW_SCH_TYPE_FOLLOW_HW
-+};
-+
-+enum {
-+ VOW_SCH_POLICY_SRR, /* Shared Round-Robin */
-+ VOW_SCH_POLICY_WRR /* Weighted Round-Robin */
-+};
-+
-+enum vow_drr_ctrl_id {
-+ VOW_DRR_CTRL_STA_ALL,
-+ VOW_DRR_CTRL_STA_BSS_GROUP,
-+ VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND = 0x10,
-+ VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL = 0x28,
-+ VOW_DRR_CTRL_STA_PAUSE = 0x30
-+};
-+
-+struct mt7996_vow_ctrl {
-+ bool atf_enable;
-+ bool watf_enable;
-+ u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
-+ u8 max_deficit;
-+ u8 sch_type;
-+ u8 sch_policy;
-+};
-+
-+struct mt7996_vow_sta_ctrl {
-+ bool paused;
-+ u8 bss_grp_idx;
-+ u8 drr_quantum[IEEE80211_NUM_ACS];
-+ u64 tx_airtime;
-+ spinlock_t lock;
-+};
-+
- struct mt7996_sta {
- struct mt76_wcid wcid; /* must be first */
-
-@@ -235,6 +316,8 @@ struct mt7996_sta {
- u8 flowid_mask;
- struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT];
- } twt;
-+
-+ struct mt7996_vow_sta_ctrl vow;
- };
-
- struct mt7996_vif {
-@@ -494,6 +577,7 @@ struct mt7996_dev {
-
- u8 wtbl_size_group;
-
-+ struct mt7996_vow_ctrl vow;
- #ifdef CONFIG_MTK_DEBUG
- u16 wlan_idx;
- struct {
-@@ -734,10 +818,12 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
--int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
- int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
- void mt7996_mcu_scs_sta_poll(struct work_struct *work);
- int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
-+int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
-+ enum vow_drr_ctrl_id id);
-+int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
-
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-@@ -787,6 +873,14 @@ static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
- return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx);
- }
-
-+static inline bool
-+mt7996_vow_should_enable(struct mt7996_dev *dev)
-+{
-+ return !wiphy_ext_feature_isset(mt76_hw(dev)->wiphy,
-+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS) ||
-+ mtk_wed_device_active(&dev->mt76.mmio.wed);
-+}
-+
- void mt7996_mac_init(struct mt7996_dev *dev);
- u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
- bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2012-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2012-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch
deleted file mode 100644
index 7b3e94a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2012-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch
+++ /dev/null
@@ -1,397 +0,0 @@
-From 12a52d5e5a8643d0fe1448469d228aeeb85df362 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Thu, 12 Oct 2023 10:04:54 +0800
-Subject: [PATCH 2012/2032] mtk: wifi: mt76: mt7996: wed: add SER0.5 support w/
- wed3.0
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- dma.c | 9 ++--
- dma.h | 4 +-
- mt76.h | 14 ++++--
- mt792x_dma.c | 6 +--
- mt7996/dma.c | 20 ++++++--
- mt7996/init.c | 127 +++++++++++++++++++++++++++++++-----------------
- mt7996/mac.c | 25 ++++++++++
- mt7996/mt7996.h | 1 +
- wed.c | 4 +-
- 9 files changed, 146 insertions(+), 64 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index da21f641..e23b744b 100644
---- a/dma.c
-+++ b/dma.c
-@@ -218,9 +218,9 @@ void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
- mt76_dma_sync_idx(dev, q);
- }
-
--void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
-+void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
- {
-- __mt76_dma_queue_reset(dev, q, true);
-+ __mt76_dma_queue_reset(dev, q, reset);
- }
-
- static int
-@@ -540,7 +540,8 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
- if (!q->queued)
- return NULL;
-
-- if (mt76_queue_is_wed_rro_data(q))
-+ if (mt76_queue_is_wed_rro_data(q) ||
-+ mt76_queue_is_wed_rro_msdu_pg(q))
- return NULL;
-
- if (!mt76_queue_is_wed_rro_ind(q)) {
-@@ -792,7 +793,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
- return 0;
- }
-
-- mt76_dma_queue_reset(dev, q);
-+ mt76_dma_queue_reset(dev, q, true);
-
- return 0;
- }
-diff --git a/dma.h b/dma.h
-index 1de5a2b2..3a8c2e55 100644
---- a/dma.h
-+++ b/dma.h
-@@ -83,12 +83,12 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- bool allow_direct);
- void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
- bool reset_idx);
--void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q);
-+void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
-
- static inline void
- mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
- {
-- dev->queue_ops->reset_q(dev, q);
-+ dev->queue_ops->reset_q(dev, q, true);
- if (mtk_wed_device_active(&dev->mmio.wed))
- mt76_wed_dma_setup(dev, q, true);
- }
-diff --git a/mt76.h b/mt76.h
-index 17418e86..6ac8a279 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -296,7 +296,7 @@ struct mt76_queue_ops {
-
- void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
-
-- void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q);
-+ void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
- };
-
- enum mt76_phy_type {
-@@ -1731,8 +1731,13 @@ static inline bool mt76_queue_is_wed_rro_ind(struct mt76_queue *q)
- static inline bool mt76_queue_is_wed_rro_data(struct mt76_queue *q)
- {
- return mt76_queue_is_wed_rro(q) &&
-- (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA ||
-- FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
-+ (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA);
-+}
-+
-+static inline bool mt76_queue_is_wed_rro_msdu_pg(struct mt76_queue *q)
-+{
-+ return mt76_queue_is_wed_rro(q) &&
-+ (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
- }
-
- static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
-@@ -1741,7 +1746,8 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
- return false;
-
- return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX ||
-- mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q);
-+ mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q) ||
-+ mt76_queue_is_wed_rro_msdu_pg(q);
-
- }
-
-diff --git a/mt792x_dma.c b/mt792x_dma.c
-index 5cc2d59b..c224bcc8 100644
---- a/mt792x_dma.c
-+++ b/mt792x_dma.c
-@@ -181,13 +181,13 @@ mt792x_dma_reset(struct mt792x_dev *dev, bool force)
-
- /* reset hw queues */
- for (i = 0; i < __MT_TXQ_MAX; i++)
-- mt76_queue_reset(dev, dev->mphy.q_tx[i]);
-+ mt76_queue_reset(dev, dev->mphy.q_tx[i], true);
-
- for (i = 0; i < __MT_MCUQ_MAX; i++)
-- mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
-+ mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
-
- mt76_for_each_q_rx(&dev->mt76, i)
-- mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
-+ mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
-
- mt76_tx_status_check(&dev->mt76, true);
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 5d85e9ea..d9e1b17f 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -711,21 +711,31 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
- }
-
- for (i = 0; i < __MT_MCUQ_MAX; i++)
-- mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
-+ mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
-
- mt76_for_each_q_rx(&dev->mt76, i) {
-- if (mtk_wed_device_active(&dev->mt76.mmio.wed))
-+ if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
- if (mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]) ||
-- mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i]))
-+ mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i])) {
-+ if (force && mt76_queue_is_wed_rro_data(&dev->mt76.q_rx[i]))
-+ mt76_queue_reset(dev, &dev->mt76.q_rx[i], false);
- continue;
-+ }
-+ }
-
-- mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
-+ mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
- }
-
- mt76_tx_status_check(&dev->mt76, true);
-
-- mt76_for_each_q_rx(&dev->mt76, i)
-+ mt76_for_each_q_rx(&dev->mt76, i) {
-+ if (mtk_wed_device_active(&dev->mt76.mmio.wed) && force &&
-+ (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
-+ mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i])))
-+ continue;
-+
- mt76_queue_rx_reset(dev, i);
-+ }
-
- mt7996_dma_enable(dev, !force);
- }
-diff --git a/mt7996/init.c b/mt7996/init.c
-index b902bcc5..a9720120 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -724,11 +724,91 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
- msleep(20);
- }
-
--static int mt7996_wed_rro_init(struct mt7996_dev *dev)
-+void mt7996_rro_hw_init(struct mt7996_dev *dev)
- {
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
-+ int i;
-+
-+ if (!dev->has_rro)
-+ return;
-+
-+ if (is_mt7992(&dev->mt76)) {
-+ /* set emul 3.0 function */
-+ mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
-+ MT_RRO_3_0_EMU_CONF_EN_MASK);
-+
-+ mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
-+ dev->wed_rro.addr_elem[0].phy_addr);
-+ } else {
-+ /* TODO: remove line after WM has set */
-+ mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
-+
-+ /* setup BA bitmap cache address */
-+ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
-+ dev->wed_rro.ba_bitmap[0].phy_addr);
-+ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
-+ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
-+ dev->wed_rro.ba_bitmap[1].phy_addr);
-+ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
-+
-+ /* setup Address element address */
-+ for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
-+ mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
-+ reg += 4;
-+ }
-+
-+ /* setup Address element address - separate address segment mode */
-+ mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
-+ MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
-+ }
-+ wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
-+ if (is_mt7996(&dev->mt76))
-+ wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
-+ else
-+ wed->wlan.ind_cmd.particular_sid = 1;
-+ wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
-+ wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
-+ wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
-+
-+ mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
-+ mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
-+ MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
-+
-+ /* particular session configure */
-+ /* use max session idx + 1 as particular session id */
-+ mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
-+
-+ if (is_mt7992(&dev->mt76)) {
-+ reg = MT_RRO_MSDU_PG_SEG_ADDR0;
-+
-+ mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG,
-+ MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN);
-+
-+ /* setup Msdu page address */
-+ for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
-+ mt76_wr(dev, reg, dev->wed_rro.msdu_pg[i].phy_addr >> 4);
-+ reg += 4;
-+ }
-+ mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
-+ MT_RRO_PARTICULAR_CONFG_EN |
-+ FIELD_PREP(MT_RRO_PARTICULAR_SID, 1));
-+ } else {
-+ mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
-+ MT_RRO_PARTICULAR_CONFG_EN |
-+ FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
-+ }
-+ /* interrupt enable */
-+ mt76_wr(dev, MT_RRO_HOST_INT_ENA,
-+ MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
-+#endif
-+}
-+
-+static int mt7996_wed_rro_init(struct mt7996_dev *dev)
-+{
-+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
-+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- struct mt7996_wed_rro_addr *addr;
- void *ptr;
- int i;
-@@ -788,50 +868,9 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
- addr++;
- }
-
-- /* rro hw init */
-- /* TODO: remove line after WM has set */
-- mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
--
-- /* setup BA bitmap cache address */
-- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
-- dev->wed_rro.ba_bitmap[0].phy_addr);
-- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
-- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
-- dev->wed_rro.ba_bitmap[1].phy_addr);
-- mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
--
-- /* setup Address element address */
-- for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
-- mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
-- reg += 4;
-- }
--
-- /* setup Address element address - separate address segment mode */
-- mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
-- MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
--
-- wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
-- wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
-- wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
-- wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
-- wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
--
-- mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
-- mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
-- MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
--
-- /* particular session configure */
-- /* use max session idx + 1 as particular session id */
-- mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
-- mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
-- MT_RRO_PARTICULAR_CONFG_EN |
-- FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
--
-- /* interrupt enable */
-- mt76_wr(dev, MT_RRO_HOST_INT_ENA,
-- MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
--
- /* rro ind cmd queue init */
-+ mt7996_rro_hw_init(dev);
-+
- return mt7996_dma_rro_init(dev);
- #else
- return 0;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 8de4ab9a..48e4ce90 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1768,6 +1768,31 @@ mt7996_mac_restart(struct mt7996_dev *dev)
- if (ret)
- goto out;
-
-+ if (mtk_wed_device_active(&dev->mt76.mmio.wed) && dev->has_rro) {
-+ u32 wed_irq_mask = dev->mt76.mmio.irqmask |
-+ MT_INT_RRO_RX_DONE |
-+ MT_INT_TX_DONE_BAND2;
-+
-+ mt7996_rro_hw_init(dev);
-+ mt76_for_each_q_rx(&dev->mt76, i) {
-+ if (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
-+ mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i]))
-+ mt76_queue_rx_reset(dev, i);
-+ }
-+
-+ mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
-+ mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
-+ mt7996_irq_enable(dev, wed_irq_mask);
-+ mt7996_irq_disable(dev, 0);
-+ }
-+
-+ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) {
-+ mt76_wr(dev, MT_INT_PCIE1_MASK_CSR,
-+ MT_INT_TX_RX_DONE_EXT);
-+ mtk_wed_device_start(&dev->mt76.mmio.wed_hif2,
-+ MT_INT_TX_RX_DONE_EXT);
-+ }
-+
- /* set the necessary init items */
- ret = mt7996_mcu_set_eeprom(dev);
- if (ret)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b1abe42b..84b34ea9 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -713,6 +713,7 @@ extern const struct mt76_testmode_ops mt7996_testmode_ops;
- struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- void __iomem *mem_base, u32 device_id);
- void mt7996_wfsys_reset(struct mt7996_dev *dev);
-+void mt7996_rro_hw_init(struct mt7996_dev *dev);
- irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
- u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
- int mt7996_register_device(struct mt7996_dev *dev);
-diff --git a/wed.c b/wed.c
-index 1c6d53c8..61a6badf 100644
---- a/wed.c
-+++ b/wed.c
-@@ -155,7 +155,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
- case MT76_WED_Q_TXFREE:
- /* WED txfree queue needs ring to be initialized before setup */
- q->flags = 0;
-- mt76_dma_queue_reset(dev, q);
-+ mt76_dma_queue_reset(dev, q, true);
- mt76_dma_rx_fill(dev, q, false);
-
- ret = mtk_wed_device_txfree_ring_setup(q->wed, q->regs);
-@@ -184,7 +184,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
- break;
- case MT76_WED_RRO_Q_IND:
- q->flags &= ~MT_QFLAG_WED;
-- mt76_dma_queue_reset(dev, q);
-+ mt76_dma_queue_reset(dev, q, true);
- mt76_dma_rx_fill(dev, q, false);
- mtk_wed_device_ind_rx_ring_setup(q->wed, q->regs);
- break;
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2013-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2013-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch
deleted file mode 100644
index 7bb9eea..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2013-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch
+++ /dev/null
@@ -1,208 +0,0 @@
-From b4130d60aec743429b0bf9156a79b99776bbdc06 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Fri, 6 Oct 2023 20:59:42 +0800
-Subject: [PATCH 2013/2032] mtk: wifi: mt76: mt7996: support backaward
- compatiable
-
-revert upstream wed trigger mode to polling mode
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
-
-[Description]
-Change the SW token size from 1024 to 15360 according to HW capability.
-
-[Release-log]
-N/A
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt76.h | 2 ++
- mt7996/mac.c | 3 ++-
- mt7996/mcu.c | 2 +-
- mt7996/mmio.c | 12 +++++++-----
- mt7996/mt7996.h | 1 +
- mt7996/pci.c | 17 +++++++++--------
- wed.c | 4 ++--
- 7 files changed, 24 insertions(+), 17 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index 6ac8a279..49b66ff2 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -48,6 +48,8 @@
-
- #define MT76_TOKEN_FREE_THR 64
-
-+#define MT76_WED_SW_TOKEN_SIZE 15360
-+
- #define MT_QFLAG_WED_RING GENMASK(1, 0)
- #define MT_QFLAG_WED_TYPE GENMASK(4, 2)
- #define MT_QFLAG_WED BIT(5)
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 48e4ce90..ed22d94e 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1781,7 +1781,7 @@ mt7996_mac_restart(struct mt7996_dev *dev)
- }
-
- mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
-- mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
-+ mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask, false);
- mt7996_irq_enable(dev, wed_irq_mask);
- mt7996_irq_disable(dev, 0);
- }
-@@ -2013,6 +2013,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
-
- mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
- true);
-+
- mt7996_irq_enable(dev, wed_irq_mask);
- mt7996_irq_disable(dev, 0);
- }
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index a907e667..08979465 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3252,7 +3252,7 @@ static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
-
- if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
- req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
-- cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
-+ cpu_to_le16(MT7996_SW_TOKEN_SIZE);
-
- return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
- &req, sizeof(req), false);
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 92ae5138..eef70faf 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -14,7 +14,7 @@
- #include "../trace.h"
- #include "../dma.h"
-
--static bool wed_enable;
-+static bool wed_enable = true;
- module_param(wed_enable, bool, 0644);
-
- static const struct __base mt7996_reg_base[] = {
-@@ -347,7 +347,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- }
-
- wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
-- wed->wlan.wpdma_rx = wed->wlan.phy_base + hif1_ofs +
-+ wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
- MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
- MT7996_RXQ_BAND0 * MT_RING_SIZE;
-
-@@ -362,7 +362,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
-
- wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG;
-
-- wed->wlan.wpdma_rx = wed->wlan.phy_base +
-+ wed->wlan.wpdma_rx[0] = wed->wlan.phy_base +
- MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
- MT7996_RXQ_BAND0 * MT_RING_SIZE;
-
-@@ -404,8 +404,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
- }
-
-- wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE;
-- wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf;
-+ wed->wlan.nbuf = MT7996_TOKEN_SIZE;
-+ wed->wlan.token_start = 0;
-
- wed->wlan.amsdu_max_subframes = 8;
- wed->wlan.amsdu_max_len = 1536;
-@@ -426,6 +426,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- *irq = wed->irq;
- dev->mt76.dma_dev = wed->dev;
-
-+ dev->mt76.token_size = MT7996_SW_TOKEN_SIZE;
-+
- return 1;
- #else
- return 0;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 84b34ea9..3ba40c3a 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -74,6 +74,7 @@
- #define MT7996_EEPROM_BLOCK_SIZE 16
- #define MT7996_TOKEN_SIZE 16384
- #define MT7996_HW_TOKEN_SIZE 8192
-+#define MT7996_SW_TOKEN_SIZE 15360
-
- #define MT7996_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */
- #define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index 05830c01..4e957771 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -171,7 +171,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
-
- ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
- if (ret < 0)
-- goto free_hif2_wed_irq_vector;
-+ goto free_wed_or_irq_vector;
-
- if (!ret) {
- ret = pci_alloc_irq_vectors(hif2_dev, 1, 1,
-@@ -180,14 +180,15 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- goto free_hif2;
-
- dev->hif2->irq = hif2_dev->irq;
-- hif2_irq = dev->hif2->irq;
-+ } else {
-+ dev->hif2->irq = irq;
- }
-
-- ret = devm_request_irq(mdev->dev, hif2_irq, mt7996_irq_handler,
-- IRQF_SHARED, KBUILD_MODNAME "-hif",
-- dev);
-+ ret = devm_request_irq(mdev->dev, dev->hif2->irq,
-+ mt7996_irq_handler, IRQF_SHARED,
-+ KBUILD_MODNAME "-hif", dev);
- if (ret)
-- goto free_hif2_wed_irq_vector;
-+ goto free_hif2_irq_vector;
-
- mt76_wr(dev, MT_INT1_MASK_CSR, 0);
- /* master switch of PCIe tnterrupt enable */
-@@ -202,8 +203,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
-
- free_hif2_irq:
- if (dev->hif2)
-- devm_free_irq(mdev->dev, hif2_irq, dev);
--free_hif2_wed_irq_vector:
-+ devm_free_irq(mdev->dev, dev->hif2->irq, dev);
-+free_hif2_irq_vector:
- if (dev->hif2) {
- if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
- mtk_wed_device_detach(&dev->mt76.mmio.wed_hif2);
-diff --git a/wed.c b/wed.c
-index 61a6badf..634c95cf 100644
---- a/wed.c
-+++ b/wed.c
-@@ -120,7 +120,7 @@ int mt76_wed_offload_enable(struct mtk_wed_device *wed)
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
-
- spin_lock_bh(&dev->token_lock);
-- dev->token_size = wed->wlan.token_start;
-+ dev->token_size = MT76_WED_SW_TOKEN_SIZE;
- spin_unlock_bh(&dev->token_lock);
-
- return !wait_event_timeout(dev->tx_wait, !dev->wed_token_count, HZ);
-@@ -204,7 +204,7 @@ void mt76_wed_offload_disable(struct mtk_wed_device *wed)
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
-
- spin_lock_bh(&dev->token_lock);
-- dev->token_size = dev->drv->token_size;
-+ dev->token_size = MT76_WED_SW_TOKEN_SIZE;
- spin_unlock_bh(&dev->token_lock);
- }
- EXPORT_SYMBOL_GPL(mt76_wed_offload_disable);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2014-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2014-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch
deleted file mode 100644
index e7a590a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2014-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch
+++ /dev/null
@@ -1,432 +0,0 @@
-From 2fd58d785c6aaf330211787b8fcbc3662a6f69d8 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Fri, 8 Sep 2023 11:57:39 +0800
-Subject: [PATCH 2014/2032] mtk: wifi: mt76: mt7996: wed: add wed support for
- mt7992
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
-
-Fix incomplete WED initialization for Kite band-1 RX ring.
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt7996/dma.c | 91 +++++++++++++++++++++++++++++++++----------------
- mt7996/init.c | 12 +++++++
- mt7996/mac.c | 4 +++
- mt7996/mmio.c | 49 ++++++++++++++++++--------
- mt7996/mt7996.h | 10 +++++-
- mt7996/pci.c | 10 ++++--
- mt7996/regs.h | 14 +++++++-
- 7 files changed, 142 insertions(+), 48 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index d9e1b17f..d62dc8ba 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -77,18 +77,23 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
- MT7996_RXQ_RRO_BAND0);
- RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND0, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND0,
- MT7996_RXQ_MSDU_PG_BAND0);
-- RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
-- MT7996_RXQ_TXFREE0);
-- /* band1 */
-- RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
-- MT7996_RXQ_MSDU_PG_BAND1);
-- /* band2 */
-- RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
-- MT7996_RXQ_RRO_BAND2);
-- RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
-- MT7996_RXQ_MSDU_PG_BAND2);
-- RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
-- MT7996_RXQ_TXFREE2);
-+ if (is_mt7996(&dev->mt76)) {
-+ RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
-+ MT7996_RXQ_TXFREE0);
-+ /* band1 */
-+ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
-+ MT7996_RXQ_MSDU_PG_BAND1);
-+ /* band2 */
-+ RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
-+ MT7996_RXQ_RRO_BAND2);
-+ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
-+ MT7996_RXQ_MSDU_PG_BAND2);
-+ RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
-+ MT7996_RXQ_TXFREE2);
-+ } else {
-+ RXQ_CONFIG(MT_RXQ_RRO_BAND1, WFDMA0, MT_INT_RX_DONE_RRO_BAND1,
-+ MT7996_RXQ_RRO_BAND1);
-+ }
-
- RXQ_CONFIG(MT_RXQ_RRO_IND, WFDMA0, MT_INT_RX_DONE_RRO_IND,
- MT7996_RXQ_RRO_IND);
-@@ -146,8 +151,13 @@ static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
- if (dev->has_rro) {
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs,
- PREFETCH(0x10));
-- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
-- PREFETCH(0x10));
-+ if (is_mt7996(&dev->mt76))
-+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
-+ PREFETCH(0x10));
-+ else
-+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND1) + ofs,
-+ PREFETCH(0x10));
-+
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs,
- PREFETCH(0x4));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
-@@ -361,12 +371,16 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- * so, redirect pcie0 rx ring3 interrupt to pcie1
- */
- if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
-- dev->has_rro)
-+ dev->has_rro) {
-+ u32 intr = is_mt7996(&dev->mt76) ?
-+ MT_WFDMA0_RX_INT_SEL_RING6 :
-+ MT_WFDMA0_RX_INT_SEL_RING9;
- mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
-- MT_WFDMA0_RX_INT_SEL_RING6);
-- else
-+ intr);
-+ } else {
- mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
- MT_WFDMA0_RX_INT_SEL_RING3);
-+ }
- }
-
- mt7996_dma_start(dev, reset, true);
-@@ -401,7 +415,7 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
- if (ret)
- return ret;
-
-- if (mt7996_band_valid(dev, MT_BAND1)) {
-+ if (mt7996_band_valid(dev, MT_BAND1) && is_mt7996(&dev->mt76)) {
- /* rx msdu page queue for band1 */
- mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags =
- MT_WED_RRO_Q_MSDU_PG(1) | MT_QFLAG_WED_RRO_EN;
-@@ -522,7 +536,9 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- return ret;
-
- /* tx free notify event from WA for band0 */
-- if (mtk_wed_device_active(wed) && !dev->has_rro) {
-+ if (mtk_wed_device_active(wed) &&
-+ ((is_mt7996(&dev->mt76) && !dev->has_rro) ||
-+ (is_mt7992(&dev->mt76)))) {
- dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
- dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed;
- }
-@@ -568,6 +584,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- } else if (mt7996_band_valid(dev, MT_BAND1)) {
- /* rx data queue for mt7992 band1 */
- rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1) + hif1_ofs;
-+ if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) {
-+ dev->mt76.q_rx[MT_RXQ_BAND1].flags = MT_WED_Q_RX(1);
-+ dev->mt76.q_rx[MT_RXQ_BAND1].wed = wed;
-+ }
-+
- ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1],
- MT_RXQ_ID(MT_RXQ_BAND1),
- MT7996_RX_RING_SIZE,
-@@ -601,17 +622,29 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- if (ret)
- return ret;
-
-- /* tx free notify event from WA for band0 */
-- dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
-- dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
-+ if (is_mt7992(&dev->mt76)) {
-+ dev->mt76.q_rx[MT_RXQ_RRO_BAND1].flags =
-+ MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
-+ dev->mt76.q_rx[MT_RXQ_RRO_BAND1].wed = wed;
-+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1],
-+ MT_RXQ_ID(MT_RXQ_RRO_BAND1),
-+ MT7996_RX_RING_SIZE,
-+ MT7996_RX_BUF_SIZE,
-+ MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
-+ if (ret)
-+ return ret;
-+ } else {
-+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
-+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
-
-- ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
-- MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
-- MT7996_RX_MCU_RING_SIZE,
-- MT7996_RX_BUF_SIZE,
-- MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
-- if (ret)
-- return ret;
-+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
-+ MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
-+ MT7996_RX_MCU_RING_SIZE,
-+ MT7996_RX_BUF_SIZE,
-+ MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
-+ if (ret)
-+ return ret;
-+ }
-
- if (mt7996_band_valid(dev, MT_BAND2)) {
- /* rx rro data queue for band2 */
-diff --git a/mt7996/init.c b/mt7996/init.c
-index a9720120..7a9b9749 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -802,6 +802,7 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
- /* interrupt enable */
- mt76_wr(dev, MT_RRO_HOST_INT_ENA,
- MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
-+
- #endif
- }
-
-@@ -854,6 +855,17 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
- dev->wed_rro.addr_elem[i].phy_addr;
- }
-
-+ for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
-+ ptr = dmam_alloc_coherent(dev->mt76.dma_dev, MT7996_RRO_MSDU_PG_SIZE_PER_CR,
-+ &dev->wed_rro.msdu_pg[i].phy_addr,
-+ GFP_KERNEL);
-+ if (!ptr)
-+ return -ENOMEM;
-+ dev->wed_rro.msdu_pg[i].ptr = ptr;
-+
-+ memset(dev->wed_rro.msdu_pg[i].ptr, 0, MT7996_RRO_MSDU_PG_SIZE_PER_CR);
-+ }
-+
- ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
- MT7996_RRO_WINDOW_MAX_LEN * sizeof(*addr),
- &dev->wed_rro.session.phy_addr,
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index ed22d94e..48cb2ac0 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2011,6 +2011,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
-
- mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
-
-+ if (is_mt7992(&dev->mt76) && dev->has_rro)
-+ mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
-+ MT_RRO_3_0_EMU_CONF_EN_MASK);
-+
- mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
- true);
-
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index eef70faf..e23c79fc 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -313,7 +313,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
-
- dev->has_rro = true;
-
-- hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-+ if (dev->hif2)
-+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-
- if (hif2)
- wed = &dev->mt76.mmio.wed_hif2;
-@@ -348,8 +349,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
-
- wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
- wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
-- MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
-- MT7996_RXQ_BAND0 * MT_RING_SIZE;
-+ MT_RXQ_RING_BASE(MT7996_RXQ_BAND2) +
-+ MT7996_RXQ_BAND2 * MT_RING_SIZE;
-
- wed->wlan.id = 0x7991;
- wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
-@@ -369,9 +370,19 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base +
- MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) +
- MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE;
-- wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
-- MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
-- MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
-+ if (is_mt7996(&dev->mt76)) {
-+ wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
-+ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
-+ MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
-+ } else {
-+ wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
-+ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
-+ MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
-+ wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
-+ MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
-+ MT7996_RXQ_BAND1 * MT_RING_SIZE;
-+ }
-+
- wed->wlan.wpdma_rx_pg = wed->wlan.phy_base +
- MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) +
- MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE;
-@@ -381,10 +392,14 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
-
- wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1;
-- wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
--
- wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1;
-- wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
-+ if (is_mt7996(&dev->mt76)) {
-+ wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
-+ wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
-+ } else {
-+ wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND1) - 1;
-+ wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND1) - 1;
-+ }
-
- wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1;
- wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1;
-@@ -392,14 +407,20 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
-
- wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1;
- wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1;
-- if (dev->has_rro) {
-- wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
-- MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
-- wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
-+ if (is_mt7996(&dev->mt76)) {
-+ if (dev->has_rro) {
-+ wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
-+ MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
-+ wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
-+ } else {
-+ wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
-+ wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
-+ MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
-+ }
- } else {
- wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
- wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
-- MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
-+ MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
- }
- dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
- }
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 3ba40c3a..d1d35e56 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -122,6 +122,10 @@
- #define MT7996_DRR_STA_AC2_QNTM_MASK GENMASK(18, 16)
- #define MT7996_DRR_STA_AC3_QNTM_MASK GENMASK(22, 20)
-
-+/* RRO 3.1 */
-+#define MT7996_RRO_MSDU_PG_CR_CNT 8
-+#define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
-+
- struct mt7996_vif;
- struct mt7996_sta;
- struct mt7996_dfs_pulse;
-@@ -181,7 +185,7 @@ enum mt7996_rxq_id {
- MT7996_RXQ_BAND1 = 5, /* for mt7992 */
- MT7996_RXQ_BAND2 = 5,
- MT7996_RXQ_RRO_BAND0 = 8,
-- MT7996_RXQ_RRO_BAND1 = 8,/* unused */
-+ MT7996_RXQ_RRO_BAND1 = 9,
- MT7996_RXQ_RRO_BAND2 = 6,
- MT7996_RXQ_MSDU_PG_BAND0 = 10,
- MT7996_RXQ_MSDU_PG_BAND1 = 11,
-@@ -541,6 +545,10 @@ struct mt7996_dev {
- void *ptr;
- dma_addr_t phy_addr;
- } session;
-+ struct {
-+ void *ptr;
-+ dma_addr_t phy_addr;
-+ } msdu_pg[MT7996_RRO_MSDU_PG_CR_CNT];
-
- struct work_struct work;
- struct list_head poll_list;
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index 4e957771..f0d3f199 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -107,7 +107,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- struct pci_dev *hif2_dev;
- struct mt7996_hif *hif2;
- struct mt7996_dev *dev;
-- int irq, hif2_irq, ret;
-+ int irq, ret;
- struct mt76_dev *mdev;
-
- hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
-@@ -143,6 +143,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- mdev = &dev->mt76;
- mt7996_wfsys_reset(dev);
- hif2 = mt7996_pci_init_hif2(pdev);
-+ if (hif2)
-+ dev->hif2 = hif2;
-
- ret = mt7996_mmio_wed_init(dev, pdev, false, &irq);
- if (ret < 0)
-@@ -167,9 +169,11 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
-
- if (hif2) {
- hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
-- dev->hif2 = hif2;
-+ ret = 0;
-+
-+ if (is_mt7996(&dev->mt76))
-+ ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
-
-- ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
- if (ret < 0)
- goto free_wed_or_irq_vector;
-
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 8d1462a7..352d1b29 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -77,6 +77,8 @@ enum offs_rev {
- #define MT_RRO_BA_BITMAP_BASE1 MT_RRO_TOP(0xC)
- #define WF_RRO_AXI_MST_CFG MT_RRO_TOP(0xB8)
- #define WF_RRO_AXI_MST_CFG_DIDX_OK BIT(12)
-+
-+#define MT_RRO_ADDR_ARRAY_BASE0 MT_RRO_TOP(0x30)
- #define MT_RRO_ADDR_ARRAY_BASE1 MT_RRO_TOP(0x34)
- #define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE BIT(31)
-
-@@ -97,6 +99,14 @@ enum offs_rev {
-
- #define MT_RRO_ADDR_ELEM_SEG_ADDR0 MT_RRO_TOP(0x400)
-
-+#define MT_RRO_3_0_EMU_CONF MT_RRO_TOP(0x600)
-+#define MT_RRO_3_0_EMU_CONF_EN_MASK BIT(11)
-+
-+#define MT_RRO_3_1_GLOBAL_CONFIG MT_RRO_TOP(0x604)
-+#define MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN BIT(0)
-+
-+#define MT_RRO_MSDU_PG_SEG_ADDR0 MT_RRO_TOP(0x620)
-+
- #define MT_RRO_ACK_SN_CTRL MT_RRO_TOP(0x50)
- #define MT_RRO_ACK_SN_CTRL_SN_MASK GENMASK(27, 16)
- #define MT_RRO_ACK_SN_CTRL_SESSION_MASK GENMASK(11, 0)
-@@ -402,6 +412,7 @@ enum offs_rev {
- #define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154)
- #define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3)
- #define MT_WFDMA0_RX_INT_SEL_RING6 BIT(6)
-+#define MT_WFDMA0_RX_INT_SEL_RING9 BIT(9)
-
- #define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4)
-
-@@ -503,13 +514,14 @@ enum offs_rev {
- #define MT_INT_RX_DONE_WA_EXT BIT(3) /* for mt7992 */
- #define MT_INT_RX_DONE_WA_TRI BIT(3)
- #define MT_INT_RX_TXFREE_MAIN BIT(17)
-+#define MT_INT_RX_TXFREE_BAND1 BIT(15)
- #define MT_INT_RX_TXFREE_TRI BIT(15)
- #define MT_INT_RX_DONE_BAND2_EXT BIT(23)
- #define MT_INT_RX_TXFREE_EXT BIT(26)
- #define MT_INT_MCU_CMD BIT(29)
-
- #define MT_INT_RX_DONE_RRO_BAND0 BIT(16)
--#define MT_INT_RX_DONE_RRO_BAND1 BIT(16)
-+#define MT_INT_RX_DONE_RRO_BAND1 BIT(17)
- #define MT_INT_RX_DONE_RRO_BAND2 BIT(14)
- #define MT_INT_RX_DONE_RRO_IND BIT(11)
- #define MT_INT_RX_DONE_MSDU_PG_BAND0 BIT(18)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2015-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2015-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch
deleted file mode 100644
index a0a1b01..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2015-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch
+++ /dev/null
@@ -1,177 +0,0 @@
-From 5fa40f1780c96c66b3c7a01ac43c8bdebe8b746e Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Wed, 13 Sep 2023 17:35:43 +0800
-Subject: [PATCH 2015/2032] mtk: wifi: mt76: mt7992: wed: add 2pcie one wed
- support
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- mt7996/dma.c | 13 +++++++++++--
- mt7996/mmio.c | 7 +++----
- mt7996/mtk_debug.h | 5 +++++
- mt7996/mtk_debugfs.c | 25 ++++++++++++++++++-------
- mt7996/regs.h | 2 ++
- 5 files changed, 39 insertions(+), 13 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index d62dc8ba..c23b0d65 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -355,6 +355,13 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- MT_WFDMA_HOST_CONFIG_PDMA_BAND |
- MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
-
-+ if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
-+ is_mt7992(&dev->mt76)) {
-+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+ MT_WFDMA_HOST_CONFIG_PDMA_BAND |
-+ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+ }
-+
- /* AXI read outstanding number */
- mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
- MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
-@@ -374,7 +381,8 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- dev->has_rro) {
- u32 intr = is_mt7996(&dev->mt76) ?
- MT_WFDMA0_RX_INT_SEL_RING6 :
-- MT_WFDMA0_RX_INT_SEL_RING9;
-+ MT_WFDMA0_RX_INT_SEL_RING9 |
-+ MT_WFDMA0_RX_INT_SEL_RING5;
- mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
- intr);
- } else {
-@@ -630,10 +638,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- MT_RXQ_ID(MT_RXQ_RRO_BAND1),
- MT7996_RX_RING_SIZE,
- MT7996_RX_BUF_SIZE,
-- MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
-+ MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + hif1_ofs);
- if (ret)
- return ret;
- } else {
-+ /* tx free notify event from WA for band0 */
- dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
- dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
-
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index e23c79fc..764c1244 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -375,10 +375,10 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
- MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
- } else {
-- wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
-+ wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
- MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
- MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
-- wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
-+ wed->wlan.wpdma_rx[1] = wed->wlan.phy_base + hif1_ofs +
- MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
- MT7996_RXQ_BAND1 * MT_RING_SIZE;
- }
-@@ -516,10 +516,9 @@ void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
- if (mtk_wed_device_active(&mdev->mmio.wed)) {
- mtk_wed_device_irq_set_mask(&mdev->mmio.wed,
- mdev->mmio.irqmask);
-- if (mtk_wed_device_active(&mdev->mmio.wed_hif2)) {
-+ if (mtk_wed_device_active(&mdev->mmio.wed_hif2))
- mtk_wed_device_irq_set_mask(&mdev->mmio.wed_hif2,
- mdev->mmio.irqmask);
-- }
- } else {
- mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
- mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
-diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
-index 27d8f1cb..da2a6072 100644
---- a/mt7996/mtk_debug.h
-+++ b/mt7996/mtk_debug.h
-@@ -561,6 +561,11 @@ struct queue_desc {
- #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
- #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
- #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x590) // 8590
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL1_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x594) // 8594
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL2_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x598) // 8598
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL3_ADDR (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x59c) // 859C
-+
- //MCU DMA
- //#define WF_WFDMA_MCU_DMA0_BASE 0x02000
- #define WF_WFDMA_MCU_DMA0_BASE 0x54000000
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 03f88780..6eea2c3c 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -536,14 +536,22 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
- dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
-- dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+ if (is_mt7996(&dev->mt76))
-+ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+ else
-+ dump_dma_rx_ring_info(s, dev, "R6:TxDone0(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
- dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
- dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
-- dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
-- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+ if (is_mt7996(&dev->mt76))
-+ dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+ else
-+ dump_dma_rx_ring_info(s, dev, "R9:BUF0(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
- dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
- WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
- dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
-@@ -561,15 +569,18 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
- WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
- dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
- WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
--
- dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
- WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
- dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
- WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
-- dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-- WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
-+ if (is_mt7996(&dev->mt76))
-+ dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
- dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
- WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
-+ if (is_mt7992(&dev->mt76))
-+ dump_dma_rx_ring_info(s, dev, "R9:BUF1(MAC2H)", "Both",
-+ WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR);
- }
-
- /* MCU DMA information */
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 352d1b29..a3b62339 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -411,6 +411,7 @@ enum offs_rev {
-
- #define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154)
- #define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3)
-+#define MT_WFDMA0_RX_INT_SEL_RING5 BIT(5)
- #define MT_WFDMA0_RX_INT_SEL_RING6 BIT(6)
- #define MT_WFDMA0_RX_INT_SEL_RING9 BIT(9)
-
-@@ -451,6 +452,7 @@ enum offs_rev {
-
- #define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30)
- #define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0)
-+#define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 BIT(21)
- #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1 BIT(22)
-
- #define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2016-mtk-wifi-mt76-mt7996-add-SER-state-log-for-debug.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2016-mtk-wifi-mt76-mt7996-add-SER-state-log-for-debug.patch
deleted file mode 100644
index 62ae0f3..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2016-mtk-wifi-mt76-mt7996-add-SER-state-log-for-debug.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 720514e0de2e86d79e4423b824734014c6040e1b Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 6 Nov 2023 16:37:23 +0800
-Subject: [PATCH 2016/2032] mtk: wifi: mt76: mt7996: add SER state log for
- debug.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- mt7996/mac.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 48cb2ac0..912ae650 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2172,6 +2172,9 @@ void mt7996_coredump(struct mt7996_dev *dev, u8 state)
-
- void mt7996_reset(struct mt7996_dev *dev)
- {
-+ dev_info(dev->mt76.dev, "%s SER recovery state: 0x%08x\n",
-+ wiphy_name(dev->mt76.hw->wiphy), READ_ONCE(dev->recovery.state));
-+
- if (!dev->recovery.hw_init_done)
- return;
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2017-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2017-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch
deleted file mode 100644
index 5958fe2..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2017-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 253dc25979390b559324187a5a2d1e35552a93c8 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Mon, 6 Nov 2023 10:16:34 +0800
-Subject: [PATCH 2017/2032] mtk: wifi: mt76: mt7996: Remove wed rro ring add
- napi at init state
-
-without this patch. rro ring will add napi at initial state. once rro ring add napi, it will have chance to be used by host driver. if host driver accessed the ring data, it will cause some issue.
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
----
- dma.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/dma.c b/dma.c
-index e23b744b..38701c71 100644
---- a/dma.c
-+++ b/dma.c
-@@ -1017,6 +1017,10 @@ mt76_dma_init(struct mt76_dev *dev,
- init_completion(&dev->mmio.wed_reset_complete);
-
- mt76_for_each_q_rx(dev, i) {
-+ if (mtk_wed_device_active(&dev->mmio.wed) &&
-+ mt76_queue_is_wed_rro(&dev->q_rx[i]))
-+ continue;
-+
- netif_napi_add(&dev->napi_dev, &dev->napi[i], poll);
- mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
- napi_enable(&dev->napi[i]);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2018-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2018-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
deleted file mode 100644
index 3bfbbc4..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2018-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 4ce1116a988fa2b0172ec96797a0a81ff4899c35 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Wed, 29 Nov 2023 13:56:52 +0800
-Subject: [PATCH 2018/2032] mtk: wifi: mt76: mt7996: Remove wed_stop during L1
- SER
-
-Align logan L1 SER flow. During L1 SER, didn't need to close wed interrupt.
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/mac.c | 6 ------
- 1 file changed, 6 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 912ae650..142e9372 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1954,12 +1954,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
- dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
- wiphy_name(dev->mt76.hw->wiphy));
-
-- if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
-- mtk_wed_device_stop(&dev->mt76.mmio.wed_hif2);
--
-- if (mtk_wed_device_active(&dev->mt76.mmio.wed))
-- mtk_wed_device_stop(&dev->mt76.mmio.wed);
--
- ieee80211_stop_queues(mt76_hw(dev));
- if (phy2)
- ieee80211_stop_queues(phy2->mt76->hw);
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2019-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2019-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch
deleted file mode 100644
index 4b021cb..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2019-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-From 302b6abf2ae7eb56c7e437d67b2aa49a7d47d4fa Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Wed, 29 Nov 2023 15:51:04 +0800
-Subject: [PATCH 2019/2032] mtk: wifi: mt76: mt7996: Refactor rro del ba
- command format
-
-1. remove unused struct
-2. refactor upstream del ba command format
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/mcu.h | 50 +++-----------------------------------------------
- 1 file changed, 3 insertions(+), 47 deletions(-)
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 7b8540f6..a05dd6a5 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -273,7 +273,9 @@ struct mt7996_mcu_wed_rro_ba_delete_event {
- __le16 len;
-
- __le16 session_id;
-- u8 __rsv2[2];
-+ __le16 mld_id;
-+ u8 tid;
-+ u8 __rsv[3];
- } __packed;
-
- enum {
-@@ -298,52 +300,6 @@ struct mt7996_mcu_thermal_notify {
- u8 __rsv2[4];
- } __packed;
-
--struct mt7996_mcu_rro_event {
-- struct mt7996_mcu_rxd rxd;
--
-- u8 __rsv1[4];
--
-- __le16 tag;
-- __le16 len;
--} __packed;
--
--struct mt7996_mcu_rro_ba {
-- __le16 tag;
-- __le16 len;
--
-- __le16 wlan_id;
-- u8 tid;
-- u8 __rsv1;
-- __le32 status;
-- __le16 session_id;
-- u8 __rsv2[2];
--} __packed;
--
--struct mt7996_mcu_rro_ba_del_chk_done {
-- __le16 tag;
-- __le16 len;
--
-- __le16 session_id;
-- __le16 mld_id;
-- u8 tid;
-- u8 __rsv[3];
--} __packed;
--
--enum {
-- UNI_RRO_BA_SESSION_STATUS = 0,
-- UNI_RRO_BA_SESSION_TBL = 1,
-- UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
-- UNI_RRO_BA_SESSION_MAX_NUM
--};
--
--struct mt7996_mcu_rro_del_ba {
-- struct mt7996_mcu_rro_event event;
--
-- u8 wlan_idx;
-- u8 tid;
-- u8 __rsv2[2];
--};
--
- enum mt7996_chan_mib_offs {
- UNI_MIB_OBSS_AIRTIME = 26,
- UNI_MIB_NON_WIFI_TIME = 27,
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2020-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2020-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch
deleted file mode 100644
index 6860f6c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2020-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch
+++ /dev/null
@@ -1,784 +0,0 @@
-From da85f2d8c8f3147aab467ba9d65c6457cba0ad1e Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Fri, 17 Nov 2023 18:08:06 +0800
-Subject: [PATCH 2020/2032] mtk: wifi: mt76: mt7996: get airtime and RSSI via
- MCU commands
-
-Direct access to WTBL for airtime and RSSI may cause synchronization issue with FW.
-Moreover, frequent access to WTBL, whenever TX-Free-Done event is received, leads to heavy CPU overheads.
-Therefore, indirect access to WTBL, through FW, with lower frequence is performed.
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt76.h | 20 +++++
- mt76_connac_mcu.h | 14 +++-
- mt7996/debugfs.c | 17 ++---
- mt7996/mac.c | 145 ++++++-----------------------------
- mt7996/mcu.c | 177 +++++++++++++++++++++++++++++++++++++++++--
- mt7996/mcu.h | 32 +++++++-
- mt7996/mt7996.h | 26 ++++++-
- mt7996/mtk_debugfs.c | 71 +++++++++++++++++
- mt7996/regs.h | 2 +
- 9 files changed, 361 insertions(+), 143 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index 49b66ff2..c7816721 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -327,11 +327,15 @@ struct mt76_sta_stats {
- u32 tx_packets; /* unit: MSDU */
- u32 tx_retries;
- u32 tx_failed;
-+ u32 tx_total_mpdu_cnt;
-+ u32 tx_failed_mpdu_cnt;
-+ u64 tx_airtime;
- /* WED RX */
- u64 rx_bytes;
- u32 rx_packets;
- u32 rx_errors;
- u32 rx_drops;
-+ u64 rx_airtime;
- };
-
- enum mt76_wcid_flags {
-@@ -1330,6 +1334,22 @@ static inline int mt76_decr(int val, int size)
-
- u8 mt76_ac_to_hwq(u8 ac);
-
-+static inline u8
-+mt76_ac_to_tid(u8 ac)
-+{
-+ static const u8 ac_to_tid[] = {
-+ [IEEE80211_AC_BE] = 0,
-+ [IEEE80211_AC_BK] = 1,
-+ [IEEE80211_AC_VI] = 4,
-+ [IEEE80211_AC_VO] = 6
-+ };
-+
-+ if (WARN_ON(ac >= IEEE80211_NUM_ACS))
-+ return 0;
-+
-+ return ac_to_tid[ac];
-+}
-+
- static inline struct ieee80211_txq *
- mtxq_to_txq(struct mt76_txq *mtxq)
- {
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 266ee711..4f4b7b4f 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1368,11 +1368,23 @@ enum {
- UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT,
- };
-
-+enum UNI_PER_STA_INFO_TAG {
-+ UNI_PER_STA_RSSI,
-+ UNI_PER_STA_CONTENTION_RX_RATE,
-+ UNI_PER_STA_PER,
-+ UNI_PER_STA_SNR,
-+ UNI_PER_STA_TX_RATE,
-+ UNI_PER_STA_TX_CNT,
-+ UNI_PER_STA_TID_SN_GET,
-+ UNI_PER_STA_TID_SN_SET,
-+ UNI_PER_STA_MAX_NUM
-+};
-+
- enum UNI_ALL_STA_INFO_TAG {
- UNI_ALL_STA_TXRX_RATE,
- UNI_ALL_STA_TX_STAT,
- UNI_ALL_STA_TXRX_ADM_STAT,
-- UNI_ALL_STA_TXRX_AIR_TIME,
-+ UNI_ALL_STA_TXRX_AIRTIME,
- UNI_ALL_STA_DATA_TX_RETRY_COUNT,
- UNI_ALL_STA_GI_MODE,
- UNI_ALL_STA_TXRX_MSDU_COUNT,
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 8e4ceeeb..6ccf0827 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -987,12 +987,11 @@ mt7996_airtime_read(struct seq_file *s, void *data)
- {
- struct mt7996_dev *dev = dev_get_drvdata(s->private);
- struct mt76_dev *mdev = &dev->mt76;
-- struct mt7996_vow_sta_ctrl *vow;
-+ struct mt76_sta_stats *stats;
- struct ieee80211_sta *sta;
- struct mt7996_sta *msta;
- struct mt76_wcid *wcid;
- struct mt76_vif *vif;
-- u64 airtime;
- u16 i;
-
- seq_printf(s, "VoW Airtime Information:\n");
-@@ -1004,16 +1003,16 @@ mt7996_airtime_read(struct seq_file *s, void *data)
-
- msta = container_of(wcid, struct mt7996_sta, wcid);
- sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
-- vow = &msta->vow;
- vif = &msta->vif->mt76;
-+ stats = &wcid->stats;
-
-- spin_lock_bh(&vow->lock);
-- airtime = vow->tx_airtime;
-- vow->tx_airtime = 0;
-- spin_unlock_bh(&vow->lock);
-+ seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
-+ "TxAirtime: %llu\tRxAirtime: %llu\n",
-+ sta->addr, i, vif->band_idx, vif->omac_idx,
-+ stats->tx_airtime, stats->rx_airtime);
-
-- seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
-- sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
-+ stats->tx_airtime = 0;
-+ stats->rx_airtime = 0;
- }
- rcu_read_unlock();
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 142e9372..901c2036 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -12,8 +12,6 @@
- #include "mcu.h"
- #include "vendor.h"
-
--#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
--
- static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
- .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
- .radar_pattern = {
-@@ -93,110 +91,6 @@ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
- return MT_WTBL_LMAC_OFFS(wcid, dw);
- }
-
--static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
--{
-- static const u8 ac_to_tid[] = {
-- [IEEE80211_AC_BE] = 0,
-- [IEEE80211_AC_BK] = 1,
-- [IEEE80211_AC_VI] = 4,
-- [IEEE80211_AC_VO] = 6
-- };
-- struct ieee80211_sta *sta;
-- struct mt7996_sta *msta;
-- struct mt7996_vow_sta_ctrl *vow;
-- u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
-- LIST_HEAD(sta_poll_list);
-- int i;
--
-- spin_lock_bh(&dev->mt76.sta_poll_lock);
-- list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list);
-- spin_unlock_bh(&dev->mt76.sta_poll_lock);
--
-- rcu_read_lock();
--
-- while (true) {
-- bool clear = false;
-- u32 addr, val;
-- u16 idx;
-- s8 rssi[4];
--
-- spin_lock_bh(&dev->mt76.sta_poll_lock);
-- if (list_empty(&sta_poll_list)) {
-- spin_unlock_bh(&dev->mt76.sta_poll_lock);
-- break;
-- }
-- msta = list_first_entry(&sta_poll_list,
-- struct mt7996_sta, wcid.poll_list);
-- list_del_init(&msta->wcid.poll_list);
-- spin_unlock_bh(&dev->mt76.sta_poll_lock);
--
-- idx = msta->wcid.idx;
--
-- /* refresh peer's airtime reporting */
-- addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20);
--
-- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
-- u32 tx_last = msta->airtime_ac[i];
-- u32 rx_last = msta->airtime_ac[i + 4];
--
-- msta->airtime_ac[i] = mt76_rr(dev, addr);
-- msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
--
-- tx_time[i] = msta->airtime_ac[i] - tx_last;
-- rx_time[i] = msta->airtime_ac[i + 4] - rx_last;
--
-- if ((tx_last | rx_last) & BIT(30))
-- clear = true;
--
-- addr += 8;
-- }
--
-- if (clear) {
-- mt7996_mac_wtbl_update(dev, idx,
-- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-- memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
-- }
--
-- if (!msta->wcid.sta)
-- continue;
--
-- sta = container_of((void *)msta, struct ieee80211_sta,
-- drv_priv);
-- vow = &msta->vow;
-- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
-- u8 q = mt76_connac_lmac_mapping(i);
-- u32 tx_cur = tx_time[q];
-- u32 rx_cur = rx_time[q];
-- u8 tid = ac_to_tid[i];
--
-- if (!tx_cur && !rx_cur)
-- continue;
--
-- ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
--
-- spin_lock_bh(&vow->lock);
-- vow->tx_airtime += tx_cur;
-- spin_unlock_bh(&vow->lock);
-- }
--
-- /* get signal strength of resp frames (CTS/BA/ACK) */
-- addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34);
-- val = mt76_rr(dev, addr);
--
-- rssi[0] = to_rssi(GENMASK(7, 0), val);
-- rssi[1] = to_rssi(GENMASK(15, 8), val);
-- rssi[2] = to_rssi(GENMASK(23, 16), val);
-- rssi[3] = to_rssi(GENMASK(31, 14), val);
--
-- msta->ack_signal =
-- mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
--
-- ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
-- }
--
-- rcu_read_unlock();
--}
--
- void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
- struct ieee80211_vif *vif, bool enable)
- {
-@@ -1206,8 +1100,6 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- }
- }
-
-- mt7996_mac_sta_poll(dev);
--
- if (wake)
- mt76_set_tx_blocked(&dev->mt76, false);
-
-@@ -2383,31 +2275,42 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
-
- void mt7996_mac_work(struct work_struct *work)
- {
-- struct mt7996_phy *phy;
-- struct mt76_phy *mphy;
--
-- mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
-- mac_work.work);
-- phy = mphy->priv;
-+ struct mt76_phy *mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
-+ mac_work.work);
-+ struct mt7996_phy *phy = mphy->priv;
-+ struct mt76_dev *mdev = mphy->dev;
-
-- mutex_lock(&mphy->dev->mutex);
-+ mutex_lock(&mdev->mutex);
-
- mt76_update_survey(mphy);
- if (++mphy->mac_work_count == 5) {
-+ int i;
-+
- mphy->mac_work_count = 0;
-
- mt7996_mac_update_stats(phy);
-
-- mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_RATE);
-- if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
-- mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_ADM_STAT);
-- mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_MSDU_COUNT);
-+ /* Update DEV-wise information only in
-+ * the MAC work of the first band running.
-+ */
-+ for (i = MT_BAND0; i <= mphy->band_idx; ++i) {
-+ if (i == mphy->band_idx) {
-+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
-+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
-+ mt7996_mcu_get_rssi(mdev);
-+ if (mtk_wed_device_active(&mdev->mmio.wed)) {
-+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
-+ mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
-+ }
-+ } else if (mt7996_band_valid(phy->dev, i) &&
-+ test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
-+ break;
- }
- }
-
-- mutex_unlock(&mphy->dev->mutex);
-+ mutex_unlock(&mdev->mutex);
-
-- mt76_tx_status_check(mphy->dev, false);
-+ mt76_tx_status_check(mdev, false);
-
- ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
- MT7996_WATCHDOG_TIME);
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 08979465..4907f729 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -560,7 +560,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- u16 wlan_idx;
- struct mt76_wcid *wcid;
- struct mt76_phy *mphy;
-- u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
-+ struct ieee80211_sta *sta;
-+ u32 tx_bytes, rx_bytes, tx_airtime, rx_airtime, tx_packets, rx_packets;
-
- switch (le16_to_cpu(res->tag)) {
- case UNI_ALL_STA_TXRX_RATE:
-@@ -581,7 +582,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- break;
-
- mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
-- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-+ for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ac++) {
- tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
- rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
-
-@@ -613,6 +614,24 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- __mt7996_stat_to_netdev(mphy, wcid, 0, 0,
- tx_packets, rx_packets);
- break;
-+ case UNI_ALL_STA_TXRX_AIRTIME:
-+ wlan_idx = le16_to_cpu(res->airtime[i].wlan_idx);
-+ wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
-+ sta = wcid_to_sta(wcid);
-+ if (!sta)
-+ continue;
-+
-+ for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ++ac) {
-+ u8 lmac_ac = mt76_connac_lmac_mapping(ac);
-+ tx_airtime = le32_to_cpu(res->airtime[i].tx[lmac_ac]);
-+ rx_airtime = le32_to_cpu(res->airtime[i].rx[lmac_ac]);
-+
-+ wcid->stats.tx_airtime += tx_airtime;
-+ wcid->stats.rx_airtime += rx_airtime;
-+ ieee80211_sta_register_airtime(sta, mt76_ac_to_tid(ac),
-+ tx_airtime, rx_airtime);
-+ }
-+ break;
- default:
- break;
- }
-@@ -2242,8 +2261,6 @@ mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
- vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
- vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
- vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
-- vow->tx_airtime = 0;
-- spin_lock_init(&vow->lock);
-
- ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
- if (ret)
-@@ -4849,9 +4866,155 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val)
- sizeof(req), true);
- }
-
--int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
-+int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
-+ u16 sta_num, u16 *sta_list)
-+{
-+#define PER_STA_INFO_MAX_NUM 90
-+ struct mt7996_mcu_per_sta_info_event *res;
-+ struct mt76_wcid *wcid;
-+ struct sk_buff *skb;
-+ u16 wlan_idx;
-+ int i, ret;
-+ struct {
-+ u8 __rsv1;
-+ u8 unsolicit;
-+ u8 __rsv2[2];
-+
-+ __le16 tag;
-+ __le16 len;
-+ __le16 sta_num;
-+ u8 __rsv3[2];
-+ __le16 sta_list[PER_STA_INFO_MAX_NUM];
-+ } __packed req = {
-+ .unsolicit = 0,
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .sta_num = cpu_to_le16(sta_num)
-+ };
-+
-+ if (sta_num > PER_STA_INFO_MAX_NUM)
-+ return -EINVAL;
-+
-+ for (i = 0; i < sta_num; ++i)
-+ req.sta_list[i] = cpu_to_le16(sta_list[i]);
-+
-+ ret = mt76_mcu_send_and_get_msg(dev, MCU_WM_UNI_CMD(PER_STA_INFO),
-+ &req, sizeof(req), true, &skb);
-+ if (ret)
-+ return ret;
-+
-+ res = (struct mt7996_mcu_per_sta_info_event *)skb->data;
-+ if (le16_to_cpu(res->tag) != tag) {
-+ ret = -EINVAL;
-+ goto out;
-+ }
-+
-+ rcu_read_lock();
-+ switch (tag) {
-+ case UNI_PER_STA_RSSI:
-+ for (i = 0; i < sta_num; ++i) {
-+ struct mt7996_sta *msta;
-+ struct mt76_phy *phy;
-+ s8 rssi[4];
-+ u8 *rcpi;
-+
-+ wlan_idx = le16_to_cpu(res->rssi[i].wlan_idx);
-+ wcid = rcu_dereference(dev->wcid[wlan_idx]);
-+ if (wcid) {
-+ rcpi = res->rssi[i].rcpi;
-+ rssi[0] = to_rssi(MT_PRXV_RCPI0, rcpi[0]);
-+ rssi[1] = to_rssi(MT_PRXV_RCPI0, rcpi[1]);
-+ rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
-+ rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
-+
-+ msta = container_of(wcid, struct mt7996_sta, wcid);
-+ phy = msta->vif->phy->mt76;
-+ msta->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
-+ ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
-+ } else {
-+ ret = -EINVAL;
-+ dev_err(dev->dev, "Failed to update RSSI for "
-+ "invalid WCID: %hu\n", wlan_idx);
-+ }
-+ }
-+ break;
-+ case UNI_PER_STA_TX_CNT:
-+ for (i = 0; i < sta_num; ++i) {
-+ wlan_idx = le16_to_cpu(res->tx_cnt[i].wlan_idx);
-+ wcid = rcu_dereference(dev->wcid[wlan_idx]);
-+ if (wcid) {
-+ wcid->stats.tx_total_mpdu_cnt +=
-+ le32_to_cpu(res->tx_cnt[i].total);
-+ wcid->stats.tx_failed_mpdu_cnt +=
-+ le32_to_cpu(res->tx_cnt[i].failed);
-+ } else {
-+ ret = -EINVAL;
-+ dev_err(dev->dev, "Failed to update TX MPDU counts "
-+ "for invalid WCID: %hu\n", wlan_idx);
-+ }
-+ }
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ dev_err(dev->dev, "Unknown UNI_PER_STA_INFO_TAG: %d\n", tag);
-+ }
-+ rcu_read_unlock();
-+out:
-+ dev_kfree_skb(skb);
-+ return ret;
-+}
-+
-+int mt7996_mcu_get_rssi(struct mt76_dev *dev)
-+{
-+ u16 sta_list[PER_STA_INFO_MAX_NUM];
-+ LIST_HEAD(sta_poll_list);
-+ struct mt7996_sta *msta;
-+ int i, ret;
-+ bool empty = false;
-+
-+ spin_lock_bh(&dev->sta_poll_lock);
-+ list_splice_init(&dev->sta_poll_list, &sta_poll_list);
-+ spin_unlock_bh(&dev->sta_poll_lock);
-+
-+ while (!empty) {
-+ for (i = 0; i < PER_STA_INFO_MAX_NUM; ++i) {
-+ spin_lock_bh(&dev->sta_poll_lock);
-+ if (list_empty(&sta_poll_list)) {
-+ spin_unlock_bh(&dev->sta_poll_lock);
-+
-+ if (i == 0)
-+ return 0;
-+
-+ empty = true;
-+ break;
-+ }
-+ msta = list_first_entry(&sta_poll_list,
-+ struct mt7996_sta,
-+ wcid.poll_list);
-+ list_del_init(&msta->wcid.poll_list);
-+ spin_unlock_bh(&dev->sta_poll_lock);
-+
-+ sta_list[i] = msta->wcid.idx;
-+ }
-+
-+ ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
-+ i, sta_list);
-+ if (ret) {
-+ /* Add STAs, whose RSSI has not been updated,
-+ * back to polling list.
-+ */
-+ spin_lock_bh(&dev->sta_poll_lock);
-+ list_splice(&sta_poll_list, &dev->sta_poll_list);
-+ spin_unlock_bh(&dev->sta_poll_lock);
-+ break;
-+ }
-+ }
-+
-+ return ret;
-+}
-+
-+int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag)
- {
-- struct mt7996_dev *dev = phy->dev;
- struct {
- u8 _rsv[4];
-
-@@ -4862,7 +5025,7 @@ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
- .len = cpu_to_le16(sizeof(req) - 4),
- };
-
-- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO),
-+ return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(ALL_STA_INFO),
- &req, sizeof(req), false);
- }
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index a05dd6a5..cd327451 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -199,6 +199,31 @@ struct mt7996_mcu_mib {
- __le64 data;
- } __packed;
-
-+struct per_sta_rssi {
-+ __le16 wlan_idx;
-+ u8 __rsv[2];
-+ u8 rcpi[4];
-+} __packed;
-+
-+struct per_sta_tx_cnt {
-+ __le16 wlan_idx;
-+ u8 __rsv[2];
-+ __le32 total;
-+ __le32 failed;
-+} __packed;
-+
-+struct mt7996_mcu_per_sta_info_event {
-+ u8 __rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ union {
-+ struct per_sta_rssi rssi[0];
-+ struct per_sta_tx_cnt tx_cnt[0];
-+ };
-+} __packed;
-+
- struct all_sta_trx_rate {
- __le16 wlan_idx;
- u8 __rsv1[2];
-@@ -237,13 +262,18 @@ struct mt7996_mcu_all_sta_info_event {
- __le32 tx_bytes[IEEE80211_NUM_ACS];
- __le32 rx_bytes[IEEE80211_NUM_ACS];
- } adm_stat[0] __packed;
--
- struct {
- __le16 wlan_idx;
- u8 rsv[2];
- __le32 tx_msdu_cnt;
- __le32 rx_msdu_cnt;
- } msdu_cnt[0] __packed;
-+ struct {
-+ __le16 wlan_idx;
-+ u8 __rsv[2];
-+ __le32 tx[IEEE80211_NUM_ACS];
-+ __le32 rx[IEEE80211_NUM_ACS];
-+ } airtime[0] __packed;
- } __packed;
- } __packed;
-
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index d1d35e56..c5b83648 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -126,6 +126,8 @@
- #define MT7996_RRO_MSDU_PG_CR_CNT 8
- #define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
-
-+#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
-+
- struct mt7996_vif;
- struct mt7996_sta;
- struct mt7996_dfs_pulse;
-@@ -298,8 +300,6 @@ struct mt7996_vow_sta_ctrl {
- bool paused;
- u8 bss_grp_idx;
- u8 drr_quantum[IEEE80211_NUM_ACS];
-- u64 tx_airtime;
-- spinlock_t lock;
- };
-
- struct mt7996_sta {
-@@ -308,7 +308,6 @@ struct mt7996_sta {
- struct mt7996_vif *vif;
-
- struct list_head rc_list;
-- u32 airtime_ac[8];
-
- int ack_signal;
- struct ewma_avg_signal avg_ack_signal;
-@@ -405,6 +404,21 @@ struct mt7996_air_monitor_ctrl {
- };
- #endif
-
-+struct mt7996_rro_ba_session {
-+ u32 ack_sn :12;
-+ u32 win_sz :3;
-+ u32 bn :1;
-+ u32 last_in_sn :12;
-+ u32 bc :1;
-+ u32 bd :1;
-+ u32 sat :1;
-+ u32 cn :1;
-+ u32 within_cnt :12;
-+ u32 to_sel :3;
-+ u32 rsv :1;
-+ u32 last_in_rxtime :12;
-+};
-+
- struct mt7996_phy {
- struct mt76_phy *mt76;
- struct mt7996_dev *dev;
-@@ -594,6 +608,7 @@ struct mt7996_dev {
- u32 fw_dbg_module;
- u8 fw_dbg_lv;
- u32 bcn_total_cnt[__MT_MAX_BAND];
-+ u32 sid;
- } dbg;
- const struct mt7996_dbg_reg_desc *dbg_reg;
- #endif
-@@ -819,7 +834,10 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
- int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
- void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
- void mt7996_mcu_exit(struct mt7996_dev *dev);
--int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
-+int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
-+ u16 sta_num, u16 *sta_list);
-+int mt7996_mcu_get_rssi(struct mt76_dev *dev);
-+int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
- int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
- int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 6eea2c3c..916c7c06 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3047,6 +3047,69 @@ mt7996_vow_drr_dbg(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
- mt7996_vow_drr_dbg, "%lld\n");
-
-+static int
-+mt7996_rro_session_read(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ struct mt7996_rro_ba_session *tbl;
-+ u32 value[2];
-+
-+ mt76_wr(dev, MT_RRO_DBG_RD_CTRL, MT_RRO_DBG_RD_EXEC +
-+ (dev->dbg.sid >> 1) + 0x200);
-+
-+ if (dev->dbg.sid & 0x1) {
-+ value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
-+ value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(3));
-+ } else {
-+ value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(0));
-+ value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(1));
-+ }
-+
-+ tbl = (struct mt7996_rro_ba_session *)&value[0];
-+
-+ seq_printf(s, " seid %d:\nba session table DW0:%08x DW2:%08x\n",
-+ dev->dbg.sid, value[0], value[1]);
-+
-+ seq_printf(s, "ack_sn = 0x%x, last_in_sn = 0x%x, sat/bn/bc/bd/cn = %d/%d/%d/%d/%d\n",
-+ tbl->ack_sn, tbl->last_in_sn, tbl->sat, tbl->bn, tbl->bc, tbl->bd, tbl->cn);
-+
-+ seq_printf(s, "within_cnt = %d, to_sel = %d, last_in_rxtime = %d\n",
-+ tbl->within_cnt, tbl->to_sel, tbl->last_in_rxtime);
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_show_rro_mib(struct seq_file *s, void *data)
-+{
-+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+ u32 reg[12];
-+
-+ seq_printf(s, "RRO mib Info:\n");
-+
-+ reg[0] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(0));
-+ reg[1] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(1));
-+ reg[2] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(2));
-+ reg[3] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(3));
-+ reg[4] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(4));
-+ reg[5] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(5));
-+ reg[6] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(6));
-+ reg[7] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(7));
-+ reg[8] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(8));
-+ reg[9] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(9));
-+ reg[10] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(10));
-+ reg[11] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(11));
-+
-+ seq_printf(s, "STEP_ONE/WITHIN/SURPASS = %x/%x/%x\n", reg[0], reg[3], reg[4]);
-+ seq_printf(s, "REPEAT/OLDPKT/BAR = %x/%x/%x\n", reg[1], reg[2], reg[5]);
-+ seq_printf(s, "SURPASS with big gap = %x\n", reg[6]);
-+ seq_printf(s, "DISCONNECT/INVALID = %x/%x\n", reg[7], reg[8]);
-+ seq_printf(s, "TO(Step one)/TO(flush all) = %x/%x\n", reg[9], reg[10]);
-+ seq_printf(s, "buf ran out = %x\n", reg[11]);
-+
-+ return 0;
-+}
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -3146,6 +3209,14 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
-
- debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
-
-+ if (dev->has_rro) {
-+ debugfs_create_u32("rro_sid", 0600, dir, &dev->dbg.sid);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "rro_sid_info", dir,
-+ mt7996_rro_session_read);
-+ debugfs_create_devm_seqfile(dev->mt76.dev, "rro_mib", dir,
-+ mt7996_show_rro_mib);
-+ }
-+
- return 0;
- }
-
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index a3b62339..476b23c3 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -122,6 +122,8 @@ enum offs_rev {
- #define MT_MCU_INT_EVENT_DMA_INIT BIT(1)
- #define MT_MCU_INT_EVENT_RESET_DONE BIT(3)
-
-+#define WF_RRO_TOP_STATISTIC(_n) MT_RRO_TOP(0x180 + _n * 0x4)
-+
- /* PLE */
- #define MT_PLE_BASE 0x820c0000
- #define MT_PLE(ofs) (MT_PLE_BASE + (ofs))
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2021-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2021-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch
deleted file mode 100644
index 6366876..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2021-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch
+++ /dev/null
@@ -1,218 +0,0 @@
-From 8871427856374447f4275cde02936e73e452c080 Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Thu, 4 Jan 2024 09:47:00 +0800
-Subject: [PATCH 2021/2032] mtk: wifi: mt76: mt7996: add support for WMM PBC
- configuration
-
-Query per-AC-queue packet statistics from WA, and determine if multi-AC transmission is ongoing.
-If it is, enable WMM mode in WA. Otherwise, disable WMM mode.
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt76_connac_mcu.h | 2 ++
- mt7996/init.c | 2 ++
- mt7996/mac.c | 4 +++
- mt7996/mcu.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h | 15 +++++++++
- mt7996/mt7996.h | 4 +++
- 6 files changed, 105 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 4f4b7b4f..b2b8f2a2 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1026,6 +1026,7 @@ enum {
- MCU_EXT_EVENT_ASSERT_DUMP = 0x23,
- MCU_EXT_EVENT_RDD_REPORT = 0x3a,
- MCU_EXT_EVENT_CSA_NOTIFY = 0x4f,
-+ MCU_EXT_EVENT_BSS_ACQ_PKT_CNT = 0x52,
- MCU_EXT_EVENT_WA_TX_STAT = 0x74,
- MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
- MCU_EXT_EVENT_MURU_CTRL = 0x9f,
-@@ -1224,6 +1225,7 @@ enum {
- MCU_EXT_CMD_TXDPD_CAL = 0x60,
- MCU_EXT_CMD_CAL_CACHE = 0x67,
- MCU_EXT_CMD_RED_ENABLE = 0x68,
-+ MCU_EXT_CMD_PKT_BUDGET_CTRL = 0x6c,
- MCU_EXT_CMD_SET_RADAR_TH = 0x7c,
- MCU_EXT_CMD_SET_RDD_PATTERN = 0x7d,
- MCU_EXT_CMD_MWDS_SUPPORT = 0x80,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 7a9b9749..90f3a417 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -1493,6 +1493,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
- INIT_WORK(&dev->dump_work, mt7996_mac_dump_work);
- mutex_init(&dev->dump_mutex);
-
-+ INIT_WORK(&dev->wmm_pbc_work, mt7996_mcu_wmm_pbc_work);
-+
- ret = mt7996_init_hardware(dev);
- if (ret)
- return ret;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 901c2036..88e1fd14 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2302,6 +2302,10 @@ void mt7996_mac_work(struct work_struct *work)
- mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
- mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
- }
-+
-+ if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
-+ BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
-+ dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
- } else if (mt7996_band_valid(phy->dev, i) &&
- test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
- break;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 4907f729..6405c2fa 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -666,6 +666,82 @@ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
- phy->throttle_state = n->duty_percent;
- }
-
-+void mt7996_mcu_wmm_pbc_work(struct work_struct *work)
-+{
-+#define WMM_PBC_QUEUE_NUM 5
-+#define WMM_PBC_BSS_ALL 0xff
-+#define WMM_PBC_WLAN_IDX_ALL 0xffff
-+#define WMM_PBC_BOUND_DEFAULT 0xffff
-+#define WMM_PBC_LOW_BOUND_VO 1900
-+#define WMM_PBC_LOW_BOUND_VI 1900
-+#define WMM_PBC_LOW_BOUND_BE 1500
-+#define WMM_PBC_LOW_BOUND_BK 900
-+#define WMM_PBC_LOW_BOUND_MGMT 32
-+ struct mt7996_dev *dev = container_of(work, struct mt7996_dev, wmm_pbc_work);
-+ struct {
-+ u8 bss_idx;
-+ u8 queue_num;
-+ __le16 wlan_idx;
-+ u8 band_idx;
-+ u8 __rsv[3];
-+ struct {
-+ __le16 low;
-+ __le16 up;
-+ } __packed bound[WMM_PBC_QUEUE_NUM];
-+ } __packed req = {
-+ .bss_idx = WMM_PBC_BSS_ALL,
-+ .queue_num = WMM_PBC_QUEUE_NUM,
-+ .wlan_idx = cpu_to_le16(WMM_PBC_WLAN_IDX_ALL),
-+ .band_idx = dev->mphy.band_idx,
-+ };
-+ int i, ret;
-+
-+#define pbc_acq_low_bound_config(_ac, _bound) \
-+ req.bound[mt76_connac_lmac_mapping(_ac)].low = dev->wmm_pbc_enable ? cpu_to_le16(_bound) : 0
-+ pbc_acq_low_bound_config(IEEE80211_AC_VO, WMM_PBC_LOW_BOUND_VO);
-+ pbc_acq_low_bound_config(IEEE80211_AC_VI, WMM_PBC_LOW_BOUND_VI);
-+ pbc_acq_low_bound_config(IEEE80211_AC_BE, WMM_PBC_LOW_BOUND_BE);
-+ pbc_acq_low_bound_config(IEEE80211_AC_BK, WMM_PBC_LOW_BOUND_BK);
-+ req.bound[4].low = dev->wmm_pbc_enable
-+ ? cpu_to_le16(WMM_PBC_LOW_BOUND_MGMT) : 0;
-+
-+ for (i = 0; i < WMM_PBC_QUEUE_NUM; ++i)
-+ req.bound[i].up = cpu_to_le16(WMM_PBC_BOUND_DEFAULT);
-+
-+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(PKT_BUDGET_CTRL),
-+ &req, sizeof(req), true);
-+ if (ret)
-+ dev_err(dev->mt76.dev, "Failed to configure WMM PBC.\n");
-+}
-+
-+static void
-+mt7996_mcu_rx_bss_acq_pkt_cnt(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+ struct mt7996_mcu_bss_acq_pkt_cnt_event *event = (struct mt7996_mcu_bss_acq_pkt_cnt_event *)skb->data;
-+ u32 bitmap = le32_to_cpu(event->bss_bitmap);
-+ u64 sum[IEEE80211_NUM_ACS] = {0};
-+ u8 ac_cnt = 0;
-+ int i, j;
-+
-+ for (i = 0; (i < BSS_ACQ_PKT_CNT_BSS_NUM) && (bitmap & (1 << i)); ++i) {
-+ for (j = IEEE80211_AC_VO; j < IEEE80211_NUM_ACS; ++j)
-+ sum[j] += le32_to_cpu(event->bss[i].cnt[mt76_connac_lmac_mapping(j)]);
-+ }
-+
-+ for (i = IEEE80211_AC_VO; i < IEEE80211_NUM_ACS; ++i) {
-+ if (sum[i] > WMM_PKT_THRESHOLD)
-+ ++ac_cnt;
-+ }
-+
-+ if (ac_cnt > 1 && !dev->wmm_pbc_enable) {
-+ dev->wmm_pbc_enable = true;
-+ queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
-+ } else if (ac_cnt <= 1 && dev->wmm_pbc_enable) {
-+ dev->wmm_pbc_enable = false;
-+ queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
-+ }
-+}
-+
- static void
- mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
- {
-@@ -675,6 +751,8 @@ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
- case MCU_EXT_EVENT_FW_LOG_2_HOST:
- mt7996_mcu_rx_log_message(dev, skb);
- break;
-+ case MCU_EXT_EVENT_BSS_ACQ_PKT_CNT:
-+ mt7996_mcu_rx_bss_acq_pkt_cnt(dev, skb);
- default:
- break;
- }
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index cd327451..9dc7946b 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -375,10 +375,25 @@ enum {
- MCU_WA_PARAM_CMD_DEBUG,
- };
-
-+#define BSS_ACQ_PKT_CNT_BSS_NUM 24
-+#define BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL 0x00ffffff
-+#define BSS_ACQ_PKT_CNT_READ_CLR BIT(31)
-+#define WMM_PKT_THRESHOLD 100
-+
-+struct mt7996_mcu_bss_acq_pkt_cnt_event {
-+ struct mt7996_mcu_rxd rxd;
-+
-+ __le32 bss_bitmap;
-+ struct {
-+ __le32 cnt[IEEE80211_NUM_ACS];
-+ } __packed bss[BSS_ACQ_PKT_CNT_BSS_NUM];
-+} __packed;
-+
- enum {
- MCU_WA_PARAM_PDMA_RX = 0x04,
- MCU_WA_PARAM_CPU_UTIL = 0x0b,
- MCU_WA_PARAM_RED_EN = 0x0e,
-+ MCU_WA_PARAM_BSS_ACQ_PKT_CNT = 0x12,
- MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
- MCU_WA_PARAM_RED_CONFIG = 0x40,
- };
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index c5b83648..6ea024ef 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -601,6 +601,9 @@ struct mt7996_dev {
- u8 wtbl_size_group;
-
- struct mt7996_vow_ctrl vow;
-+
-+ bool wmm_pbc_enable;
-+ struct work_struct wmm_pbc_work;
- #ifdef CONFIG_MTK_DEBUG
- u16 wlan_idx;
- struct {
-@@ -852,6 +855,7 @@ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
- int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
- enum vow_drr_ctrl_id id);
- int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
-+void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
-
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2022-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2022-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch
deleted file mode 100644
index 8cf8f0f..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2022-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch
+++ /dev/null
@@ -1,325 +0,0 @@
-From f513775361c7aae98ca2f816edb62b1744350325 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Thu, 1 Feb 2024 10:32:42 +0800
-Subject: [PATCH 2022/2032] mtk: wifi: mt76: mt7996: eagle support extra
- option_type
-
-1. eagle + mt7988d option_type 2 support
-2. eagle single pcie support
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
-
-1. adjust pcie outstanding value by pcie speed. not no longer by option_type.
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/dma.c | 51 +++++++++++++++++++++++++++++++++----
- mt7996/init.c | 67 ++++++++++++++++++++++++++++++++++++++-----------
- mt7996/main.c | 15 +++++++++--
- mt7996/mt7996.h | 5 ++++
- mt7996/pci.c | 2 +-
- mt7996/regs.h | 5 ++++
- 6 files changed, 123 insertions(+), 22 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index c23b0d65..3dc0e8a1 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -12,12 +12,20 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc,
- {
- struct mt7996_dev *dev = phy->dev;
- u32 flags = 0;
-+ int i;
-+
-+ if (phy->mt76->band_idx == MT_BAND1 && !dev->hif2 && is_mt7996(&dev->mt76)) {
-+ phy->mt76->q_tx[0] = phy->mt76->dev->phys[MT_BAND0]->q_tx[0];
-+ for (i = 1; i <= MT_TXQ_PSD; i++)
-+ phy->mt76->q_tx[i] = phy->mt76->q_tx[0];
-+ return 0;
-+ }
-
- if (mtk_wed_device_active(wed)) {
- ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
- idx -= MT_TXQ_ID(0);
-
-- if (phy->mt76->band_idx == MT_BAND2)
-+ if (wed == &dev->mt76.mmio.wed_hif2)
- flags = MT_WED_Q_TX(0);
- else
- flags = MT_WED_Q_TX(idx);
-@@ -102,8 +110,20 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
- /* data tx queue */
- TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
- if (is_mt7996(&dev->mt76)) {
-- TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-- TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
-+ if (dev->hif2) {
-+ if (dev->option_type == 2) {
-+ /* bn1:ring21 bn2:ring19 */
-+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
-+ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+ } else {
-+ /* default bn1:ring19 bn2:ring21 */
-+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
-+ }
-+ } else {
-+ /* single pcie bn0/1:ring18 bn2:ring19 */
-+ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+ }
- } else {
- TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
- }
-@@ -352,8 +372,20 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE);
-
- mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-- MT_WFDMA_HOST_CONFIG_PDMA_BAND |
-- MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
-+ MT_WFDMA_HOST_CONFIG_PDMA_BAND);
-+
-+ mt76_clear(dev, MT_WFDMA_HOST_CONFIG,
-+ MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
-+ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
-+ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
-+
-+ if (dev->option_type == 2)
-+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+ MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
-+ MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+ else
-+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
-
- if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
- is_mt7992(&dev->mt76)) {
-@@ -366,6 +398,15 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
- MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
-
-+ if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
-+ (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
-+ mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
-+ WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
-+ FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x3));
-+ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
-+ MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
-+ FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, 0x3));
-+ }
- /* WFDMA rx threshold */
- mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_45_TH + hif1_ofs, 0xc000c);
- mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_67_TH + hif1_ofs, 0x10008);
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 90f3a417..85fedca6 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -500,7 +500,7 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
- void mt7996_mac_init(struct mt7996_dev *dev)
- {
- #define HIF_TXD_V2_1 0x21
-- int i;
-+ int i, rx_path_type, rro_bypass, txfree_path;
-
- mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
-
-@@ -514,22 +514,45 @@ void mt7996_mac_init(struct mt7996_dev *dev)
- }
-
- /* rro module init */
-- if (is_mt7996(&dev->mt76))
-- mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
-- else
-- mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE,
-- dev->hif2 ? 7 : 0);
-+ switch (dev->option_type) {
-+ case 2:
-+ /* eagle + 7988d */
-+ rx_path_type = 3;
-+ rro_bypass = dev->has_rro ? 1 : 3;
-+ txfree_path = dev->has_rro ? 0 : 1;
-+ break;
-+ case 3:
-+ /* eagle + Airoha */
-+ rx_path_type = 6;
-+ rro_bypass = dev->has_rro ? 1 : 3;
-+ txfree_path = dev->has_rro ? 0 : 1;
-+ break;
-+ case 4:
-+ /* Bollinger */
-+ rx_path_type = 2;
-+ rro_bypass = dev->has_rro ? 1 : 3;
-+ txfree_path = dev->has_rro ? 0 : 1;
-+ break;
-+ default:
-+ if (is_mt7996(&dev->mt76))
-+ rx_path_type = 2;
-+ else
-+ rx_path_type = 7;
-+
-+ rro_bypass = dev->has_rro ? 1 : 3;
-+ txfree_path = dev->has_rro ? 0 : 1;
-+ break;
-+ }
-+
-+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, dev->hif2 ? rx_path_type : 0);
-+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, rro_bypass);
-+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, txfree_path);
-
- if (dev->has_rro) {
- u16 timeout;
-
- timeout = mt76_rr(dev, MT_HW_REV) == MT_HW_REV1 ? 512 : 128;
- mt7996_mcu_set_rro(dev, UNI_RRO_SET_FLUSH_TIMEOUT, timeout);
-- mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 1);
-- mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 0);
-- } else {
-- mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
-- mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
- }
-
- mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
-@@ -607,9 +630,22 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- if (phy)
- return 0;
-
-- if (is_mt7996(&dev->mt76) && band == MT_BAND2 && dev->hif2) {
-- hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-- wed = &dev->mt76.mmio.wed_hif2;
-+ if (is_mt7996(&dev->mt76) && dev->hif2) {
-+ switch (dev->option_type) {
-+ case 2:
-+ /* eagle + 7988d */
-+ if (band == MT_BAND1) {
-+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-+ wed = &dev->mt76.mmio.wed_hif2;
-+ }
-+ break;
-+ default:
-+ if (band == MT_BAND2) {
-+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-+ wed = &dev->mt76.mmio.wed_hif2;
-+ }
-+ break;
-+ }
- }
-
- mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band);
-@@ -1048,6 +1084,9 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
- static int mt7996_init_hardware(struct mt7996_dev *dev)
- {
- int ret, idx;
-+ struct device_node *np = dev->mt76.dev->of_node;
-+
-+ of_property_read_u32(np, "option_type", &dev->option_type);
-
- mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
- if (is_mt7992(&dev->mt76)) {
-diff --git a/mt7996/main.c b/mt7996/main.c
-index e1c107fb..fd6fd78b 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1583,8 +1583,19 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- struct mt7996_phy *phy = mt7996_hw_phy(hw);
- struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
-
-- if (phy != &dev->phy && phy->mt76->band_idx == MT_BAND2)
-- wed = &dev->mt76.mmio.wed_hif2;
-+ if (phy != &dev->phy && dev->hif2) {
-+ switch (dev->option_type) {
-+ case 2:
-+ /* eagle + 7988d */
-+ if (phy->mt76->band_idx == MT_BAND1)
-+ wed = &dev->mt76.mmio.wed_hif2;
-+ break;
-+ default:
-+ if (phy->mt76->band_idx == MT_BAND2)
-+ wed = &dev->mt76.mmio.wed_hif2;
-+ break;
-+ }
-+ }
-
- if (!mtk_wed_device_active(wed))
- return -ENODEV;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 6ea024ef..2ca6e55d 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -8,6 +8,7 @@
-
- #include <linux/interrupt.h>
- #include <linux/ktime.h>
-+#include <linux/pci.h>
- #include "../mt76_connac.h"
- #include "regs.h"
-
-@@ -350,6 +351,8 @@ struct mt7996_hif {
- struct device *dev;
- void __iomem *regs;
- int irq;
-+ enum pci_bus_speed speed;
-+ enum pcie_link_width width;
- };
-
- struct mt7996_scs_ctrl {
-@@ -574,6 +577,8 @@ struct mt7996_dev {
- u8 eeprom_mode;
- u32 bg_nxt_freq;
-
-+ u32 option_type;
-+
- bool ibf;
- u8 fw_debug_wm;
- u8 fw_debug_wa;
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index f0d3f199..24d69d4d 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -5,7 +5,6 @@
-
- #include <linux/kernel.h>
- #include <linux/module.h>
--#include <linux/pci.h>
-
- #include "mt7996.h"
- #include "mac.h"
-@@ -93,6 +92,7 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
- hif->dev = &pdev->dev;
- hif->regs = pcim_iomap_table(pdev)[0];
- hif->irq = pdev->irq;
-+ pcie_bandwidth_available(pdev, NULL, &hif->speed, &hif->width);
- spin_lock_bh(&hif_lock);
- list_add(&hif->list, &hif_list);
- spin_unlock_bh(&hif_lock);
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 476b23c3..050637c1 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -435,6 +435,7 @@ enum offs_rev {
- #define WF_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0)
- #define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD BIT(18)
- #define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE BIT(14)
-+#define WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK GENMASK(27, 24)
-
- #define WF_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4)
- #define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE BIT(31)
-@@ -454,6 +455,7 @@ enum offs_rev {
-
- #define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30)
- #define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0)
-+#define MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 BIT(20)
- #define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 BIT(21)
- #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1 BIT(22)
-
-@@ -463,6 +465,9 @@ enum offs_rev {
- #define MT_WFDMA_AXI_R2A_CTRL MT_WFDMA_EXT_CSR(0x500)
- #define MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK GENMASK(4, 0)
-
-+#define MT_WFDMA_AXI_R2A_CTRL2 MT_WFDMA_EXT_CSR(0x508)
-+#define MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK GENMASK(31, 28)
-+
- #define MT_PCIE_RECOG_ID 0xd7090
- #define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0)
- #define MT_PCIE_RECOG_ID_SEM BIT(31)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2023-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2023-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch
deleted file mode 100644
index aa7a600..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2023-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From 918509adeb892bda553d29794bf475831a49f6d0 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 27 Jul 2023 19:35:32 +0800
-Subject: [PATCH 2023/2032] mtk: wifi: mt76: mt7996: support enable/disable
- thermal protection mechanism
-
-This commit adds a new debugfs thermal_enable to enable/disable thermal
-protection mechanism. The purpose of this commit is for autotest to
-verify thermal protection mechanism.
-
-[Usage]
-Enable thermal protection: echo 1 > thermal_enable
-Disable thermal protection: echo 0 > thermal_enable
-
-Please note that if you re-enable thermal protection mechanism, all the
-configuration values will be retained from the exising configuration,
-rather than using the default values.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/main.c | 1 +
- mt7996/mt7996.h | 1 +
- mt7996/mtk_debugfs.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 47 insertions(+)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index fd6fd78b..91c06cfb 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -83,6 +83,7 @@ int mt7996_run(struct ieee80211_hw *hw)
- #ifdef CONFIG_MTK_DEBUG
- phy->sr_enable = true;
- phy->enhanced_sr_enable = true;
-+ phy->thermal_protection_enable = true;
-
- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
- !dev->dbg.sku_disable);
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 2ca6e55d..d6f7828e 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -483,6 +483,7 @@ struct mt7996_phy {
- #ifdef CONFIG_MTK_DEBUG
- bool sr_enable:1;
- bool enhanced_sr_enable:1;
-+ bool thermal_protection_enable:1;
- #endif
- };
-
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 916c7c06..afef17cf 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3110,6 +3110,49 @@ mt7996_show_rro_mib(struct seq_file *s, void *data)
- return 0;
- }
-
-+static int
-+mt7996_thermal_enable_get(void *data, u64 *enable)
-+{
-+ struct mt7996_phy *phy = data;
-+
-+ *enable = phy->thermal_protection_enable;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_thermal_enable_set(void *data, u64 action)
-+{
-+ struct mt7996_phy *phy = data;
-+ int ret;
-+ u8 throttling;
-+
-+ if (action > 1)
-+ return -EINVAL;
-+
-+ if (!!action == phy->thermal_protection_enable)
-+ return 0;
-+
-+ ret = mt7996_mcu_set_thermal_protect(phy, !!action);
-+ if (ret)
-+ return ret;
-+
-+ if (!!!action)
-+ goto out;
-+
-+ throttling = MT7996_THERMAL_THROTTLE_MAX - phy->cdev_state;
-+ ret = mt7996_mcu_set_thermal_throttling(phy, throttling);
-+ if (ret)
-+ return ret;
-+
-+out:
-+ phy->thermal_protection_enable = !!action;
-+
-+ return 0;
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
-+ mt7996_thermal_enable_set, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -3217,6 +3260,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- mt7996_show_rro_mib);
- }
-
-+ debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
-+
- return 0;
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2024-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2024-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch
deleted file mode 100644
index 8f0e9b2..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2024-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From 4d28b5d5f45a4494fffd03694caeff4f59765b80 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 4 Jan 2024 19:53:37 +0800
-Subject: [PATCH 2024/2032] mtk: wifi: mt76: mt7996: support thermal recal
- debug command
-
-Add support thermal recal debug command.
-
-Usage:
-$ echo val > debugfs/thermal_recal
-
-The val can be the following values:
-0 = disable
-1 = enable
-2 = manual trigger
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt76_connac_mcu.h | 1 +
- mt7996/mt7996.h | 1 +
- mt7996/mtk_debugfs.c | 17 +++++++++++++++++
- mt7996/mtk_mcu.c | 21 +++++++++++++++++++++
- 4 files changed, 40 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index b2b8f2a2..97c2f5c0 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1284,6 +1284,7 @@ enum {
- MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
- MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- MCU_UNI_CMD_PRECAL_RESULT = 0x47,
-+ MCU_UNI_CMD_THERMAL_CAL = 0x4c,
- MCU_UNI_CMD_RRO = 0x57,
- MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
- MCU_UNI_CMD_PER_STA_INFO = 0x6d,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index d6f7828e..b5673bdd 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -1027,6 +1027,7 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
- void mt7996_tm_update_channel(struct mt7996_phy *phy);
-
- int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
-+int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action);
- #endif
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index afef17cf..25c21f37 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3153,6 +3153,22 @@ out:
- DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
- mt7996_thermal_enable_set, "%lld\n");
-
-+static int
-+mt7996_thermal_recal_set(void *data, u64 val)
-+{
-+#define THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER 2
-+#define THERMAL_DEBUG_MODE_RECAL 1
-+ struct mt7996_dev *dev = data;
-+
-+ if (val > THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER)
-+ return -EINVAL;
-+
-+ return mt7996_mcu_thermal_debug(dev, THERMAL_DEBUG_MODE_RECAL, val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_recal, NULL,
-+ mt7996_thermal_recal_set, "%llu\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- struct mt7996_dev *dev = phy->dev;
-@@ -3261,6 +3277,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- }
-
- debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
-+ debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
-
- return 0;
- }
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index 9a6636fd..063c2516 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -1280,4 +1280,25 @@ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
- sizeof(req), true);
- }
-
-+int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action)
-+{
-+ struct {
-+ u8 __rsv1[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+
-+ u8 mode;
-+ u8 action;
-+ u8 __rsv2[2];
-+ } __packed req = {
-+ .tag = cpu_to_le16(mode),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .mode = mode,
-+ .action = action,
-+ };
-+
-+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(THERMAL_CAL), &req,
-+ sizeof(req), true);
-+}
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2025-mtk-wifi-mt76-mt7996-Porting-wifi6-txpower-fix-to-ea.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2025-mtk-wifi-mt76-mt7996-Porting-wifi6-txpower-fix-to-ea.patch
deleted file mode 100644
index f1c1890..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2025-mtk-wifi-mt76-mt7996-Porting-wifi6-txpower-fix-to-ea.patch
+++ /dev/null
@@ -1,470 +0,0 @@
-From 456a6bddcbec9ba8c18648735327542b837dae0d Mon Sep 17 00:00:00 2001
-From: Allen Ye <allen.ye@mediatek.com>
-Date: Thu, 25 Jan 2024 10:57:08 +0800
-Subject: [PATCH 2025/2032] mtk: wifi: mt76: mt7996: Porting wifi6 txpower fix
- to eagle
-
-Refactor txpower flow.
-1. Fix wrong bbp CR address
-2. Ignore RegDB power limit when we have single sku table. And dump more informaiton in debugfs.
-3. Refactor get_txpower ops flow, we only consider CCK and OFDM power value as maximum.
-4. Remove sku_disable due to SQC is over and default enable both sku tables.
-
-
-Fix wrong power value when user set limit close to path table limit.
-
----
- eeprom.c | 20 ++++----
- mt7996/init.c | 14 ++++-
- mt7996/main.c | 11 ++--
- mt7996/mcu.c | 41 ++++++++++++---
- mt7996/mt7996.h | 3 ++
- mt7996/mtk_debugfs.c | 120 ++++++++++++++++++++++++++++---------------
- mt7996/regs.h | 10 ++--
- 7 files changed, 149 insertions(+), 70 deletions(-)
-
-diff --git a/eeprom.c b/eeprom.c
-index adb87924..57b9b769 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -336,9 +336,10 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
- static void
- mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- const __be32 *data, size_t len, s8 target_power,
-- s8 nss_delta, s8 *max_power)
-+ s8 nss_delta)
- {
- int i, cur;
-+ s8 max_power = -128;
-
- if (!data)
- return;
-@@ -350,7 +351,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- break;
-
- mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
-- target_power, nss_delta, max_power);
-+ target_power, nss_delta, &max_power);
- if (--cur > 0)
- continue;
-
-@@ -433,17 +434,17 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
- mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
- ARRAY_SIZE(dest->mcs), val, len,
-- target_power, txs_delta, &max_power);
-+ target_power, txs_delta);
-
- val = mt76_get_of_array(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
- mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
- ARRAY_SIZE(dest->ru), val, len,
-- target_power, txs_delta, &max_power);
-+ target_power, txs_delta);
-
- val = mt76_get_of_array(np, "rates-eht", &len, ARRAY_SIZE(dest->eht[0]) + 1);
- mt76_apply_multi_array_limit(dest->eht[0], ARRAY_SIZE(dest->eht[0]),
- ARRAY_SIZE(dest->eht), val, len,
-- target_power, txs_delta, &max_power);
-+ target_power, txs_delta);
-
- if (dest_path == NULL)
- return max_power;
-@@ -465,17 +466,14 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- val = mt76_get_of_array(np, "paths-ru", &len, ARRAY_SIZE(dest_path->ru[0]) + 1);
- mt76_apply_multi_array_limit(dest_path->ru[0], ARRAY_SIZE(dest_path->ru[0]),
- ARRAY_SIZE(dest_path->ru), val, len,
-- target_power_combine, txs_delta, &max_power_backoff);
-+ target_power_combine, txs_delta);
-
- val = mt76_get_of_array(np, "paths-ru-bf", &len, ARRAY_SIZE(dest_path->ru_bf[0]) + 1);
- mt76_apply_multi_array_limit(dest_path->ru_bf[0], ARRAY_SIZE(dest_path->ru_bf[0]),
- ARRAY_SIZE(dest_path->ru_bf), val, len,
-- target_power_combine, txs_delta, &max_power_backoff);
-+ target_power_combine, txs_delta);
-
-- if (max_power_backoff == target_power_combine)
-- return max_power;
--
-- return max_power_backoff;
-+ return max_power;
- }
- EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 85fedca6..bc8cfdbd 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -296,7 +296,11 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
- int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
- struct mt76_power_limits limits;
- struct mt76_power_path_limits limits_path;
-+ struct device_node *np;
-
-+ phy->sku_limit_en = true;
-+ phy->sku_path_en = true;
-+ np = mt76_find_power_limits_node(&dev->mt76);
- for (i = 0; i < sband->n_channels; i++) {
- struct ieee80211_channel *chan = &sband->channels[i];
- int target_power = mt7996_eeprom_get_target_power(dev, chan);
-@@ -306,10 +310,16 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
- &limits,
- &limits_path,
- target_power);
-+ if (!limits_path.ofdm[0])
-+ phy->sku_path_en = false;
-+
- target_power += nss_delta;
- target_power = DIV_ROUND_UP(target_power, 2);
-- chan->max_power = min_t(int, chan->max_reg_power,
-- target_power);
-+ if (!np)
-+ chan->max_power = min_t(int, chan->max_reg_power,
-+ target_power);
-+ else
-+ chan->max_power = target_power;
- chan->orig_mpwr = target_power;
- }
- }
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 91c06cfb..ca8e6125 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -84,11 +84,16 @@ int mt7996_run(struct ieee80211_hw *hw)
- phy->sr_enable = true;
- phy->enhanced_sr_enable = true;
- phy->thermal_protection_enable = true;
--
- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
-- !dev->dbg.sku_disable);
-+ dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
-+
-+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
-+ dev->dbg.sku_disable ? 0 : phy->sku_path_en);
- #else
-- ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL, true);
-+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
-+ phy->sku_limit_en);
-+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
-+ phy->sku_path_en);
- #endif
- if (ret)
- goto out;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6405c2fa..52651693 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5126,6 +5126,27 @@ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
- sizeof(req), true);
- }
-
-+static void
-+mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
-+{
-+ struct mt76_phy *mphy = phy->mt76;
-+ struct ieee80211_channel *chan = mphy->main_chan;
-+ int e2p_power_limit = 0;
-+
-+ if (chan == NULL) {
-+ mphy->txpower_cur = tx_power;
-+ return;
-+ }
-+
-+ e2p_power_limit = mt7996_eeprom_get_target_power(phy->dev, chan);
-+ e2p_power_limit += mt7996_eeprom_get_power_delta(phy->dev, chan->band);
-+
-+ if (phy->sku_limit_en)
-+ mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power);
-+ else
-+ mphy->txpower_cur = e2p_power_limit;
-+}
-+
- int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- {
- #define TX_POWER_LIMIT_TABLE_RATE 0
-@@ -5151,12 +5172,20 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- struct mt76_power_limits la = {};
- struct mt76_power_path_limits la_path = {};
- struct sk_buff *skb;
-- int i, ret, tx_power;
-+ int i, ret, txpower_limit;
-+
-+ if (hw->conf.power_level == INT_MIN)
-+ hw->conf.power_level = 127;
-+ txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
-
-- tx_power = mt7996_get_power_bound(phy, hw->conf.power_level);
-- tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
-- &la, &la_path, tx_power);
-- mphy->txpower_cur = tx_power;
-+ if (phy->sku_limit_en) {
-+ txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
-+ &la, &la_path, txpower_limit);
-+ mt7996_update_max_txpower_cur(phy, txpower_limit);
-+ } else {
-+ mt7996_update_max_txpower_cur(phy, txpower_limit);
-+ return 0;
-+ }
-
- skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
- sizeof(req) + MT7996_SKU_PATH_NUM);
-@@ -5192,7 +5221,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- return ret;
-
- /* only set per-path power table when it's configured */
-- if (!la_path.ofdm[0])
-+ if (!phy->sku_path_en)
- return 0;
-
- skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b5673bdd..f1308112 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -461,6 +461,9 @@ struct mt7996_phy {
-
- u8 muru_onoff;
-
-+ bool sku_limit_en;
-+ bool sku_path_en;
-+
- #ifdef CONFIG_NL80211_TESTMODE
- struct {
- u32 *reg_backup;
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 25c21f37..3f82b58c 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2454,6 +2454,7 @@ mt7996_get_txpower_info(struct file *file, char __user *user_buf,
- struct mt7996_phy *phy = file->private_data;
- struct mt7996_mcu_txpower_event *event;
- struct txpower_basic_info *basic_info;
-+ struct device_node *np;
- static const size_t size = 2048;
- int len = 0;
- ssize_t ret;
-@@ -2510,7 +2511,10 @@ mt7996_get_txpower_info(struct file *file, char __user *user_buf,
- len += scnprintf(buf + len, size - len,
- " Theraml Compensation Value: %d\n",
- basic_info->thermal_compensate_value);
--
-+ np = mt76_find_power_limits_node(phy->mt76->dev);
-+ len += scnprintf(buf + len, size - len,
-+ " RegDB: %s\n",
-+ !np ? "enable" : "disable");
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-
- out:
-@@ -2526,9 +2530,9 @@ static const struct file_operations mt7996_txpower_info_fops = {
- .llseek = default_llseek,
- };
-
--#define mt7996_txpower_puts(rate) \
-+#define mt7996_txpower_puts(rate, _len) \
- ({ \
-- len += scnprintf(buf + len, size - len, "%-21s:", #rate " (TMAC)"); \
-+ len += scnprintf(buf + len, size - len, "%-*s:", _len, #rate " (TMAC)"); \
- for (i = 0; i < mt7996_sku_group_len[SKU_##rate]; i++, offs++) \
- len += scnprintf(buf + len, size - len, " %6d", \
- event->phy_rate_info.frame_power[offs][band_idx]); \
-@@ -2542,9 +2546,15 @@ mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
- struct mt7996_phy *phy = file->private_data;
- struct mt7996_dev *dev = phy->dev;
- struct mt7996_mcu_txpower_event *event;
-+ struct ieee80211_channel *chan = phy->mt76->chandef.chan;
-+ struct ieee80211_supported_band sband;
- u8 band_idx = phy->mt76->band_idx;
- static const size_t size = 5120;
- int i, offs = 0, len = 0;
-+ u32 target_power = 0;
-+ int n_chains = hweight16(phy->mt76->chainmask);
-+ int nss_delta = mt76_tx_power_nss_delta(n_chains);
-+ int pwr_delta;
- ssize_t ret;
- char *buf;
- u32 reg;
-@@ -2566,41 +2576,45 @@ mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
- band_idx, phy->mt76->chandef.chan->hw_value);
- len += scnprintf(buf + len, size - len, "%-21s %6s %6s %6s %6s\n",
- " ", "1m", "2m", "5m", "11m");
-- mt7996_txpower_puts(CCK);
-+ mt7996_txpower_puts(CCK, 21);
-
- len += scnprintf(buf + len, size - len,
- "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
- " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m",
- "54m");
-- mt7996_txpower_puts(OFDM);
-+ mt7996_txpower_puts(OFDM, 21);
-
- len += scnprintf(buf + len, size - len,
- "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
- " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4",
- "mcs5", "mcs6", "mcs7");
-- mt7996_txpower_puts(HT20);
-+ mt7996_txpower_puts(HT20, 21);
-
- len += scnprintf(buf + len, size - len,
- "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
- " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
- "mcs6", "mcs7", "mcs32");
-- mt7996_txpower_puts(HT40);
-+ mt7996_txpower_puts(HT40, 21);
-
- len += scnprintf(buf + len, size - len,
- "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
- " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
- "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
-- mt7996_txpower_puts(VHT20);
-- mt7996_txpower_puts(VHT40);
-- mt7996_txpower_puts(VHT80);
-- mt7996_txpower_puts(VHT160);
-- mt7996_txpower_puts(HE26);
-- mt7996_txpower_puts(HE52);
-- mt7996_txpower_puts(HE106);
-- mt7996_txpower_puts(HE242);
-- mt7996_txpower_puts(HE484);
-- mt7996_txpower_puts(HE996);
-- mt7996_txpower_puts(HE2x996);
-+ mt7996_txpower_puts(VHT20, 21);
-+ mt7996_txpower_puts(VHT40, 21);
-+ mt7996_txpower_puts(VHT80, 21);
-+ mt7996_txpower_puts(VHT160, 21);
-+ mt7996_txpower_puts(HE26, 21);
-+ mt7996_txpower_puts(HE52, 21);
-+ mt7996_txpower_puts(HE106, 21);
-+ len += scnprintf(buf + len, size - len, "BW20/");
-+ mt7996_txpower_puts(HE242, 16);
-+ len += scnprintf(buf + len, size - len, "BW40/");
-+ mt7996_txpower_puts(HE484, 16);
-+ len += scnprintf(buf + len, size - len, "BW80/");
-+ mt7996_txpower_puts(HE996, 16);
-+ len += scnprintf(buf + len, size - len, "BW160/");
-+ mt7996_txpower_puts(HE2x996, 15);
-
- len += scnprintf(buf + len, size - len,
- "%-21s %6s %6s %6s %6s %6s %6s %6s %6s ",
-@@ -2608,22 +2622,27 @@ mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
- len += scnprintf(buf + len, size - len,
- "%6s %6s %6s %6s %6s %6s %6s %6s\n",
- "mcs8", "mcs9", "mcs10", "mcs11", "mcs12", "mcs13", "mcs14", "mcs15");
-- mt7996_txpower_puts(EHT26);
-- mt7996_txpower_puts(EHT52);
-- mt7996_txpower_puts(EHT106);
-- mt7996_txpower_puts(EHT242);
-- mt7996_txpower_puts(EHT484);
-- mt7996_txpower_puts(EHT996);
-- mt7996_txpower_puts(EHT2x996);
-- mt7996_txpower_puts(EHT4x996);
-- mt7996_txpower_puts(EHT26_52);
-- mt7996_txpower_puts(EHT26_106);
-- mt7996_txpower_puts(EHT484_242);
-- mt7996_txpower_puts(EHT996_484);
-- mt7996_txpower_puts(EHT996_484_242);
-- mt7996_txpower_puts(EHT2x996_484);
-- mt7996_txpower_puts(EHT3x996);
-- mt7996_txpower_puts(EHT3x996_484);
-+ mt7996_txpower_puts(EHT26, 21);
-+ mt7996_txpower_puts(EHT52, 21);
-+ mt7996_txpower_puts(EHT106, 21);
-+ len += scnprintf(buf + len, size - len, "BW20/");
-+ mt7996_txpower_puts(EHT242, 16);
-+ len += scnprintf(buf + len, size - len, "BW40/");
-+ mt7996_txpower_puts(EHT484, 16);
-+ len += scnprintf(buf + len, size - len, "BW80/");
-+ mt7996_txpower_puts(EHT996, 16);
-+ len += scnprintf(buf + len, size - len, "BW160/");
-+ mt7996_txpower_puts(EHT2x996, 15);
-+ len += scnprintf(buf + len, size - len, "BW320/");
-+ mt7996_txpower_puts(EHT4x996, 15);
-+ mt7996_txpower_puts(EHT26_52, 21);
-+ mt7996_txpower_puts(EHT26_106, 21);
-+ mt7996_txpower_puts(EHT484_242, 21);
-+ mt7996_txpower_puts(EHT996_484, 21);
-+ mt7996_txpower_puts(EHT996_484_242, 21);
-+ mt7996_txpower_puts(EHT2x996_484, 21);
-+ mt7996_txpower_puts(EHT3x996, 21);
-+ mt7996_txpower_puts(EHT3x996_484, 21);
-
- len += scnprintf(buf + len, size - len, "\nePA Gain: %d\n",
- event->phy_rate_info.epa_gain);
-@@ -2632,16 +2651,33 @@ mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
- len += scnprintf(buf + len, size - len, "Min Power Bound: %d\n",
- event->phy_rate_info.min_power_bound);
-
-- reg = MT_WF_PHYDFE_BAND_TPC_CTRL_STAT0(band_idx);
-+ reg = MT_WF_PHYDFE_TSSI_TXCTRL01(band_idx);
- len += scnprintf(buf + len, size - len,
-- "BBP TX Power (target power from TMAC) : %6ld [0.5 dBm]\n",
-- mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER_TMAC));
-+ "\nBBP TX Power (target power from TMAC) : %6ld [0.5 dBm]\n",
-+ mt76_get_field(dev, reg, MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC));
- len += scnprintf(buf + len, size - len,
-- "BBP TX Power (target power from RMAC) : %6ld [0.5 dBm]\n",
-- mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER_RMAC));
-+ "RegDB maximum power:\t%d [dBm]\n",
-+ chan->max_reg_power);
-+
-+ if (chan->band == NL80211_BAND_2GHZ)
-+ sband = phy->mt76->sband_2g.sband;
-+ else if (chan->band == NL80211_BAND_5GHZ)
-+ sband = phy->mt76->sband_5g.sband;
-+ else if (chan->band == NL80211_BAND_6GHZ)
-+ sband = phy->mt76->sband_6g.sband;
-+
-+ pwr_delta = mt7996_eeprom_get_power_delta(dev, sband.band);
-+
-+ target_power = max_t(u32, target_power, mt7996_eeprom_get_target_power(dev, chan));
-+ target_power += pwr_delta + nss_delta;
-+ target_power = DIV_ROUND_UP(target_power, 2);
-+ len += scnprintf(buf + len, size - len,
-+ "eeprom maximum power:\t%d [dBm]\n",
-+ target_power);
-+
- len += scnprintf(buf + len, size - len,
-- "BBP TX Power (TSSI module power input) : %6ld [0.5 dBm]\n",
-- mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER_TSSI));
-+ "nss_delta:\t%d [0.5 dBm]\n",
-+ nss_delta);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-
-@@ -2660,7 +2696,7 @@ static const struct file_operations mt7996_txpower_sku_fops = {
-
- #define mt7996_txpower_path_puts(rate, arr_length) \
- ({ \
-- len += scnprintf(buf + len, size - len, "%-23s:", #rate " (TMAC)"); \
-+ len += scnprintf(buf + len, size - len, "%23s:", #rate " (TMAC)"); \
- for (i = 0; i < arr_length; i++, offs++) \
- len += scnprintf(buf + len, size - len, " %4d", \
- event->backoff_table_info.frame_power[offs]); \
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 050637c1..a0e4b3e1 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -718,6 +718,10 @@ enum offs_rev {
- ((_wf) << 16) + (ofs))
- #define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
-
-+/* PHYDFE CTRL */
-+#define MT_WF_PHYDFE_TSSI_TXCTRL01(_band) MT_WF_PHYRX_CSD(_band, 0, 0xc718)
-+#define MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC GENMASK(31, 24)
-+
- /* PHY CTRL */
- #define MT_WF_PHY_BAND_BASE 0x83080000
- #define MT_WF_PHY_BAND(_band, ofs) (MT_WF_PHY_BAND_BASE + \
-@@ -735,12 +739,6 @@ enum offs_rev {
- #define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0)
- #define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9)
-
--/* PHYDFE CTRL */
--#define MT_WF_PHYDFE_BAND_TPC_CTRL_STAT0(_phy) MT_WF_PHY_BAND(_phy, 0xe7a0)
--#define MT_WF_PHY_TPC_POWER_TMAC GENMASK(15, 8)
--#define MT_WF_PHY_TPC_POWER_RMAC GENMASK(23, 16)
--#define MT_WF_PHY_TPC_POWER_TSSI GENMASK(31, 24)
--
- /* PHYRX CSD BAND */
- #define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHY_BAND(_band, 0x8230)
- #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18)
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2026-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2026-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch
deleted file mode 100644
index 9cece6d..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2026-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch
+++ /dev/null
@@ -1,1090 +0,0 @@
-From 7bbed79a6d043eb149cf3b6be7982ae845ad8450 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Sat, 20 Jan 2024 12:03:24 +0800
-Subject: [PATCH 2026/2032] mtk: wifi: mt76: mt7996: Add connac3 csi feature.
-
-1. format align to wifi6.
-2. add bw320 support.
-3. add active mode.
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- mt76_connac_mcu.h | 2 +
- mt7996/init.c | 22 +++
- mt7996/main.c | 4 +
- mt7996/mcu.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h | 105 +++++++++++
- mt7996/mt7996.h | 55 ++++++
- mt7996/vendor.c | 208 +++++++++++++++++++++
- mt7996/vendor.h | 50 +++++
- 8 files changed, 911 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 97c2f5c0..8c0200a3 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1049,6 +1049,7 @@ enum {
- MCU_UNI_EVENT_THERMAL = 0x35,
- MCU_UNI_EVENT_NIC_CAPAB = 0x43,
- MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
-+ MCU_UNI_EVENT_CSI_REPORT = 0x4A,
- MCU_UNI_EVENT_WED_RRO = 0x57,
- MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
- MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
-@@ -1284,6 +1285,7 @@ enum {
- MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
- MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- MCU_UNI_CMD_PRECAL_RESULT = 0x47,
-+ MCU_UNI_CMD_CSI_CTRL = 0x4A,
- MCU_UNI_CMD_THERMAL_CAL = 0x4c,
- MCU_UNI_CMD_RRO = 0x57,
- MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index bc8cfdbd..a3d7a2ed 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -734,6 +734,24 @@ error:
- return ret;
- }
-
-+#ifdef CONFIG_MTK_VENDOR
-+static int mt7996_unregister_csi(struct mt7996_phy *phy)
-+{
-+ struct csi_data *c, *tmp_c;
-+
-+ spin_lock_bh(&phy->csi.lock);
-+ phy->csi.enable = 0;
-+
-+ list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
-+ list_del(&c->node);
-+ kfree(c);
-+ }
-+ spin_unlock_bh(&phy->csi.lock);
-+
-+ return 0;
-+}
-+#endif
-+
- static void
- mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
- {
-@@ -742,6 +760,10 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
- if (!phy)
- return;
-
-+#ifdef CONFIG_MTK_VENDOR
-+ mt7996_unregister_csi(phy);
-+#endif
-+
- mt7996_unregister_thermal(phy);
-
- mphy = phy->dev->mt76.phys[band];
-diff --git a/mt7996/main.c b/mt7996/main.c
-index ca8e6125..97101985 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -797,6 +797,10 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- int i;
-
-+#ifdef CONFIG_MTK_VENDOR
-+ mt7996_mcu_set_csi(&dev->phy, 2, 8, 1, 0, sta->addr);
-+#endif
-+
- mt7996_mcu_add_sta(dev, vif, sta, false);
-
- mt7996_mac_wtbl_update(dev, msta->wcid.idx,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 52651693..9e04ea2b 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -638,6 +638,263 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- }
- }
-
-+static int
-+csi_integrate_segment_data(struct mt7996_phy *phy, struct csi_data *csi)
-+{
-+ struct csi_data *csi_temp = NULL;
-+
-+ if (csi->segment_num == 0 && csi->remain_last == 0)
-+ return CSI_CHAIN_COMPLETE;
-+ else if (csi->segment_num == 0 && csi->remain_last == 1) {
-+ memcpy(&phy->csi.buffered_csi,
-+ csi, sizeof(struct csi_data));
-+
-+ return CSI_CHAIN_SEGMENT_FIRST;
-+ } else if (csi->segment_num != 0) {
-+ csi_temp = &phy->csi.buffered_csi;
-+ if (csi->chain_info != csi_temp->chain_info ||
-+ csi->segment_num != (csi_temp->segment_num + 1))
-+ return CSI_CHAIN_SEGMENT_ERR;
-+
-+ memcpy(&csi_temp->data_i[csi_temp->data_num],
-+ csi->data_i, csi->data_num * sizeof(s16));
-+
-+ memcpy(&csi_temp->data_q[csi_temp->data_num],
-+ csi->data_q, csi->data_num * sizeof(s16));
-+
-+ csi_temp->data_num += csi->data_num;
-+ csi_temp->segment_num = csi->segment_num;
-+ csi_temp->remain_last = csi->remain_last;
-+
-+ if (csi->remain_last == 0)
-+ return CSI_CHAIN_SEGMENT_LAST;
-+ else if (csi->remain_last == 1)
-+ return CSI_CHAIN_SEGMENT_MIDDLE;
-+ }
-+
-+ return CSI_CHAIN_ERR;
-+}
-+
-+static int
-+mt7996_mcu_csi_report_data(struct mt7996_phy *phy, u8 *tlv_buf, u32 len)
-+{
-+ int ret, i;
-+ struct csi_data *current_csi;
-+ struct csi_data *target_csi;
-+ struct csi_tlv *tlv_data;
-+ u8 *buf_tmp;
-+ u32 rx_info, tx_rx_idx;
-+ u32 buf_len_last, offset;
-+
-+ buf_tmp = tlv_buf;
-+ buf_len_last = len;
-+ offset = sizeof(((struct csi_tlv *)0)->basic);
-+
-+ current_csi = kzalloc(sizeof(*current_csi), GFP_KERNEL);
-+ if (!current_csi)
-+ return -ENOMEM;
-+
-+ while (buf_len_last >= offset) {
-+ u32 tag, len;
-+ s16 *data_tmp = NULL;
-+
-+ tlv_data = (struct csi_tlv *)buf_tmp;
-+ tag = le32_to_cpu(tlv_data->basic.tag);
-+ len = le32_to_cpu(tlv_data->basic.len);
-+
-+ switch (tag) {
-+ case CSI_EVENT_FW_VER:
-+ current_csi->fw_ver = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_CBW:
-+ current_csi->ch_bw = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_RSSI:
-+ current_csi->rssi = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_SNR:
-+ current_csi->snr = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_BAND:
-+ current_csi->band = le32_to_cpu(tlv_data->info);
-+
-+ if (current_csi->band != phy->mt76->band_idx) {
-+ kfree(current_csi);
-+ return -EINVAL;
-+ }
-+
-+ break;
-+ case CSI_EVENT_CSI_NUM:
-+ current_csi->data_num = le32_to_cpu(tlv_data->info);
-+
-+ if (current_csi->data_num > CSI_BW80_DATA_COUNT) {
-+ kfree(current_csi);
-+ return -EINVAL;
-+ }
-+
-+ break;
-+ case CSI_EVENT_CSI_I_DATA:
-+ if (len != sizeof(s16) * current_csi->data_num) {
-+ kfree(current_csi);
-+ return -EINVAL;
-+ }
-+
-+ data_tmp = tlv_data->data;
-+ for (i = 0; i < current_csi->data_num; i++)
-+ current_csi->data_i[i] = le16_to_cpu(*(data_tmp + i));
-+ break;
-+ case CSI_EVENT_CSI_Q_DATA:
-+ if (len != sizeof(s16) * current_csi->data_num) {
-+ kfree(current_csi);
-+ return -EINVAL;
-+ }
-+
-+ data_tmp = tlv_data->data;
-+ for (i = 0; i < current_csi->data_num; i++)
-+ current_csi->data_q[i] = le16_to_cpu(*(data_tmp + i));
-+ break;
-+ case CSI_EVENT_DBW:
-+ current_csi->data_bw = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_CH_IDX:
-+ current_csi->pri_ch_idx = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_TA:
-+ memcpy(current_csi->ta, tlv_data->mac, ETH_ALEN);
-+ break;
-+ case CSI_EVENT_EXTRA_INFO:
-+ current_csi->ext_info = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_RX_MODE:
-+ rx_info = le32_to_cpu(tlv_data->info);
-+ current_csi->rx_mode = u32_get_bits(rx_info, GENMASK(15, 0));
-+ current_csi->rx_rate = u32_get_bits(rx_info, GENMASK(31, 16));
-+ break;
-+ case CSI_EVENT_H_IDX:
-+ current_csi->chain_info = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_TX_RX_IDX:
-+ tx_rx_idx = le32_to_cpu(tlv_data->info);
-+ current_csi->tx_idx = u32_get_bits(tx_rx_idx, GENMASK(31, 16));
-+ current_csi->rx_idx = u32_get_bits(tx_rx_idx, GENMASK(15, 0));
-+ break;
-+ case CSI_EVENT_TS:
-+ current_csi->ts = le32_to_cpu(tlv_data->info);
-+
-+ if (phy->csi.interval &&
-+ current_csi->ts < phy->csi.last_record + phy->csi.interval) {
-+ kfree(current_csi);
-+ return 0;
-+ }
-+
-+ break;
-+ case CSI_EVENT_PKT_SN:
-+ current_csi->pkt_sn = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_BW_SEG:
-+ current_csi->segment_num = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_REMAIN_LAST:
-+ current_csi->remain_last = le32_to_cpu(tlv_data->info);
-+ break;
-+ case CSI_EVENT_TR_STREAM:
-+ current_csi->tr_stream = le32_to_cpu(tlv_data->info);
-+ break;
-+ default:
-+ break;
-+ };
-+
-+ buf_len_last -= (offset + len);
-+
-+ if (buf_len_last >= offset)
-+ buf_tmp += (offset + len);
-+ }
-+
-+ /* integret the bw80 segment */
-+ if (current_csi->ch_bw >= CSI_BW80) {
-+ ret = csi_integrate_segment_data(phy, current_csi);
-+
-+ switch (ret) {
-+ case CSI_CHAIN_ERR:
-+ case CSI_CHAIN_SEGMENT_ERR:
-+ kfree(current_csi);
-+ return -EINVAL;
-+ break;
-+ case CSI_CHAIN_SEGMENT_FIRST:
-+ case CSI_CHAIN_SEGMENT_MIDDLE:
-+ kfree(current_csi);
-+ return 0;
-+ break;
-+ case CSI_CHAIN_COMPLETE:
-+ target_csi = current_csi;
-+ break;
-+ case CSI_CHAIN_SEGMENT_LAST:
-+ target_csi = current_csi;
-+ memcpy(target_csi, &phy->csi.buffered_csi, sizeof(struct csi_data));
-+ memset(&phy->csi.buffered_csi, 0, sizeof(struct csi_data));
-+ break;
-+ default:
-+ break;
-+ }
-+ } else {
-+ target_csi = current_csi;
-+ }
-+
-+ /* put the csi data into list */
-+ INIT_LIST_HEAD(&target_csi->node);
-+ spin_lock_bh(&phy->csi.lock);
-+
-+ if (!phy->csi.enable) {
-+ kfree(target_csi);
-+ goto out;
-+ }
-+
-+ list_add_tail(&target_csi->node, &phy->csi.list);
-+ phy->csi.count++;
-+
-+ if (phy->csi.count > CSI_MAX_BUF_NUM) {
-+ struct csi_data *old;
-+
-+ old = list_first_entry(&phy->csi.list,
-+ struct csi_data, node);
-+
-+ list_del(&old->node);
-+ kfree(old);
-+ phy->csi.count--;
-+ }
-+
-+ if (target_csi->chain_info & BIT(15)) /* last chain */
-+ phy->csi.last_record = target_csi->ts;
-+
-+out:
-+ spin_unlock_bh(&phy->csi.lock);
-+ return 0;
-+}
-+
-+void
-+mt7996_mcu_csi_report_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+ struct mt7996_mcu_csi_event *event;
-+ struct mt76_phy *mphy;
-+ struct mt7996_phy *phy;
-+
-+ event = (struct mt7996_mcu_csi_event *)skb->data;
-+
-+ mphy = dev->mt76.phys[event->band_idx];
-+ if (!mphy)
-+ return;
-+
-+ phy = mphy->priv;
-+
-+ switch (le16_to_cpu(event->tag)) {
-+ case UNI_EVENT_CSI_DATA:
-+ mt7996_mcu_csi_report_data(phy, event->tlv_buf, le16_to_cpu(event->len) - 4);
-+ break;
-+ default:
-+ break;
-+ }
-+}
-+
- static void
- mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
- {
-@@ -881,6 +1138,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- case MCU_UNI_EVENT_BF:
- mt7996_mcu_rx_bf_event(dev, skb);
- break;
-+#endif
-+#ifdef CONFIG_MTK_VENDOR
-+ case MCU_UNI_EVENT_CSI_REPORT:
-+ mt7996_mcu_csi_report_event(dev, skb);
-+ break;
- #endif
- default:
- break;
-@@ -5671,4 +5933,207 @@ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
-
- mt7996_mcu_add_beacon(hw, vif, val);
- }
-+
-+static int mt7996_mcu_set_csi_enable(struct mt7996_phy *phy, u16 tag)
-+{
-+ struct {
-+ u8 band;
-+ u8 rsv1[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ } __packed req = {
-+ .band = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ };
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+ sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_frame_type(struct mt7996_phy *phy, u16 tag, u8 type_idx, u32 type)
-+{
-+ struct {
-+ u8 band;
-+ u8 rsv1[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 frame_type_idx;
-+ u8 frame_type;
-+ u8 rsv2[2];
-+ } __packed req = {
-+ .band = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .frame_type_idx = type_idx,
-+ .frame_type = type,
-+ };
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+ sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_chain_filter(struct mt7996_phy *phy, u16 tag, u8 func, u32 value)
-+{
-+ struct {
-+ u8 band;
-+ u8 rsv1[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 function;
-+ u8 chain_value;
-+ u8 rsv2[2];
-+ } __packed req = {
-+ .band = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .function = func,
-+ .chain_value = value,
-+ };
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+ sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_sta_filter(struct mt7996_phy *phy, u16 tag, u32 op, u8 *sta_mac)
-+{
-+ struct {
-+ u8 band;
-+ u8 rsv1[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 operation;
-+ u8 rsv2[1];
-+ u8 mac[6];
-+ } __packed req = {
-+ .band = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .operation = op,
-+ };
-+
-+ memcpy(req.mac, sta_mac, ETH_ALEN);
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+ sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_active_mode(struct mt7996_phy *phy, u16 tag,
-+ u32 interval, u8 frame_idx, u8 subframe_idx, u32 bitmap)
-+{
-+ struct {
-+ u8 band;
-+ u8 rsv1[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ __le16 interval; /* uint: ms */
-+ u8 frame_type_idx;
-+ u8 subframe_type_idx;
-+ __le32 bitmap; /* sta wcid bitmap */
-+ u8 rsv2[4];
-+ } __packed req = {
-+ .band = phy->mt76->band_idx,
-+ .tag = cpu_to_le16(tag),
-+ .len = cpu_to_le16(sizeof(req) - 4),
-+ .interval = cpu_to_le16(interval),
-+ .frame_type_idx = frame_idx,
-+ .subframe_type_idx = subframe_idx,
-+ .bitmap = cpu_to_le32(bitmap),
-+ };
-+
-+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+ sizeof(req), false);
-+}
-+
-+void mt7996_csi_wcid_bitmap_update(void *data, struct ieee80211_sta *sta)
-+{
-+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+ struct mt7996_phy *phy = msta->vif->phy;
-+ struct csi_bitmap_info_update *sta_info = (struct csi_bitmap_info_update *)data;
-+ u16 wcid = 0;
-+
-+#define CSI_ACTIVE_MODE_ADD 1
-+#define CSI_ACTIVE_MODE_REMOVE 0
-+
-+ if (!memcmp(sta_info->addr, sta->addr, ETH_ALEN)) {
-+ wcid = msta->wcid.idx;
-+
-+ /* active mode: only support station with wcid less than 32 */
-+ if (wcid > 32)
-+ return;
-+
-+ if (sta_info->action == CSI_ACTIVE_MODE_ADD)
-+ phy->csi.active_bitmap |= BIT(wcid);
-+ else if (sta_info->action == CSI_ACTIVE_MODE_REMOVE)
-+ phy->csi.active_bitmap &= ~(BIT(wcid));
-+ }
-+}
-+
-+int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
-+ u8 cfg, u8 v1, u32 v2, u8 *mac_addr)
-+{
-+ switch (mode) {
-+ case CSI_CONTROL_MODE_STOP:
-+ return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_STOP);
-+ case CSI_CONTROL_MODE_START:
-+ return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_START);
-+ case CSI_CONTROL_MODE_SET:
-+ switch (cfg) {
-+ case CSI_CONFIG_FRAME_TYPE:
-+ if (v2 > 255)
-+ return -EINVAL;
-+
-+ return mt7996_mcu_set_csi_frame_type(phy,
-+ UNI_CMD_CSI_SET_FRAME_TYPE, v1, v2);
-+ case CSI_CONFIG_CHAIN_FILTER:
-+ if (v2 > 255)
-+ return -EINVAL;
-+
-+ return mt7996_mcu_set_csi_chain_filter(phy,
-+ UNI_CMD_CSI_SET_CHAIN_FILTER, v1, v2);
-+ case CSI_CONFIG_STA_FILTER:
-+ if (!is_valid_ether_addr(mac_addr))
-+ return -EINVAL;
-+
-+ if (v2 > 255)
-+ return -EINVAL;
-+
-+ return mt7996_mcu_set_csi_sta_filter(phy,
-+ UNI_CMD_CSI_SET_STA_FILTER, v2, mac_addr);
-+ case CSI_CONFIG_ACTIVE_MODE:
-+ if (is_valid_ether_addr(mac_addr)) {
-+ struct csi_bitmap_info_update sta_info;
-+
-+ if (v2 > 255)
-+ return -EINVAL;
-+
-+ memcpy(sta_info.addr, mac_addr, ETH_ALEN);
-+ sta_info.action = v2;
-+
-+ ieee80211_iterate_stations_atomic(phy->mt76->hw,
-+ mt7996_csi_wcid_bitmap_update, &sta_info);
-+ return 0;
-+ } else {
-+ u8 frame_type = v1 & 0x3;
-+ u8 frame_subtype = (v1 & 0x3c) >> 2;
-+
-+ /* active mode: max interval is 3000ms */
-+ if (v2 > 3000)
-+ return -EINVAL;
-+
-+ return mt7996_mcu_set_csi_active_mode(phy, UNI_CMD_CSI_SET_ACTIVE_MODE,
-+ v2, frame_type, frame_subtype, phy->csi.active_bitmap);
-+ }
-+ default:
-+ return -EINVAL;
-+ }
-+ default:
-+ return -EINVAL;
-+ }
-+}
- #endif
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 9dc7946b..7721a01b 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -1112,4 +1112,109 @@ struct fixed_rate_table_ctrl {
- u8 _rsv2;
- } __packed;
-
-+#ifdef CONFIG_MTK_VENDOR
-+struct mt7996_mcu_csi_event {
-+ struct mt7996_mcu_rxd rxd;
-+
-+ u8 band_idx;
-+ u8 _rsv[3];
-+
-+ __le16 tag;
-+ __le16 len;
-+ u8 tlv_buf[0];
-+};
-+
-+enum UNI_EVENT_CSI_TAG_T {
-+ UNI_EVENT_CSI_DATA = 0,
-+ UNI_EVENT_CSI_MAX_NUM
-+};
-+
-+struct csi_tlv {
-+ struct {
-+ __le32 tag;
-+ __le32 len;
-+ } basic;
-+ union {
-+ u8 mac[ETH_ALEN];
-+ __le32 info;
-+ s16 data[0];
-+ };
-+} __packed;
-+
-+struct csi_bitmap_info_update {
-+ u8 action;
-+ u8 addr[ETH_ALEN];
-+};
-+
-+#define CSI_MAX_BUF_NUM 3000
-+
-+enum CSI_EVENT_TLV_TAG {
-+ CSI_EVENT_FW_VER,
-+ CSI_EVENT_CBW,
-+ CSI_EVENT_RSSI,
-+ CSI_EVENT_SNR,
-+ CSI_EVENT_BAND,
-+ CSI_EVENT_CSI_NUM,
-+ CSI_EVENT_CSI_I_DATA,
-+ CSI_EVENT_CSI_Q_DATA,
-+ CSI_EVENT_DBW,
-+ CSI_EVENT_CH_IDX,
-+ CSI_EVENT_TA,
-+ CSI_EVENT_EXTRA_INFO,
-+ CSI_EVENT_RX_MODE,
-+ CSI_EVENT_RSVD1,
-+ CSI_EVENT_RSVD2,
-+ CSI_EVENT_RSVD3,
-+ CSI_EVENT_RSVD4,
-+ CSI_EVENT_H_IDX,
-+ CSI_EVENT_TX_RX_IDX,
-+ CSI_EVENT_TS,
-+ CSI_EVENT_PKT_SN,
-+ CSI_EVENT_BW_SEG,
-+ CSI_EVENT_REMAIN_LAST,
-+ CSI_EVENT_TR_STREAM,
-+ CSI_EVENT_TLV_TAG_NUM,
-+};
-+
-+enum CSI_CHAIN_TYPE {
-+ CSI_CHAIN_ERR,
-+ CSI_CHAIN_COMPLETE,
-+ CSI_CHAIN_SEGMENT_FIRST,
-+ CSI_CHAIN_SEGMENT_MIDDLE,
-+ CSI_CHAIN_SEGMENT_LAST,
-+ CSI_CHAIN_SEGMENT_ERR,
-+};
-+
-+enum CSI_CONTROL_MODE_T {
-+ CSI_CONTROL_MODE_STOP,
-+ CSI_CONTROL_MODE_START,
-+ CSI_CONTROL_MODE_SET,
-+ CSI_CONTROL_MODE_NUM
-+};
-+
-+enum CSI_CONFIG_ITEM_T {
-+ CSI_CONFIG_RSVD1,
-+ CSI_CONFIG_WF,
-+ CSI_CONFIG_RSVD2,
-+ CSI_CONFIG_FRAME_TYPE,
-+ CSI_CONFIG_TX_PATH,
-+ CSI_CONFIG_OUTPUT_FORMAT,
-+ CSI_CONFIG_INFO,
-+ CSI_CONFIG_CHAIN_FILTER,
-+ CSI_CONFIG_STA_FILTER,
-+ CSI_CONFIG_ACTIVE_MODE,
-+ CSI_CONFIG_ITEM_NUM
-+};
-+
-+/* CSI config Tag */
-+enum UNI_CMD_CSI_TAG_T {
-+ UNI_CMD_CSI_STOP = 0,
-+ UNI_CMD_CSI_START = 1,
-+ UNI_CMD_CSI_SET_FRAME_TYPE = 2,
-+ UNI_CMD_CSI_SET_CHAIN_FILTER = 3,
-+ UNI_CMD_CSI_SET_STA_FILTER = 4,
-+ UNI_CMD_CSI_SET_ACTIVE_MODE = 5,
-+};
-+#endif
-+
- #endif
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index f1308112..f172eea2 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -405,6 +405,47 @@ struct mt7996_air_monitor_ctrl {
- struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
- struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
- };
-+
-+enum {
-+ CSI_BW20,
-+ CSI_BW40,
-+ CSI_BW80,
-+ CSI_BW160,
-+ CSI_BW320
-+};
-+
-+#define CSI_BW20_DATA_COUNT 64
-+#define CSI_BW40_DATA_COUNT 128
-+#define CSI_BW80_DATA_COUNT 256
-+#define CSI_BW160_DATA_COUNT 512
-+#define CSI_BW320_DATA_COUNT 1024
-+
-+struct csi_data {
-+ u8 fw_ver;
-+ u8 ch_bw;
-+ u16 data_num;
-+ s16 data_i[CSI_BW320_DATA_COUNT];
-+ s16 data_q[CSI_BW320_DATA_COUNT];
-+ u8 band;
-+ s8 rssi;
-+ u8 snr;
-+ u32 ts;
-+ u8 data_bw;
-+ u8 pri_ch_idx;
-+ u8 ta[ETH_ALEN];
-+ u32 ext_info;
-+ u16 rx_mode;
-+ u16 rx_rate;
-+ u32 chain_info;
-+ u16 tx_idx;
-+ u16 rx_idx;
-+ u32 segment_num;
-+ u8 remain_last;
-+ u16 pkt_sn;
-+ u8 tr_stream;
-+
-+ struct list_head node;
-+};
- #endif
-
- struct mt7996_rro_ba_session {
-@@ -482,6 +523,18 @@ struct mt7996_phy {
- u8 rts_bw_sig;
- spinlock_t amnt_lock;
- struct mt7996_air_monitor_ctrl amnt_ctrl;
-+
-+ struct {
-+ struct list_head list;
-+ spinlock_t lock;
-+ u32 count;
-+ bool enable;
-+
-+ struct csi_data buffered_csi;
-+ u32 active_bitmap;
-+ u32 interval;
-+ u32 last_record;
-+ } csi;
- #endif
- #ifdef CONFIG_MTK_DEBUG
- bool sr_enable:1;
-@@ -996,6 +1049,8 @@ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
- int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
- int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
- void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
-+int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
-+ u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
- #endif
-
- int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index c87cc5c1..c37f1dba 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -117,6 +117,18 @@ beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
- [MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
- };
-
-+static const struct nla_policy
-+csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
-+ [MTK_VENDOR_ATTR_CSI_CTRL_CFG] = {.type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
-+ [MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
-+ [MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
-+};
-+
- struct mt7996_amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
-@@ -932,7 +944,188 @@ static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
-
- return 0;
- }
-+static int mt7996_vendor_csi_ctrl(struct wiphy *wiphy,
-+ struct wireless_dev *wdev,
-+ const void *data,
-+ int data_len)
-+{
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
-+ int err;
-+
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
-+ csi_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+
-+ if (tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG]) {
-+ u8 mode = 0, type = 0, v1 = 0;
-+ u32 v2 = 0;
-+ u8 mac_addr[ETH_ALEN] = {};
-+ struct nlattr *cur;
-+ int rem;
-+
-+ nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG], rem) {
-+ switch (nla_type(cur)) {
-+ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE:
-+ mode = nla_get_u8(cur);
-+ break;
-+ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE:
-+ type = nla_get_u8(cur);
-+ break;
-+ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1:
-+ v1 = nla_get_u8(cur);
-+ break;
-+ case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2:
-+ v2 = nla_get_u32(cur);
-+ break;
-+ default:
-+ return -EINVAL;
-+ };
-+ }
-+
-+ if (tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR]) {
-+ u8 idx = 0;
-+
-+ nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR], rem) {
-+ mac_addr[idx++] = nla_get_u8(cur);
-+ }
-+ }
-+
-+ err = mt7996_mcu_set_csi(phy, mode, type, v1, v2, mac_addr);
-+ if (err < 0)
-+ return err;
-+
-+ spin_lock_bh(&phy->csi.lock);
-
-+ phy->csi.enable = !!mode;
-+
-+ /* clean up old csi stats */
-+ if ((mode == CSI_CONTROL_MODE_STOP || mode == CSI_CONTROL_MODE_SET)
-+ && !list_empty(&phy->csi.list)) {
-+ struct csi_data *c, *tmp_c;
-+
-+ list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
-+ list_del(&c->node);
-+ kfree(c);
-+ phy->csi.count--;
-+ }
-+ } else if (mode == CSI_CONTROL_MODE_START) {
-+ phy->csi.last_record = 0;
-+ }
-+
-+ spin_unlock_bh(&phy->csi.lock);
-+
-+ if (mode == CSI_CONTROL_MODE_SET && type == CSI_CONFIG_STA_FILTER && v1 == 2)
-+ phy->csi.interval = v2;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_vendor_csi_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+ struct sk_buff *skb, const void *data, int data_len,
-+ unsigned long *storage)
-+{
-+#define RESERVED_SET BIT(31)
-+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {0};
-+ int err = 0;
-+
-+ if (*storage & RESERVED_SET) {
-+ if ((*storage & GENMASK(15, 0)) == 0)
-+ return -ENOENT;
-+ (*storage)--;
-+ }
-+
-+ if (data) {
-+ err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
-+ csi_ctrl_policy, NULL);
-+ if (err)
-+ return err;
-+ }
-+
-+ if (!(*storage & RESERVED_SET) && tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]) {
-+ *storage = nla_get_u16(tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]);
-+ *storage |= RESERVED_SET;
-+ }
-+
-+ spin_lock_bh(&phy->csi.lock);
-+
-+ if (!list_empty(&phy->csi.list)) {
-+ struct csi_data *csi;
-+ void *a, *b;
-+ int i;
-+
-+ csi = list_first_entry(&phy->csi.list, struct csi_data, node);
-+
-+ a = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_CTRL_DATA);
-+ if (!a)
-+ goto out;
-+
-+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_VER, 1) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_RSSI, csi->rssi) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_SNR, csi->snr) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_BW, csi->data_bw) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_CH_IDX, csi->pri_ch_idx) ||
-+ nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_MODE, csi->rx_mode))
-+ goto out;
-+
-+ if (nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_TX_ANT, csi->tx_idx) ||
-+ nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_RX_ANT, csi->rx_idx))
-+ goto out;
-+
-+ if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_INFO, csi->ext_info) ||
-+ nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO, csi->chain_info) ||
-+ nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_TS, csi->ts))
-+ goto out;
-+
-+ b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_TA);
-+ if (!b)
-+ goto out;
-+
-+ for (i = 0; i < ARRAY_SIZE(csi->ta); i++)
-+ if (nla_put_u8(skb, i, csi->ta[i]))
-+ goto out;
-+ nla_nest_end(skb, b);
-+
-+ if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_NUM, csi->data_num))
-+ goto out;
-+
-+ b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_I);
-+ if (!b)
-+ goto out;
-+
-+ for (i = 0; i < csi->data_num; i++)
-+ if (nla_put_u16(skb, i, csi->data_i[i]))
-+ goto out;
-+ nla_nest_end(skb, b);
-+
-+ b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_Q);
-+ if (!b)
-+ goto out;
-+
-+ for (i = 0; i < csi->data_num; i++)
-+ if (nla_put_u16(skb, i, csi->data_q[i]))
-+ goto out;
-+ nla_nest_end(skb, b);
-+
-+ nla_nest_end(skb, a);
-+
-+ list_del(&csi->node);
-+ kfree(csi);
-+ phy->csi.count--;
-+
-+ err = phy->csi.count;
-+ }
-+out:
-+ spin_unlock_bh(&phy->csi.lock);
-+
-+ return err;
-+}
-
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- {
-@@ -1061,6 +1254,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- .policy = beacon_ctrl_policy,
- .maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
- },
-+ {
-+ .info = {
-+ .vendor_id = MTK_NL80211_VENDOR_ID,
-+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL,
-+ },
-+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+ WIPHY_VENDOR_CMD_NEED_RUNNING,
-+ .doit = mt7996_vendor_csi_ctrl,
-+ .dumpit = mt7996_vendor_csi_ctrl_dump,
-+ .policy = csi_ctrl_policy,
-+ .maxattr = MTK_VENDOR_ATTR_CSI_CTRL_MAX,
-+ },
- };
-
- void mt7996_vendor_register(struct mt7996_phy *phy)
-@@ -1068,6 +1273,9 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
- phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
- phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
-
-+ INIT_LIST_HEAD(&phy->csi.list);
-+ spin_lock_init(&phy->csi.lock);
-+
- spin_lock_init(&phy->amnt_lock);
- }
- #endif
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index e7d88828..7c9d12bb 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -7,6 +7,7 @@
-
- enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
- MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
- MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-@@ -238,6 +239,55 @@ enum mtk_vendor_attr_beacon_ctrl {
- NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
- };
-
-+enum mtk_vendor_attr_csi_ctrl {
-+ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
-+ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_DATA,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
-+ MTK_VENDOR_ATTR_CSI_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_csi_data {
-+ MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
-+ MTK_VENDOR_ATTR_CSI_DATA_PAD,
-+
-+ MTK_VENDOR_ATTR_CSI_DATA_VER,
-+ MTK_VENDOR_ATTR_CSI_DATA_TS,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSSI,
-+ MTK_VENDOR_ATTR_CSI_DATA_SNR,
-+ MTK_VENDOR_ATTR_CSI_DATA_BW,
-+ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
-+ MTK_VENDOR_ATTR_CSI_DATA_TA,
-+ MTK_VENDOR_ATTR_CSI_DATA_NUM,
-+ MTK_VENDOR_ATTR_CSI_DATA_I,
-+ MTK_VENDOR_ATTR_CSI_DATA_Q,
-+ MTK_VENDOR_ATTR_CSI_DATA_INFO,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
-+ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
-+ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
-+ MTK_VENDOR_ATTR_CSI_DATA_MODE,
-+ MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-+ MTK_VENDOR_ATTR_CSI_DATA_MAX =
-+ NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
-+};
- #endif
-
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2027-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2027-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
deleted file mode 100644
index 42650f3..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/2027-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
+++ /dev/null
@@ -1,628 +0,0 @@
-From f55be6f6596db120b2e48a7c87fcacf9d9df25ef Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Thu, 11 Jan 2024 08:55:13 +0800
-Subject: [PATCH 2027/2032] mtk: wifi: mt76: mt7992: add support to enable
- index FW log for ConsysPlanet
-
-Add support to enable and record index FW log, which is the input for ConsysPlanet, via mt76-test command.
-
-Usage:
-1. Foreground logging
- 1) Start: mt76-test phy0 idxlog
- 2) Stop: Ctrl + C
-2. Background logging
- 1) Start: mt76-test phy0 idxlog &
- 2) Stop: killall mt76-test
-3. Logging with FW Parser
- 1) Start Ethernet recording of FW Parser.
- 2) Start: mt76-test phy0 idxlog <PC's IP Address>
- 3) Stop: Ctrl + C
- 4) Stop FW Parser.
-
-Log Files
-- FW Log: FW text message
- - Location: /tmp/log/clog_(timestamp)/WIFI_FW_(timestamp).clog
-- Driver Log: log message printed at driver layer
- - Location: /tmp/log/clog_(timestamp)/WIFI_KERNEL_(timestamp).clog
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt7996/debugfs.c | 90 +++++++++++++++++--
- mt7996/mac.c | 10 ++-
- mt7996/mcu.c | 34 +++++++-
- mt7996/mcu.h | 4 +-
- mt7996/mt7996.h | 3 +
- tools/fwlog.c | 218 ++++++++++++++++++++++++++++++++++------------
- tools/main.c | 2 +
- tools/mt76-test.h | 3 +
- 8 files changed, 295 insertions(+), 69 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 6ccf0827..c236ec98 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -425,8 +425,8 @@ create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
- {
- struct dentry *f;
-
-- f = debugfs_create_file("fwlog_data", mode, parent, buf,
-- &relay_file_operations);
-+ f = debugfs_create_file(filename[0] == 'f' ? "fwlog_data" : "idxlog_data",
-+ mode, parent, buf, &relay_file_operations);
- if (IS_ERR(f))
- return NULL;
-
-@@ -517,6 +517,53 @@ mt7996_fw_debug_bin_get(void *data, u64 *val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get,
- mt7996_fw_debug_bin_set, "%lld\n");
-
-+static int
-+mt7996_idxlog_enable_get(void *data, u64 *val)
-+{
-+ struct mt7996_dev *dev = data;
-+
-+ *val = dev->idxlog_enable;
-+
-+ return 0;
-+}
-+
-+static int
-+mt7996_idxlog_enable_set(void *data, u64 val)
-+{
-+ static struct rchan_callbacks relay_cb = {
-+ .create_buf_file = create_buf_file_cb,
-+ .remove_buf_file = remove_buf_file_cb,
-+ };
-+ struct mt7996_dev *dev = data;
-+
-+ if (dev->idxlog_enable == !!val)
-+ return 0;
-+
-+ if (!dev->relay_idxlog) {
-+ dev->relay_idxlog = relay_open("idxlog_data", dev->debugfs_dir,
-+ 1500, 512, &relay_cb, NULL);
-+ if (!dev->relay_idxlog)
-+ return -ENOMEM;
-+ }
-+
-+ dev->idxlog_enable = !!val;
-+
-+ if (val) {
-+ int ret = mt7996_mcu_fw_time_sync(&dev->mt76);
-+ if (ret)
-+ return ret;
-+
-+ /* Reset relay channel only when it is not being written to. */
-+ relay_reset(dev->relay_idxlog);
-+ }
-+
-+ return mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM,
-+ val ? MCU_FW_LOG_RELAY_IDX : 0);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_idxlog_enable, mt7996_idxlog_enable_get,
-+ mt7996_idxlog_enable_set, "%llu\n");
-+
- static int
- mt7996_fw_util_wa_show(struct seq_file *file, void *data)
- {
-@@ -1037,6 +1084,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
- debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
- debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
-+ debugfs_create_file("idxlog_enable", 0600, dir, dev, &fops_idxlog_enable);
- /* TODO: wm fw cpu utilization */
- debugfs_create_file("fw_util_wa", 0400, dir, dev,
- &mt7996_fw_util_wa_fops);
-@@ -1103,6 +1151,32 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
- spin_unlock_irqrestore(&lock, flags);
- }
-
-+static void
-+mt7996_debugfs_write_idxlog(struct mt7996_dev *dev, const void *data, int len)
-+{
-+ static DEFINE_SPINLOCK(lock);
-+ unsigned long flags;
-+ void *dest;
-+
-+ if (!dev->relay_idxlog)
-+ return;
-+
-+ spin_lock_irqsave(&lock, flags);
-+
-+ dest = relay_reserve(dev->relay_idxlog, len + 4);
-+ if (!dest)
-+ dev_err(dev->mt76.dev, "Failed to reserve slot in %s\n",
-+ dev->relay_idxlog->base_filename);
-+ else {
-+ *(u32 *)dest = len;
-+ dest += 4;
-+ memcpy(dest, data, len);
-+ relay_flush(dev->relay_idxlog);
-+ }
-+
-+ spin_unlock_irqrestore(&lock, flags);
-+}
-+
- void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len)
- {
- struct {
-@@ -1127,11 +1201,15 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
-
- bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
- {
-- if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC)
-- return false;
-+ bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
-
-- if (dev->relay_fwlog)
-- mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
-+ if (is_fwlog) {
-+ if (dev->relay_fwlog)
-+ mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
-+ } else if (dev->relay_idxlog)
-+ mt7996_debugfs_write_idxlog(dev, data, len);
-+ else
-+ return false;
-
- return true;
- }
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 88e1fd14..c2a8e752 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2283,11 +2283,9 @@ void mt7996_mac_work(struct work_struct *work)
- mutex_lock(&mdev->mutex);
-
- mt76_update_survey(mphy);
-- if (++mphy->mac_work_count == 5) {
-+ if (++mphy->mac_work_count % 5 == 0) {
- int i;
-
-- mphy->mac_work_count = 0;
--
- mt7996_mac_update_stats(phy);
-
- /* Update DEV-wise information only in
-@@ -2306,6 +2304,12 @@ void mt7996_mac_work(struct work_struct *work)
- if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
- BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
- dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
-+
-+ if (mphy->mac_work_count == 100) {
-+ if (phy->dev->idxlog_enable && mt7996_mcu_fw_time_sync(mdev))
-+ dev_err(mdev->dev, "Failed to synchronize time with FW.\n");
-+ mphy->mac_work_count = 0;
-+ }
- } else if (mt7996_band_valid(phy->dev, i) &&
- test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
- break;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 9e04ea2b..0cb4cfa5 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -397,6 +397,7 @@ static void
- mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
- {
- #define UNI_EVENT_FW_LOG_FORMAT 0
-+#define UNI_EVENT_FW_LOG_MEMORY 1
- struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
- const char *data = (char *)&rxd[1] + 4, *type;
- struct tlv *tlv = (struct tlv *)data;
-@@ -408,7 +409,8 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
- goto out;
- }
-
-- if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT)
-+ if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT &&
-+ le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_MEMORY)
- return;
-
- data += sizeof(*tlv) + 4;
-@@ -3444,6 +3446,36 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level)
- sizeof(data), false);
- }
-
-+int mt7996_mcu_fw_time_sync(struct mt76_dev *dev)
-+{
-+ struct {
-+ u8 _rsv[4];
-+
-+ __le16 tag;
-+ __le16 len;
-+ __le32 sec;
-+ __le32 usec;
-+ } data = {
-+ .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_TIME_SYNC),
-+ .len = cpu_to_le16(sizeof(data) - 4),
-+ };
-+ struct timespec64 ts;
-+ struct tm tm;
-+
-+ ktime_get_real_ts64(&ts);
-+ data.sec = cpu_to_le32((u32)ts.tv_sec);
-+ data.usec = cpu_to_le32((u32)(ts.tv_nsec / 1000));
-+
-+ /* Dump synchronized time for ConsysPlanet to parse. */
-+ time64_to_tm(ts.tv_sec, 0, &tm);
-+ dev_info(dev->dev, "%ld-%02d-%02d %02d:%02d:%02d.%ld UTC\n",
-+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec / 1000);
-+
-+ return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(WSYS_CONFIG), &data,
-+ sizeof(data), true);
-+}
-+
- static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled)
- {
- struct {
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 7721a01b..826cf204 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -357,7 +357,8 @@ enum {
- MCU_FW_LOG_WM,
- MCU_FW_LOG_WA,
- MCU_FW_LOG_TO_HOST,
-- MCU_FW_LOG_RELAY = 16
-+ MCU_FW_LOG_RELAY = 16,
-+ MCU_FW_LOG_RELAY_IDX = 40
- };
-
- enum {
-@@ -949,6 +950,7 @@ enum {
- UNI_WSYS_CONFIG_FW_LOG_CTRL,
- UNI_WSYS_CONFIG_FW_DBG_CTRL,
- UNI_CMD_CERT_CFG = 6,
-+ UNI_WSYS_CONFIG_FW_TIME_SYNC, /* UNI_CMD_FW_TIME_SYNC in FW */
- };
-
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index f172eea2..10886cb1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -642,9 +642,11 @@ struct mt7996_dev {
- u8 fw_debug_bin;
- u16 fw_debug_seq;
- bool fw_debug_muru_disable;
-+ bool idxlog_enable;
-
- struct dentry *debugfs_dir;
- struct rchan *relay_fwlog;
-+ struct rchan *relay_idxlog;
-
- void *cal;
- u32 cur_prek_offset;
-@@ -896,6 +898,7 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
- int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
- int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
- int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
-+int mt7996_mcu_fw_time_sync(struct mt76_dev *dev);
- int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
- void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
- void mt7996_mcu_exit(struct mt7996_dev *dev);
-diff --git a/tools/fwlog.c b/tools/fwlog.c
-index 3c6a61d7..0e2de2dc 100644
---- a/tools/fwlog.c
-+++ b/tools/fwlog.c
-@@ -29,10 +29,9 @@ static const char *debugfs_path(const char *phyname, const char *file)
- static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
- {
- FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
--
- if (!f) {
-- fprintf(stderr, "Could not open fw_debug_bin file\n");
-- return 1;
-+ perror("fopen");
-+ return -1;
- }
-
- if (en && val)
-@@ -47,6 +46,21 @@ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
- return 0;
- }
-
-+static int mt76_set_idxlog_enable(const char *phyname, bool enable)
-+{
-+ FILE *f = fopen(debugfs_path(phyname, "idxlog_enable"), "w");
-+ if (!f) {
-+ perror("fopen");
-+ return -1;
-+ }
-+
-+ fprintf(f, "%hhu", enable);
-+
-+ fclose(f);
-+
-+ return 0;
-+}
-+
- int read_retry(int fd, void *buf, int len)
- {
- int out_len = 0;
-@@ -80,105 +94,193 @@ static void handle_signal(int sig)
- done = true;
- }
-
--int mt76_fwlog(const char *phyname, int argc, char **argv)
-+static int mt76_log_socket(struct sockaddr_in *remote, char *ip)
- {
--#define BUF_SIZE 1504
- struct sockaddr_in local = {
- .sin_family = AF_INET,
- .sin_addr.s_addr = INADDR_ANY,
- };
-- struct sockaddr_in remote = {
-- .sin_family = AF_INET,
-- .sin_port = htons(55688),
-- };
-- char *buf = calloc(BUF_SIZE, sizeof(char));
-- int ret = 0;
-- /* int yes = 1; */
-- int s, fd;
--
-- if (argc < 1) {
-- fprintf(stderr, "need destination address\n");
-- return 1;
-- }
-+ int s, ret;
-
-- if (!inet_aton(argv[0], &remote.sin_addr)) {
-- fprintf(stderr, "invalid destination address\n");
-- return 1;
-+ remote->sin_family = AF_INET;
-+ remote->sin_port = htons(55688);
-+ if (!inet_aton(ip, &remote->sin_addr)) {
-+ fprintf(stderr, "Invalid destination IP address: %s\n", ip);
-+ return -EINVAL;
- }
-
- s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (s < 0) {
- perror("socket");
-- return 1;
-+ return s;
- }
-
-- /* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
-- if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
-+ ret = bind(s, (struct sockaddr *)&local, sizeof(local));
-+ if (ret) {
- perror("bind");
-- return 1;
-+ close(s);
-+ return ret;
- }
-
-- if (mt76_set_fwlog_en(phyname, true, argv[1]))
-- return 1;
-+ return s;
-+}
-+
-+static int mt76_log_relay(int in_fd, int out_fd, struct sockaddr_in *remote)
-+{
-+ char *buf = malloc(FWLOG_BUF_SIZE);
-+ int ret = 0;
-
-- fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
-- if (fd < 0) {
-- fprintf(stderr, "Could not open fwlog_data file: %s\n", strerror(errno));
-- ret = 1;
-- goto out;
-+ if (!buf) {
-+ perror("malloc");
-+ return -ENOMEM;
- }
-
- signal(SIGTERM, handle_signal);
- signal(SIGINT, handle_signal);
- signal(SIGQUIT, handle_signal);
-
-- while (1) {
-+ while (!done) {
- struct pollfd pfd = {
-- .fd = fd,
-- .events = POLLIN | POLLHUP | POLLERR,
-+ .fd = in_fd,
-+ .events = POLLIN,
- };
- uint32_t len;
-- int r;
--
-- if (done)
-- break;
-+ int rc;
-
- poll(&pfd, 1, -1);
-
-- r = read_retry(fd, &len, sizeof(len));
-- if (r < 0)
-+ rc = read_retry(in_fd, &len, sizeof(len));
-+ if (rc < 0) {
-+ if (!done) {
-+ fprintf(stderr, "Failed to read relay file.\n");
-+ ret = -1;
-+ }
- break;
--
-- if (!r)
-+ }
-+ if (!rc)
- continue;
-
-- if (len > BUF_SIZE) {
-- fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
-- ret = 1;
-+ if (len > FWLOG_BUF_SIZE) {
-+ fprintf(stderr, "Log size was too large: %u bytes\n", len);
-+ ret = -ENOMEM;
- break;
- }
-
-- if (done)
-+ rc = read_retry(in_fd, buf, len);
-+ if (rc < 0) {
-+ if (!done) {
-+ fprintf(stderr, "Failed to read relay file.\n");
-+ ret = -1;
-+ }
- break;
--
-- r = read_retry(fd, buf, len);
-- if (done)
-+ }
-+ if (rc != len) {
-+ fprintf(stderr, "Expected log size: %u bytes\n", len);
-+ fprintf(stderr, "Read log size: %u bytes\n", rc);
-+ ret = -EIO;
- break;
-+ }
-
-- if (r != len) {
-- fprintf(stderr, "Short read: %d < %d\n", r, len);
-- ret = 1;
-+ if (remote)
-+ rc = sendto(out_fd, buf, len, 0, (struct sockaddr *)remote, sizeof(*remote));
-+ else
-+ rc = write(out_fd, buf, len);
-+ if (rc < 0) {
-+ perror("sendto/write");
-+ ret = -1;
- break;
- }
-+ }
-+
-+ free(buf);
-+
-+ return ret;
-+}
-+
-+int mt76_fwlog(const char *phyname, int argc, char **argv)
-+{
-+ struct sockaddr_in remote;
-+ int in_fd, out_fd, ret;
-+
-+ if (argc < 1) {
-+ fprintf(stderr, "need destination address\n");
-+ return -EINVAL;
-+ }
-+
-+ out_fd = mt76_log_socket(&remote, argv[0]);
-+ if (out_fd < 0)
-+ return out_fd;
-+
-+ ret = mt76_set_fwlog_en(phyname, true, argv[1]);
-+ if (ret)
-+ goto close;
-
-- /* send buf */
-- sendto(s, buf, len, 0, (struct sockaddr *)&remote, sizeof(remote));
-+ in_fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
-+ if (in_fd < 0) {
-+ perror("open");
-+ goto disable;
- }
-
-- close(fd);
-+ if (mt76_log_relay(in_fd, out_fd, &remote))
-+ fprintf(stderr, "Failed to relay FW log.\n");
-
--out:
-- mt76_set_fwlog_en(phyname, false, NULL);
-+ close(in_fd);
-+disable:
-+ ret = mt76_set_fwlog_en(phyname, false, NULL);
-+close:
-+ close(out_fd);
-+
-+ return ret;
-+}
-+
-+int mt76_idxlog(const char *phyname, int argc, char **argv)
-+{
-+#define IDXLOG_FILE_PATH "/tmp/log/WIFI_FW.clog"
-+ struct sockaddr_in remote;
-+ int in_fd, out_fd, ret;
-+
-+ if (argc) {
-+ out_fd = mt76_log_socket(&remote, argv[0]);
-+ if (out_fd < 0)
-+ return out_fd;
-+ } else {
-+ out_fd = open(IDXLOG_FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR);
-+ if (out_fd < 0) {
-+ perror("open");
-+ return -1;
-+ }
-+ }
-+
-+ ret = mt76_set_idxlog_enable(phyname, true);
-+ if (ret)
-+ goto close;
-+
-+ in_fd = open(debugfs_path(phyname, "idxlog_data"), O_RDONLY);
-+ if (in_fd < 0) {
-+ perror("open");
-+ goto disable;
-+ }
-+
-+ if (mt76_log_relay(in_fd, out_fd, argc ? &remote : NULL))
-+ fprintf(stderr, "Failed to relay index log.\n");
-+
-+ close(in_fd);
-+disable:
-+ ret = mt76_set_idxlog_enable(phyname, false);
-+close:
-+ close(out_fd);
-+
-+ if (argc)
-+ system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
-+ "clog_dir=/tmp/log/clog_${timestamp};"
-+ "mkdir ${clog_dir};"
-+ "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
-+ else
-+ system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
-+ "clog_dir=/tmp/log/clog_${timestamp};"
-+ "mkdir ${clog_dir};"
-+ "mv /tmp/log/WIFI_FW.clog ${clog_dir}/WIFI_FW_${timestamp}.clog;"
-+ "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
-
- return ret;
- }
-diff --git a/tools/main.c b/tools/main.c
-index 699a9eea..e9e25567 100644
---- a/tools/main.c
-+++ b/tools/main.c
-@@ -198,6 +198,8 @@ int main(int argc, char **argv)
- ret = mt76_eeprom(phy, argc, argv);
- else if (!strcmp(cmd, "fwlog"))
- ret = mt76_fwlog(phyname, argc, argv);
-+ else if (!strcmp(cmd, "idxlog"))
-+ ret = mt76_idxlog(phyname, argc, argv);
- else
- usage();
-
-diff --git a/tools/mt76-test.h b/tools/mt76-test.h
-index d2fafa86..b9d508c5 100644
---- a/tools/mt76-test.h
-+++ b/tools/mt76-test.h
-@@ -22,6 +22,8 @@
- #define EEPROM_FILE_PATH_FMT "/tmp/mt76-test-%s"
- #define EEPROM_PART_SIZE 20480
-
-+#define FWLOG_BUF_SIZE 1504
-+
- struct nl_msg;
- struct nlattr;
-
-@@ -61,5 +63,6 @@ extern unsigned char *eeprom_data;
- void usage(void);
- int mt76_eeprom(int phy, int argc, char **argv);
- int mt76_fwlog(const char *phyname, int argc, char **argv);
-+int mt76_idxlog(const char *phyname, int argc, char **argv);
-
- #endif
---
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc b/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
index 38d3c2c..4764a18 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
@@ -1,94 +1,119 @@
#patch patches (come from openwrt/lede/target/linux/mediatek)
SRC_URI_append = " \
file://0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch \
- file://0002-mtk-wifi-mt76-connac-use-peer-address-for-station-BM.patch \
- file://0003-mtk-wifi-mt76-mt7996-disable-rx-header-translation-f.patch \
- file://0004-mtk-wifi-mt76-mt7996-set-RCPI-value-in-rate-control-.patch \
- file://0005-mtk-wifi-mt76-mt7996-enable-hw-cso-module.patch \
- file://0006-mtk-wifi-mt76-mt7996-fix-non-main-BSS-no-beacon-issu.patch \
- file://0007-mtk-wifi-mt76-mt7996-initialize-variable-to-avoid-un.patch \
- file://0008-mtk-wifi-mt76-mt7996-enable-ser-query.patch \
- file://0009-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch \
- file://0010-mtk-wifi-mt76-mt7996-add-eagle-default-bin-of-differ.patch \
- file://0011-mtk-wifi-mt76-mt7996-add-kite-fw-default-bin-for-dif.patch \
- file://0012-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch \
- file://0013-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch \
- file://0014-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch \
- file://0015-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch \
- file://0016-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch \
- file://0017-mtk-wifi-mt76-mt7996-add-sanity-check-for-NAPI-sched.patch \
- file://0999-mtk-wifi-mt76-mt7996-for-build-pass.patch \
- file://1000-mtk-wifi-mt76-mt7996-add-debug-tool.patch \
- file://1001-mtk-wifi-mt76-mt7996-support-record-muru-algo-log-wh.patch \
- file://1002-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch \
- file://1003-mtk-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch \
- file://1004-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch \
- file://1005-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch \
- file://1006-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch \
- file://1007-mtk-wifi-mt76-mt7996-add-txpower-support.patch \
- file://1008-mtk-wifi-mt76-mt7996-add-single-sku.patch \
- file://1009-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch \
- file://1010-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch \
- file://1011-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch \
- file://1012-mtk-wifi-mt76-mt7996-add-mu-vendor-command-support.patch \
- file://1013-mtk-wifi-mt76-mt7996-Add-air-monitor-support.patch \
- file://1014-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch \
- file://1015-mtk-wifi-mt76-mt7996-add-vendor-cmd-to-get-available.patch \
- file://1016-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch \
- file://1017-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch \
- file://1018-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch \
- file://1019-mtk-wifi-mt76-mt7996-add-vendor-subcmd-EDCCA-ctrl-en.patch \
- file://1020-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch \
- file://1021-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch \
- file://1022-mtk-wifi-mt76-mt7996-add-eagle-iFEM-HWITS-ZWDFS-SW-w.patch \
- file://1023-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch \
- file://1024-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch \
- file://1025-mtk-wifi-mt76-mt7996-add-ibf-control-vendor-cmd.patch \
- file://1026-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch \
- file://1027-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch \
- file://1028-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch \
- file://1029-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch \
- file://1030-mtk-wifi-mt76-mt7996-add-three-wire-pta-support.patch \
- file://1031-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch \
- file://1032-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch \
- file://1033-mtk-wifi-mt76-mt7996-add-cert-patch.patch \
- file://1034-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch \
- file://1035-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch \
- file://1036-mtk-wifi-mt76-testmode-add-channel-68-96.patch \
- file://1037-mtk-wifi-mt76-mt7996-support-enable-disable-pp-featu.patch \
- file://1038-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch \
- file://1039-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch \
- file://1040-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch \
- file://1041-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch \
- file://1042-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch \
- file://1043-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch \
- file://1044-mtk-wifi-mt76-mt7996-support-disable-muru-debug-info.patch \
- file://2000-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch \
- file://2001-mtk-wifi-mt76-rework-wed-rx-flow.patch \
- file://2002-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch \
- file://2003-mtk-wifi-mt76-add-random-early-drop-support.patch \
- file://2004-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch \
- file://2005-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch \
- file://2006-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch \
- file://2007-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch \
- file://2008-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch \
- file://2009-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch \
- file://2010-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch \
- file://2011-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch \
- file://2012-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch \
- file://2013-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch \
- file://2014-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch \
- file://2015-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch \
- file://2016-mtk-wifi-mt76-mt7996-add-SER-state-log-for-debug.patch \
- file://2017-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch \
- file://2018-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch \
- file://2019-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch \
- file://2020-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch \
- file://2021-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch \
- file://2022-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch \
- file://2023-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch \
- file://2024-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch \
- file://2025-mtk-wifi-mt76-mt7996-Porting-wifi6-txpower-fix-to-ea.patch \
- file://2026-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch \
- file://2027-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch \
+ file://0002-bp-sync-upstream-changes.patch \
+ file://0003-mtk-wifi-mt76-mt7996-let-upper-layer-handle-MGMT-fra.patch \
+ file://0004-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch \
+ file://0005-mtk-wifi-mt76-mt7996-fix-MBSS.patch \
+ file://0006-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch \
+ file://0007-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch \
+ file://0008-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch \
+ file://0009-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch \
+ file://0010-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch \
+ file://0011-mtk-wifi-mt76-mt7996-enable-ser-query.patch \
+ file://0012-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch \
+ file://0013-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch \
+ file://0014-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch \
+ file://0015-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch \
+ file://0016-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch \
+ file://0017-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch \
+ file://0018-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch \
+ file://0019-mtk-wifi-mt76-mt7996-for-build-pass.patch \
+ file://0020-mtk-wifi-mt76-mt7996-add-debug-tool.patch \
+ file://0021-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch \
+ file://0022-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch \
+ file://0023-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch \
+ file://0024-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch \
+ file://0025-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch \
+ file://0026-mtk-wifi-mt76-mt7996-add-txpower-support.patch \
+ file://0027-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch \
+ file://0028-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch \
+ file://0029-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch \
+ file://0030-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch \
+ file://0031-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch \
+ file://0032-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch \
+ file://0033-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch \
+ file://0034-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch \
+ file://0035-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch \
+ file://0036-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch \
+ file://0037-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch \
+ file://0038-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch \
+ file://0039-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch \
+ file://0040-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch \
+ file://0041-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch \
+ file://0042-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch \
+ file://0043-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch \
+ file://0044-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch \
+ file://0045-mtk-wifi-mt76-mt7996-add-cert-patch.patch \
+ file://0046-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch \
+ file://0047-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch \
+ file://0048-mtk-wifi-mt76-testmode-add-channel-68-96.patch \
+ file://0049-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch \
+ file://0050-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch \
+ file://0051-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch \
+ file://0052-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch \
+ file://0053-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch \
+ file://0054-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch \
+ file://0055-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch \
+ file://0056-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch \
+ file://0057-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch \
+ file://0058-mtk-wifi-mt76-rework-wed-rx-flow.patch \
+ file://0059-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch \
+ file://0060-mtk-wifi-mt76-add-random-early-drop-support.patch \
+ file://0061-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch \
+ file://0062-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch \
+ file://0063-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch \
+ file://0064-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch \
+ file://0065-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch \
+ file://0066-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch \
+ file://0067-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch \
+ file://0068-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch \
+ file://0069-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch \
+ file://0070-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch \
+ file://0071-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch \
+ file://0072-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch \
+ file://0073-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch \
+ file://0074-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch \
+ file://0075-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch \
+ file://0076-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch \
+ file://0077-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch \
+ file://0078-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch \
+ file://0079-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch \
+ file://0080-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch \
+ file://0081-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch \
+ file://0082-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch \
+ file://0083-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch \
+ file://0084-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch \
+ file://0085-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch \
+ file://0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch \
+ file://0087-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch \
+ file://0088-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch \
+ file://0089-wifi-mt76-mt7996-enable-MLO-capability.patch \
+ file://0090-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch \
+ file://0091-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch \
+ file://0092-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch \
+ file://0093-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch \
+ file://0094-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch \
+ file://0095-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch \
+ file://0096-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch \
+ file://0097-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch \
+ file://0098-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch \
+ file://0099-wifi-mt76-connac-rework-connac-helpers.patch \
+ file://0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch \
+ file://0101-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch \
+ file://0102-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch \
+ file://0103-wifi-mt76-mt7996-implement-mld-address-translation.patch \
+ file://0104-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch \
+ file://0105-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch \
+ file://0106-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch \
+ file://0107-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch \
+ file://0108-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch \
+ file://0109-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch \
+ file://0110-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch \
+ file://0111-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch \
+ file://0112-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch \
+ file://0113-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch \
+ file://0114-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch \
+ file://0115-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch \
+ file://0116-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch \
"
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7915_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7915_rom_patch.bin
index 87ad985..10d3d8f 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7915_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7915_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wa.bin
index 16af788..83b635b 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wm.bin
index a2c95da..d09cce2 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7915_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7916_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7916_rom_patch.bin
index a145348..fee3cd8 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7916_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7916_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wa.bin
index fe19921..b6ddf15 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wm.bin
index 6c0ecdb..c7162db 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7916_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_rom_patch.bin
index 0f956b4..34c821e 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wa.bin
index 75dba3c..fd2fc49 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wm.bin
index a526bdd..8288120 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wo.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wo.bin
index aaf9865..4f807ba 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wo.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wo.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wo_0.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wo_0.bin
deleted file mode 100644
index 799ad67..0000000
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7981_wo_0.bin
+++ /dev/null
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch.bin
index d0f1844..0c285b4 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch_mt7975.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch_mt7975.bin
index b40b936..8271633 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch_mt7975.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_rom_patch_mt7975.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wa.bin
index ae937a8..867a764 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm.bin
index da731ad..5561f9f 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm_mt7975.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm_mt7975.bin
index 5664006..c6efce6 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm_mt7975.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wm_mt7975.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_0.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_0.bin
index 91d591f..d2a7674 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_0.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_0.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_1.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_1.bin
index 508e6a4..f067ac3 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_1.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7986_wo_1.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin
index 1f01751..3203f47 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin
index f277232..6bb8783 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_24.bin
index 1d128b7..0ee44c4 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_24.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin
index 31d86e1..62b15ff 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin
index cf61154..c6127eb 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_24.bin
index 50d3a2e..556abce 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_24.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin
index 27c7c00..0ddfc82 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin
index f62759a..54c9f8d 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_24.bin
index f37af42..cee6d27 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_24.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin
index b3b3529..ca62f15 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin
index 6623ad1..f645c9b 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_24.bin
index 7700262..f2b68ec 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_24.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin
index 74ade16..66cc70b 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin
index 04a3139..5643c9a 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_24.bin
index b1f5bad..f4a2305 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_24.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
index d30bb8b..e9765d9 100755
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_eeprom_2i5i6i.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_eeprom_2i5i6i.bin
new file mode 100644
index 0000000..5ad238a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_eeprom_2i5i6i.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
index 6952a7f..524024f 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin
index cdba71d..17a1475 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
index e376241..3e04fe1 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin
index 1e54cfc..bf1ff57 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
index 84f59d7..c26be71 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin
index 232949e..c685756 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
index b9766ec..e275cbb 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin
index 074de8c..04a5951 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/linux-mt76_3.x.bb b/recipes-wifi/linux-mt76/linux-mt76_3.x.bb
index 92c37fa..223e7fa 100644
--- a/recipes-wifi/linux-mt76/linux-mt76_3.x.bb
+++ b/recipes-wifi/linux-mt76/linux-mt76_3.x.bb
@@ -5,7 +5,7 @@
inherit module
-require mt76-3x.inc
+require mt76.inc
SRC_URI = " \
git://git@github.com/openwrt/mt76.git;protocol=https;branch=master \
file://COPYING;subdir=git \
diff --git a/recipes-wifi/linux-mt76/mt76-3x.inc b/recipes-wifi/linux-mt76/mt76-3x.inc
deleted file mode 100644
index 3cee0cc..0000000
--- a/recipes-wifi/linux-mt76/mt76-3x.inc
+++ /dev/null
@@ -1 +0,0 @@
-SRCREV_mt7988 = "2135e201e7a9339e018d4e2d4a33c73266e674d7"
diff --git a/recipes-wifi/linux-mt76/mt76-test.bb b/recipes-wifi/linux-mt76/mt76-test.bb
index b84adb4..853e974 100644
--- a/recipes-wifi/linux-mt76/mt76-test.bb
+++ b/recipes-wifi/linux-mt76/mt76-test.bb
@@ -10,7 +10,6 @@
PV = "1.0"
require mt76.inc
-require mt76-3x.inc
SRC_URI = " \
git://git@github.com/openwrt/mt76.git;protocol=https \
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
new file mode 100644
index 0000000..c8e7f92
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
@@ -0,0 +1,101 @@
+From f5102b209870065c3e3719dd113892eafd4bb59e Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:31 +0530
+Subject: [PATCH 001/104] hostapd: MLO: fix for_each_mld_link macro
+
+Currently for_each_mld_link macro uses 3 nested for loops. Since now the
+affliated links are linked together via linked list, the logic can
+be improvised by using dl_list_for_each macro instead which uses one for
+loop.
+
+Modify for_each_mld_link macro to use dl_list_for_each instead.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/beacon.c | 10 +---------
+ src/ap/hostapd.h | 17 +++--------------
+ src/ap/sta_info.c | 4 +---
+ 3 files changed, 5 insertions(+), 26 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 32865f667..195c7bbd9 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -945,7 +945,6 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
+ {
+ struct probe_resp_params sta_info_params;
+ struct hostapd_data *link;
+- unsigned int probed_mld_id, i, j;
+
+ params->mld_ap = NULL;
+ params->mld_info = os_zalloc(sizeof(*params->mld_info));
+@@ -956,14 +955,7 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
+ "MLD: Got ML probe request with AP MLD ID %d for links %04x",
+ mld_id, links);
+
+- /*
+- * We want to include the AP MLD ID in the response if it was
+- * included in the request.
+- */
+- probed_mld_id = mld_id != -1 ? mld_id : hostapd_get_mld_id(hapd);
+-
+- for_each_mld_link(link, i, j, hapd->iface->interfaces,
+- probed_mld_id) {
++ for_each_mld_link(link, hapd) {
+ struct mld_link_info *link_info;
+ size_t buflen;
+ u8 mld_link_id = link->mld_link_id;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index affe4f604..d12efb104 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -817,19 +817,8 @@ struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
+
+ bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+
+-#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
+- for (_iface_idx = 0; \
+- _iface_idx < (_ifaces)->count; \
+- _iface_idx++) \
+- for (_bss_idx = 0; \
+- _bss_idx < \
+- (_ifaces)->iface[_iface_idx]->num_bss; \
+- _bss_idx++) \
+- for (_link = \
+- (_ifaces)->iface[_iface_idx]->bss[_bss_idx]; \
+- _link && _link->conf->mld_ap && \
+- hostapd_get_mld_id(_link) == _mld_id; \
+- _link = NULL)
++#define for_each_mld_link(partner, self) \
++ dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
+
+ #else /* CONFIG_IEEE80211BE */
+
+@@ -838,7 +827,7 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+ return true;
+ }
+
+-#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
++#define for_each_mld_link(partner, self) \
+ if (false)
+
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 122880a3d..2423ff189 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -1761,10 +1761,8 @@ static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+ {
+ struct hostapd_data *tmp_hapd;
+- unsigned int i, j;
+
+- for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
+- hostapd_get_mld_id(hapd)) {
++ for_each_mld_link(tmp_hapd, hapd) {
+ struct sta_info *tmp_sta;
+
+ if (hapd == tmp_hapd)
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
new file mode 100644
index 0000000..86c2263
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
@@ -0,0 +1,31 @@
+From 3ca32441ecd9d1a52f736d4a4fffdc24de629e90 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:32 +0530
+Subject: [PATCH 002/104] hostapd: MLO: frame link add command on per BSS basis
+
+Currently function nl80211_link_add() creates the link add NL message on
+drv basis which in turn uses drv's first BSS always. In order to support
+link add for various other interfaces, use BSS handler to create the NL
+message.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 4949de577..042bc97a8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13876,7 +13876,7 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
+ }
+ }
+
+- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
++ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ADD_LINK);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
new file mode 100644
index 0000000..8dfcc5c
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
@@ -0,0 +1,29 @@
+From 695a2dbff28bb259c2b4f8bfdfb9040f81ab7e90 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:33 +0530
+Subject: [PATCH 003/104] nl80211: Print the interface name in debug during
+ link add
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 042bc97a8..98948bfb1 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13900,8 +13900,8 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
+ bss->valid_links |= BIT(link_id);
+ bss->links[link_id].ctx = bss_ctx;
+
+- wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x",
+- bss->valid_links);
++ wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x on %s",
++ bss->valid_links, bss->ifname);
+ return 0;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
new file mode 100644
index 0000000..5f56b72
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
@@ -0,0 +1,29 @@
+From 49a31ee63f482c930e001e2b6a13bf9261fcf5de Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:34 +0530
+Subject: [PATCH 004/104] hostapd: MLO: send link_id on sta_deauth()
+
+Function i802_sta_deauth() already has the link_id passed to it in its
+arguments. Use that to pass it down to send mlme handler.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 98948bfb1..e5fa22b59 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8254,7 +8254,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth), 0, 0, 0, 0,
+- 0, NULL, 0, 0, -1);
++ 0, NULL, 0, 0, link_id);
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
new file mode 100644
index 0000000..e8adbc0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
@@ -0,0 +1,77 @@
+From ec1fdb73853632e9a9003f8c59620d1e12f6d2d0 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:35 +0530
+Subject: [PATCH 005/104] hostapd: MLO: handle auth/assoc on link address
+
+Modify authentication and association frames to be always sent with link
+address as A1 and A3 for ease of Tx status handling.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 25 ++-----------------------
+ 1 file changed, 2 insertions(+), 23 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 5a3132de4..b20300bab 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -416,14 +416,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
+ struct wpabuf *ml_resp = NULL;
+
+ #ifdef CONFIG_IEEE80211BE
+- /*
+- * Once a non-AP MLD is added to the driver, the addressing should use
+- * the MLD MAC address. Thus, use the MLD address instead of translating
+- * the addresses.
+- */
+ if (ap_sta_is_mld(hapd, sta)) {
+- sa = hapd->mld->mld_addr;
+-
+ ml_resp = hostapd_ml_auth_resp(hapd);
+ if (!ml_resp)
+ return -1;
+@@ -444,7 +437,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
+ WLAN_FC_STYPE_AUTH);
+ os_memcpy(reply->da, dst, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
+- os_memcpy(reply->bssid, bssid, ETH_ALEN);
++ os_memcpy(reply->bssid, sa, ETH_ALEN);
+
+ reply->u.auth.auth_alg = host_to_le16(auth_alg);
+ reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
+@@ -3265,14 +3258,9 @@ static void handle_auth(struct hostapd_data *hapd,
+ bssid = mgmt->bssid;
+
+ #ifdef CONFIG_IEEE80211BE
+- /*
+- * Once a non-AP MLD is added to the driver, the addressing should use
+- * the MLD MAC address. It is the responsibility of the driver to
+- * handle the translations.
+- */
+ if (ap_sta_is_mld(hapd, sta)) {
+ dst = sta->addr;
+- bssid = hapd->mld->mld_addr;
++ bssid = hapd->own_addr;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+
+@@ -4823,15 +4811,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+ WLAN_FC_STYPE_ASSOC_RESP));
+
+-#ifdef CONFIG_IEEE80211BE
+- /*
+- * Once a non-AP MLD is added to the driver, the addressing should use
+- * MLD MAC address.
+- */
+- if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
+- sa = hapd->mld->mld_addr;
+-#endif /* CONFIG_IEEE80211BE */
+-
+ os_memcpy(reply->da, addr, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
+ os_memcpy(reply->bssid, sa, ETH_ALEN);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
new file mode 100644
index 0000000..2a7bacd
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
@@ -0,0 +1,93 @@
+From 4be7c245a946016c41a69c7469e00d22aaa32a46 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:36 +0530
+Subject: [PATCH 006/104] hostapd: MLO: reset auth state machine's ML info
+
+Currently auth state machine ML info is set only when the it is created
+newly. However, if the association is tried again, the state machine will
+exist already and hence the ML info will not be refreshed. This leads to
+an issue where if in the subsequent association request, the MLD info is
+different than old info then validation of it will fail.
+
+Fix this issue by refreshing the auth state machine's ML info every time
+association request is handled.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 32 ++++++++++++++++++--------------
+ src/ap/wpa_auth.c | 1 +
+ 2 files changed, 19 insertions(+), 14 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index b20300bab..98398ccdd 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4032,15 +4032,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+
+ if (hapd->conf->wpa && wpa_ie) {
+ enum wpa_validate_result res;
++#ifdef CONFIG_IEEE80211BE
++ struct mld_info *info = &sta->mld_info;
++ bool init = false;
++#endif /* CONFIG_IEEE80211BE */
+
+ wpa_ie -= 2;
+ wpa_ie_len += 2;
+
+ if (!sta->wpa_sm) {
+-#ifdef CONFIG_IEEE80211BE
+- struct mld_info *info = &sta->mld_info;
+-#endif /* CONFIG_IEEE80211BE */
+-
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr,
+ p2p_dev_addr);
+@@ -4050,19 +4050,23 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ "Failed to initialize RSN state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+-
+ #ifdef CONFIG_IEEE80211BE
+- if (ap_sta_is_mld(hapd, sta)) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Set ML info in RSN Authenticator");
+- wpa_auth_set_ml_info(sta->wpa_sm,
+- hapd->mld->mld_addr,
+- sta->mld_assoc_link_id,
+- info);
+- }
+-#endif /* CONFIG_IEEE80211BE */
++ init = true;
+ }
+
++ if (ap_sta_is_mld(hapd, sta)) {
++ wpa_printf(MSG_DEBUG,
++ "MLD: %s ML info in RSN Authenticator",
++ init ? "Set" : "Reset");
++ wpa_auth_set_ml_info(sta->wpa_sm,
++ hapd->mld->mld_addr,
++ sta->mld_assoc_link_id,
++ info);
++ }
++#else
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 01a10b23c..0d15c4209 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -6820,6 +6820,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ return;
+
+ os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
++ sm->n_mld_affiliated_links = 0;
+
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "MLD: Initialization");
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
new file mode 100644
index 0000000..9afd8f4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
@@ -0,0 +1,201 @@
+From d56daa4ebdf544a30f30986097edd6d5f9b8674f Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:37 +0530
+Subject: [PATCH 007/104] hostapd: MLO: add support for cohosted ML BSS
+
+Currently MLO is being supported with an assumption of only single BSS per
+link in the hostapd conf file. This needs to be extended when cohosted ML
+BSS exist in the same config file.
+
+Extend the support for cohosted BSSes. This is required for MBSSID MLO
+support as well.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/main.c | 38 +++++++-------------------
+ src/ap/hostapd.c | 70 +++++++++++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 73 insertions(+), 35 deletions(-)
+
+diff --git a/hostapd/main.c b/hostapd/main.c
+index a43d3a5be..524a10274 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -158,6 +158,9 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 *b = conf->bssid;
+ struct wpa_driver_capa capa;
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_data *h_hapd = NULL;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
+ wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
+@@ -165,35 +168,10 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ }
+
+ #ifdef CONFIG_IEEE80211BE
+- for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
+- struct hostapd_iface *h = iface->interfaces->iface[i];
+- struct hostapd_data *h_hapd = h->bss[0];
+- struct hostapd_bss_config *hconf = h_hapd->conf;
+-
+- if (h == iface) {
+- wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
+- continue;
+- }
+-
+- if (!hconf->mld_ap) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Skip non-MLD");
+- continue;
+- }
+-
+- if (!hostapd_is_ml_partner(hapd, h_hapd)) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Skip non matching MLD vif name");
+- continue;
+- }
+-
+- wpa_printf(MSG_DEBUG, "MLD: Found matching MLD interface");
+- if (!h_hapd->drv_priv) {
+- wpa_printf(MSG_DEBUG,
+- "MLD: Matching MLD BSS not initialized yet");
+- continue;
+- }
++ if (conf->mld_ap)
++ h_hapd = hostapd_mld_get_first_bss(hapd);
+
++ if (h_hapd) {
+ hapd->drv_priv = h_hapd->drv_priv;
+ hapd->interface_added = h_hapd->interface_added;
+
+@@ -214,6 +192,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ }
+
+ hostapd_mld_add_link(hapd);
++ wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
+
+ goto setup_mld;
+ }
+@@ -298,6 +278,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
+ os_memcpy(hapd->own_addr, b, ETH_ALEN);
+
+ hostapd_mld_add_link(hapd);
++ wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
+ }
+
+ setup_mld:
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f8cb6432d..ff1d8f9d0 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1333,6 +1333,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+ int flush_old_stations = 1;
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_data *h_hapd = NULL;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (!hostapd_mld_is_first_bss(hapd))
+ wpa_printf(MSG_DEBUG,
+@@ -1379,6 +1382,21 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ } while (mac_in_conf(hapd->iconf, hapd->own_addr));
+ }
+
++#ifdef CONFIG_IEEE80211BE
++ if (conf->mld_ap) {
++ h_hapd = hostapd_mld_get_first_bss(hapd);
++
++ if (h_hapd) {
++ hapd->drv_priv = h_hapd->drv_priv;
++ hapd->interface_added = h_hapd->interface_added;
++ hostapd_mld_add_link(hapd);
++ wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
++ goto setup_mld;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ hapd->interface_added = 1;
+ if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+ conf->iface, addr, hapd,
+@@ -1393,8 +1411,33 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+
+ if (!addr)
+ os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
++
++#ifdef CONFIG_IEEE80211BE
++ if (hapd->conf->mld_ap) {
++ wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
++ hapd->mld_link_id, hapd->conf->iface);
++ os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN);
++ hostapd_mld_add_link(hapd);
++ }
+ }
+
++setup_mld:
++
++ if (hapd->conf->mld_ap && !first) {
++ wpa_printf(MSG_DEBUG,
++ "MLD: Set link_id=%u, mld_addr=" MACSTR
++ ", own_addr=" MACSTR,
++ hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
++ MAC2STR(hapd->own_addr));
++
++ if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
++ hapd->own_addr))
++ return -1;
++ }
++#else
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ if (conf->wmm_enabled < 0)
+ conf->wmm_enabled = hapd->iconf->ieee80211n |
+ hapd->iconf->ieee80211ax;
+@@ -4679,17 +4722,30 @@ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
+ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ u8 link_id)
+ {
+- unsigned int i;
++ struct hostapd_iface *iface;
++ struct hostapd_data *bss;
++ struct hostapd_bss_config *conf;
++ unsigned int i, j;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
+- struct hostapd_data *h_hapd = h->bss[0];
+-
+- if (!hostapd_is_ml_partner(hapd, h_hapd))
++ iface = hapd->iface->interfaces->iface[i];
++ if (!iface)
+ continue;
+
+- if (h_hapd->mld_link_id == link_id)
+- return h_hapd;
++ for (j = 0; j < iface->num_bss; j++) {
++ bss = iface->bss[j];
++ conf = bss->conf;
++
++ if (!conf->mld_ap ||
++ !hostapd_is_ml_partner(hapd, bss))
++ continue;
++
++ if (!bss->drv_priv)
++ continue;
++
++ if (bss->mld_link_id == link_id)
++ return bss;
++ }
+ }
+
+ return NULL;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
new file mode 100644
index 0000000..42c4b67
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
@@ -0,0 +1,368 @@
+From 27dbd9d9796d656c8cf78d51d48162208080a987 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:38 +0530
+Subject: [PATCH 008/104] hostapd: MLO: extend support for cohosted ML BSS
+
+Modify necessary helper apis to support multiple BSS support for MLO to
+make the changes scalable.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 116 ++++++++++++++++------------------------
+ src/ap/ieee802_11_eht.c | 27 +++-------
+ src/ap/wpa_auth_glue.c | 52 +++++++++++-------
+ 3 files changed, 89 insertions(+), 106 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 98398ccdd..26e3d8356 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4567,7 +4567,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ bool offload)
+ {
+ #ifdef CONFIG_IEEE80211BE
+- unsigned int i, j;
++ unsigned int i;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return 0;
+@@ -4582,25 +4582,25 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+- struct hostapd_iface *iface = NULL;
++ struct hostapd_data *bss = NULL;
+ struct mld_link_info *link = &sta->mld_info.links[i];
++ bool link_bss_found = false;
+
+ if (!link->valid)
+ continue;
+
+- for (j = 0; j < hapd->iface->interfaces->count; j++) {
+- iface = hapd->iface->interfaces->iface[j];
++ for_each_mld_link(bss, hapd) {
++ if (bss == hapd)
++ continue;
+
+- if (hapd->iface == iface)
++ if (bss->mld_link_id != i)
+ continue;
+
+- if (hostapd_is_ml_partner(hapd, iface->bss[0]) &&
+- i == iface->bss[0]->mld_link_id)
+- break;
++ link_bss_found = true;
++ break;
+ }
+
+- if (!iface || j == hapd->iface->interfaces->count ||
+- TEST_FAIL()) {
++ if (!link_bss_found || TEST_FAIL()) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: No link match for link_id=%u", i);
+
+@@ -4613,7 +4613,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
+ } else {
+- if (ieee80211_ml_process_link(iface->bss[0], sta, link,
++ if (ieee80211_ml_process_link(bss, sta, link,
+ ies, ies_len, reassoc,
+ offload))
+ return -1;
+@@ -5777,7 +5777,7 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *assoc_hapd, *tmp_hapd;
+ struct sta_info *assoc_sta;
+- unsigned int i, link_id;
++ struct sta_info *tmp_sta;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return false;
+@@ -5790,45 +5790,27 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
+ if (!assoc_sta)
+ return false;
+
+- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+- for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
+- struct sta_info *tmp_sta;
+-
+- if (!assoc_sta->mld_info.links[link_id].valid)
+- continue;
++ for_each_mld_link(tmp_hapd, assoc_hapd) {
++ if (tmp_hapd == assoc_hapd)
++ continue;
+
+- tmp_hapd =
+- assoc_hapd->iface->interfaces->iface[i]->bss[0];
++ if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
++ continue;
+
+- if (!hostapd_is_ml_partner(assoc_hapd, tmp_hapd))
++ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
++ tmp_sta = tmp_sta->next) {
++ if (tmp_sta->mld_assoc_link_id !=
++ assoc_sta->mld_assoc_link_id ||
++ tmp_sta->aid != assoc_sta->aid)
+ continue;
+
+- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+- tmp_sta = tmp_sta->next) {
+- /*
+- * Remove the station on which the association
+- * was done only after all other link stations
+- * are removed. Since there is only a single
+- * station per struct hostapd_hapd with the
+- * same association link simply break out from
+- * the loop.
+- */
+- if (tmp_sta == assoc_sta)
+- break;
+-
+- if (tmp_sta->mld_assoc_link_id !=
+- assoc_sta->mld_assoc_link_id ||
+- tmp_sta->aid != assoc_sta->aid)
+- continue;
+-
+- if (!disassoc)
+- hostapd_deauth_sta(tmp_hapd, tmp_sta,
+- mgmt);
+- else
+- hostapd_disassoc_sta(tmp_hapd, tmp_sta,
+- mgmt);
+- break;
+- }
++ if (!disassoc)
++ hostapd_deauth_sta(tmp_hapd, tmp_sta,
++ mgmt);
++ else
++ hostapd_disassoc_sta(tmp_hapd, tmp_sta,
++ mgmt);
++ break;
+ }
+ }
+
+@@ -6451,38 +6433,34 @@ static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
+ struct sta_info *sta, bool ok)
+ {
+ #ifdef CONFIG_IEEE80211BE
+- unsigned int i, link_id;
++ struct hostapd_data *tmp_hapd;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+- struct mld_link_info *link = &sta->mld_info.links[link_id];
++ for_each_mld_link(tmp_hapd, hapd) {
++ struct mld_link_info *link =
++ &sta->mld_info.links[tmp_hapd->mld_link_id];
++ struct sta_info *tmp_sta;
+
+- if (!link->valid)
++ if (tmp_hapd == hapd)
+ continue;
+
+- for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- struct sta_info *tmp_sta;
+- struct hostapd_data *tmp_hapd =
+- hapd->iface->interfaces->iface[i]->bss[0];
++ if (!link->valid)
++ continue;
+
+- if (!hostapd_is_ml_partner(tmp_hapd, hapd))
++ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
++ tmp_sta = tmp_sta->next) {
++ if (tmp_sta == sta ||
++ tmp_sta->mld_assoc_link_id !=
++ sta->mld_assoc_link_id ||
++ tmp_sta->aid != sta->aid)
+ continue;
+
+- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+- tmp_sta = tmp_sta->next) {
+- if (tmp_sta == sta ||
+- tmp_sta->mld_assoc_link_id !=
+- sta->mld_assoc_link_id ||
+- tmp_sta->aid != sta->aid)
+- continue;
+-
+- ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
+- tmp_sta, link,
+- ok);
+- break;
+- }
++ ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
++ tmp_sta, link,
++ ok);
++ break;
+ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 7365057ad..353a4116e 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -1029,7 +1029,7 @@ const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
+ static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+ struct sta_info *sta)
+ {
+- u8 i, link_id;
++ u8 link_id;
+ struct mld_info *info = &sta->mld_info;
+
+ if (!ap_sta_is_mld(hapd, sta)) {
+@@ -1049,31 +1049,20 @@ static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct hostapd_data *other_hapd;
+
+- if (!info->links[link_id].valid)
++ if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
+ continue;
+
+- for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+-
+- if (hapd == other_hapd)
+- continue;
+-
+- if (hostapd_is_ml_partner(hapd, other_hapd) &&
+- link_id == other_hapd->mld_link_id)
+- break;
+- }
+-
+- if (i == hapd->iface->interfaces->count &&
+- link_id != hapd->mld_link_id) {
++ other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
++ if (!other_hapd) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
+ link_id);
+ return -1;
+ }
+
+- if (i < hapd->iface->interfaces->count)
+- os_memcpy(info->links[link_id].local_addr,
+- other_hapd->own_addr,
+- ETH_ALEN);
++ os_memcpy(info->links[link_id].local_addr,
++ other_hapd->own_addr,
++ ETH_ALEN);
++
+ }
+
+ return 0;
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index 012f2b803..d3cd44695 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -1537,7 +1537,7 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+ struct wpa_auth_ml_rsn_info *info)
+ {
+ struct hostapd_data *hapd = ctx;
+- unsigned int i, j;
++ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
+ info->n_mld_links);
+@@ -1547,26 +1547,33 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+
+ for (i = 0; i < info->n_mld_links; i++) {
+ unsigned int link_id = info->links[i].link_id;
++ struct hostapd_data *bss = NULL;
++ bool link_bss_found = false;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
+ link_id);
+
+- for (j = 0; j < hapd->iface->interfaces->count; j++) {
+- struct hostapd_iface *iface =
+- hapd->iface->interfaces->iface[j];
++ if (hapd->mld_link_id == link_id) {
++ wpa_auth_ml_get_rsn_info(hapd->wpa_auth,
++ &info->links[i]);
++ continue;
++ }
+
+- if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
+- link_id != iface->bss[0]->mld_link_id ||
+- !iface->bss[0]->wpa_auth)
++ for_each_mld_link(bss, hapd) {
++ if (bss == hapd)
+ continue;
+
+- wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
++ if (bss->mld_link_id != link_id)
++ continue;
++
++ wpa_auth_ml_get_rsn_info(bss->wpa_auth,
+ &info->links[i]);
++ link_bss_found = true;
+ break;
+ }
+
+- if (j == hapd->iface->interfaces->count)
++ if (!link_bss_found)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+@@ -1579,7 +1586,7 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
+ struct wpa_auth_ml_key_info *info)
+ {
+ struct hostapd_data *hapd = ctx;
+- unsigned int i, j;
++ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
+ info->n_mld_links);
+@@ -1588,29 +1595,38 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
+ return -1;
+
+ for (i = 0; i < info->n_mld_links; i++) {
++ struct hostapd_data *bss = NULL;
+ u8 link_id = info->links[i].link_id;
++ bool link_bss_found = false;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link info CB: link_id=%u",
+ link_id);
+
+- for (j = 0; j < hapd->iface->interfaces->count; j++) {
+- struct hostapd_iface *iface =
+- hapd->iface->interfaces->iface[j];
++ if (hapd->mld_link_id == link_id) {
++ wpa_auth_ml_get_key_info(hapd->wpa_auth,
++ &info->links[i],
++ info->mgmt_frame_prot,
++ info->beacon_prot);
++ continue;
++ }
++
++ for_each_mld_link(bss, hapd) {
++ if (bss == hapd)
++ continue;
+
+- if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
+- link_id != iface->bss[0]->mld_link_id ||
+- !iface->bss[0]->wpa_auth)
++ if (bss->mld_link_id != link_id)
+ continue;
+
+- wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
++ wpa_auth_ml_get_key_info(bss->wpa_auth,
+ &info->links[i],
+ info->mgmt_frame_prot,
+ info->beacon_prot);
++ link_bss_found = true;
+ break;
+ }
+
+- if (j == hapd->iface->interfaces->count)
++ if (!link_bss_found)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
new file mode 100644
index 0000000..6b9fc79
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
@@ -0,0 +1,120 @@
+From 30cd94f678f5f85703854812f0deb6467b37df5f Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:39 +0530
+Subject: [PATCH 009/104] hostapd: MLO: pass link_id in get_hapd_bssid helper
+ function
+
+Currently get_hapd_bssid() function matches the given bssid in all bsses
+of its own iface. However with MLO, there is requirement to check its
+own partner BSS at least.
+
+Make changes to compare its link partners as well and if link id passed
+matches with the link id of the partner then return it.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 47 +++++++++++++++++++++++++-----------------
+ 1 file changed, 28 insertions(+), 19 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 2d3206909..adac2d478 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1750,7 +1750,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
+ #define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+- const u8 *bssid)
++ const u8 *bssid, int link_id)
+ {
+ size_t i;
+
+@@ -1761,8 +1761,30 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ return HAPD_BROADCAST;
+
+ for (i = 0; i < iface->num_bss; i++) {
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_data *hapd, *p_hapd;
++
++ hapd = iface->bss[i];
++ if (ether_addr_equal(bssid, hapd->own_addr) ||
++ (hapd->conf->mld_ap &&
++ ether_addr_equal(bssid, hapd->mld->mld_addr) &&
++ link_id == hapd->mld_link_id)) {
++ return hapd;
++ } else if (hapd->conf->mld_ap) {
++ for_each_mld_link(p_hapd, hapd) {
++ if (p_hapd == hapd)
++ continue;
++
++ if (ether_addr_equal(bssid, p_hapd->own_addr) ||
++ (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
++ link_id == p_hapd->mld_link_id))
++ return p_hapd;
++ }
++ }
++#else
+ if (ether_addr_equal(bssid, iface->bss[i]->own_addr))
+ return iface->bss[i];
++#endif /*CONFIG_IEEE80211BE */
+ }
+
+ return NULL;
+@@ -1773,7 +1795,7 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+ const u8 *bssid, const u8 *addr,
+ int wds)
+ {
+- hapd = get_hapd_bssid(hapd->iface, bssid);
++ hapd = get_hapd_bssid(hapd->iface, bssid, -1);
+ if (hapd == NULL || hapd == HAPD_BROADCAST)
+ return;
+
+@@ -1813,14 +1835,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+ if (bssid == NULL)
+ return 0;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hapd->conf->mld_ap &&
+- ether_addr_equal(hapd->mld->mld_addr, bssid))
+- is_mld = true;
+-#endif /* CONFIG_IEEE80211BE */
+-
+- if (!is_mld)
+- hapd = get_hapd_bssid(iface, bssid);
++ hapd = get_hapd_bssid(iface, bssid, rx_mgmt->link_id);
+
+ if (!hapd) {
+ u16 fc = le_to_host16(hdr->frame_control);
+@@ -1872,17 +1887,11 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+ struct ieee80211_hdr *hdr;
+ struct hostapd_data *orig_hapd, *tmp_hapd;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hapd->conf->mld_ap && link_id != -1) {
+- tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
+- if (tmp_hapd)
+- hapd = tmp_hapd;
+- }
+-#endif /* CONFIG_IEEE80211BE */
+ orig_hapd = hapd;
+
+ hdr = (struct ieee80211_hdr *) buf;
+- tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
++ hapd = switch_link_hapd(hapd, link_id);
++ tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
+ if (tmp_hapd) {
+ hapd = tmp_hapd;
+ #ifdef CONFIG_IEEE80211BE
+@@ -1899,7 +1908,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+ if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
+ buf[24] != WLAN_ACTION_PUBLIC)
+ return;
+- hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
++ hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2, link_id);
+ if (!hapd || hapd == HAPD_BROADCAST)
+ return;
+ /*
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/001-wolfssl-init-RNG-with-ECC-key.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/001-wolfssl-init-RNG-with-ECC-key.patch
deleted file mode 100644
index 269dcaa..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/001-wolfssl-init-RNG-with-ECC-key.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 21ce83b4ae2b9563175fdb4fc4312096cc399cf8 Mon Sep 17 00:00:00 2001
-From: David Bauer <mail@david-bauer.net>
-Date: Wed, 5 May 2021 00:44:34 +0200
-Subject: [PATCH] wolfssl: add RNG to EC key
-
-Since upstream commit 6467de5a8840 ("Randomize z ordinates in
-scalar mult when timing resistant") WolfSSL requires a RNG for
-the EC key when built hardened which is the default.
-
-Set the RNG for the EC key to fix connections for OWE clients.
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- src/crypto/crypto_wolfssl.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/src/crypto/crypto_wolfssl.c
-+++ b/src/crypto/crypto_wolfssl.c
-@@ -1340,6 +1340,7 @@ int ecc_projective_add_point(ecc_point *
-
- struct crypto_ec {
- ecc_key key;
-+ WC_RNG rng;
- mp_int a;
- mp_int prime;
- mp_int order;
-@@ -1394,6 +1395,8 @@ struct crypto_ec * crypto_ec_init(int gr
- return NULL;
-
- if (wc_ecc_init(&e->key) != 0 ||
-+ wc_InitRng(&e->rng) != 0 ||
-+ wc_ecc_set_rng(&e->key, &e->rng) != 0 ||
- wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
- mp_init(&e->a) != MP_OKAY ||
- mp_init(&e->prime) != MP_OKAY ||
-@@ -1425,6 +1428,7 @@ void crypto_ec_deinit(struct crypto_ec*
- mp_clear(&e->order);
- mp_clear(&e->prime);
- mp_clear(&e->a);
-+ wc_FreeRng(&e->rng);
- wc_ecc_free(&e->key);
- os_free(e);
- }
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
new file mode 100644
index 0000000..de6b62e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
@@ -0,0 +1,64 @@
+From 33c9e4624040e1e0f331260c239fccdccbe52528 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:40 +0530
+Subject: [PATCH 010/104] hostapd: MLO: pass ctx in mlme_event_mgmt()
+
+Add support to pass ctx in mlme_event_mgmt(). This will help in to route
+the event properly to link BSS.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 2 +-
+ src/drivers/driver.h | 8 ++++++++
+ src/drivers/driver_nl80211_event.c | 1 +
+ 3 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index adac2d478..3b24aa4f4 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1810,8 +1810,8 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+ const u8 *bssid;
+ struct hostapd_frame_info fi;
+ int ret;
+- bool is_mld = false;
+
++ hapd = rx_mgmt->ctx ? rx_mgmt->ctx : hapd;
+ hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
+ iface = hapd->iface;
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index d67c949b6..a7455ef6e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -6345,6 +6345,14 @@ union wpa_event_data {
+ */
+ void *drv_priv;
+
++ /**
++ * ctx - Pointer to store ctx of private BSS information
++ *
++ * If not set to NULL, this is used for forwarding the packet
++ * to right link BSS of ML BSS.
++ */
++ void *ctx;
++
+ /**
+ * freq - Frequency (in MHz) on which the frame was received
+ */
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 51b27bd5e..1ca8b5bce 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1367,6 +1367,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ event.rx_mgmt.frame_len = len;
+ event.rx_mgmt.ssi_signal = ssi_signal;
+ event.rx_mgmt.drv_priv = bss;
++ event.rx_mgmt.ctx = bss->ctx;
+ event.rx_mgmt.link_id = link_id;
+
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
new file mode 100644
index 0000000..1d9c2fd
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
@@ -0,0 +1,136 @@
+From 0b72d2a8002e79886433ee85fd23661ec4d3d731 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:41 +0530
+Subject: [PATCH 011/104] hostapd: MLO: move mgmt and control port Tx status to
+ per BSS handling
+
+Currently management and control port transmit status is handled on drv's
+first BSS only. However to support multiple MLDs there is requirement to
+handle it in on a given BSS.
+
+Add changes to use the passed BSS instead of always going with drv's first
+BSS.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/drivers/driver_nl80211_event.c | 25 +++++++++++++------------
+ 1 file changed, 13 insertions(+), 12 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 1ca8b5bce..f5778cdaf 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -22,7 +22,7 @@
+
+
+ static void
+-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
++nl80211_control_port_frame_tx_status(struct i802_bss *bss,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie);
+
+@@ -1374,12 +1374,13 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ }
+
+
+-static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
++static void mlme_event_mgmt_tx_status(struct i802_bss *bss,
+ struct nlattr *cookie, const u8 *frame,
+ size_t len, struct nlattr *ack)
+ {
+ union wpa_event_data event;
+ const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u16 fc = le_to_host16(hdr->frame_control);
+ u64 cookie_val = 0;
+
+@@ -1398,7 +1399,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+ WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Work around misdelivered control port TX status for EAPOL");
+- nl80211_control_port_frame_tx_status(drv, frame, len, ack,
++ nl80211_control_port_frame_tx_status(bss, frame, len, ack,
+ cookie);
+ return;
+ }
+@@ -1434,7 +1435,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+ event.tx_status.ack = ack != NULL;
+ event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
+ drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
+- wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
++ wpa_supplicant_event(bss->ctx, EVENT_TX_STATUS, &event);
+ }
+
+
+@@ -1742,7 +1743,7 @@ static void mlme_event(struct i802_bss *bss,
+ nla_len(frame), link_id);
+ break;
+ case NL80211_CMD_FRAME_TX_STATUS:
+- mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
++ mlme_event_mgmt_tx_status(bss, cookie, nla_data(frame),
+ nla_len(frame), ack);
+ break;
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+@@ -3652,8 +3653,7 @@ static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
+ }
+
+
+-static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+- struct nlattr **tb)
++static void nl80211_control_port_frame(struct i802_bss *bss, struct nlattr **tb)
+ {
+ u8 *src_addr;
+ u16 ethertype;
+@@ -3682,7 +3682,7 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+ MAC2STR(src_addr));
+ break;
+ case ETH_P_PAE:
+- drv_event_eapol_rx2(drv->ctx, src_addr,
++ drv_event_eapol_rx2(bss->ctx, src_addr,
+ nla_data(tb[NL80211_ATTR_FRAME]),
+ nla_len(tb[NL80211_ATTR_FRAME]),
+ encrypted, link_id);
+@@ -3698,10 +3698,11 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+
+
+ static void
+-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
++nl80211_control_port_frame_tx_status(struct i802_bss *bss,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie)
+ {
++ struct wpa_driver_nl80211_data *drv = bss->drv;
+ union wpa_event_data event;
+
+ if (!cookie || len < ETH_HLEN)
+@@ -3720,7 +3721,7 @@ nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+ nla_get_u64(cookie) == drv->eapol_tx_cookie ?
+ drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
+
+- wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
++ wpa_supplicant_event(bss->ctx, EVENT_EAPOL_TX_STATUS, &event);
+ }
+
+
+@@ -4065,7 +4066,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
+ if (!frame)
+ break;
+- nl80211_control_port_frame_tx_status(drv,
++ nl80211_control_port_frame_tx_status(bss,
+ nla_data(frame),
+ nla_len(frame),
+ tb[NL80211_ATTR_ACK],
+@@ -4238,7 +4239,7 @@ int process_bss_event(struct nl_msg *msg, void *arg)
+ nl80211_external_auth(bss->drv, tb);
+ break;
+ case NL80211_CMD_CONTROL_PORT_FRAME:
+- nl80211_control_port_frame(bss->drv, tb);
++ nl80211_control_port_frame(bss, tb);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
new file mode 100644
index 0000000..960569b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
@@ -0,0 +1,113 @@
+From ac474b8dc6eb9d6a7562a714c0bbdcda47a3d858 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:42 +0530
+Subject: [PATCH 012/104] hostapd: make hostapd_eapol_tx_status() function
+ static
+
+hostapd_eapol_tx_status() function is being used only at one place in
+drv_callbacks. However, it is defined in ieee802_11.c which does not
+suit there.
+
+Hence, being the function definition in drv_callbacks.c and make it static.
+
+No functionality changes.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 25 +++++++++++++++++++++++++
+ src/ap/ieee802_11.c | 28 ----------------------------
+ src/ap/ieee802_11.h | 2 --
+ 3 files changed, 25 insertions(+), 30 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 3b24aa4f4..12e6b3f36 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2348,6 +2348,31 @@ err:
+ }
+ #endif /* CONFIG_OWE */
+
++static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
++ const u8 *data, size_t len, int ack)
++{
++ struct sta_info *sta;
++ struct hostapd_iface *iface = hapd->iface;
++
++ sta = ap_get_sta(hapd, dst);
++ if (sta == NULL && iface->num_bss > 1) {
++ size_t j;
++ for (j = 0; j < iface->num_bss; j++) {
++ hapd = iface->bss[j];
++ sta = ap_get_sta(hapd, dst);
++ if (sta)
++ break;
++ }
++ }
++ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
++ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
++ MACSTR " that is not currently associated",
++ MAC2STR(dst));
++ return;
++ }
++
++ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
++}
+
+ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 26e3d8356..9f7e9afdd 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6874,34 +6874,6 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+ }
+
+-
+-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+- const u8 *data, size_t len, int ack)
+-{
+- struct sta_info *sta;
+- struct hostapd_iface *iface = hapd->iface;
+-
+- sta = ap_get_sta(hapd, dst);
+- if (sta == NULL && iface->num_bss > 1) {
+- size_t j;
+- for (j = 0; j < iface->num_bss; j++) {
+- hapd = iface->bss[j];
+- sta = ap_get_sta(hapd, dst);
+- if (sta)
+- break;
+- }
+- }
+- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+- wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+- MACSTR " that is not currently associated",
+- MAC2STR(dst));
+- return;
+- }
+-
+- ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+-}
+-
+-
+ void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
+ {
+ struct sta_info *sta;
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index a35486d46..262e0ce14 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -132,8 +132,6 @@ int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
+ u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
+ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len, int ack);
+-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+- const u8 *data, size_t len, int ack);
+ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds);
+ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
new file mode 100644
index 0000000..ab69545
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
@@ -0,0 +1,181 @@
+From aa339ee77d60fe9314182cf0e60fa2da4da72b44 Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:43 +0530
+Subject: [PATCH 013/104] hostapd: MLO: handle link_id in EAPOL Tx status
+ handler
+
+Add link id support in EAPOL Tx status handler so that event can be
+routed to appropriate link BSS.
+
+In order to support this, modify hostapd_find_by_sta() function to check
+each BSS's other parnter link BSS sta list as well.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 108 +++++++++++++++--------------------------
+ 1 file changed, 38 insertions(+), 70 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 12e6b3f36..064c7abae 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1945,53 +1945,46 @@ static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+
+
+ static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
+- const u8 *src, bool rsn)
++ const u8 *src, bool rsn,
++ struct sta_info **sta_ret)
+ {
++ struct hostapd_data *hapd;
+ struct sta_info *sta;
+ unsigned int j;
+
++ if (sta_ret)
++ *sta_ret = NULL;
++
+ for (j = 0; j < iface->num_bss; j++) {
+- sta = ap_get_sta(iface->bss[j], src);
++ hapd = iface->bss[j];
++ sta = ap_get_sta(hapd, src);
+ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
+- (!rsn || sta->wpa_sm))
+- return iface->bss[j];
+- }
+-
+- return NULL;
+-}
+-
+-
++ (!rsn || sta->wpa_sm)) {
++ if (sta_ret)
++ *sta_ret = sta;
++ return hapd;
+ #ifdef CONFIG_IEEE80211BE
+-static bool search_mld_sta(struct hostapd_data **p_hapd, const u8 *src)
+-{
+- struct hostapd_data *hapd = *p_hapd;
+- unsigned int i;
+-
+- /* Search for STA on other MLO BSSs */
+- for (i = 0; i < hapd->iface->interfaces->count; i++) {
+- struct hostapd_iface *h =
+- hapd->iface->interfaces->iface[i];
+- struct hostapd_data *h_hapd = h->bss[0];
+-
+- if (!hostapd_is_ml_partner(h_hapd, hapd))
+- continue;
++ } else if (hapd->conf->mld_ap) {
++ struct hostapd_data *p_hapd;
+
+- h_hapd = hostapd_find_by_sta(h, src, false);
+- if (h_hapd) {
+- struct sta_info *sta = ap_get_sta(h_hapd, src);
++ for_each_mld_link(p_hapd, hapd) {
++ if (p_hapd == hapd)
++ continue;
+
+- if (sta && sta->mld_info.mld_sta &&
+- sta->mld_assoc_link_id != h_hapd->mld_link_id)
+- continue;
+- *p_hapd = h_hapd;
+- return true;
++ sta = ap_get_sta(p_hapd, src);
++ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
++ (!rsn || sta->wpa_sm)) {
++ if (sta_ret)
++ *sta_ret = sta;
++ return p_hapd;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
+ }
+ }
+
+- return false;
++ return NULL;
+ }
+-#endif /* CONFIG_IEEE80211BE */
+-
+
+ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+ const u8 *data, size_t data_len,
+@@ -2001,28 +1994,10 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+ struct hostapd_data *orig_hapd = hapd;
+
+ #ifdef CONFIG_IEEE80211BE
+- if (link_id != -1) {
+- struct hostapd_data *h_hapd;
+-
+- hapd = switch_link_hapd(hapd, link_id);
+- h_hapd = hostapd_find_by_sta(hapd->iface, src, true);
+- if (!h_hapd)
+- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
+- true);
+- if (!h_hapd)
+- h_hapd = hostapd_find_by_sta(hapd->iface, src, false);
+- if (!h_hapd)
+- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
+- false);
+- if (h_hapd)
+- hapd = h_hapd;
+- } else if (hapd->conf->mld_ap) {
+- search_mld_sta(&hapd, src);
+- } else {
+- hapd = hostapd_find_by_sta(hapd->iface, src, false);
+- }
++ hapd = switch_link_hapd(hapd, link_id);
++ hapd = hostapd_find_by_sta(hapd->iface, src, true, NULL);
+ #else /* CONFIG_IEEE80211BE */
+- hapd = hostapd_find_by_sta(hapd->iface, src, false);
++ hapd = hostapd_find_by_sta(hapd->iface, src, false, NULL);
+ #endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd) {
+@@ -2349,22 +2324,15 @@ err:
+ #endif /* CONFIG_OWE */
+
+ static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+- const u8 *data, size_t len, int ack)
++ const u8 *data, size_t len, int ack,
++ int link_id)
+ {
+ struct sta_info *sta;
+- struct hostapd_iface *iface = hapd->iface;
+
+- sta = ap_get_sta(hapd, dst);
+- if (sta == NULL && iface->num_bss > 1) {
+- size_t j;
+- for (j = 0; j < iface->num_bss; j++) {
+- hapd = iface->bss[j];
+- sta = ap_get_sta(hapd, dst);
+- if (sta)
+- break;
+- }
+- }
+- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
++ hapd = switch_link_hapd(hapd, link_id);
++ hapd = hostapd_find_by_sta(hapd->iface, dst, false, &sta);
++
++ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+ MACSTR " that is not currently associated",
+ MAC2STR(dst));
+@@ -2431,11 +2399,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ break;
+ case EVENT_EAPOL_TX_STATUS:
+- hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
+ hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
+ data->eapol_tx_status.data,
+ data->eapol_tx_status.data_len,
+- data->eapol_tx_status.ack);
++ data->eapol_tx_status.ack,
++ data->eapol_tx_status.link_id);
+ break;
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ hostapd_client_poll_ok(hapd, data->client_poll.addr);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
new file mode 100644
index 0000000..0cec211
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
@@ -0,0 +1,77 @@
+From 4be307ebbc6b94b6a334855a9efe633d77ca98fe Mon Sep 17 00:00:00 2001
+From: Sriram R <quic_srirrama@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:44 +0530
+Subject: [PATCH 014/104] hostapd: MLO: update all partner's link beacon
+
+Whenever there is a beacon update of any one of the link, all its other
+partner's link beacon should be refreshed.
+
+Add changes to update all partner's link beacon.
+
+Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/beacon.c | 27 +++++++++++++++++++--------
+ 1 file changed, 19 insertions(+), 8 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 195c7bbd9..b780d98e4 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2648,7 +2648,7 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ struct hostapd_iface *iface = hapd->iface;
+ int ret;
+ size_t i, j;
+- bool is_6g;
++ bool is_6g, hapd_mld = false;
+
+ ret = __ieee802_11_set_beacon(hapd);
+ if (ret != 0)
+@@ -2657,26 +2657,37 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ if (!iface->interfaces || iface->interfaces->count <= 1)
+ return 0;
+
++#ifdef CONFIG_IEEE80211BE
++ hapd_mld = hapd->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
++
+ /* Update Beacon frames in case of 6 GHz colocation or AP MLD */
+ is_6g = is_6ghz_op_class(iface->conf->op_class);
+ for (j = 0; j < iface->interfaces->count; j++) {
+ struct hostapd_iface *other;
+- bool mld_ap = false;
++ bool other_iface_6g;
+
+ other = iface->interfaces->iface[j];
+ if (other == iface || !other || !other->conf)
+ continue;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hostapd_is_ml_partner(hapd, other->bss[0]))
+- mld_ap = true;
+-#endif /* CONFIG_IEEE80211BE */
++ other_iface_6g = is_6ghz_op_class(other->conf->op_class);
+
+- if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
+- !mld_ap)
++ if (is_6g == other_iface_6g && !hapd_mld)
+ continue;
+
+ for (i = 0; i < other->num_bss; i++) {
++#ifdef CONFIG_IEEE80211BE
++ bool mld_ap = false;
++
++ if (hapd_mld && other->bss[i]->conf->mld_ap &&
++ hostapd_is_ml_partner(hapd, other->bss[i]))
++ mld_ap = true;
++
++ if (is_6g == other_iface_6g && !mld_ap)
++ continue;
++#endif /* CONFIG_IEEE80211BE */
++
+ if (other->bss[i] && other->bss[i]->started)
+ __ieee802_11_set_beacon(other->bss[i]);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
new file mode 100644
index 0000000..3dd5690
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
@@ -0,0 +1,36 @@
+From 6eeca68d65795783243d3634627b4ac8f79e3d15 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:45 +0530
+Subject: [PATCH 015/104] hostapd: MLO: skip assoc link processing in ML info
+
+Currently during processing ML info in association request, all links are
+iterated over. However, the assoc link info will not be present in the
+ML info hence following print is observed during ML association (assoc link
+is 1) -
+
+MLD: No link match for link_id=1
+
+Add changes to skip processing for the assoc link. No functionality
+changes.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 9f7e9afdd..39c63f29b 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4586,7 +4586,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ struct mld_link_info *link = &sta->mld_info.links[i];
+ bool link_bss_found = false;
+
+- if (!link->valid)
++ if (!link->valid || i == sta->mld_assoc_link_id)
+ continue;
+
+ for_each_mld_link(bss, hapd) {
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
new file mode 100644
index 0000000..0c17b68
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
@@ -0,0 +1,335 @@
+From 11cfbaf42eaadf0fd7b50d13f0b7664c1675dc11 Mon Sep 17 00:00:00 2001
+From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:46 +0530
+Subject: [PATCH 016/104] hostapd: MLO: Enhance wpa state machine
+
+Add required ML Specific members in wpa_authenticator and struct
+wpa_state_machine to maintain self and partner link information.
+
+Maintain state machine object in all associated link stations and
+destroy/remove references from the same whenever link stations are getting
+removed.
+
+Increase the wpa_group object reference count for all links in which ML
+station is getting associated and release the same whenever link stations
+are getting removed.
+
+Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ieee802_11.c | 9 ++--
+ src/ap/sta_info.c | 35 ++++++++++++++-
+ src/ap/wpa_auth.c | 101 +++++++++++++++++++++++++++++++++++++++++---
+ src/ap/wpa_auth.h | 16 +++++++
+ src/ap/wpa_auth_i.h | 8 ++++
+ 5 files changed, 159 insertions(+), 10 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 39c63f29b..9d04bdf43 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4467,6 +4467,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ }
+
+ sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
++ sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
++
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
+@@ -4474,7 +4476,6 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ }
+
+ ap_sta_set_mld(sta, true);
+- sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
+ os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+@@ -4501,9 +4502,11 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ ieee802_11_update_beacons(hapd->iface);
+ }
+
+- /* RSN Authenticator should always be the one on the original station */
++ /* Maintain state machine reference on all link STAs, this is needed
++ * during Group rekey handling.
++ */
+ wpa_auth_sta_deinit(sta->wpa_sm);
+- sta->wpa_sm = NULL;
++ sta->wpa_sm = origin_sta->wpa_sm;
+
+ /*
+ * Do not initialize the EAPOL state machine.
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 2423ff189..d483aa9d3 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -199,6 +199,26 @@ static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ }
+
++#ifdef CONFIG_IEEE80211BE
++static void set_for_each_partner_link_sta(struct hostapd_data *hapd,
++ struct sta_info *psta, void *data)
++{
++ struct sta_info *lsta;
++ struct hostapd_data *lhapd;
++
++ if (!ap_sta_is_mld(hapd, psta))
++ return;
++
++ for_each_mld_link(lhapd, hapd) {
++ if (lhapd == hapd)
++ continue;
++
++ lsta = ap_get_sta(lhapd, psta->addr);
++ if (lsta)
++ lsta->wpa_sm = data;
++ }
++}
++#endif /* CONFIG_IEEE80211BE */
+
+ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+@@ -317,8 +337,17 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+
+ #ifdef CONFIG_IEEE80211BE
+ if (!ap_sta_is_mld(hapd, sta) ||
+- hapd->mld_link_id == sta->mld_assoc_link_id)
++ hapd->mld_link_id == sta->mld_assoc_link_id) {
+ wpa_auth_sta_deinit(sta->wpa_sm);
++ /* Remove refrences from partner links. */
++ set_for_each_partner_link_sta(hapd, sta, NULL);
++ }
++
++ /* Release group references in case non assoc link STA is removed
++ * before assoc link STA
++ */
++ if (hostapd_sta_is_link_sta(hapd, sta))
++ wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id);
+ #else
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ #endif /* CONFIG_IEEE80211BE */
+@@ -903,8 +932,10 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd,
+ ieee802_1x_free_station(hapd, sta);
+ #ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf->mld_ap ||
+- hapd->mld_link_id == sta->mld_assoc_link_id)
++ hapd->mld_link_id == sta->mld_assoc_link_id) {
+ wpa_auth_sta_deinit(sta->wpa_sm);
++ set_for_each_partner_link_sta(hapd, sta, NULL);
++ }
+ #else
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 0d15c4209..8c1052c25 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -102,6 +102,59 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+ return sm->addr;
+ }
+
++#ifdef CONFIG_IEEE80211BE
++void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
++{
++ int link_id;
++
++ if (!sm || release_link_id >= MAX_NUM_MLD_LINKS)
++ return;
++
++ for_each_sm_auth(sm, link_id)
++ if (link_id == release_link_id) {
++ wpa_group_put(sm->mld_links[link_id].wpa_auth,
++ sm->mld_links[link_id].wpa_auth->group);
++ sm->mld_links[link_id].wpa_auth = NULL;
++ }
++}
++
++static int wpa_get_link_sta_auth(struct wpa_authenticator *wpa_auth, void *data)
++{
++ struct wpa_get_link_auth_ctx *ctx = data;
++
++ if (os_memcmp(wpa_auth->addr, ctx->addr, ETH_ALEN) != 0)
++ return 0;
++ ctx->wpa_auth = wpa_auth;
++ return 1;
++}
++
++static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void *data)
++{
++ struct wpa_get_link_auth_ctx *ctx = data;
++
++ if (!wpa_auth->is_ml || os_memcmp(wpa_auth->mld_addr, ctx->addr, ETH_ALEN) != 0 ||
++ !wpa_auth->primary_auth)
++ return 0;
++
++ ctx->wpa_auth = wpa_auth;
++ return 1;
++}
++
++static struct wpa_authenticator *
++wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
++{
++ struct wpa_get_link_auth_ctx ctx;
++
++ if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
++ return wpa_auth;
++
++ ctx.addr = wpa_auth->mld_addr;
++ ctx.wpa_auth = NULL;
++ wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
++
++ return ctx.wpa_auth;
++}
++#endif /* CONFIG_IEEE80211BE */
+
+ static inline int wpa_auth_mic_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+@@ -798,6 +851,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+
+ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+ {
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++
+ #ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr)) {
+ wpa_printf(MSG_DEBUG,
+@@ -821,6 +878,13 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+ os_free(sm->last_rx_eapol_key);
+ os_free(sm->wpa_ie);
+ os_free(sm->rsnxe);
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id) {
++ wpa_group_put(sm->mld_links[link_id].wpa_auth,
++ sm->mld_links[link_id].wpa_auth->group);
++ sm->mld_links[link_id].wpa_auth = NULL;
++ }
++#endif /* CONFIG_IEEE80211BE */
+ wpa_group_put(sm->wpa_auth, sm->group);
+ #ifdef CONFIG_DPP2
+ wpabuf_clear_free(sm->dpp_z);
+@@ -831,7 +895,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+
+ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+ {
+- struct wpa_authenticator *wpa_auth;
++ struct wpa_authenticator *wpa_auth, *primary_wpa_auth;
+
+ if (!sm)
+ return;
+@@ -840,10 +904,18 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+ if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "strict rekeying - force GTK rekey since STA is leaving");
++
++#ifdef CONFIG_IEEE80211BE
++ if (wpa_auth->is_ml && !wpa_auth->primary_auth)
++ primary_wpa_auth = wpa_get_primary_wpa_auth(wpa_auth);
++ else
++#endif /* CONFIG_IEEE80211BE */
++ primary_wpa_auth = wpa_auth;
++
+ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
+- wpa_auth, NULL) == -1)
++ primary_wpa_auth, NULL) == -1)
+ eloop_register_timeout(0, 500000, wpa_rekey_gtk,
+- wpa_auth, NULL);
++ primary_wpa_auth, NULL);
+ }
+
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+@@ -6835,6 +6907,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &info->links[link_id];
+ struct mld_link *sm_link = &sm->mld_links[link_id];
++ struct wpa_get_link_auth_ctx ctx;
+
+ sm_link->valid = link->valid;
+ if (!link->valid)
+@@ -6849,10 +6922,28 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ MAC2STR(sm_link->own_addr),
+ MAC2STR(sm_link->peer_addr));
+
+- if (link_id != mld_assoc_link_id)
++ ml_rsn_info.links[i++].link_id = link_id;
++
++ if (link_id != mld_assoc_link_id) {
+ sm->n_mld_affiliated_links++;
++ ctx.addr = link->local_addr;
++ ctx.wpa_auth = NULL;
++ wpa_auth_for_each_auth(sm->wpa_auth, wpa_get_link_sta_auth, &ctx);
++
++ if (ctx.wpa_auth) {
++ sm_link->wpa_auth = ctx.wpa_auth;
++ wpa_group_get(sm_link->wpa_auth,
++ sm_link->wpa_auth->group);
++ }
++ } else {
++ sm_link->wpa_auth = sm->wpa_auth;
++ }
+
+- ml_rsn_info.links[i++].link_id = link_id;
++ if (!sm_link->wpa_auth)
++ wpa_printf(MSG_ERROR, "Unable to find authenticator object for "
++ "ML STA " MACSTR " on link " MACSTR " link id %d",
++ MAC2STR(sm->own_mld_addr), MAC2STR(sm_link->own_addr),
++ link_id);
+ }
+
+ ml_rsn_info.n_mld_links = i;
+diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
+index c74862307..1446872f3 100644
+--- a/src/ap/wpa_auth.h
++++ b/src/ap/wpa_auth.h
+@@ -647,4 +647,20 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_key_info *info,
+ bool mgmt_frame_prot, bool beacon_prot);
+
++#ifdef CONFIG_IEEE80211BE
++void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
++ int release_link_id);
++
++#define for_each_sm_auth(sm, link_id) \
++ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \
++ if (sm->mld_links[link_id].valid && \
++ sm->mld_links[link_id].wpa_auth && \
++ sm->wpa_auth != sm->mld_links[link_id].wpa_auth) \
++
++struct wpa_get_link_auth_ctx {
++ u8 *addr;
++ struct wpa_authenticator *wpa_auth;
++};
++#endif /* CONFIG_IEEE80211BE */
++
+ #endif /* WPA_AUTH_H */
+diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
+index 9ba830415..9ba90749d 100644
+--- a/src/ap/wpa_auth_i.h
++++ b/src/ap/wpa_auth_i.h
+@@ -186,6 +186,7 @@ struct wpa_state_machine {
+ size_t rsne_len;
+ const u8 *rsnxe;
+ size_t rsnxe_len;
++ struct wpa_authenticator *wpa_auth;
+ } mld_links[MAX_NUM_MLD_LINKS];
+ #endif /* CONFIG_IEEE80211BE */
+ };
+@@ -262,6 +263,13 @@ struct wpa_authenticator {
+ #ifdef CONFIG_P2P
+ struct bitfield *ip_pool;
+ #endif /* CONFIG_P2P */
++
++#ifdef CONFIG_IEEE80211BE
++ bool is_ml;
++ u8 mld_addr[ETH_ALEN];
++ u8 link_id;
++ bool primary_auth;
++#endif /* CONFIG_IEEE80211BE */
+ };
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
new file mode 100644
index 0000000..5895635
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
@@ -0,0 +1,799 @@
+From 5e6164cb6143d55409c08ae9bfd859efa188e383 Mon Sep 17 00:00:00 2001
+From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:47 +0530
+Subject: [PATCH 017/104] hostapd: MLO: add support for MLO rekey
+
+Currently wpa group rekey is not supported for ML Stations when non-assoc
+link initiates a group rekey, to support the same following changes have
+been made-
+ * Calculate links specific MLO GTK/IGTK and BIGTK KDE lengths based on
+ corresponding cipher and key instead of taking length of one link and
+ multiplying it by no of associated links.
+ * For MLD, Arm group key rekey timer on one of the links and whenever it
+ fires do group key rekey for all links.
+
+Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
+Co-developed-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
+Signed-off-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/drv_callbacks.c | 2 +-
+ src/ap/ieee802_11.c | 13 +-
+ src/ap/wpa_auth.c | 310 +++++++++++++++---
+ src/ap/wpa_auth.h | 9 +-
+ src/ap/wpa_auth_glue.c | 22 ++
+ src/ap/wpa_auth_i.h | 1 +
+ src/ap/wpa_auth_ie.c | 12 +-
+ src/common/wpa_common.h | 1 +
+ tests/fuzzing/eapol-key-auth/eapol-key-auth.c | 2 +-
+ wpa_supplicant/ibss_rsn.c | 2 +-
+ 10 files changed, 317 insertions(+), 57 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 064c7abae..dc21977ff 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -528,7 +528,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ elems.rsnxe ? elems.rsnxe - 2 : NULL,
+ elems.rsnxe ? elems.rsnxe_len + 2 : 0,
+ elems.mdie, elems.mdie_len,
+- elems.owe_dh, elems.owe_dh_len);
++ elems.owe_dh, elems.owe_dh_len, NULL);
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ switch (res) {
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 9d04bdf43..7ee18f4ae 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -1887,7 +1887,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ elems.rsnxe ? elems.rsnxe - 2 : NULL,
+ elems.rsnxe ? elems.rsnxe_len + 2 : 0,
+- elems.mdie, elems.mdie_len, NULL, 0);
++ elems.mdie, elems.mdie_len, NULL, 0, NULL);
+ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+@@ -3778,7 +3778,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd,
+ rsn_ie_len += 2;
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq, rsn_ie, rsn_ie_len,
+- NULL, 0, NULL, 0, owe_dh, owe_dh_len);
++ NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
+ status = wpa_res_to_status_code(res);
+ if (status != WLAN_STATUS_SUCCESS)
+ goto end;
+@@ -3867,6 +3867,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ const u8 *p2p_dev_addr = NULL;
++ struct hostapd_data *assoc_hapd;
++ struct sta_info *assoc_sta = NULL;
+
+ resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+@@ -4041,6 +4043,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ wpa_ie_len += 2;
+
+ if (!sta->wpa_sm) {
++ if (!link)
++ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta,
++ &assoc_hapd);
++
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr,
+ p2p_dev_addr);
+@@ -4076,7 +4082,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ elems->rsnxe ? elems->rsnxe_len + 2 :
+ 0,
+ elems->mdie, elems->mdie_len,
+- elems->owe_dh, elems->owe_dh_len);
++ elems->owe_dh, elems->owe_dh_len,
++ assoc_sta ? assoc_sta->wpa_sm : NULL);
+ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 8c1052c25..7a07dcc4c 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -71,6 +71,9 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+ static int ieee80211w_kde_len(struct wpa_state_machine *sm);
+ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
++static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
++ struct wpa_group *group);
++
+
+ static const u32 eapol_key_timeout_first = 100; /* ms */
+ static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+@@ -102,6 +105,22 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+ return sm->addr;
+ }
+
++static void wpa_update_gkeydone(struct wpa_state_machine *sm, int update)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++ if (!sm || !sm->wpa_auth)
++ return;
++
++ sm->wpa_auth->group->GKeyDoneStations += update;
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations += update;
++#endif /* CONFIG_IEEE80211BE */
++}
++
+ #ifdef CONFIG_IEEE80211BE
+ void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
+ {
+@@ -139,10 +158,12 @@ static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void
+ ctx->wpa_auth = wpa_auth;
+ return 1;
+ }
++#endif /* CONFIG_IEEE80211BE */
+
+ static struct wpa_authenticator *
+ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
+ {
++#ifdef CONFIG_IEEE80211BE
+ struct wpa_get_link_auth_ctx ctx;
+
+ if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
+@@ -153,8 +174,10 @@ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
+ wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
+
+ return ctx.wpa_auth;
+-}
++#else
++ return wpa_auth;
+ #endif /* CONFIG_IEEE80211BE */
++}
+
+ static inline int wpa_auth_mic_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+@@ -420,15 +443,16 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+ }
+ }
+
+-
+-static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
++static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth)
+ {
+- struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct wpa_group *group, *next;
+
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+ group = wpa_auth->group;
+ while (group) {
++ wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator("
++ MACSTR "), group vlan %d",
++ MAC2STR(wpa_auth->addr), group->vlan_id);
+ wpa_group_get(wpa_auth, group);
+
+ group->GTKReKey = true;
+@@ -441,6 +465,80 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+ wpa_group_put(wpa_auth, group);
+ group = next;
+ }
++}
++
++#ifdef CONFIG_IEEE80211BE
++static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth)
++{
++ struct wpa_group *group, *next;
++
++ group = wpa_auth->group;
++ while (group) {
++ wpa_group_get(wpa_auth, group);
++
++ wpa_group_update_gtk(wpa_auth, group);
++ next = group->next;
++ wpa_group_put(wpa_auth, group);
++ group = next;
++ }
++}
++
++static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx)
++{
++ u8 *mld_addr = ctx;
++
++ if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
++ return 0;
++
++ wpa_update_all_gtks(wpa_auth);
++ return 0;
++}
++
++static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth,
++ void *ctx)
++{
++ u8 *mld_addr = ctx;
++
++ if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
++ return 0;
++
++ wpa_rekey_all_groups(wpa_auth);
++ return 0;
++}
++#endif /* CONFIG_IEEE80211BE */
++
++static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
++{
++ struct wpa_authenticator *wpa_auth = eloop_ctx;
++
++#ifdef CONFIG_IEEE80211BE
++ if (wpa_auth->is_ml) {
++ /* Non Primary ML authenticator eloop timer for group rekey is never
++ * started and shouldn't fire too check and warn just in case
++ */
++ if (!wpa_auth->primary_auth) {
++ wpa_printf(MSG_DEBUG,
++ "WPA: Can't start GTK rekey on non-primary ML authenticator");
++ return;
++ }
++ /*
++ * Generate all the new I/BIG/GTKs
++ */
++ wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb,
++ wpa_auth->mld_addr);
++
++ /*
++ * Send all the generated I/BIG/GTKs to the respective
++ * stations via G1 messages
++ */
++ wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb,
++ wpa_auth->mld_addr);
++ } else {
++ wpa_rekey_all_groups(wpa_auth);
++ }
++#else
++ wpa_rekey_all_groups(wpa_auth);
++#endif /* CONFIG_IEEE80211BE */
+
+ if (wpa_auth->conf.wpa_group_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
+@@ -590,8 +688,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
+ wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
+ if (!wpa_auth)
+ return NULL;
++
+ os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+ os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
++
++#ifdef CONFIG_IEEE80211BE
++ if (conf->mld_addr) {
++ wpa_auth->is_ml = true;
++ wpa_auth->link_id = conf->link_id;
++ wpa_auth->primary_auth = !conf->first_link_auth;
++ os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ wpa_auth->cb = cb;
+ wpa_auth->cb_ctx = cb_ctx;
+
+@@ -635,7 +744,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
+ wpa_rekey_gmk, wpa_auth, NULL);
+ }
+
++#ifdef CONFIG_IEEE80211BE
++ /* For ML AP, run Group rekey timer only on one link(first) and whenever
++ * it fires do rekey on all associated ML links at one shot.
++ */
++ if ((!wpa_auth->is_ml || !conf->first_link_auth) &&
++ wpa_auth->conf.wpa_group_rekey) {
++#else
+ if (wpa_auth->conf.wpa_group_rekey) {
++#endif /* CONFIG_IEEE80211BE */
+ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
+ wpa_rekey_gtk, wpa_auth, NULL);
+ }
+@@ -699,6 +816,10 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
+ struct wpa_group *group, *prev;
+
+ eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
++
++ /* TODO: assign ML Primary authenticator to next link auth and
++ * start rekey timer.
++ */
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+@@ -868,7 +989,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+ }
+ #endif /* CONFIG_P2P */
+ if (sm->GUpdateStationKeys) {
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ }
+ #ifdef CONFIG_IEEE80211R_AP
+@@ -1669,12 +1790,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key Request for GTK rekeying");
+- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
++
++ eloop_cancel_timeout(wpa_rekey_gtk,
++ wpa_get_primary_wpa_auth(wpa_auth), NULL);
+ if (wpa_auth_gtk_rekey_in_process(wpa_auth))
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
+ "skip new GTK rekey - already in process");
+ else
+- wpa_rekey_gtk(wpa_auth, NULL);
++ wpa_rekey_gtk(wpa_get_primary_wpa_auth(wpa_auth), NULL);
+ }
+ } else {
+ /* Do not allow the same key replay counter to be reused. */
+@@ -2207,7 +2330,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
+ * Reauthentication cancels the pending group key
+ * update for this STA.
+ */
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ sm->PtkGroupInit = true;
+ }
+@@ -2284,7 +2407,7 @@ SM_STATE(WPA_PTK, INITIALIZE)
+
+ sm->keycount = 0;
+ if (sm->GUpdateStationKeys)
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = false;
+@@ -4058,41 +4181,54 @@ static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
+ wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
+ }
+
++#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN)
+
+ static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+ {
+- struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+- struct wpa_group *gsm = sm->group;
+- size_t gtk_len = gsm->GTK_len;
+- size_t igtk_len;
+- size_t kde_len;
+- unsigned int n_links;
++ struct wpa_authenticator *wpa_auth;
++ size_t kde_len = 0;
++ int link_id;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+- n_links = sm->n_mld_affiliated_links + 1;
++ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++ if (!sm->mld_links[link_id].valid)
++ continue;
++
++ wpa_auth = sm->mld_links[link_id].wpa_auth;
++ if (!wpa_auth || !wpa_auth->group)
++ continue;
+
+- /* MLO GTK KDE for each link */
+- kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
++ /* MLO GTK KDE
++ * Header + Key-idx and Link-id + PN
++ */
++ kde_len += (KDE_HDR_LEN + 1 + WPA_MLO_GTK_KDE_PN_LEN);
++ kde_len += wpa_auth->group->GTK_len;
+
+- if (!sm->mgmt_frame_prot)
+- return kde_len;
++ if (!sm->mgmt_frame_prot)
++ continue;
+
+- /* MLO IGTK KDE for each link */
+- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
++ if (wpa_auth->conf.tx_bss_auth)
++ wpa_auth = wpa_auth->conf.tx_bss_auth;
+
+- if (wpa_auth->conf.tx_bss_auth) {
+- wpa_auth = wpa_auth->conf.tx_bss_auth;
+- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+- }
++ /* MLO IGTK KDE
++ * Header + Key-idx & IPN + Link-id
++ */
++ kde_len += (KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1);
++ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+- if (!wpa_auth->conf.beacon_prot)
+- return kde_len;
++ if (!wpa_auth->conf.beacon_prot)
++ continue;
++
++ /* MLO BIGTK KDE
++ * Header + Key-idx & IPN + Link-id
++ */
++ kde_len += (KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1);
++ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
++ }
+
+- /* MLO BIGTK KDE for each link */
+- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
++ wpa_printf(MSG_DEBUG, "MLO Group kdes len = %zu", kde_len);
+
+ return kde_len;
+ }
+@@ -4102,6 +4238,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ {
+ struct wpa_auth_ml_key_info ml_key_info;
+ unsigned int i, link_id;
++ u8 *start = pos;
+
+ /* First fetch the key information from all the authenticators */
+ os_memset(&ml_key_info, 0, sizeof(ml_key_info));
+@@ -4153,8 +4290,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ i++;
+ }
+
+- if (!sm->mgmt_frame_prot)
++ if (!sm->mgmt_frame_prot) {
++ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
+ return pos;
++ }
+
+ /* Add MLO IGTK KDEs */
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+@@ -4193,8 +4332,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ i++;
+ }
+
+- if (!sm->wpa_auth->conf.beacon_prot)
++ if (!sm->wpa_auth->conf.beacon_prot) {
++ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
+ return pos;
++ }
+
+ /* Add MLO BIGTK KDEs */
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+@@ -4234,6 +4375,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+ i++;
+ }
+
++ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
+ return pos;
+ }
+
+@@ -4274,6 +4416,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+ {
+ #ifdef CONFIG_IEEE80211BE
+ u8 link_id;
++ u8 *start = pos;
+
+ if (sm->mld_assoc_link_id < 0)
+ return pos;
+@@ -4324,6 +4467,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+ }
+ }
+
++ wpa_printf(MSG_DEBUG, "RSN: MLO Link kde len = %ld", pos - start);
+ pos = wpa_auth_ml_group_kdes(sm, pos);
+ #endif /* CONFIG_IEEE80211BE */
+
+@@ -5106,7 +5250,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+ #endif /* CONFIG_OCV */
+
+ if (sm->GUpdateStationKeys)
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ sm->GTimeoutCtr = 0;
+ /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+@@ -5121,7 +5265,7 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR)
+ {
+ SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+ if (sm->GUpdateStationKeys)
+- sm->group->GKeyDoneStations--;
++ wpa_update_gkeydone(sm, -1);
+ sm->GUpdateStationKeys = false;
+ sm->Disconnect = true;
+ sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
+@@ -5415,18 +5559,11 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+
+ #endif /* CONFIG_WNM_AP */
+
+-
+-static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+- struct wpa_group *group)
++static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
++ struct wpa_group *group)
+ {
+ int tmp;
+
+- wpa_printf(MSG_DEBUG,
+- "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
+- group->vlan_id);
+- group->changed = true;
+- group->wpa_group_state = WPA_GROUP_SETKEYS;
+- group->GTKReKey = false;
+ tmp = group->GM;
+ group->GM = group->GN;
+ group->GN = tmp;
+@@ -5440,6 +5577,24 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ * counting the STAs that are marked with GUpdateStationKeys instead of
+ * including all STAs that could be in not-yet-completed state. */
+ wpa_gtk_update(wpa_auth, group);
++}
++
++static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
++ struct wpa_group *group)
++{
++ wpa_printf(MSG_DEBUG,
++ "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
++ group->vlan_id);
++ group->changed = true;
++ group->wpa_group_state = WPA_GROUP_SETKEYS;
++ group->GTKReKey = false;
++
++#ifdef CONFIG_IEEE80211BE
++ if (wpa_auth->is_ml)
++ goto skip_update;
++#endif /* CONFIG_IEEE80211BE */
++
++ wpa_group_update_gtk(wpa_auth, group);
+
+ if (group->GKeyDoneStations) {
+ wpa_printf(MSG_DEBUG,
+@@ -5447,6 +5602,10 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ group->GKeyDoneStations);
+ group->GKeyDoneStations = 0;
+ }
++
++#ifdef CONFIG_IEEE80211BE
++skip_update:
++#endif /* CONFIG_IEEE80211BE */
+ wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
+ wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
+ group->GKeyDoneStations);
+@@ -5564,6 +5723,57 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+ }
+ }
+
++static void wpa_mark_group_change(struct wpa_state_machine *sm, bool change)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++
++ if (!sm || !sm->wpa_auth)
++ return;
++ sm->wpa_auth->group->changed = change;
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ sm->mld_links[link_id].wpa_auth->group->changed = change;
++#endif /* CONFIG_IEEE80211BE */
++}
++
++static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++
++ if (!sm || !sm->wpa_auth)
++ return;
++ wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group);
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ wpa_group_sm_step(sm->mld_links[link_id].wpa_auth,
++ sm->mld_links[link_id].wpa_auth->group);
++#endif /* CONFIG_IEEE80211BE */
++}
++
++static bool wpa_group_sm_changed(struct wpa_state_machine *sm)
++{
++#ifdef CONFIG_IEEE80211BE
++ int link_id;
++#endif /* CONFIG_IEEE80211BE */
++ bool changed;
++
++ if (!sm || !sm->wpa_auth)
++ return false;
++ changed = sm->wpa_auth->group->changed;
++
++#ifdef CONFIG_IEEE80211BE
++ for_each_sm_auth(sm, link_id)
++ changed |= sm->mld_links[link_id].wpa_auth->group->changed;
++#endif /* CONFIG_IEEE80211BE */
++
++ return changed;
++}
+
+ static int wpa_sm_step(struct wpa_state_machine *sm)
+ {
+@@ -5584,7 +5794,7 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
+ break;
+
+ sm->changed = false;
+- sm->wpa_auth->group->changed = false;
++ wpa_mark_group_change(sm, false);
+
+ SM_STEP_RUN(WPA_PTK);
+ if (sm->pending_deinit)
+@@ -5592,8 +5802,8 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
+ SM_STEP_RUN(WPA_PTK_GROUP);
+ if (sm->pending_deinit)
+ break;
+- wpa_group_sm_step(sm->wpa_auth, sm->group);
+- } while (sm->changed || sm->wpa_auth->group->changed);
++ wpa_group_sm_step_links(sm);
++ } while (sm->changed || wpa_group_sm_changed(sm));
+ sm->in_step_loop = 0;
+
+ if (sm->pending_deinit) {
+@@ -6807,8 +7017,10 @@ int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+ {
+ if (!wpa_auth)
+ return -1;
+- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+- return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
++ eloop_cancel_timeout(wpa_rekey_gtk,
++ wpa_get_primary_wpa_auth(wpa_auth), NULL);
++ return eloop_register_timeout(0, 0, wpa_rekey_gtk,
++ wpa_get_primary_wpa_auth(wpa_auth), NULL);
+ }
+
+
+diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
+index 1446872f3..331d217b5 100644
+--- a/src/ap/wpa_auth.h
++++ b/src/ap/wpa_auth.h
+@@ -285,6 +285,12 @@ struct wpa_auth_config {
+ * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
+ * and in BSSs that are not part of a Multi-BSSID set. */
+ struct wpa_authenticator *tx_bss_auth;
++
++#ifdef CONFIG_IEEE80211BE
++ u8 *mld_addr;
++ int link_id;
++ struct wpa_authenticator *first_link_auth;
++#endif /* CONFIG_IEEE80211BE */
+ };
+
+ typedef enum {
+@@ -429,7 +435,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsnxe, size_t rsnxe_len,
+ const u8 *mdie, size_t mdie_len,
+- const u8 *owe_dh, size_t owe_dh_len);
++ const u8 *owe_dh, size_t owe_dh_len,
++ struct wpa_state_machine *assoc_sm);
+ int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len);
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index d3cd44695..1726c7201 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -1713,6 +1713,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+
+ hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+ _conf.msg_ctx = hapd->msg_ctx;
++
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ if (tx_bss != hapd)
+ _conf.tx_bss_auth = tx_bss->wpa_auth;
+@@ -1753,6 +1754,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+ !!(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
+
++#ifdef CONFIG_IEEE80211BE
++ _conf.mld_addr = NULL;
++ _conf.link_id = -1;
++ _conf.first_link_auth = NULL;
++
++ if (hapd->conf->mld_ap) {
++ struct hostapd_data *lhapd;
++
++ _conf.mld_addr = hapd->mld->mld_addr;
++ _conf.link_id = hapd->mld_link_id;
++
++ for_each_mld_link(lhapd, hapd) {
++ if (lhapd == hapd)
++ continue;
++
++ if (lhapd->wpa_auth)
++ _conf.first_link_auth = lhapd->wpa_auth;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
+ if (hapd->wpa_auth == NULL) {
+ wpa_printf(MSG_ERROR, "WPA initialization failed.");
+diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
+index 9ba90749d..29bb66733 100644
+--- a/src/ap/wpa_auth_i.h
++++ b/src/ap/wpa_auth_i.h
+@@ -176,6 +176,7 @@ struct wpa_state_machine {
+ u8 peer_mld_addr[ETH_ALEN];
+ s8 mld_assoc_link_id;
+ u8 n_mld_affiliated_links;
++ u16 valid_links;
+
+ struct mld_link {
+ bool valid;
+diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
+index a5f2861c9..bf2303e4f 100644
+--- a/src/ap/wpa_auth_ie.c
++++ b/src/ap/wpa_auth_ie.c
+@@ -608,7 +608,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsnxe, size_t rsnxe_len,
+ const u8 *mdie, size_t mdie_len,
+- const u8 *owe_dh, size_t owe_dh_len)
++ const u8 *owe_dh, size_t owe_dh_len,
++ struct wpa_state_machine *assoc_sm)
+ {
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ struct wpa_ie_data data;
+@@ -956,6 +957,15 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ else
+ sm->wpa = WPA_VERSION_WPA;
+
++ if (assoc_sm) {
++ /* For ML Association Link STA cannot choose a different
++ * akm or pairwise cipher from assoc STA
++ */
++ if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt)
++ return WPA_INVALID_AKMP;
++ if (sm->pairwise != assoc_sm->pairwise)
++ return WPA_INVALID_PAIRWISE;
++ }
+ #if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
+ if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
+diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
+index 01efeea3a..24ceed600 100644
+--- a/src/common/wpa_common.h
++++ b/src/common/wpa_common.h
+@@ -24,6 +24,7 @@
+ #define WPA_PASN_PMK_LEN 32
+ #define WPA_PASN_MAX_MIC_LEN 24
+ #define WPA_MAX_RSNXE_LEN 4
++#define WPA_MLO_GTK_KDE_PN_LEN 6
+
+ #define OWE_DH_GROUP 19
+
+diff --git a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
+index bb46422c6..17f69fd76 100644
+--- a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
++++ b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
+@@ -262,7 +262,7 @@ static int auth_init(struct wpa *wpa)
+ }
+
+ if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie,
+- supp_ie_len, NULL, 0, NULL, 0, NULL, 0) !=
++ supp_ie_len, NULL, 0, NULL, 0, NULL, 0, NULL) !=
+ WPA_IE_OK) {
+ wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+ return -1;
+diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
+index 554268a47..2d06f1a6a 100644
+--- a/wpa_supplicant/ibss_rsn.c
++++ b/wpa_supplicant/ibss_rsn.c
+@@ -484,7 +484,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
+ "\x00\x0f\xac\x04"
+ "\x01\x00\x00\x0f\xac\x04"
+ "\x01\x00\x00\x0f\xac\x02"
+- "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) !=
++ "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, NULL) !=
+ WPA_IE_OK) {
+ wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+ return -1;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
new file mode 100644
index 0000000..ce4a844
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
@@ -0,0 +1,156 @@
+From 8ac142806112477fa012414a2bdea22239e474a4 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:48 +0530
+Subject: [PATCH 018/104] hostapd: MLO: send link id during flushing stations
+
+Currently, whenever a BSS is set up, it sends flush all stations via
+command - NL80211_CMD_DEL_STATION on its interface. However, in case
+of MLO, station could have been connected to other links by the time
+this link is coming up. Since there is no link id currently being
+passed, all those stations entries are also removed in the driver which is
+wrong.
+
+Hence add change to send link id along with the command during MLO so that
+the driver can use this link id and flush only those stations which are
+using the passed link id as one of its links.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ap_drv_ops.c | 10 +++++++++-
+ src/drivers/driver.h | 4 +++-
+ src/drivers/driver_atheros.c | 2 +-
+ src/drivers/driver_bsd.c | 2 +-
+ src/drivers/driver_hostap.c | 2 +-
+ src/drivers/driver_nl80211.c | 17 ++++++++++++++---
+ 6 files changed, 29 insertions(+), 8 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 0d493b837..32722084d 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -624,9 +624,17 @@ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+
+ int hostapd_flush(struct hostapd_data *hapd)
+ {
++ int link_id = -1;
++
+ if (hapd->driver == NULL || hapd->driver->flush == NULL)
+ return 0;
+- return hapd->driver->flush(hapd->drv_priv);
++
++#ifdef CONFIG_IEEE80211BE
++ if (hapd->conf && hapd->conf->mld_ap)
++ link_id = hapd->mld_link_id;
++#endif /* CONFIG_IEEE80211BE */
++
++ return hapd->driver->flush(hapd->drv_priv, link_id);
+ }
+
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index a7455ef6e..e672a1787 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3578,13 +3578,15 @@ struct wpa_driver_ops {
+ /**
+ * flush - Flush all association stations (AP only)
+ * @priv: Private driver interface data
++ * @link_id: In case of MLO, valid link_id on which all associated stations
++ * will be flushed. -1 otherwise.
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function requests the driver to disassociate all associated
+ * stations. This function does not need to be implemented if the
+ * driver does not process association frames internally.
+ */
+- int (*flush)(void *priv);
++ int (*flush)(void *priv, int link_id);
+
+ /**
+ * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
+diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
+index ae7f0e535..71863306a 100644
+--- a/src/drivers/driver_atheros.c
++++ b/src/drivers/driver_atheros.c
+@@ -632,7 +632,7 @@ atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+
+
+ static int
+-atheros_flush(void *priv)
++atheros_flush(void *priv, int link_id)
+ {
+ u8 allsta[IEEE80211_ADDR_LEN];
+ os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
+index 850637f0d..82d8a0186 100644
+--- a/src/drivers/driver_bsd.c
++++ b/src/drivers/driver_bsd.c
+@@ -946,7 +946,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+
+
+ static int
+-bsd_flush(void *priv)
++bsd_flush(void *priv, int link_id)
+ {
+ u8 allsta[IEEE80211_ADDR_LEN];
+
+diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
+index d3520aacc..3aa5860bc 100644
+--- a/src/drivers/driver_hostap.c
++++ b/src/drivers/driver_hostap.c
+@@ -572,7 +572,7 @@ static int hostap_set_ssid(void *priv, const u8 *buf, int len)
+ }
+
+
+-static int hostap_flush(void *priv)
++static int hostap_flush(void *priv, int link_id)
+ {
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index e5fa22b59..9ac621ae6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -7729,25 +7729,36 @@ static int i802_set_frag(void *priv, int frag)
+ }
+
+
+-static int i802_flush(void *priv)
++static int i802_flush(void *priv, int link_id)
+ {
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+ int res;
+
+- wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+- bss->ifname);
++ if (link_id == NL80211_DRV_LINK_ID_NA)
++ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
++ bss->ifname);
++ else
++ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (with link %d)",
++ bss->ifname, link_id);
+
+ /*
+ * XXX: FIX! this needs to flush all VLANs too
+ */
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
++ if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
++ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
++ goto fail;
++
+ res = send_and_recv_cmd(bss->drv, msg);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+ "(%s)", res, strerror(-res));
+ }
+ return res;
++fail:
++ nlmsg_free(msg);
++ return -1;
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
new file mode 100644
index 0000000..77f3ddd
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
@@ -0,0 +1,84 @@
+From 3d12a39b10565a10bec40b53cf6e69b60115a35f Mon Sep 17 00:00:00 2001
+From: Harshitha Prem <quic_hprem@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:49 +0530
+Subject: [PATCH 019/104] hostapd: MLO: display link details in status command
+
+Currently, link id and number of link details of a MLD AP interface is not
+displayed in status command of hostapd_cli.
+
+Add changes to display the link id and number of link details.
+
+The details would be seen as below for a MLD AP interface:
+
+$ hostapd_cli -i wlan0 status | grep link
+num_links=1
+link_id=0
+link_addr=AA:BB:CC:DD:EE:FF
+
+$ hostapd_cli -i wlan1 status | grep link
+num_links=2
+link_id=0
+link_addr=AA:BB:CC:DD:EE:FF
+partner_link_id=1
+partner_link_addr=AA:BB:CC:DD:EE:AA
+
+The above details would not be displayed for non-MLD AP interfaces.
+
+Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
+Co-developed-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
+Signed-off-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/ctrl_iface_ap.c | 36 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 272317774..2cfef4bd4 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -887,6 +887,42 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ return len;
+ len += ret;
+ }
++
++ if (hapd->conf->mld_ap) {
++ struct hostapd_data *link_bss;
++
++ ret = os_snprintf(buf + len, buflen - len,
++ "num_links=%d\n",
++ hapd->mld->num_links);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++
++ /* self bss */
++ ret = os_snprintf(buf + len, buflen - len,
++ "link_id=%d\n"
++ "link_addr=" MACSTR "\n",
++ hapd->mld_link_id,
++ MAC2STR(hapd->own_addr));
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++
++ /* partner bss */
++ for_each_mld_link(link_bss, hapd) {
++ if (link_bss == hapd)
++ continue;
++
++ ret = os_snprintf(buf + len, buflen - len,
++ "partner_link_id=%d\n"
++ "partner_link_addr=" MACSTR "\n",
++ link_bss->mld_link_id,
++ MAC2STR(link_bss->own_addr));
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
++ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
new file mode 100644
index 0000000..879adf1
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
@@ -0,0 +1,748 @@
+From 8affcd80f5143fa23d3f21427b6b9f11af35ef5d Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:50 +0530
+Subject: [PATCH 020/104] hostapd: fix RNR building for co-location and MLO
+
+Currently with MLO changes, RNR formation for co-location or MLO
+was not working as expected. Hence make it work as per the
+expectation.
+
+For example, during co-location, if the BSS is also its ML partner
+then there is no need to include a separate TBTT for it.
+
+Also, during co-location, if the BSS is not its partner but it is
+ML capable, then the TBTT length should be 16 bytes and it should
+include the MLD Parameters for it in the RNR.
+
+During co-location, for a given Neighbor AP (operating on a given
+channel and op-class) if it has BSSes which are ML capable as well
+as BSSes which are not, then there should be two Neighbor AP Info
+present. One indicating TBTT length as 13 bytes and one indicating
+TBTT info length as 16 bytes.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ src/ap/beacon.c | 12 +-
+ src/ap/ieee802_11.c | 387 ++++++++++++++++++++++++++++++++------------
+ src/ap/ieee802_11.h | 5 +-
+ 3 files changed, 290 insertions(+), 114 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index b780d98e4..4354dfae3 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -677,7 +677,7 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
+ params->known_bss,
+ params->known_bss_len, NULL);
+ if (!params->is_ml_sta_info)
+- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
++ buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true);
+ buflen += hostapd_mbo_ie_len(hapd);
+ buflen += hostapd_eid_owe_trans_len(hapd);
+ buflen += hostapd_eid_dpp_cc_len(hapd);
+@@ -797,7 +797,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+
+ if (!params->is_ml_sta_info)
+- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
++ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true);
+ pos = hostapd_eid_fils_indic(hapd, pos, 0);
+ pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
+
+@@ -1946,7 +1946,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
+ total_len += 3;
+ }
+
+- total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
++ total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
+
+ pos = hostapd_eid_fils_indic(hapd, buf, 0);
+ buf_len = pos - buf;
+@@ -2020,7 +2020,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
+ /* Fill in the Length field value */
+ *length_pos = pos - (length_pos + 1);
+
+- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
++ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
+
+ /* FILS Indication element */
+ if (buf_len) {
+@@ -2126,7 +2126,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ hapd == hostapd_mbssid_get_tx_bss(hapd))
+ tail_len += 5; /* Multiple BSSID Configuration element */
+- tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
++ tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
+ tail_len += hostapd_mbo_ie_len(hapd);
+ tail_len += hostapd_eid_owe_trans_len(hapd);
+ tail_len += hostapd_eid_dpp_cc_len(hapd);
+@@ -2262,7 +2262,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+
+ tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+
+- tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
++ tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
+ tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+ tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 7ee18f4ae..9a23c7240 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7273,20 +7273,21 @@ static size_t
+ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ size_t *current_len,
+- struct mbssid_ie_profiles *skip_profiles)
++ struct mbssid_ie_profiles *skip_profiles,
++ bool mld_update)
+ {
+ size_t total_len = 0, len = *current_len;
+- int tbtt_count = 0;
+- size_t i, start = 0;
+- bool ap_mld = false;
++ int tbtt_count, total_tbtt_count = 0;
++ size_t i, start;
++ u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+
+-#ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
+-#endif /* CONFIG_IEEE80211BE */
++repeat_rnr_len:
++ start = 0;
++ tbtt_count = 0;
+
+ while (start < hapd->iface->num_bss) {
+ if (!len ||
+- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
++ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
+ len = RNR_HEADER_LEN;
+ total_len += RNR_HEADER_LEN;
+@@ -7298,10 +7299,15 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+
+ for (i = start; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
++ bool ap_mld = false;
+
+ if (!bss || !bss->conf || !bss->started)
+ continue;
+
++#ifdef CONFIG_IEEE80211BE
++ ap_mld = !!bss->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
++
+ if (bss == reporting_hapd ||
+ bss->conf->ignore_broadcast_ssid)
+ continue;
+@@ -7310,23 +7316,71 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+ i >= skip_profiles->start && i < skip_profiles->end)
+ continue;
+
+- if (len + RNR_TBTT_INFO_LEN > 255 ||
++ /* No need to report if length is for normal TBTT and the BSS
++ * is a MLD. MLD TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
++ continue;
++
++ /* No need to report if length is for MLD TBTT and the BSS
++ * is not MLD. Normal TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
++ continue;
++
++#ifdef CONFIG_IEEE80211BE
++ /* If building for co-location and they are ML partners,
++ * no need to include since the ML RNR will carry this.
++ */
++ if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
++ continue;
++
++ /* If building for ML RNR and they are not ML parnters,
++ * don't include.
++ */
++ if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
++ continue;
++#endif /* CONFIG_IEEE80211BE */
++
++ if (len + tbtt_info_len > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ break;
+
+- if (!ap_mld) {
+- len += RNR_TBTT_INFO_LEN;
+- total_len += RNR_TBTT_INFO_LEN;
+- } else {
+- len += RNR_TBTT_INFO_MLD_LEN;
+- total_len += RNR_TBTT_INFO_MLD_LEN;
+- }
++ len += tbtt_info_len;
++ total_len += tbtt_info_len;
+ tbtt_count++;
+ }
+ start = i;
+ }
+
+- if (!tbtt_count)
++ total_tbtt_count += tbtt_count;
++
++ /* If building for co-location, re-build again but this time include
++ * ML TBTTs.
++ */
++ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
++ tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
++
++ /* If no TBTT was found, then adjust the len and total_len since
++ * it would have incremented before we checked all bss.
++ */
++ if (!tbtt_count) {
++ len -= RNR_TBTT_HEADER_LEN;
++ total_len -= RNR_TBTT_HEADER_LEN;
++ }
++
++ goto repeat_rnr_len;
++ }
++
++ /* this is possible when it re-built and in that no suitable TBTT was
++ * found. Adjust the length accordingly.
++ */
++ if (!tbtt_count && total_tbtt_count) {
++ len -= RNR_TBTT_HEADER_LEN;
++ total_len -= RNR_TBTT_HEADER_LEN;
++ }
++
++ if (!total_tbtt_count)
+ total_len = 0;
+ else
+ *current_len = len;
+@@ -7375,8 +7429,8 @@ static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
+ }
+
+
+-static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+- size_t *current_len)
++static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
++ size_t *current_len)
+ {
+ struct hostapd_iface *iface;
+ size_t len = 0;
+@@ -7387,34 +7441,57 @@ static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+- bool ap_mld = false;
+-
+-#ifdef CONFIG_IEEE80211BE
+- if (hostapd_is_ml_partner(hapd, iface->bss[0]))
+- ap_mld = true;
+-#endif /* CONFIG_IEEE80211BE */
+
+- if (iface == hapd->iface ||
+- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
++ if (!iface || iface == hapd->iface ||
++ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
+ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+- current_len, NULL);
++ current_len, NULL, false);
+ }
+
+ return len;
+ }
+
+-
+-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
++static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
++ size_t *current_len)
+ {
+- size_t total_len = 0, current_len = 0;
+- enum colocation_mode mode = get_colocation_mode(hapd);
+- bool ap_mld = false;
++ size_t len = 0;
+
+ #ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
++ struct hostapd_iface *iface;
++ size_t i;
++
++ if (!hapd->iface || !hapd->iface->interfaces)
++ return 0;
++
++ if (!hapd->conf->mld_ap)
++ return 0;
++
++ /* TODO allow for FILS/Action as well */
++ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
++ return 0;
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ iface = hapd->iface->interfaces->iface[i];
++
++ if (!iface || iface == hapd->iface)
++ continue;
++
++ if (hapd->iface->freq == iface->freq)
++ continue;
++
++ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
++ current_len, NULL, true);
++ }
+ #endif /* CONFIG_IEEE80211BE */
++ return len;
++}
++
++size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params)
++{
++ size_t total_len = 0, current_len = 0;
++ enum colocation_mode mode = get_colocation_mode(hapd);
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+@@ -7423,29 +7500,35 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+- if (mode == COLOCATED_LOWER_BAND || ap_mld)
++ if (mode == COLOCATED_LOWER_BAND)
+ total_len +=
+- hostapd_eid_rnr_multi_iface_len(hapd,
+- ¤t_len);
++ hostapd_eid_rnr_colocation_len(hapd,
++ ¤t_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+ !hapd->iconf->mbssid)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ ¤t_len,
+- NULL);
++ NULL, false);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ ¤t_len,
+- NULL);
++ NULL, false);
+ break;
+
+ default:
+ break;
+ }
+
++ /* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
++ if (include_mld_params &&
++ (type != WLAN_FC_STYPE_BEACON ||
++ hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
++ total_len += hostapd_eid_rnr_mlo_len(hapd, type, ¤t_len);
++
+ return total_len;
+ }
+
+@@ -7509,13 +7592,14 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ struct mbssid_ie_profiles *skip_profiles,
+ size_t i, u8 *tbtt_count, size_t *len,
+- u8 **pos)
++ u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
++ u8 op_class, bool mld_update)
+ {
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_data *bss = iface->bss[i];
+ u8 bss_param = 0;
+- bool ap_mld = false;
+ u8 *eid = *pos;
++ bool ap_mld = false;
+
+ #ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+@@ -7529,10 +7613,47 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ && i >= skip_profiles->start && i < skip_profiles->end)
+ return false;
+
++ /* No need to report if length is for normal TBTT and the BSS
++ * is a MLD. MLD TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
++ return false;
++
++ /* No need to report if length is for MLD TBTT and the BSS
++ * is not MLD. Normal TBTT will include this.
++ */
++ if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
++ return false;
++
++#ifdef CONFIG_IEEE80211BE
++ /* If building for co-location and they are ML partners,
++ * no need to include since the ML RNR will carry this.
++ */
++ if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
++ return false;
++
++ /* If building for ML RNR and they are not ML parnters,
++ * don't include.
++ */
++ if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
++ return false;
++#endif /* CONFIG_IEEE80211BE */
++
+ if (*len + RNR_TBTT_INFO_LEN > 255 ||
+ *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ return true;
+
++ if (!(*tbtt_count)) {
++ /* Add Neighbor report header info only if there is at least
++ * one tbtt info available
++ */
++ *tbtt_count_pos = eid++;
++ *eid++ = tbtt_info_len;
++ *eid++ = op_class;
++ *eid++ = bss->iconf->channel;
++ *len += RNR_TBTT_HEADER_LEN;
++ }
++
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ os_memcpy(eid, bss->own_addr, ETH_ALEN);
+ eid += ETH_ALEN;
+@@ -7556,29 +7677,36 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ *eid++ = bss_param;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
+
+- if (!ap_mld) {
+- *len += RNR_TBTT_INFO_LEN;
+- } else {
+ #ifdef CONFIG_IEEE80211BE
+- u8 param_ch = hapd->eht_mld_bss_param_change;
+-
+- if (hostapd_is_ml_partner(bss, reporting_hapd))
+- *eid++ = 0;
+- else
+- *eid++ = hostapd_get_mld_id(hapd);
+-
+- *eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
+- *eid = (param_ch >> 4) & 0xF;
++ if (ap_mld) {
++ u8 param_ch = bss->eht_mld_bss_param_change;
++ bool is_partner;
++
++ /* If bss is not partner of the reporting_hapd then
++ * a) MLD ID advertised shall be 255.
++ * b) Link ID advertised shall be 15.
++ * c) BPCC advertised shall be 255
++ */
++ is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
++ /* MLD ID */
++ *eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
++ /* Link ID (Bit 3 to Bit 0)
++ * BPCC (Bit 4 to Bit 7)
++ */
++ *eid++ = is_partner ?
++ bss->mld_link_id | ((param_ch & 0xF) << 4) :
++ (MAX_NUM_MLD_LINKS | 0xF0);
++ /* BPCC (Bit 3 to Bit 0) */
++ *eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
+ #ifdef CONFIG_TESTING_OPTIONS
+- if (hapd->conf->mld_indicate_disabled)
++ if (bss->conf->mld_indicate_disabled)
+ *eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ eid++;
+-
+- *len += RNR_TBTT_INFO_MLD_LEN;
+-#endif /* CONFIG_IEEE80211BE */
+ }
++#endif /* CONFIG_IEEE80211BE */
+
++ *len += tbtt_info_len;
+ (*tbtt_count)++;
+ *pos = eid;
+
+@@ -7589,18 +7717,16 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ u8 *eid, size_t *current_len,
+- struct mbssid_ie_profiles *skip_profiles)
++ struct mbssid_ie_profiles *skip_profiles,
++ bool mld_update)
+ {
+ struct hostapd_iface *iface = hapd->iface;
+- size_t i, start = 0;
++ size_t i, start;
+ size_t len = *current_len;
+- u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
+- u8 tbtt_count = 0, op_class, channel;
+- bool ap_mld = false;
+-
+-#ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
+-#endif /* CONFIG_IEEE80211BE */
++ u8 *eid_start = eid, *size_offset = (eid - len) + 1;
++ u8 *tbtt_count_pos = size_offset + 1;
++ u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
++ u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+
+ if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
+ return eid;
+@@ -7612,9 +7738,12 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ NUM_HOSTAPD_MODES)
+ return eid;
+
++repeat_rnr:
++ start = 0;
++ tbtt_count = 0;
+ while (start < iface->num_bss) {
+ if (!len ||
+- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
++ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
+ eid_start = eid;
+ *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+@@ -7623,34 +7752,42 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ tbtt_count = 0;
+ }
+
+- tbtt_count_pos = eid++;
+- *eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+- *eid++ = op_class;
+- *eid++ = hapd->iconf->channel;
+- len += RNR_TBTT_HEADER_LEN;
+-
+ for (i = start; i < iface->num_bss; i++) {
+ if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
+ skip_profiles, i,
+- &tbtt_count, &len, &eid))
++ &tbtt_count, &len, &eid,
++ &tbtt_count_pos, tbtt_info_len,
++ op_class, mld_update))
+ break;
+ }
+
+ start = i;
+- *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+- *size_offset = (eid - size_offset) - 1;
++
++ if (tbtt_count) {
++ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
++ *size_offset = (eid - size_offset) - 1;
++ }
++ }
++
++ total_tbtt_count += tbtt_count;
++
++ /* If building for co-location, re-build again but this time include
++ * ML TBTTs.
++ */
++ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
++ tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
++ goto repeat_rnr;
+ }
+
+- if (tbtt_count == 0)
++ if (!total_tbtt_count)
+ return eid_start;
+
+ *current_len = len;
+ return eid;
+ }
+
+-
+-static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
+- size_t *current_len)
++u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
++ size_t *current_len)
+ {
+ struct hostapd_iface *iface;
+ size_t i;
+@@ -7660,35 +7797,56 @@ static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+- bool ap_mld = false;
+
+-#ifdef CONFIG_IEEE80211BE
+- if (hostapd_is_ml_partner(hapd, iface->bss[0]))
+- ap_mld = true;
+-#endif /* CONFIG_IEEE80211BE */
+-
+- if (iface == hapd->iface ||
+- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
++ if (!iface || iface == hapd->iface ||
++ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+- current_len, NULL);
++ current_len, NULL, false);
+ }
+
+ return eid;
+ }
+
++u8 *hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
++ u8 *eid, size_t *current_len)
++{
++#ifdef CONFIG_IEEE80211BE
++ struct hostapd_iface *iface;
++ size_t i;
++
++ if (!hapd->iface || !hapd->iface->interfaces)
++ return eid;
++
++ if (!hapd->conf->mld_ap)
++ return eid;
++
++ /* TODO allow for FILS/Action as well */
++ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
++ return eid;
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ iface = hapd->iface->interfaces->iface[i];
++
++ if (!iface || iface == hapd->iface)
++ continue;
++
++ if (hapd->iface->freq == iface->freq)
++ continue;
+
+-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
++ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
++ current_len, NULL, true);
++ }
++#endif /* CONFIG_IEEE80211BE */
++ return eid;
++}
++
++u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, bool include_mld_params)
+ {
+ u8 *eid_start = eid;
+ size_t current_len = 0;
+ enum colocation_mode mode = get_colocation_mode(hapd);
+- bool ap_mld = false;
+-
+-#ifdef CONFIG_IEEE80211BE
+- ap_mld = !!hapd->conf->mld_ap;
+-#endif /* CONFIG_IEEE80211BE */
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+@@ -7697,26 +7855,34 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+- if (mode == COLOCATED_LOWER_BAND || ap_mld)
+- eid = hostapd_eid_rnr_multi_iface(hapd, eid,
+- ¤t_len);
++ if (mode == COLOCATED_LOWER_BAND)
++ eid = hostapd_eid_rnr_colocation(hapd, eid,
++ ¤t_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+ !hapd->iconf->mbssid)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+- ¤t_len, NULL);
++ ¤t_len, NULL,
++ false);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+- ¤t_len, NULL);
++ ¤t_len, NULL,
++ false);
+ break;
+
+ default:
+ return eid_start;
+ }
+
++ /* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
++ if (include_mld_params &&
++ (type != WLAN_FC_STYPE_BEACON ||
++ hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
++ eid = hostapd_eid_rnr_mlo(hapd, type, eid, ¤t_len);
++
+ if (eid == eid_start + 2)
+ return eid_start;
+
+@@ -7815,6 +7981,11 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ size_t known_bss_len, size_t *rnr_len)
+ {
+ size_t len = 0, bss_index = 1;
++ bool ap_mld = false;
++
++#ifdef CONFIG_IEEE80211BE
++ ap_mld = !!hapd->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_type != WLAN_FC_STYPE_BEACON &&
+@@ -7847,12 +8018,12 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+
+ *rnr_len += hostapd_eid_rnr_iface_len(
+ hapd, hostapd_mbssid_get_tx_bss(hapd),
+- &rnr_cur_len, &skip_profiles);
++ &rnr_cur_len, &skip_profiles, ap_mld);
+ }
+ }
+
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
+- *rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
++ *rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
+
+ return len;
+ }
+@@ -7978,7 +8149,11 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ {
+ size_t bss_index = 1, cur_len = 0;
+ u8 elem_index = 0, *rnr_start_eid = rnr_eid;
+- bool add_rnr;
++ bool add_rnr, ap_mld = false;
++
++#ifdef CONFIG_IEEE80211BE
++ ap_mld = !!hapd->conf->mld_ap;
++#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_stype != WLAN_FC_STYPE_BEACON &&
+@@ -8023,7 +8198,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ cur_len = 0;
+ rnr_eid = hostapd_eid_rnr_iface(
+ hapd, hostapd_mbssid_get_tx_bss(hapd),
+- rnr_eid, &cur_len, &skip_profiles);
++ rnr_eid, &cur_len, &skip_profiles, ap_mld);
+ }
+ }
+
+@@ -8035,8 +8210,8 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ if (hapd->conf->rnr)
+ rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
+ if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
+- rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
+- &cur_len);
++ rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
++ &cur_len);
+ }
+
+ return eid;
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 262e0ce14..078f4baf9 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -225,8 +225,9 @@ void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
+ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+ u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len);
+-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
+-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
++size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params);
++u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
++ bool include_mld_params);
+ int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int res, struct radius_sta *info);
+ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
new file mode 100644
index 0000000..a4eb061
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
@@ -0,0 +1,271 @@
+From c43241d046e8a6ae75549c23d470b94f16c74ca7 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:51 +0530
+Subject: [PATCH 021/104] tests: MLO: add basic cohosted MLDs functionality
+ testing
+
+Add test case to test basic cohosted MLDs functionality. Add helper
+functions to create the configuration file, start hostapd instance.
+
+Client connectivty test case will be added via a subsequent change.
+
+eht_mld_cohosted_discovery: 2 co-hosted MLDs without non-MLD RNR. Basic
+bring up and beacon, MLD RNR, scan validation.
+
+eht_mld_cohosted_discovery_with_rnr: Same like eht_mld_cohosted_discovery
+but additionally non-MLD RNR (rnr=1) is also enabled. Validate the non-MLD
+RNR as well.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ tests/hwsim/test_eht.py | 230 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 230 insertions(+)
+
+diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
+index a012fe4e7..732406219 100644
+--- a/tests/hwsim/test_eht.py
++++ b/tests/hwsim/test_eht.py
+@@ -15,6 +15,7 @@ from tshark import run_tshark
+ from test_gas import hs20_ap_params
+ from test_dpp import check_dpp_capab, wait_auth_success
+ from test_rrm import build_beacon_request, run_req_beacon, BeaconReport
++import os, subprocess, time, tempfile
+
+ def eht_verify_wifi_version(dev):
+ status = dev.get_status()
+@@ -1823,3 +1824,232 @@ def test_eht_mlo_csa(dev, apdev):
+ traffic_test(wpas, hapd0)
+
+ #TODO: CSA on non-first link
++
++def create_base_conf_file(iface, channel, prefix='hostapd-', hw_mode='g',
++ op_class=None):
++ # Create configuration file and add phy characteristics
++ fd, fname = tempfile.mkstemp(dir='/tmp',
++ prefix=prefix + iface + "-chan-" + str(channel) + "-")
++ f = os.fdopen(fd, 'w')
++
++ f.write("driver=nl80211\n")
++ f.write("hw_mode=" + str(hw_mode) + "\n")
++ f.write("ieee80211n=1\n")
++ if hw_mode == 'a' and \
++ (op_class is None or \
++ op_class not in [131, 132, 133, 134, 135, 136, 137]):
++ f.write("ieee80211ac=1\n")
++ f.write("ieee80211ax=1\n")
++ f.write("ieee80211be=1\n")
++ f.write("channel=" + str(channel) + "\n")
++
++ return f, fname
++
++def append_bss_conf_to_file(f, ifname, params, first=False):
++ # Add BSS specific characteristics
++ config = "bss"
++
++ if first:
++ config = "interface"
++
++ f.write("\n" + config + "=%s\n" % ifname)
++
++ for k, v in list(params.items()):
++ f.write("{}={}\n".format(k,v))
++
++ f.write("mld_ap=1\n")
++
++def dump_config(fname):
++ with open(fname, 'r') as f:
++ cfg = f.read()
++ logger.debug("hostapd config: " + str(fname) + "\n" + cfg)
++
++def get_config(iface, count, ssid, passphrase, channel, bssid_regex,
++ rnr=False, debug=False):
++ f, fname = create_base_conf_file(iface, channel=channel)
++ hapds = []
++
++ for i in range(count):
++ if i == 0:
++ ifname = iface
++ else:
++ ifname = iface + "-" + str(i)
++
++ set_ssid = ssid + str(i)
++ set_passphrase = passphrase + str(i)
++ params = hostapd.wpa2_params(ssid=set_ssid, passphrase=set_passphrase,
++ wpa_key_mgmt="SAE", ieee80211w="2")
++ params['sae_pwe'] = "2"
++ params['group_mgmt_cipher'] = "AES-128-CMAC"
++ params['beacon_prot'] = "1"
++ params["ctrl_interface"] = "/var/run/hostapd/chan_" + str(channel)
++ params["bssid"] = bssid_regex % (i + 1)
++
++ if rnr:
++ params["rnr"]="1"
++
++ append_bss_conf_to_file(f, ifname, params, first=(i == 0))
++
++ hapds.append([ifname, params["ctrl_interface"], i])
++
++ f.close()
++
++ if debug:
++ dump_config(fname)
++
++ return fname, hapds
++
++def start_ap(prefix, configs):
++ pid = prefix + ".hostapd.pid"
++ configs = configs.split()
++
++ cmd = ['../../hostapd/hostapd', '-ddKtB', '-P', pid, '-f',
++ prefix + ".hostapd-log"]
++
++ cmd = cmd + configs
++
++ logger.info("Starting APs")
++ res = subprocess.check_call(cmd)
++ if res != 0:
++ raise Exception("Could not start hostapd: %s" % str(res))
++
++ # Wait for hostapd to complete initialization and daemonize.
++ time.sleep(2)
++
++ if not os.path.exists(pid):
++ raise Exception("hostapd did not create PID file.")
++
++def get_mld_devs(hapd_iface, count, prefix, rnr=False):
++ fname1, hapds1 = get_config(hapd_iface, count=count, ssid="mld-",
++ passphrase="qwertyuiop-", channel=1,
++ bssid_regex="02:00:00:00:07:%02x",
++ rnr=rnr, debug=True)
++ fname2, hapds2 = get_config(hapd_iface, count=count, ssid="mld-",
++ passphrase="qwertyuiop-", channel=6,
++ bssid_regex="02:00:00:00:08:%02x",
++ rnr=rnr, debug=True)
++
++ start_ap(prefix, fname1 + " " + fname2)
++
++ hapd_mld1_link0 = hostapd.Hostapd(ifname=hapds1[0][0], ctrl=hapds1[0][1],
++ bssidx=hapds1[0][2])
++ hapd_mld1_link1 = hostapd.Hostapd(ifname=hapds2[0][0], ctrl=hapds2[0][1],
++ bssidx=hapds2[0][2])
++
++ hapd_mld2_link0 = hostapd.Hostapd(ifname=hapds1[1][0], ctrl=hapds1[1][1],
++ bssidx=hapds1[1][2])
++ hapd_mld2_link1 = hostapd.Hostapd(ifname=hapds2[1][0], ctrl=hapds2[1][1],
++ bssidx=hapds2[1][2])
++
++ if not hapd_mld1_link0.ping():
++ raise Exception("Could not ping hostapd")
++
++ if not hapd_mld1_link1.ping():
++ raise Exception("Could not ping hostapd")
++
++ if not hapd_mld2_link0.ping():
++ raise Exception("Could not ping hostapd")
++
++ if not hapd_mld2_link1.ping():
++ raise Exception("Could not ping hostapd")
++
++ os.remove(fname1)
++ os.remove(fname2)
++
++ return [hapd_mld1_link0, hapd_mld1_link1, hapd_mld2_link0, hapd_mld2_link1]
++
++def stop_mld_devs(hapds, pid):
++ pid = pid + ".hostapd.pid"
++
++ if "OK" not in hapds[0].request("TERMINATE"):
++ raise Exception("Failed to terminate hostapd process")
++
++ ev = hapds[0].wait_event(["CTRL-EVENT-TERMINATING"], timeout=15)
++ if ev is None:
++ raise Exception("CTRL-EVENT-TERMINATING not seen")
++
++ time.sleep(0.5)
++
++ if os.path.exists(pid):
++ raise Exception("PID file exits after process termination")
++
++def eht_parse_rnr(bss, rnr=False, exp_bssid=None):
++ partner_rnr_pattern = re.compile(".*ap_info.*, mld ID=0, link ID=",
++ re.MULTILINE)
++ ml_pattern = re.compile(".*multi-link:.*, MLD addr=.*", re.MULTILINE)
++
++ if partner_rnr_pattern.search(bss) is None:
++ raise Exception("RNR element not found for first link of first MLD")
++
++ if ml_pattern.search(bss) is None:
++ raise Exception("ML element not found for first link of first MLD")
++
++ if not rnr:
++ return
++
++ coloc_rnr_pattern = re.compile(".*ap_info.*, mld ID=255, link ID=..",
++ re.MULTILINE)
++
++ if coloc_rnr_pattern.search(bss) is None:
++ raise Exception("RNR element not found for co-located BSS")
++
++ line = coloc_rnr_pattern.search(bss).group()
++ if line.count('bssid') > 1:
++ raise Exception("More than one BSS found for co-located RNR")
++
++ # Get the BSSID carried in the RNR
++ index = line.rindex('bssid')
++ bssid = line[index+len('bssid')+1:].split(',')[0]
++
++ # Get the MLD ID carried in the RNR
++ index = line.rindex('link ID')
++ link_id = line[index+len('link ID')+1:].split(',')[0]
++
++ if link_id != "15":
++ raise Exception("Unexpected link ID for co-located BSS which is not own partner")
++
++ if bssid != exp_bssid:
++ raise Exception("Unexpected BSSID for co-located BSS")
++
++def eht_mld_cohosted_discovery(dev, apdev, params, rnr=False):
++ with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
++ HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface):
++
++ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
++ wpas.interface_add(wpas_iface)
++
++ hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
++ rnr=rnr)
++
++ # Only scan link 0
++ res = wpas.request("SCAN freq=2412")
++ if "FAIL" in res:
++ raise Exception("Failed to start scan")
++
++ ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"])
++ if ev is None:
++ raise Exception("Scan did not start")
++
++ ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
++ if ev is None:
++ raise Exception("Scan did not complete")
++
++ logger.info("Scan done")
++
++ bss = wpas.request("BSS " + hapds[0].own_addr())
++ logger.info("BSS 0_0: " + str(bss))
++ eht_parse_rnr(bss, rnr, hapds[2].own_addr())
++
++ bss = wpas.request("BSS " + hapds[2].own_addr())
++ logger.info("BSS 1_0: " + str(bss))
++ eht_parse_rnr(bss, rnr, hapds[0].own_addr())
++
++ stop_mld_devs(hapds, params['prefix'])
++
++def test_eht_mld_cohosted_discovery(dev, apdev, params):
++ """EHT 2 AP MLDs discovery"""
++ eht_mld_cohosted_discovery(dev, apdev, params)
++
++def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
++ """EHT 2 AP MLDs discovery (with co-location RNR)"""
++ eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
new file mode 100644
index 0000000..af3c390
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
@@ -0,0 +1,67 @@
+From 29a075f5ea644abdfb9bd93f79b05c72bb9fb78c Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 28 Mar 2024 23:46:52 +0530
+Subject: [PATCH 022/104] tests: MLO: add cohosted MLDs connectivity testing
+
+Add test case 'eht_mld_cohosted_connectivity' which creates two 2 link AP
+MLDs and connect 2 links MLD client to each one of them and test data
+traffic.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ tests/hwsim/test_eht.py | 42 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
+index 732406219..f09d31878 100644
+--- a/tests/hwsim/test_eht.py
++++ b/tests/hwsim/test_eht.py
+@@ -2053,3 +2053,45 @@ def test_eht_mld_cohosted_discovery(dev, apdev, params):
+ def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
+ """EHT 2 AP MLDs discovery (with co-location RNR)"""
+ eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
++
++def test_eht_mld_cohosted_connectivity(dev, apdev, params):
++ """EHT 2 AP MLDs with 2 MLD clients connection"""
++ with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
++ HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface), \
++ HWSimRadio(use_mlo=True) as (wpas_radio1, wpas_iface1):
++
++ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
++ wpas.interface_add(wpas_iface)
++
++ wpas1 = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
++ wpas1.interface_add(wpas_iface1)
++
++ hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
++ rnr=False)
++
++ passphrase = "qwertyuiop-"
++ ssid = "mld-"
++
++ # Connect one client to first AP MLD and verify traffic on both links
++ wpas.set("sae_pwe", "1")
++ wpas.connect(ssid+"0", sae_password=passphrase+"0", scan_freq="2412",
++ key_mgmt="SAE", ieee80211w="2")
++
++ eht_verify_status(wpas, hapds[0], 2412, 20, is_ht=True, mld=True,
++ valid_links=3, active_links=3)
++ eht_verify_wifi_version(wpas)
++
++ traffic_test(wpas, hapds[0])
++ traffic_test(wpas, hapds[1])
++
++ # Connect another client to second AP MLD and verify traffic on both links
++ wpas1.set("sae_pwe", "1")
++ wpas1.connect(ssid+"1", sae_password=passphrase+"1", scan_freq="2437",
++ key_mgmt="SAE", ieee80211w="2")
++
++ eht_verify_status(wpas1, hapds[3], 2437, 20, is_ht=True, mld=True,
++ valid_links=3, active_links=3)
++ eht_verify_wifi_version(wpas1)
++
++ traffic_test(wpas1, hapds[3])
++ traffic_test(wpas1, hapds[2])
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
new file mode 100644
index 0000000..9c4f7a4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
@@ -0,0 +1,372 @@
+From 84123bd3df810acd8d463a31d519005cfd0cc8d0 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:13:01 +0800
+Subject: [PATCH 023/104] backport: hostapd: afcd: add AFC daemon support
+
+Introduce Automated Frequency Coordination Daemon (AFCD) support
+for UNII-5 and UNII-7 6GHz bands.
+AFCD will be used by hostapd AFC client in order to forward the AFC
+request to the AFC coordinator and decouple AFC connection management
+from hostapd.
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFCD is tested with AFC DUT Test Harness [0].
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ afc/.gitignore | 1 +
+ afc/Makefile | 31 ++++++
+ afc/afcd.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 324 insertions(+)
+ create mode 100644 afc/.gitignore
+ create mode 100644 afc/Makefile
+ create mode 100644 afc/afcd.c
+
+diff --git a/afc/.gitignore b/afc/.gitignore
+new file mode 100644
+index 000000000..8d8cca905
+--- /dev/null
++++ b/afc/.gitignore
+@@ -0,0 +1 @@
++afcd
+diff --git a/afc/Makefile b/afc/Makefile
+new file mode 100644
+index 000000000..a83bd01db
+--- /dev/null
++++ b/afc/Makefile
+@@ -0,0 +1,31 @@
++ALL=afcd
++
++include ../src/build.rules
++
++CFLAGS += -I../src/utils
++CFLAGS += -I../src
++
++OBJS=afcd.o
++OBJS += ../src/utils/common.o
++OBJS += ../src/utils/wpa_debug.o
++OBJS += ../src/utils/wpabuf.o
++
++ifndef CONFIG_OS
++ifdef CONFIG_NATIVE_WINDOWS
++CONFIG_OS=win32
++else
++CONFIG_OS=unix
++endif
++endif
++OBJS += ../src/utils/os_$(CONFIG_OS).o
++
++LIBS += -lcurl
++
++_OBJS_VAR := OBJS
++include ../src/objs.mk
++afcd: $(OBJS)
++ $(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
++ @$(E) " LD " $@
++
++clean: common-clean
++ rm -f core *~
+diff --git a/afc/afcd.c b/afc/afcd.c
+new file mode 100644
+index 000000000..f502846c5
+--- /dev/null
++++ b/afc/afcd.c
+@@ -0,0 +1,292 @@
++/*
++ * Automated Frequency Coordination Daemon
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <curl/curl.h>
++#include <sys/un.h>
++#include <sys/stat.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#define CURL_TIMEOUT 60
++#define AFCD_SOCK "afcd.sock"
++
++struct curl_ctx {
++ char *buf;
++ size_t buf_len;
++};
++
++static volatile bool exiting;
++
++static char *path = "/var/run";
++static char *bearer_token;
++static char *url;
++static int port = 443;
++
++
++static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
++ void *userdata)
++{
++ struct curl_ctx *ctx = userdata;
++ char *buf;
++
++ buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
++ if (!buf)
++ return 0;
++
++ ctx->buf = buf;
++ os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
++ buf[ctx->buf_len + size * nmemb] = '\0';
++ ctx->buf_len += size * nmemb;
++
++ return size * nmemb;
++}
++
++
++static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
++{
++ struct curl_slist *headers = NULL;
++ CURL *curl;
++ int ret;
++
++ wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
++
++ curl_global_init(CURL_GLOBAL_ALL);
++ curl = curl_easy_init();
++ if (!curl)
++ return -ENOMEM;
++
++ headers = curl_slist_append(headers, "Accept: application/json");
++ headers = curl_slist_append(headers,
++ "Content-Type: application/json");
++ headers = curl_slist_append(headers, "charset: utf-8");
++
++ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
++ curl_easy_setopt(curl, CURLOPT_URL, url);
++ curl_easy_setopt(curl, CURLOPT_PORT, port);
++ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
++ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
++ afcd_curl_cb_write);
++ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
++ curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
++ curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
++ curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
++ if (bearer_token)
++ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
++ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
++ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
++ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
++ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
++ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
++
++ ret = curl_easy_perform(curl);
++ if (ret != CURLE_OK)
++ wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
++ curl_easy_strerror(ret));
++
++ curl_easy_cleanup(curl);
++ curl_global_cleanup();
++
++ return ret == CURLE_OK ? 0 : -EINVAL;
++}
++
++
++static void handle_term(int sig)
++{
++ wpa_printf(MSG_ERROR, "Received signal %d", sig);
++ exiting = true;
++}
++
++
++static void usage(void)
++{
++ wpa_printf(MSG_ERROR,
++ "%s:\n"
++ "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
++ __func__);
++}
++
++
++#define BUFSIZE 8192
++static int afcd_server_run(void)
++{
++ size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
++ struct sockaddr_un addr = {
++ .sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++ .sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++ };
++ int sockfd, ret = 0;
++ char *fname = NULL;
++ unsigned char *buf;
++ fd_set read_set;
++
++ if (len >= sizeof(addr.sun_path))
++ return -EINVAL;
++
++ if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
++ return -EINVAL;
++
++ buf = os_malloc(BUFSIZE);
++ if (!buf)
++ return -ENOMEM;
++
++ fname = os_malloc(len + 1);
++ if (!fname) {
++ ret = -ENOMEM;
++ goto free_buf;
++ }
++
++ os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
++ fname[len] = '\0';
++ os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
++
++ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (sockfd < 0) {
++ wpa_printf(MSG_ERROR, "Failed creating socket");
++ ret = -errno;
++ goto unlink;
++ }
++
++ if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ wpa_printf(MSG_ERROR, "Failed to bind socket");
++ ret = -errno;
++ goto close;
++ }
++
++ if (listen(sockfd, 10) < 0) {
++ wpa_printf(MSG_ERROR, "Failed to listen on socket");
++ ret = -errno;
++ goto close;
++ }
++
++ FD_ZERO(&read_set);
++ while (!exiting) {
++ socklen_t addr_len = sizeof(addr);
++ struct sockaddr_in6 c_addr;
++ struct timeval timeout = {
++ .tv_sec = 1,
++ };
++ struct curl_ctx ctx = {};
++ int fd;
++
++ FD_SET(sockfd, &read_set);
++ if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
++ if (errno != EINTR) {
++ wpa_printf(MSG_ERROR,
++ "Select failed on socket");
++ ret = -errno;
++ break;
++ }
++ continue;
++ }
++
++ if (!FD_ISSET(sockfd, &read_set))
++ continue;
++
++ fd = accept(sockfd, (struct sockaddr *)&c_addr,
++ &addr_len);
++ if (fd < 0) {
++ if (errno != EINTR) {
++ wpa_printf(MSG_ERROR,
++ "Failed accepting connections");
++ ret = -errno;
++ break;
++ }
++ continue;
++ }
++
++ os_memset(buf, 0, BUFSIZE);
++ if (recv(fd, buf, BUFSIZE, 0) <= 0) {
++ close(fd);
++ continue;
++ }
++
++ wpa_printf(MSG_DEBUG, "Received request: %s", buf);
++ if (!afcd_send_request(&ctx, buf)) {
++ wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
++ send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
++ free(ctx.buf);
++ }
++ close(fd);
++ }
++close:
++ close(sockfd);
++unlink:
++ unlink(fname);
++ os_free(fname);
++free_buf:
++ os_free(buf);
++
++ return ret;
++}
++
++
++int main(int argc, char **argv)
++{
++ bool daemonize = false;
++ char *pid_file = NULL;
++
++ if (os_program_init())
++ return -1;
++
++ for (;;) {
++ int c = getopt(argc, argv, "u:p:t:D:P:hdB");
++
++ if (c < 0)
++ break;
++
++ switch (c) {
++ case 'h':
++ usage();
++ return 0;
++ case 'B':
++ daemonize = true;
++ break;
++ case 'D':
++ path = optarg;
++ break;
++ case 'P':
++ os_free(pid_file);
++ pid_file = os_rel2abs_path(optarg);
++ break;
++ case 'u':
++ url = optarg;
++ break;
++ case 'p':
++ port = atoi(optarg);
++ break;
++ case 'd':
++ if (wpa_debug_level > 0)
++ wpa_debug_level--;
++ break;
++ case 't':
++ bearer_token = optarg;
++ break;
++ default:
++ usage();
++ return -EINVAL;
++ }
++ }
++
++ if (!url) {
++ usage();
++ return -EINVAL;
++ }
++
++ if (daemonize && os_daemonize(pid_file)) {
++ wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
++ return -EINVAL;
++ }
++
++ signal(SIGTERM, handle_term);
++ signal(SIGINT, handle_term);
++
++ return afcd_server_run();
++}
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
new file mode 100644
index 0000000..aac2145
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
@@ -0,0 +1,55 @@
+From 524c84524695034b8d531d70b546d5479d59641f Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:17:31 +0800
+Subject: [PATCH 024/104] backport: hostapd: export hostapd_is_usable_chans
+ utility routine
+
+This is a preliminary patch to introduce AFC support.
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/hw_features.c | 2 +-
+ src/ap/hw_features.h | 6 ++++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index fd401d78a..e652d7504 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -995,7 +995,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
+-static int hostapd_is_usable_chans(struct hostapd_iface *iface)
++int hostapd_is_usable_chans(struct hostapd_iface *iface)
+ {
+ int secondary_freq;
+ struct hostapd_channel_data *pri_chan;
+diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
+index c682c6d20..eeffb1abd 100644
+--- a/src/ap/hw_features.h
++++ b/src/ap/hw_features.h
+@@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface);
+ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode);
+ int hostapd_determine_mode(struct hostapd_iface *iface);
++int hostapd_is_usable_chans(struct hostapd_iface *iface);
+ #else /* NEED_AP_MLME */
+ static inline void
+ hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+@@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
+ return 0;
+ }
+
++static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
++{
++ return 1;
++}
++
+ #endif /* NEED_AP_MLME */
+
+ #endif /* HW_FEATURES_H */
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
new file mode 100644
index 0000000..357ca69
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
@@ -0,0 +1,1542 @@
+From c635af2f526c7dc7a862e5c6fed5f2015d8e85b6 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:18:37 +0800
+Subject: [PATCH 025/104] backport: hostapd: ap: add AFC client support
+
+Introduce Automated Frequency Coordination (AFC) support for UNII-5 and
+UNII-7 6GHz bands.
+AFC client will connect to AFCD providing AP related parameter for AFC
+coordinator (e.g. geolocation, supported frequencies, ..).
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFC hostapd client is tested with AFC DUT Test Harness [0].
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ hostapd/Makefile | 6 +
+ hostapd/config_file.c | 262 ++++++++++++
+ hostapd/defconfig | 3 +
+ hostapd/hostapd.conf | 42 ++
+ src/ap/afc.c | 918 ++++++++++++++++++++++++++++++++++++++++++
+ src/ap/ap_config.c | 16 +
+ src/ap/ap_config.h | 47 +++
+ src/ap/hostapd.c | 60 +++
+ src/ap/hostapd.h | 29 ++
+ src/ap/hw_features.c | 2 +
+ 10 files changed, 1385 insertions(+)
+ create mode 100644 src/ap/afc.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index b3cb68673..405e05e5f 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -103,6 +103,12 @@ CFLAGS += -DCONFIG_TAXONOMY
+ OBJS += ../src/ap/taxonomy.o
+ endif
+
++ifdef CONFIG_AFC
++CFLAGS += -DCONFIG_AFC
++OBJS += ../src/ap/afc.o
++LIBS += -ljson-c
++endif
++
+ ifdef CONFIG_MODULE_TESTS
+ CFLAGS += -DCONFIG_MODULE_TESTS
+ OBJS += hapd_module_tests.o
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 56b2df3ae..261905368 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -2436,6 +2436,191 @@ static int get_u16(const char *pos, int line, u16 *ret_val)
+ #endif /* CONFIG_IEEE80211BE */
+
+
++#ifdef CONFIG_AFC
++static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos)
++{
++ struct cert_id *c = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p;
++
++ c = os_realloc_array(c, count + 1, sizeof(*c));
++ if (!c)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ':');
++ if (!p)
++ goto error;
++
++ *p++ = '\0';
++ if (!p || !p[0])
++ goto error;
++
++ c[i].rulset = os_malloc(os_strlen(pos) + 1);
++ if (!c[i].rulset)
++ goto error;
++
++ os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1);
++ pos = p;
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ c[i].id = os_malloc(os_strlen(pos) + 1);
++ if (!c[i].id)
++ goto error;
++
++ os_strlcpy(c[i].id, pos, os_strlen(pos) + 1);
++ pos = p;
++ }
++
++ conf->afc.n_cert_ids = count;
++ conf->afc.cert_ids = c;
++
++ return 0;
++
++error:
++ for (i = 0; i < count; i++) {
++ os_free(c[i].rulset);
++ os_free(c[i].id);
++ }
++ os_free(c);
++
++ return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data,
++ unsigned int *n_linear_polygon_data,
++ char *pos)
++{
++ struct afc_linear_polygon *d = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p, *end;
++
++ d = os_realloc_array(d, count + 1, sizeof(*d));
++ if (!d)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ':');
++ if (!p)
++ goto error;
++
++ *p++ = '\0';
++ if (!p || !p[0])
++ goto error;
++
++ d[i].longitude = strtod(pos, &end);
++ if (*end)
++ goto error;
++
++ pos = p;
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ d[i].latitude = strtod(pos, &end);
++ if (*end)
++ goto error;
++
++ pos = p;
++ }
++
++ *n_linear_polygon_data = count;
++ *data = d;
++
++ return 0;
++
++error:
++ os_free(d);
++ return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos)
++{
++ struct afc_freq_range *f = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p;
++
++ f = os_realloc_array(f, count + 1, sizeof(*f));
++ if (!f)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ':');
++ if (!p)
++ goto error;
++
++ *p++ = '\0';
++ if (!p || !p[0])
++ goto error;
++
++ f[i].low_freq = atoi(pos);
++ pos = p;
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ f[i].high_freq = atoi(pos);
++ pos = p;
++ }
++
++ conf->afc.n_freq_range = count;
++ conf->afc.freq_range = f;
++
++ return 0;
++
++error:
++ os_free(f);
++ return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
++{
++ unsigned int *oc = NULL;
++ int i, count = 0;
++
++ while (pos && pos[0]) {
++ char *p;
++
++ oc = os_realloc_array(oc, count + 1, sizeof(*oc));
++ if (!oc)
++ return -ENOMEM;
++
++ i = count;
++ count++;
++
++ p = os_strchr(pos, ',');
++ if (p)
++ *p++ = '\0';
++
++ oc[i] = atoi(pos);
++ pos = p;
++ }
++
++ conf->afc.n_op_class = count;
++ conf->afc.op_class = oc;
++
++ return 0;
++}
++#endif /* CONFIG_AFC */
++
++
+ static int hostapd_config_fill(struct hostapd_config *conf,
+ struct hostapd_bss_config *bss,
+ const char *buf, char *pos, int line)
+@@ -3862,6 +4047,83 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ bss->unsol_bcast_probe_resp_interval = val;
++#ifdef CONFIG_AFC
++ } else if (os_strcmp(buf, "afcd_sock") == 0) {
++ conf->afc.socket = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.socket)
++ return 1;
++
++ os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_request_version") == 0) {
++ conf->afc.request.version = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.request.version)
++ return 1;
++
++ os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_request_id") == 0) {
++ conf->afc.request.id = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.request.id)
++ return 1;
++
++ os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_serial_number") == 0) {
++ conf->afc.request.sn = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.request.sn)
++ return 1;
++
++ os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_cert_ids") == 0) {
++ if (hostapd_afc_parse_cert_ids(conf, pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_location_type") == 0) {
++ conf->afc.location.type = atoi(pos);
++ if (conf->afc.location.type != ELLIPSE &&
++ conf->afc.location.type != LINEAR_POLYGON &&
++ conf->afc.location.type != RADIAL_POLYGON)
++ return 1;
++ } else if (os_strcmp(buf, "afc_linear_polygon") == 0) {
++ if (hostapd_afc_parse_position_data(
++ &conf->afc.location.linear_polygon_data,
++ &conf->afc.location.n_linear_polygon_data,
++ pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_radial_polygon") == 0) {
++ if (hostapd_afc_parse_position_data(
++ (struct afc_linear_polygon **)
++ &conf->afc.location.radial_polygon_data,
++ &conf->afc.location.n_radial_polygon_data,
++ pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_major_axis") == 0) {
++ conf->afc.location.major_axis = atoi(pos);
++ } else if (os_strcmp(buf, "afc_minor_axis") == 0) {
++ conf->afc.location.minor_axis = atoi(pos);
++ } else if (os_strcmp(buf, "afc_orientation") == 0) {
++ conf->afc.location.orientation = atoi(pos);
++ } else if (os_strcmp(buf, "afc_height") == 0) {
++ char *end;
++
++ conf->afc.location.height = strtod(pos, &end);
++ if (*end)
++ return 1;
++ } else if (os_strcmp(buf, "afc_height_type") == 0) {
++ conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1);
++ if (!conf->afc.location.height_type)
++ return 1;
++
++ os_strlcpy(conf->afc.location.height_type, pos,
++ os_strlen(pos) + 1);
++ } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) {
++ conf->afc.location.vertical_tolerance = atoi(pos);
++ } else if (os_strcmp(buf, "afc_min_power") == 0) {
++ conf->afc.min_power = atoi(pos);
++ } else if (os_strcmp(buf, "afc_freq_range") == 0) {
++ if (hostapd_afc_parse_freq_range(conf, pos))
++ return 1;
++ } else if (os_strcmp(buf, "afc_op_class") == 0) {
++ if (hostapd_afc_parse_op_class(conf, pos))
++ return 1;
++#endif /* CONFIG_AFC */
+ } else if (os_strcmp(buf, "mbssid") == 0) {
+ int mbssid = atoi(pos);
+ if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
+diff --git a/hostapd/defconfig b/hostapd/defconfig
+index 550db697b..66bf894eb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -425,3 +425,6 @@ CONFIG_DPP2=y
+
+ # Wi-Fi Aware unsynchronized service discovery (NAN USD)
+ #CONFIG_NAN_USD=y
++
++# Enable Automated Frequency Coordination for 6GHz outdoor
++#CONFIG_AFC=y
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index d80abcac0..0d10998af 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0
+ # Valid range: 0..20 TUs; default is 0 (disabled)
+ #unsol_bcast_probe_resp_interval=0
+
++##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands #######
++
++# AFC daemon connection socket
++#afcd_sock=/var/run/afcd.sock
++
++# AFC request identification parameters
++#afc_request_version=1.1
++#afc_request_id=11235813
++#afc_serial_number=abcdefg
++#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000
++#
++# AFC location type:
++# 0 = ellipse
++# 1 = linear polygon
++# 2 = radial polygon
++#afc_location_type=0
++#
++# AFC ellipse or linear polygon coordinations
++#afc_linear_polygon=-122.984157:37.425056
++#
++# AFC radial polygon coordinations
++#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33
++#
++# AFC ellipse major/minor axis and orientation
++#afc_major_axis=100
++#afc_minor_axis=50
++#afc_orientation=70
++#
++# AFC device elevation parameters
++#afc_height=3.0
++#afc_height_type=AGL
++#afc_vertical_tolerance=7
++#
++# AFC minimum desired TX power (dbm)
++#afc_min_power=24
++#
++# AFC request frequency ranges
++#afc_freq_range=5925:6425,6525:6875
++#
++# AFC request operation classes
++#afc_op_class=131,132,133,134,136
++
+ ##### IEEE 802.11be related configuration #####################################
+
+ #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+new file mode 100644
+index 000000000..c75d5d582
+--- /dev/null
++++ b/src/ap/afc.c
+@@ -0,0 +1,918 @@
++/*
++ * Automated Frequency Coordination
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <json-c/json.h>
++#include <sys/un.h>
++#include <time.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "hostapd.h"
++#include "acs.h"
++#include "hw_features.h"
++
++#define HOSTAPD_AFC_RETRY_TIMEOUT 180
++#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */
++#define HOSTAPD_AFC_BUFSIZE 4096
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
++
++
++static struct json_object *
++hostapd_afc_build_location_request(struct hostapd_iface *iface)
++{
++ struct json_object *location_obj, *center_obj, *ellipse_obj;
++ struct json_object *elevation_obj, *str_obj;
++ struct hostapd_config *iconf = iface->conf;
++ bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
++
++ location_obj = json_object_new_object();
++ if (!location_obj)
++ return NULL;
++
++ if (iconf->afc.location.type != LINEAR_POLYGON) {
++ struct afc_linear_polygon *lp =
++ &iconf->afc.location.linear_polygon_data[0];
++
++ ellipse_obj = json_object_new_object();
++ if (!ellipse_obj)
++ goto error;
++
++ center_obj = json_object_new_object();
++ if (!center_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "center", center_obj);
++
++ str_obj = json_object_new_double(lp->longitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "longitude", str_obj);
++ str_obj = json_object_new_double(lp->latitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "latitude", str_obj);
++
++ }
++
++ switch (iconf->afc.location.type) {
++ case LINEAR_POLYGON: {
++ struct json_object *outer_boundary_obj;
++ int i;
++
++ outer_boundary_obj = json_object_new_object();
++ if (!outer_boundary_obj)
++ goto error;
++
++ json_object_object_add(location_obj, "linearPolygon",
++ outer_boundary_obj);
++ ellipse_obj = json_object_new_array();
++ if (!ellipse_obj)
++ goto error;
++
++ json_object_object_add(outer_boundary_obj, "outerBoundary",
++ ellipse_obj);
++ for (i = 0;
++ i < iconf->afc.location.n_linear_polygon_data; i++) {
++ struct afc_linear_polygon *lp =
++ &iconf->afc.location.linear_polygon_data[i];
++
++ center_obj = json_object_new_object();
++ if (!center_obj)
++ goto error;
++
++ json_object_array_add(ellipse_obj, center_obj);
++ str_obj = json_object_new_double(lp->longitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "longitude",
++ str_obj);
++ str_obj = json_object_new_double(lp->latitude);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(center_obj, "latitude",
++ str_obj);
++ }
++ break;
++ }
++ case RADIAL_POLYGON: {
++ struct json_object *outer_boundary_obj;
++ int i;
++
++ json_object_object_add(location_obj, "radialPolygon",
++ ellipse_obj);
++
++ outer_boundary_obj = json_object_new_array();
++ if (!outer_boundary_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "outerBoundary",
++ outer_boundary_obj);
++ for (i = 0;
++ i < iconf->afc.location.n_radial_polygon_data; i++) {
++ struct afc_radial_polygon *rp =
++ &iconf->afc.location.radial_polygon_data[i];
++ struct json_object *angle_obj;
++
++ angle_obj = json_object_new_object();
++ if (!angle_obj)
++ goto error;
++
++ json_object_array_add(outer_boundary_obj, angle_obj);
++
++ str_obj = json_object_new_double(rp->angle);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(angle_obj, "angle", str_obj);
++ str_obj = json_object_new_double(rp->length);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(angle_obj, "length", str_obj);
++ }
++ break;
++ }
++ case ELLIPSE:
++ default:
++ json_object_object_add(location_obj, "ellipse", ellipse_obj);
++
++ str_obj = json_object_new_int(iconf->afc.location.major_axis);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "majorAxis", str_obj);
++ str_obj = json_object_new_int(iconf->afc.location.minor_axis);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "minorAxis", str_obj);
++ str_obj = json_object_new_int(iconf->afc.location.orientation);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(ellipse_obj, "orientation", str_obj);
++ break;
++ }
++
++ elevation_obj = json_object_new_object();
++ if (!elevation_obj)
++ goto error;
++
++ json_object_object_add(location_obj, "elevation",
++ elevation_obj);
++ str_obj = json_object_new_double(iconf->afc.location.height);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(elevation_obj, "height", str_obj);
++ str_obj = json_object_new_string(iconf->afc.location.height_type);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(elevation_obj, "heightType", str_obj);
++ str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(elevation_obj, "verticalUncertainty",
++ str_obj);
++ str_obj = json_object_new_int(is_ap_indoor);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(location_obj, "indoorDeployment", str_obj);
++
++ return location_obj;
++
++error:
++ json_object_put(location_obj);
++ return NULL;
++}
++
++
++static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
++{
++ struct json_object *chan_list_obj, *str_obj;
++ const struct oper_class_map *oper_class;
++ int chan_offset, chan;
++
++ oper_class = get_oper_class(NULL, op_class);
++ if (!oper_class)
++ return NULL;
++
++ chan_list_obj = json_object_new_array();
++ if (!chan_list_obj)
++ return NULL;
++
++ switch (op_class) {
++ case 132: /* 40MHz */
++ chan_offset = 2;
++ break;
++ case 133: /* 80MHz */
++ chan_offset = 6;
++ break;
++ case 134: /* 160MHz */
++ chan_offset = 14;
++ break;
++ default:
++ chan_offset = 0;
++ break;
++ }
++
++ for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
++ chan += oper_class->inc) {
++ str_obj = json_object_new_int(chan + chan_offset);
++ if (!str_obj) {
++ json_object_put(chan_list_obj);
++ return NULL;
++ }
++ json_object_array_add(chan_list_obj, str_obj);
++ }
++
++ return chan_list_obj;
++}
++
++
++static struct json_object *
++hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
++{
++ struct json_object *op_class_list_obj, *str_obj;
++ struct hostapd_config *iconf = iface->conf;
++ int i;
++
++ op_class_list_obj = json_object_new_array();
++ if (!op_class_list_obj)
++ return NULL;
++
++ for (i = 0; i < iconf->afc.n_op_class; i++) {
++ struct json_object *op_class_obj, *chan_list_obj;
++ u8 op_class = iconf->afc.op_class[i];
++
++ if (!is_6ghz_op_class(op_class))
++ continue;
++
++ op_class_obj = json_object_new_object();
++ if (!op_class_obj)
++ goto error;
++
++ json_object_array_add(op_class_list_obj, op_class_obj);
++ str_obj = json_object_new_int(op_class);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(op_class_obj, "globalOperatingClass",
++ str_obj);
++
++ chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
++ if (!chan_list_obj)
++ goto error;
++
++ json_object_object_add(op_class_obj, "channelCfi",
++ chan_list_obj);
++ }
++
++ return op_class_list_obj;
++
++error:
++ json_object_put(op_class_list_obj);
++ return NULL;
++}
++
++
++static struct json_object *
++hostapd_afc_build_request(struct hostapd_iface *iface)
++{
++ struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
++ struct json_object *s2_obj, *str_obj, *location_obj;
++ struct hostapd_config *iconf = iface->conf;
++ struct json_object *op_class_list_obj;
++ int i;
++
++ l1_obj = json_object_new_object();
++ if (!l1_obj)
++ return NULL;
++
++ if (iconf->afc.request.version) {
++ str_obj = json_object_new_string(iconf->afc.request.version);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(l1_obj, "version", str_obj);
++ }
++
++ la1_obj = json_object_new_array();
++ if (!la1_obj)
++ goto error;
++
++ json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
++ la1_obj);
++ l2_obj = json_object_new_object();
++ if (!l2_obj)
++ goto error;
++
++ json_object_array_add(la1_obj, l2_obj);
++ if (iconf->afc.request.id) {
++ str_obj = json_object_new_string(iconf->afc.request.id);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "requestId", str_obj);
++ }
++
++ s2_obj = json_object_new_object();
++ if (!s2_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
++ if (iconf->afc.request.sn) {
++ str_obj = json_object_new_string(iconf->afc.request.sn);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(s2_obj, "serialNumber", str_obj);
++ }
++
++ la2_obj = json_object_new_array();
++ if (!la2_obj)
++ goto error;
++
++ json_object_object_add(s2_obj, "certificationId", la2_obj);
++ for (i = 0; i < iconf->afc.n_cert_ids; i++) {
++ struct json_object *obj;
++
++ obj = json_object_new_object();
++ if (!obj)
++ goto error;
++
++ json_object_array_add(la2_obj, obj);
++ str_obj =
++ json_object_new_string(iconf->afc.cert_ids[i].rulset);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "rulesetId", str_obj);
++ str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "id", str_obj);
++ }
++
++ location_obj = hostapd_afc_build_location_request(iface);
++ if (!location_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "location", location_obj);
++ str_obj = json_object_new_int(iconf->afc.min_power);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "minDesiredPower", str_obj);
++
++ if (iconf->afc.n_freq_range) {
++ struct json_object *freq_obj;
++
++ freq_obj = json_object_new_array();
++ if (!freq_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "inquiredFrequencyRange",
++ freq_obj);
++ for (i = 0; i < iconf->afc.n_freq_range; i++) {
++ struct afc_freq_range *fr = &iconf->afc.freq_range[i];
++ struct json_object *obj;
++
++ obj = json_object_new_object();
++ if (!obj)
++ goto error;
++
++ json_object_array_add(freq_obj, obj);
++ str_obj = json_object_new_int(fr->low_freq);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "lowFrequency", str_obj);
++ str_obj = json_object_new_int(fr->high_freq);
++ if (!str_obj)
++ goto error;
++
++ json_object_object_add(obj, "highFrequency", str_obj);
++ }
++ }
++
++ op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
++ if (!op_class_list_obj)
++ goto error;
++
++ json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
++
++ wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
++ json_object_get_string(l1_obj));
++
++ return l1_obj;
++
++error:
++ json_object_put(l1_obj);
++
++ return NULL;
++}
++
++
++static int
++hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
++ struct json_object *reply_elem_obj)
++{
++ struct afc_freq_range_elem *f = NULL;
++ struct json_object *obj;
++ int i, count = 0;
++
++ if (!json_object_object_get_ex(reply_elem_obj,
++ "availableFrequencyInfo", &obj))
++ return 0;
++
++ for (i = 0; i < json_object_array_length(obj); i++) {
++ struct json_object *range_elem_obj, *freq_range_obj;
++ struct json_object *high_freq_obj, *low_freq_obj;
++ struct json_object *max_psd_obj;
++
++ range_elem_obj = json_object_array_get_idx(obj, i);
++ if (!json_object_object_get_ex(range_elem_obj,
++ "frequencyRange",
++ &freq_range_obj))
++ continue;
++
++ if (!json_object_object_get_ex(freq_range_obj,
++ "lowFrequency",
++ &low_freq_obj))
++ continue;
++
++ if (!json_object_object_get_ex(freq_range_obj,
++ "highFrequency",
++ &high_freq_obj))
++ continue;
++
++ if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
++ &max_psd_obj) &&
++ !json_object_object_get_ex(range_elem_obj, "maxPSD",
++ &max_psd_obj))
++ continue;
++
++ f = os_realloc_array(f, count + 1, sizeof(*f));
++ if (!f)
++ return -ENOMEM;
++
++ f[count].low_freq = json_object_get_int(low_freq_obj);
++ f[count].high_freq = json_object_get_int(high_freq_obj);
++ f[count++].max_psd = json_object_get_int(max_psd_obj);
++ }
++ iface->afc.freq_range = f;
++ iface->afc.num_freq_range = count;
++
++ return 0;
++}
++
++
++static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
++ int *chan_list_size, u8 op_class,
++ int center_chan, int power)
++{
++ int num_low_subchan, ch, count = *chan_list_size;
++ struct afc_chan_info_elem *c = *chan_list;
++
++ switch (op_class) {
++ case 132: /* 40MHz */
++ num_low_subchan = 2;
++ break;
++ case 133: /* 80MHz */
++ num_low_subchan = 6;
++ break;
++ case 134: /* 160MHz */
++ num_low_subchan = 14;
++ break;
++ default:
++ num_low_subchan = 0;
++ break;
++ }
++
++ for (ch = center_chan - num_low_subchan;
++ ch <= center_chan + num_low_subchan; ch += 4) {
++ int i;
++
++ for (i = 0; i < count; i++) {
++ if (c[i].chan == ch)
++ break;
++ }
++
++ if (i == count) {
++ c = os_realloc_array(c, count + 1, sizeof(*c));
++ if (!c)
++ return -ENOMEM;
++
++ c[count].chan = ch;
++ c[count++].power = power;
++ }
++ }
++
++ *chan_list_size = count;
++ *chan_list = c;
++
++ return 0;
++}
++
++
++static int
++hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
++ struct json_object *reply_elem_obj)
++{
++ struct afc_chan_info_elem *c = NULL;
++ struct json_object *obj;
++ int i, count = 0;
++
++ if (!json_object_object_get_ex(reply_elem_obj,
++ "availableChannelInfo", &obj))
++ return 0;
++
++ for (i = 0; i < json_object_array_length(obj); i++) {
++ struct json_object *range_elem_obj, *op_class_obj;
++ struct json_object *chan_cfi_obj, *max_eirp_obj;
++ int ch, op_class;
++
++ range_elem_obj = json_object_array_get_idx(obj, i);
++ if (!json_object_object_get_ex(range_elem_obj,
++ "globalOperatingClass",
++ &op_class_obj))
++ continue;
++
++ if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
++ &max_eirp_obj))
++ continue;
++
++ if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
++ &chan_cfi_obj))
++ continue;
++
++ op_class = json_object_get_int(op_class_obj);
++ for (ch = 0;
++ ch < json_object_array_length(chan_cfi_obj); ch++) {
++ struct json_object *pwr_obj;
++ struct json_object *ch_obj;
++ int channel, power;
++
++ ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
++ if (!ch_obj)
++ continue;
++
++ pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
++ if (!pwr_obj)
++ continue;
++
++ channel = json_object_get_int(ch_obj);
++ power = json_object_get_int(pwr_obj);
++
++ hostad_afc_update_chan_info(&c, &count, op_class,
++ channel, power);
++ }
++ iface->afc.chan_info_list = c;
++ iface->afc.num_chan_info = count;
++ }
++
++ return 0;
++}
++
++
++static int hostad_afc_get_timeout(struct json_object *obj)
++{
++ time_t t, now;
++ struct tm tm;
++
++ if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
++ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
++ &tm.tm_min, &tm.tm_sec) <= 0)
++ return HOSTAPD_AFC_TIMEOUT;
++
++ tm.tm_year -= 1900;
++ tm.tm_mon -= 1;
++ tm.tm_isdst = -1;
++ t = mktime(&tm);
++ time(&now);
++
++ return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
++}
++
++
++static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
++{
++ struct json_object *payload_obj, *reply_obj, *version_obj;
++ struct hostapd_config *iconf = iface->conf;
++ int i, request_timeout = -1, ret = -EINVAL;
++
++ wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
++ payload_obj = json_tokener_parse(reply);
++ if (!payload_obj)
++ return -EINVAL;
++
++ if (!json_object_object_get_ex(payload_obj, "version", &version_obj))
++ return -EINVAL;
++
++ if (iconf->afc.request.version &&
++ os_strcmp(iconf->afc.request.version,
++ json_object_get_string(version_obj)))
++ return -EINVAL;
++
++ if (!json_object_object_get_ex(payload_obj,
++ "availableSpectrumInquiryResponses",
++ &reply_obj))
++ return -EINVAL;
++
++ for (i = 0; i < json_object_array_length(reply_obj); i++) {
++ struct json_object *reply_elem_obj, *obj, *status_obj;
++ int j, status = -EINVAL;
++
++ reply_elem_obj = json_object_array_get_idx(reply_obj, i);
++ if (!reply_elem_obj)
++ continue;
++
++ if (!json_object_object_get_ex(reply_elem_obj, "requestId",
++ &obj))
++ continue;
++
++ if (iconf->afc.request.id &&
++ os_strcmp(iconf->afc.request.id,
++ json_object_get_string(obj)))
++ continue;
++
++ if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
++ &obj))
++ continue;
++
++ for (j = 0; j < iconf->afc.n_cert_ids; j++) {
++ if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
++ json_object_get_string(obj)))
++ break;
++ }
++
++ if (j == iconf->afc.n_cert_ids)
++ continue;
++
++ if (!json_object_object_get_ex(reply_elem_obj, "response",
++ &obj))
++ continue;
++
++ if (json_object_object_get_ex(obj, "shortDescription",
++ &status_obj))
++ wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
++ i, json_object_get_string(status_obj));
++
++ if (json_object_object_get_ex(obj, "responseCode",
++ &status_obj))
++ status = json_object_get_int(status_obj);
++
++ if (status < 0)
++ continue;
++
++ if (hostad_afc_parse_available_freq_info(iface,
++ reply_elem_obj) ||
++ hostad_afc_parse_available_chan_info(iface,
++ reply_elem_obj))
++ continue;
++
++ if (json_object_object_get_ex(reply_elem_obj,
++ "availabilityExpireTime",
++ &obj)) {
++ int timeout = hostad_afc_get_timeout(obj);
++
++ if (request_timeout < 0 || timeout < request_timeout)
++ request_timeout = timeout;
++ }
++
++ ret = status;
++ }
++
++ iface->afc.data_valid = true;
++ iface->afc.timeout = request_timeout;
++ if (iface->afc.timeout < 0)
++ iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++
++ return ret;
++}
++
++
++static int hostapd_afc_send_receive(struct hostapd_iface *iface)
++{
++ struct hostapd_config *iconf = iface->conf;
++ json_object *request_obj = NULL;
++ struct timeval sock_timeout = {
++ .tv_sec = 5,
++ };
++ struct sockaddr_un addr = {
++ .sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++ .sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++ };
++ char buf[HOSTAPD_AFC_BUFSIZE] = {};
++ const char *request;
++ int sockfd, ret;
++ fd_set read_set;
++
++ if (iface->afc.data_valid) {
++ /* AFC data already downloaded from the server */
++ return 0;
++ }
++
++ iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++ if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
++ wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
++ iconf->afc.socket);
++ return -EINVAL;
++ }
++
++ os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
++ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (sockfd < 0) {
++ wpa_printf(MSG_ERROR, "Failed creating AFC socket");
++ return sockfd;
++ }
++
++ if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
++ ret = -EIO;
++ goto close_sock;
++ }
++
++ request_obj = hostapd_afc_build_request(iface);
++ if (!request_obj) {
++ ret = -ENOMEM;
++ goto close_sock;
++ }
++
++ request = json_object_to_json_string(request_obj);
++ if (send(sockfd, request, strlen(request), 0) < 0) {
++ wpa_printf(MSG_ERROR, "Failed sending AFC request");
++ ret = -EIO;
++ goto close_sock;
++ }
++
++ FD_ZERO(&read_set);
++ FD_SET(sockfd, &read_set);
++ if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
++ wpa_printf(MSG_ERROR, "Select failed on AFC socket");
++ ret = -errno;
++ goto close_sock;
++ }
++
++ if (!FD_ISSET(sockfd, &read_set)) {
++ ret = -EIO;
++ goto close_sock;
++ }
++
++ ret = recv(sockfd, buf, sizeof(buf), 0);
++ if (ret <= 0)
++ goto close_sock;
++
++ ret = hostapd_afc_parse_reply(iface, buf);
++close_sock:
++ json_object_put(request_obj);
++ close(sockfd);
++
++ return ret;
++}
++
++
++static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
++{
++ const struct oper_class_map *oper_class;
++ int ch;
++
++ oper_class = get_oper_class(NULL, iface->conf->op_class);
++ if (!oper_class)
++ return false;
++
++ for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
++ ch += oper_class->inc) {
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ int i;
++
++ for (i = 0; i < mode->num_channels; i++) {
++ struct hostapd_channel_data *chan = &mode->channels[i];
++
++ if (chan->chan == ch &&
++ !(chan->flag & HOSTAPD_CHAN_DISABLED))
++ return true;
++ }
++ }
++
++ return false;
++}
++
++
++int hostapd_afc_handle_request(struct hostapd_iface *iface)
++{
++ struct hostapd_config *iconf = iface->conf;
++ int ret;
++
++ /* AFC is required just for standard power AP */
++ if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++ return 1;
++
++ if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
++ return 1;
++
++ if (iface->state == HAPD_IFACE_ACS)
++ return 1;
++
++ ret = hostapd_afc_send_receive(iface);
++ if (ret < 0) {
++ /*
++ * If the connection to the AFCD failed, resched for a
++ * future attempt.
++ */
++ wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
++ if (ret == -EIO)
++ ret = 0;
++ goto resched;
++ }
++
++ hostap_afc_disable_channels(iface);
++ if (!hostapd_afc_has_usable_chans(iface))
++ goto resched;
++
++ /* Trigger an ACS freq scan */
++ iconf->channel = 0;
++ iface->freq = 0;
++
++ if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
++ wpa_printf(MSG_ERROR, "Could not start ACS");
++ ret = -EINVAL;
++ }
++
++resched:
++ eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
++ eloop_register_timeout(iface->afc.timeout, 0,
++ hostapd_afc_timeout_handler, iface, NULL);
++
++ return ret;
++}
++
++
++static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
++{
++ os_free(iface->afc.chan_info_list);
++ os_free(iface->afc.freq_range);
++
++ iface->afc.num_freq_range = 0;
++ iface->afc.num_chan_info = 0;
++
++ iface->afc.chan_info_list = NULL;
++ iface->afc.freq_range = NULL;
++
++ iface->afc.data_valid = false;
++}
++
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_iface *iface = eloop_ctx;
++ bool restart_iface = true;
++
++ hostapd_afc_delete_data_from_server(iface);
++ if (iface->state != HAPD_IFACE_ENABLED) {
++ /* Hostapd is not fully enabled yet, toogle the interface */
++ goto restart_interface;
++ }
++
++ if (hostapd_afc_send_receive(iface) < 0 ||
++ hostapd_get_hw_features(iface)) {
++ restart_iface = false;
++ goto restart_interface;
++ }
++
++ if (hostapd_is_usable_chans(iface))
++ goto resched;
++
++ restart_iface = hostapd_afc_has_usable_chans(iface);
++ if (restart_iface) {
++ /* Trigger an ACS freq scan */
++ iface->conf->channel = 0;
++ iface->freq = 0;
++ }
++
++restart_interface:
++ hostapd_disable_iface(iface);
++ if (restart_iface)
++ hostapd_enable_iface(iface);
++resched:
++ eloop_register_timeout(iface->afc.timeout, 0,
++ hostapd_afc_timeout_handler, iface, NULL);
++}
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 1a18df617..ca67aeb41 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #endif /* CONFIG_ACS */
+ wpabuf_free(conf->lci);
+ wpabuf_free(conf->civic);
++#ifdef CONFIG_AFC
++ os_free(conf->afc.socket);
++ os_free(conf->afc.request.version);
++ os_free(conf->afc.request.id);
++ os_free(conf->afc.request.sn);
++ for (i = 0; i < conf->afc.n_cert_ids; i++) {
++ os_free(conf->afc.cert_ids[i].rulset);
++ os_free(conf->afc.cert_ids[i].id);
++ }
++ os_free(conf->afc.cert_ids);
++ os_free(conf->afc.location.height_type);
++ os_free(conf->afc.location.linear_polygon_data);
++ os_free(conf->afc.location.radial_polygon_data);
++ os_free(conf->afc.freq_range);
++ os_free(conf->afc.op_class);
++#endif /* CONFIG_AFC */
+
+ os_free(conf);
+ }
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 754d55331..2330163c4 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1225,6 +1225,53 @@ struct hostapd_config {
+ MBSSID_ENABLED = 1,
+ ENHANCED_MBSSID_ENABLED = 2,
+ } mbssid;
++
++#ifdef CONFIG_AFC
++ struct {
++ char *socket;
++ struct {
++ char *version;
++ char *id;
++ char *sn;
++ } request;
++ unsigned int n_cert_ids;
++ struct cert_id {
++ char *rulset;
++ char *id;
++ } *cert_ids;
++ struct {
++ enum afc_location_type {
++ ELLIPSE,
++ LINEAR_POLYGON,
++ RADIAL_POLYGON,
++ } type;
++ unsigned int n_linear_polygon_data;
++ struct afc_linear_polygon {
++ double longitude;
++ double latitude;
++ } *linear_polygon_data;
++ unsigned int n_radial_polygon_data;
++ struct afc_radial_polygon {
++ double length;
++ double angle;
++ } *radial_polygon_data;
++ int major_axis;
++ int minor_axis;
++ int orientation;
++ double height;
++ char *height_type;
++ int vertical_tolerance;
++ } location;
++ unsigned int n_freq_range;
++ struct afc_freq_range {
++ int low_freq;
++ int high_freq;
++ } *freq_range;
++ unsigned int n_op_class;
++ unsigned int *op_class;
++ int min_power;
++ } afc;
++#endif /* CONFIG_AFC */
+ };
+
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index ff1d8f9d0..916ac00c4 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2497,6 +2497,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ }
+ #endif /* CONFIG_MESH */
+
++#ifdef CONFIG_AFC
++ /* check AFC for 6GHz channels. */
++ res = hostapd_afc_handle_request(iface);
++ if (res <= 0) {
++ if (res < 0)
++ goto fail;
++ return res;
++ }
++#endif /* CONFIG_AFC */
++
+ if (!delay_apply_cfg &&
+ hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ hapd->iconf->channel,
+@@ -2968,6 +2978,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
+ __func__, iface->bss[j]);
+ os_free(iface->bss[j]);
+ }
++#ifdef CONFIG_AFC
++ os_free(iface->afc.chan_info_list);
++ os_free(iface->afc.freq_range);
++#endif
+ hostapd_cleanup_iface(iface);
+ }
+
+@@ -4888,3 +4902,49 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
+
+ return punct_bitmap;
+ }
++
++
++void hostap_afc_disable_channels(struct hostapd_iface *iface)
++{
++#ifdef CONFIG_AFC
++ struct hostapd_hw_modes *mode;
++ int i;
++
++ if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A)
++ return;
++
++ if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++ return;
++
++ if (!iface->afc.data_valid)
++ return;
++
++ mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A];
++ for (i = 0; i < mode->num_channels; i++) {
++ struct hostapd_channel_data *chan = &mode->channels[i];
++ int j;
++
++ if (!is_6ghz_freq(chan->freq))
++ continue;
++
++ for (j = 0; j < iface->afc.num_freq_range; j++) {
++ if (chan->freq >= iface->afc.freq_range[j].low_freq &&
++ chan->freq <= iface->afc.freq_range[j].high_freq)
++ break;
++ }
++
++ if (j != iface->afc.num_freq_range)
++ continue;
++
++ for (j = 0; j < iface->afc.num_chan_info; j++) {
++ if (chan->chan == iface->afc.chan_info_list[j].chan)
++ break;
++ }
++
++ if (j != iface->afc.num_chan_info)
++ continue;
++
++ chan->flag |= HOSTAPD_CHAN_DISABLED;
++ }
++#endif /* CONFIG_AFC */
++}
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index d12efb104..18bcb82d9 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -700,9 +700,38 @@ struct hostapd_iface {
+
+ /* Configured freq of interface is NO_IR */
+ bool is_no_ir;
++
++#ifdef CONFIG_AFC
++ struct {
++ int timeout;
++ unsigned int num_freq_range;
++ struct afc_freq_range_elem {
++ int low_freq;
++ int high_freq;
++ /**
++ * max eirp power spectral density received from
++ * the AFC coordinator for this band
++ */
++ int max_psd;
++ } *freq_range;
++ unsigned int num_chan_info;
++ struct afc_chan_info_elem {
++ int chan;
++ /**
++ * max eirp power received from the AFC coordinator
++ * for this channel
++ */
++ int power;
++ } *chan_info_list;
++ bool data_valid;
++ } afc;
++#endif /* CONFIG_AFC */
+ };
+
+ /* hostapd.c */
++void hostap_afc_disable_channels(struct hostapd_iface *iface);
++int hostapd_afc_handle_request(struct hostapd_iface *iface);
++
+ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index e652d7504..222f3dc05 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
+ iface->hw_features = modes;
+ iface->num_hw_features = num_modes;
+
++ hostap_afc_disable_channels(iface);
++
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *feature = &modes[i];
+ int dfs_enabled = hapd->iconf->ieee80211h &&
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
new file mode 100644
index 0000000..42ff1d2
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
@@ -0,0 +1,158 @@
+From b2078261e779c949218974a054dc52f3dc5493c7 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 20 Mar 2024 07:20:01 +0800
+Subject: [PATCH 026/104] backport: hostapd: update TPE IE according to AFC
+
+Update Transmit Power Envelope (TPE) IE according to the reply from AFC
+coordinator on UNII-5 or UNII-7 6GHz bands.
+
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/hostapd.c | 39 +++++++++++++++++++++++++++++++++++++
+ src/ap/hostapd.h | 2 ++
+ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++-----------------
+ 3 files changed, 70 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 916ac00c4..b899c9831 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4904,6 +4904,45 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
+ }
+
+
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++ int *power)
++{
++#ifdef CONFIG_AFC
++ int i;
++
++ if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++ return -EINVAL;
++
++ if (!iface->afc.data_valid)
++ return -EINVAL;
++
++ if (psd) {
++ for (i = 0; i < iface->afc.num_freq_range; i++) {
++ struct afc_freq_range_elem *f;
++
++ f = &iface->afc.freq_range[i];
++ if (iface->freq >= f->low_freq &&
++ iface->freq <= f->high_freq) {
++ *power = 2 * f->max_psd;
++ return 0;
++ }
++ }
++ } else {
++ for (i = 0; i < iface->afc.num_chan_info; i++) {
++ struct afc_chan_info_elem *c;
++
++ c = &iface->afc.chan_info_list[i];
++ if (c->chan == iface->conf->channel) {
++ *power = 2 * c->power;
++ return 0;
++ }
++ }
++ }
++#endif /* CONFIG_AFC */
++ return -EINVAL;
++}
++
++
+ void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ {
+ #ifdef CONFIG_AFC
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 18bcb82d9..594866fbb 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -729,6 +729,8 @@ struct hostapd_iface {
+ };
+
+ /* hostapd.c */
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++ int *power);
+ void hostap_afc_disable_channels(struct hostapd_iface *iface);
+ int hostapd_afc_handle_request(struct hostapd_iface *iface);
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 9a23c7240..179af5e28 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7047,42 +7047,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+ */
+ if (is_6ghz_op_class(iconf->op_class)) {
+ enum max_tx_pwr_interpretation tx_pwr_intrpn;
++ int err, max_eirp_psd, max_eirp_power;
+
+ /* Same Maximum Transmit Power for all 20 MHz bands */
+ tx_pwr_count = 0;
+ tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
+
+ /* Default Transmit Power Envelope for Global Operating Class */
+- if (hapd->iconf->reg_def_cli_eirp_psd != -1)
+- tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
+- else
+- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++ err = hostap_afc_get_chan_max_eirp_power(iface, true,
++ &max_eirp_psd);
++ if (err < 0) {
++ if (hapd->iconf->reg_def_cli_eirp_psd != -1)
++ max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd;
++ else
++ max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++ }
+
+ eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
+- REG_DEFAULT_CLIENT, tx_pwr);
++ REG_DEFAULT_CLIENT, max_eirp_psd);
+
+ /* Indoor Access Point must include an additional TPE for
+ * subordinate devices */
+ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
+- /* TODO: Extract PSD limits from channel data */
+- if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
+- tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
+- else
+- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++ if (err < 0) {
++ /* non-AFC connection */
++ if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
++ max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd;
++ else
++ max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++ }
+ eid = hostapd_add_tpe_info(eid, tx_pwr_count,
+ tx_pwr_intrpn,
+ REG_SUBORDINATE_CLIENT,
+- tx_pwr);
++ max_eirp_psd);
+ }
+
+- if (iconf->reg_def_cli_eirp != -1 &&
+- he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+- eid = hostapd_add_tpe_info(
+- eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
+- REG_DEFAULT_CLIENT,
+- hapd->iconf->reg_def_cli_eirp);
++ if (hostap_afc_get_chan_max_eirp_power(iface, false,
++ &max_eirp_power)) {
++ max_eirp_power = iconf->reg_def_cli_eirp;
++ if (max_eirp_power == -1 ||
++ !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++ return eid;
++ }
+
+- return eid;
++ return hostapd_add_tpe_info(eid, tx_pwr_count,
++ REGULATORY_CLIENT_EIRP,
++ REG_DEFAULT_CLIENT,
++ max_eirp_power);
+ }
+ #endif /* CONFIG_IEEE80211AX */
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
new file mode 100644
index 0000000..cbe2769
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
@@ -0,0 +1,5128 @@
+From ccb5628a9c5eae2b56cb88f43f850146863cba30 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Tue, 23 Jan 2024 16:46:44 +0800
+Subject: [PATCH 027/104] hostapd: sync 2024-01-18 openwrt/trunk src folder
+
+---
+ hostapd/radius.c | 715 +++++++++++++
+ src/ap/ubus.c | 2005 ++++++++++++++++++++++++++++++++++++
+ src/ap/ubus.h | 154 +++
+ src/ap/ucode.c | 813 +++++++++++++++
+ src/ap/ucode.h | 54 +
+ src/utils/build_features.h | 65 ++
+ src/utils/ucode.c | 502 +++++++++
+ src/utils/ucode.h | 30 +
+ wpa_supplicant/ubus.c | 280 +++++
+ wpa_supplicant/ubus.h | 55 +
+ wpa_supplicant/ucode.c | 299 ++++++
+ wpa_supplicant/ucode.h | 49 +
+ 12 files changed, 5021 insertions(+)
+ create mode 100644 hostapd/radius.c
+ create mode 100644 src/ap/ubus.c
+ create mode 100644 src/ap/ubus.h
+ create mode 100644 src/ap/ucode.c
+ create mode 100644 src/ap/ucode.h
+ create mode 100644 src/utils/build_features.h
+ create mode 100644 src/utils/ucode.c
+ create mode 100644 src/utils/ucode.h
+ create mode 100644 wpa_supplicant/ubus.c
+ create mode 100644 wpa_supplicant/ubus.h
+ create mode 100644 wpa_supplicant/ucode.c
+ create mode 100644 wpa_supplicant/ucode.h
+
+diff --git a/hostapd/radius.c b/hostapd/radius.c
+new file mode 100644
+index 000000000..362a22c27
+--- /dev/null
++++ b/hostapd/radius.c
+@@ -0,0 +1,715 @@
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "crypto/crypto.h"
++#include "crypto/tls.h"
++
++#include "ap/ap_config.h"
++#include "eap_server/eap.h"
++#include "radius/radius.h"
++#include "radius/radius_server.h"
++#include "eap_register.h"
++
++#include <libubox/blobmsg_json.h>
++#include <libubox/blobmsg.h>
++#include <libubox/avl.h>
++#include <libubox/avl-cmp.h>
++#include <libubox/kvlist.h>
++
++#include <sys/stat.h>
++#include <fnmatch.h>
++
++#define VENDOR_ID_WISPR 14122
++#define VENDOR_ATTR_SIZE 6
++
++struct radius_parse_attr_data {
++ unsigned int vendor;
++ u8 type;
++ int size;
++ char format;
++ const char *data;
++};
++
++struct radius_parse_attr_state {
++ struct hostapd_radius_attr *prev;
++ struct hostapd_radius_attr *attr;
++ struct wpabuf *buf;
++ void *attrdata;
++};
++
++struct radius_user_state {
++ struct avl_node node;
++ struct eap_user data;
++};
++
++struct radius_user_data {
++ struct kvlist users;
++ struct avl_tree user_state;
++ struct blob_attr *wildcard;
++};
++
++struct radius_state {
++ struct radius_server_data *radius;
++ struct eap_config eap;
++
++ struct radius_user_data phase1, phase2;
++ const char *user_file;
++ time_t user_file_ts;
++
++ int n_attrs;
++ struct hostapd_radius_attr *attrs;
++};
++
++struct radius_config {
++ struct tls_connection_params tls;
++ struct radius_server_conf radius;
++};
++
++enum {
++ USER_ATTR_PASSWORD,
++ USER_ATTR_HASH,
++ USER_ATTR_SALT,
++ USER_ATTR_METHODS,
++ USER_ATTR_RADIUS,
++ USER_ATTR_VLAN,
++ USER_ATTR_MAX_RATE_UP,
++ USER_ATTR_MAX_RATE_DOWN,
++ __USER_ATTR_MAX
++};
++
++static void radius_tls_event(void *ctx, enum tls_event ev,
++ union tls_event_data *data)
++{
++ switch (ev) {
++ case TLS_CERT_CHAIN_SUCCESS:
++ wpa_printf(MSG_DEBUG, "radius: remote certificate verification success");
++ break;
++ case TLS_CERT_CHAIN_FAILURE:
++ wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
++ data->cert_fail.reason,
++ data->cert_fail.depth,
++ data->cert_fail.subject,
++ data->cert_fail.reason_txt);
++ break;
++ case TLS_PEER_CERTIFICATE:
++ wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s",
++ data->peer_cert.depth,
++ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
++ data->peer_cert.subject);
++ break;
++ case TLS_ALERT:
++ if (data->alert.is_local)
++ wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s",
++ data->alert.description);
++ else
++ wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s",
++ data->alert.description);
++ break;
++ case TLS_UNSAFE_RENEGOTIATION_DISABLED:
++ /* Not applicable to TLS server */
++ break;
++ }
++}
++
++static void radius_userdata_init(struct radius_user_data *u)
++{
++ kvlist_init(&u->users, kvlist_blob_len);
++ avl_init(&u->user_state, avl_strcmp, false, NULL);
++}
++
++static void radius_userdata_free(struct radius_user_data *u)
++{
++ struct radius_user_state *s, *tmp;
++
++ kvlist_free(&u->users);
++ free(u->wildcard);
++ u->wildcard = NULL;
++ avl_remove_all_elements(&u->user_state, s, node, tmp)
++ free(s);
++}
++
++static void
++radius_userdata_load(struct radius_user_data *u, struct blob_attr *data)
++{
++ enum {
++ USERSTATE_USERS,
++ USERSTATE_WILDCARD,
++ __USERSTATE_MAX,
++ };
++ static const struct blobmsg_policy policy[__USERSTATE_MAX] = {
++ [USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE },
++ [USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY },
++ };
++ struct blob_attr *tb[__USERSTATE_MAX], *cur;
++ int rem;
++
++ if (!data)
++ return;
++
++ blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data));
++
++ blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem)
++ kvlist_set(&u->users, blobmsg_name(cur), cur);
++
++ if (tb[USERSTATE_WILDCARD])
++ u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]);
++}
++
++static void
++load_userfile(struct radius_state *s)
++{
++ enum {
++ USERDATA_PHASE1,
++ USERDATA_PHASE2,
++ __USERDATA_MAX
++ };
++ static const struct blobmsg_policy policy[__USERDATA_MAX] = {
++ [USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE },
++ [USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE },
++ };
++ struct blob_attr *tb[__USERDATA_MAX], *cur;
++ static struct blob_buf b;
++ struct stat st;
++ int rem;
++
++ if (stat(s->user_file, &st))
++ return;
++
++ if (s->user_file_ts == st.st_mtime)
++ return;
++
++ s->user_file_ts = st.st_mtime;
++ radius_userdata_free(&s->phase1);
++ radius_userdata_free(&s->phase2);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_json_from_file(&b, s->user_file);
++ blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head));
++ radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]);
++ radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]);
++
++ blob_buf_free(&b);
++}
++
++static struct blob_attr *
++radius_user_get(struct radius_user_data *s, const char *name)
++{
++ struct blob_attr *cur;
++ int rem;
++
++ cur = kvlist_get(&s->users, name);
++ if (cur)
++ return cur;
++
++ blobmsg_for_each_attr(cur, s->wildcard, rem) {
++ static const struct blobmsg_policy policy = {
++ "name", BLOBMSG_TYPE_STRING
++ };
++ struct blob_attr *pattern;
++
++ if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
++ continue;
++
++ blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur));
++ if (!name)
++ continue;
++
++ if (!fnmatch(blobmsg_get_string(pattern), name, 0))
++ return cur;
++ }
++
++ return NULL;
++}
++
++static struct radius_parse_attr_data *
++radius_parse_attr(struct blob_attr *attr)
++{
++ static const struct blobmsg_policy policy[4] = {
++ { .type = BLOBMSG_TYPE_INT32 },
++ { .type = BLOBMSG_TYPE_INT32 },
++ { .type = BLOBMSG_TYPE_STRING },
++ { .type = BLOBMSG_TYPE_STRING },
++ };
++ static struct radius_parse_attr_data data;
++ struct blob_attr *tb[4];
++ const char *format;
++
++ blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr));
++
++ if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
++ return NULL;
++
++ format = blobmsg_get_string(tb[2]);
++ if (strlen(format) != 1)
++ return NULL;
++
++ data.vendor = blobmsg_get_u32(tb[0]);
++ data.type = blobmsg_get_u32(tb[1]);
++ data.format = format[0];
++ data.data = blobmsg_get_string(tb[3]);
++ data.size = strlen(data.data);
++
++ switch (data.format) {
++ case 's':
++ break;
++ case 'x':
++ if (data.size & 1)
++ return NULL;
++ data.size /= 2;
++ break;
++ case 'd':
++ data.size = 4;
++ break;
++ default:
++ return NULL;
++ }
++
++ return &data;
++}
++
++static void
++radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size)
++{
++ struct blob_attr *data = tb[USER_ATTR_RADIUS];
++ struct blob_attr *cur;
++ int rem;
++
++ blobmsg_for_each_attr(cur, data, rem) {
++ struct radius_parse_attr_data *data;
++ size_t prev = *attr_size;
++
++ data = radius_parse_attr(cur);
++ if (!data)
++ continue;
++
++ *attr_size += data->size;
++ if (data->vendor)
++ *attr_size += VENDOR_ATTR_SIZE;
++
++ (*n_attr)++;
++ }
++
++ *n_attr += !!tb[USER_ATTR_VLAN] * 3 +
++ !!tb[USER_ATTR_MAX_RATE_UP] +
++ !!tb[USER_ATTR_MAX_RATE_DOWN];
++ *attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) +
++ !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) +
++ !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE);
++}
++
++static void *
++radius_add_attr(struct radius_parse_attr_state *state,
++ u32 vendor, u8 type, u8 len)
++{
++ struct hostapd_radius_attr *attr;
++ struct wpabuf *buf;
++ void *val;
++
++ val = state->attrdata;
++
++ buf = state->buf++;
++ buf->buf = val;
++
++ attr = state->attr++;
++ attr->val = buf;
++ attr->type = type;
++
++ if (state->prev)
++ state->prev->next = attr;
++ state->prev = attr;
++
++ if (vendor) {
++ u8 *vendor_hdr = val + 4;
++
++ WPA_PUT_BE32(val, vendor);
++ vendor_hdr[0] = type;
++ vendor_hdr[1] = len + 2;
++
++ len += VENDOR_ATTR_SIZE;
++ val += VENDOR_ATTR_SIZE;
++ attr->type = RADIUS_ATTR_VENDOR_SPECIFIC;
++ }
++
++ buf->size = buf->used = len;
++ state->attrdata += len;
++
++ return val;
++}
++
++static void
++radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state)
++{
++ struct blob_attr *data = tb[USER_ATTR_RADIUS];
++ struct hostapd_radius_attr *prev = NULL;
++ struct blob_attr *cur;
++ int len, rem;
++ void *val;
++
++ if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) {
++ char buf[5];
++
++ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4);
++ WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN);
++
++ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4);
++ WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802);
++
++ len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur));
++ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len);
++ memcpy(val, buf, len);
++ }
++
++ if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) {
++ val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4);
++ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
++ }
++
++ if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) {
++ val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4);
++ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
++ }
++
++ blobmsg_for_each_attr(cur, data, rem) {
++ struct radius_parse_attr_data *data;
++ void *val;
++ int size;
++
++ data = radius_parse_attr(cur);
++ if (!data)
++ continue;
++
++ val = radius_add_attr(state, data->vendor, data->type, data->size);
++ switch (data->format) {
++ case 's':
++ memcpy(val, data->data, data->size);
++ break;
++ case 'x':
++ hexstr2bin(data->data, val, data->size);
++ break;
++ case 'd':
++ WPA_PUT_BE32(val, atoi(data->data));
++ break;
++ }
++ }
++}
++
++static void
++radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data)
++{
++ struct blob_attr *cur;
++ int rem, n = 0;
++
++ if (!data)
++ return;
++
++ blobmsg_for_each_attr(cur, data, rem) {
++ const char *method;
++
++ if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
++ continue;
++
++ if (n == EAP_MAX_METHODS)
++ break;
++
++ method = blobmsg_get_string(cur);
++ eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor);
++ if (eap->methods[n].vendor == EAP_VENDOR_IETF &&
++ eap->methods[n].method == EAP_TYPE_NONE) {
++ if (!strcmp(method, "TTLS-PAP")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_PAP;
++ continue;
++ }
++ if (!strcmp(method, "TTLS-CHAP")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_CHAP;
++ continue;
++ }
++ if (!strcmp(method, "TTLS-MSCHAP")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
++ continue;
++ }
++ if (!strcmp(method, "TTLS-MSCHAPV2")) {
++ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
++ continue;
++ }
++ }
++ n++;
++ }
++}
++
++static struct eap_user *
++radius_user_get_state(struct radius_user_data *u, struct blob_attr *data,
++ const char *id)
++{
++ static const struct blobmsg_policy policy[__USER_ATTR_MAX] = {
++ [USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING },
++ [USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING },
++ [USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING },
++ [USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY },
++ [USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY },
++ [USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 },
++ [USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 },
++ [USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 },
++ };
++ struct blob_attr *tb[__USER_ATTR_MAX], *cur;
++ char *password_buf, *salt_buf, *name_buf;
++ struct radius_parse_attr_state astate = {};
++ struct hostapd_radius_attr *attr;
++ struct radius_user_state *state;
++ int pw_len = 0, salt_len = 0;
++ struct eap_user *eap;
++ struct wpabuf *val;
++ size_t attrsize = 0;
++ void *attrdata;
++ int n_attr = 0;
++
++ state = avl_find_element(&u->user_state, id, state, node);
++ if (state)
++ return &state->data;
++
++ blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
++
++ if ((cur = tb[USER_ATTR_SALT]) != NULL)
++ salt_len = strlen(blobmsg_get_string(cur)) / 2;
++ if ((cur = tb[USER_ATTR_HASH]) != NULL)
++ pw_len = strlen(blobmsg_get_string(cur)) / 2;
++ else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
++ pw_len = blobmsg_len(cur) - 1;
++ radius_count_attrs(tb, &n_attr, &attrsize);
++
++ state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1,
++ &password_buf, pw_len,
++ &salt_buf, salt_len,
++ &astate.attr, n_attr * sizeof(*astate.attr),
++ &astate.buf, n_attr * sizeof(*astate.buf),
++ &astate.attrdata, attrsize);
++ eap = &state->data;
++ eap->salt = salt_len ? salt_buf : NULL;
++ eap->salt_len = salt_len;
++ eap->password = pw_len ? password_buf : NULL;
++ eap->password_len = pw_len;
++ eap->force_version = -1;
++
++ if ((cur = tb[USER_ATTR_SALT]) != NULL)
++ hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len);
++ if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
++ memcpy(password_buf, blobmsg_get_string(cur), pw_len);
++ else if ((cur = tb[USER_ATTR_HASH]) != NULL) {
++ hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len);
++ eap->password_hash = 1;
++ }
++ radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]);
++
++ if (n_attr > 0) {
++ cur = tb[USER_ATTR_RADIUS];
++ eap->accept_attr = astate.attr;
++ radius_parse_attrs(tb, &astate);
++ }
++
++ state->node.key = strcpy(name_buf, id);
++ avl_insert(&u->user_state, &state->node);
++
++ return &state->data;
++
++free:
++ free(state);
++ return NULL;
++}
++
++static int radius_get_eap_user(void *ctx, const u8 *identity,
++ size_t identity_len, int phase2,
++ struct eap_user *user)
++{
++ struct radius_state *s = ctx;
++ struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1;
++ struct blob_attr *entry;
++ struct eap_user *data;
++ char *id;
++
++ if (identity_len > 512)
++ return -1;
++
++ load_userfile(s);
++
++ id = alloca(identity_len + 1);
++ memcpy(id, identity, identity_len);
++ id[identity_len] = 0;
++
++ entry = radius_user_get(u, id);
++ if (!entry)
++ return -1;
++
++ if (!user)
++ return 0;
++
++ data = radius_user_get_state(u, entry, id);
++ if (!data)
++ return -1;
++
++ *user = *data;
++ if (user->password_len > 0)
++ user->password = os_memdup(user->password, user->password_len);
++ if (user->salt_len > 0)
++ user->salt = os_memdup(user->salt, user->salt_len);
++ user->phase2 = phase2;
++
++ return 0;
++}
++
++static int radius_setup(struct radius_state *s, struct radius_config *c)
++{
++ struct eap_config *eap = &s->eap;
++ struct tls_config conf = {
++ .event_cb = radius_tls_event,
++ .tls_flags = TLS_CONN_DISABLE_TLSv1_3,
++ .cb_ctx = s,
++ };
++
++ eap->eap_server = 1;
++ eap->max_auth_rounds = 100;
++ eap->max_auth_rounds_short = 50;
++ eap->ssl_ctx = tls_init(&conf);
++ if (!eap->ssl_ctx) {
++ wpa_printf(MSG_INFO, "TLS init failed\n");
++ return 1;
++ }
++
++ if (tls_global_set_params(eap->ssl_ctx, &c->tls)) {
++ wpa_printf(MSG_INFO, "failed to set TLS parameters\n");
++ return 1;
++ }
++
++ c->radius.eap_cfg = eap;
++ c->radius.conf_ctx = s;
++ c->radius.get_eap_user = radius_get_eap_user;
++ s->radius = radius_server_init(&c->radius);
++ if (!s->radius) {
++ wpa_printf(MSG_INFO, "failed to initialize radius server\n");
++ return 1;
++ }
++
++ return 0;
++}
++
++static int radius_init(struct radius_state *s)
++{
++ memset(s, 0, sizeof(*s));
++ radius_userdata_init(&s->phase1);
++ radius_userdata_init(&s->phase2);
++}
++
++static void radius_deinit(struct radius_state *s)
++{
++ if (s->radius)
++ radius_server_deinit(s->radius);
++
++ if (s->eap.ssl_ctx)
++ tls_deinit(s->eap.ssl_ctx);
++
++ radius_userdata_free(&s->phase1);
++ radius_userdata_free(&s->phase2);
++}
++
++static int usage(const char *progname)
++{
++ fprintf(stderr, "Usage: %s <options>\n",
++ progname);
++}
++
++int radius_main(int argc, char **argv)
++{
++ static struct radius_state state = {};
++ static struct radius_config config = {};
++ const char *progname = argv[0];
++ int ret = 0;
++ int ch;
++
++ wpa_debug_setup_stdout();
++ wpa_debug_level = 0;
++
++ if (eloop_init()) {
++ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
++ return 1;
++ }
++
++ eap_server_register_methods();
++ radius_init(&state);
++
++ while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) {
++ switch (ch) {
++ case '6':
++ config.radius.ipv6 = 1;
++ break;
++ case 'C':
++ config.tls.ca_cert = optarg;
++ break;
++ case 'c':
++ if (config.tls.client_cert2)
++ return usage(progname);
++
++ if (config.tls.client_cert)
++ config.tls.client_cert2 = optarg;
++ else
++ config.tls.client_cert = optarg;
++ break;
++ case 'd':
++ config.tls.dh_file = optarg;
++ break;
++ case 'i':
++ state.eap.server_id = optarg;
++ state.eap.server_id_len = strlen(optarg);
++ break;
++ case 'k':
++ if (config.tls.private_key2)
++ return usage(progname);
++
++ if (config.tls.private_key)
++ config.tls.private_key2 = optarg;
++ else
++ config.tls.private_key = optarg;
++ break;
++ case 'K':
++ if (config.tls.private_key_passwd2)
++ return usage(progname);
++
++ if (config.tls.private_key_passwd)
++ config.tls.private_key_passwd2 = optarg;
++ else
++ config.tls.private_key_passwd = optarg;
++ break;
++ case 'p':
++ config.radius.auth_port = atoi(optarg);
++ break;
++ case 'P':
++ config.radius.acct_port = atoi(optarg);
++ break;
++ case 's':
++ config.radius.client_file = optarg;
++ break;
++ case 'u':
++ state.user_file = optarg;
++ break;
++ default:
++ return usage(progname);
++ }
++ }
++
++ if (!config.tls.client_cert || !config.tls.private_key ||
++ !config.radius.client_file || !state.eap.server_id ||
++ !state.user_file) {
++ wpa_printf(MSG_INFO, "missing options\n");
++ goto out;
++ }
++
++ ret = radius_setup(&state, &config);
++ if (ret)
++ goto out;
++
++ load_userfile(&state);
++ eloop_run();
++
++out:
++ radius_deinit(&state);
++ os_program_deinit();
++
++ return ret;
++}
+diff --git a/src/ap/ubus.c b/src/ap/ubus.c
+new file mode 100644
+index 000000000..f2041a0c9
+--- /dev/null
++++ b/src/ap/ubus.c
+@@ -0,0 +1,2005 @@
++/*
++ * hostapd / ubus support
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "utils/wpabuf.h"
++#include "common/ieee802_11_defs.h"
++#include "common/hw_features_common.h"
++#include "hostapd.h"
++#include "neighbor_db.h"
++#include "wps_hostapd.h"
++#include "sta_info.h"
++#include "ubus.h"
++#include "ap_drv_ops.h"
++#include "beacon.h"
++#include "rrm.h"
++#include "wnm_ap.h"
++#include "taxonomy.h"
++#include "airtime_policy.h"
++#include "hw_features.h"
++
++static struct ubus_context *ctx;
++static struct blob_buf b;
++static int ctx_ref;
++
++static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
++{
++ return container_of(obj, struct hostapd_data, ubus.obj);
++}
++
++struct ubus_banned_client {
++ struct avl_node avl;
++ u8 addr[ETH_ALEN];
++};
++
++static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
++{
++ if (ubus_reconnect(ctx, NULL)) {
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++ return;
++ }
++
++ ubus_add_uloop(ctx);
++}
++
++static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
++{
++ uloop_fd_delete(&ctx->sock);
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++}
++
++static bool hostapd_ubus_init(void)
++{
++ if (ctx)
++ return true;
++
++ eloop_add_uloop();
++ ctx = ubus_connect(NULL);
++ if (!ctx)
++ return false;
++
++ ctx->connection_lost = hostapd_ubus_connection_lost;
++ ubus_add_uloop(ctx);
++
++ return true;
++}
++
++static void hostapd_ubus_ref_inc(void)
++{
++ ctx_ref++;
++}
++
++static void hostapd_ubus_ref_dec(void)
++{
++ ctx_ref--;
++ if (!ctx)
++ return;
++
++ if (ctx_ref)
++ return;
++
++ uloop_fd_delete(&ctx->sock);
++ ubus_free(ctx);
++ ctx = NULL;
++}
++
++void hostapd_ubus_add_iface(struct hostapd_iface *iface)
++{
++ if (!hostapd_ubus_init())
++ return;
++}
++
++void hostapd_ubus_free_iface(struct hostapd_iface *iface)
++{
++ if (!ctx)
++ return;
++}
++
++static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *event)
++{
++ char *event_type;
++
++ if (!ctx || !obj)
++ return;
++
++ if (asprintf(&event_type, "bss.%s", event) < 0)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_string(&b, "name", bssname);
++ ubus_notify(ctx, obj, event_type, b.head, -1);
++ free(event_type);
++}
++
++static void
++hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
++{
++ struct ubus_banned_client *ban = eloop_data;
++ struct hostapd_data *hapd = user_ctx;
++
++ avl_delete(&hapd->ubus.banned, &ban->avl);
++ free(ban);
++}
++
++static void
++hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
++{
++ struct ubus_banned_client *ban;
++
++ if (time < 0)
++ time = 0;
++
++ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
++ if (!ban) {
++ if (!time)
++ return;
++
++ ban = os_zalloc(sizeof(*ban));
++ memcpy(ban->addr, addr, sizeof(ban->addr));
++ ban->avl.key = ban->addr;
++ avl_insert(&hapd->ubus.banned, &ban->avl);
++ } else {
++ eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
++ if (!time) {
++ hostapd_bss_del_ban(ban, hapd);
++ return;
++ }
++ }
++
++ eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
++}
++
++static int
++hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ return hostapd_reload_config(hapd->iface);
++}
++
++
++static void
++hostapd_parse_vht_map_blobmsg(uint16_t map)
++{
++ char label[4];
++ int16_t val;
++ int i;
++
++ for (i = 0; i < 8; i++) {
++ snprintf(label, 4, "%dss", i + 1);
++
++ val = (map & (BIT(1) | BIT(0))) + 7;
++ blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
++ map = map >> 2;
++ }
++}
++
++static void
++hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
++{
++ void *supported_mcs;
++ void *map;
++ int i;
++
++ static const struct {
++ const char *name;
++ uint32_t flag;
++ } vht_capas[] = {
++ { "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
++ { "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
++ };
++
++ for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
++ blobmsg_add_u8(&b, vht_capas[i].name,
++ !!(vhtc->vht_capabilities_info & vht_capas[i].flag));
++
++ supported_mcs = blobmsg_open_table(&b, "mcs_map");
++
++ /* RX map */
++ map = blobmsg_open_table(&b, "rx");
++ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
++ blobmsg_close_table(&b, map);
++
++ /* TX map */
++ map = blobmsg_open_table(&b, "tx");
++ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
++ blobmsg_close_table(&b, map);
++
++ blobmsg_close_table(&b, supported_mcs);
++}
++
++static void
++hostapd_parse_capab_blobmsg(struct sta_info *sta)
++{
++ void *r, *v;
++
++ v = blobmsg_open_table(&b, "capabilities");
++
++ if (sta->vht_capabilities) {
++ r = blobmsg_open_table(&b, "vht");
++ hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
++ blobmsg_close_table(&b, r);
++ }
++
++ /* ToDo: Add HT / HE capability parsing */
++
++ blobmsg_close_table(&b, v);
++}
++
++static int
++hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct hostap_sta_driver_data sta_driver_data;
++ struct sta_info *sta;
++ void *list, *c;
++ char mac_buf[20];
++ static const struct {
++ const char *name;
++ uint32_t flag;
++ } sta_flags[] = {
++ { "auth", WLAN_STA_AUTH },
++ { "assoc", WLAN_STA_ASSOC },
++ { "authorized", WLAN_STA_AUTHORIZED },
++ { "preauth", WLAN_STA_PREAUTH },
++ { "wds", WLAN_STA_WDS },
++ { "wmm", WLAN_STA_WMM },
++ { "ht", WLAN_STA_HT },
++ { "vht", WLAN_STA_VHT },
++ { "he", WLAN_STA_HE },
++ { "wps", WLAN_STA_WPS },
++ { "mfp", WLAN_STA_MFP },
++ };
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
++ list = blobmsg_open_table(&b, "clients");
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ void *r;
++ int i;
++
++ sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
++ c = blobmsg_open_table(&b, mac_buf);
++ for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
++ blobmsg_add_u8(&b, sta_flags[i].name,
++ !!(sta->flags & sta_flags[i].flag));
++
++#ifdef CONFIG_MBO
++ blobmsg_add_u8(&b, "mbo", !!(sta->cell_capa));
++#endif
++
++ r = blobmsg_open_array(&b, "rrm");
++ for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++)
++ blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]);
++ blobmsg_close_array(&b, r);
++
++ r = blobmsg_open_array(&b, "extended_capabilities");
++ /* Check if client advertises extended capabilities */
++ if (sta->ext_capability && sta->ext_capability[0] > 0) {
++ for (i = 0; i < sta->ext_capability[0]; i++) {
++ blobmsg_add_u32(&b, "", sta->ext_capability[1 + i]);
++ }
++ }
++ blobmsg_close_array(&b, r);
++
++ blobmsg_add_u32(&b, "aid", sta->aid);
++#ifdef CONFIG_TAXONOMY
++ r = blobmsg_alloc_string_buffer(&b, "signature", 1024);
++ if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0)
++ blobmsg_add_string_buffer(&b);
++#endif
++
++ /* Driver information */
++ if (hostapd_drv_read_sta_data(hapd, &sta_driver_data, sta->addr) >= 0) {
++ r = blobmsg_open_table(&b, "bytes");
++ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_bytes);
++ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_bytes);
++ blobmsg_close_table(&b, r);
++ r = blobmsg_open_table(&b, "airtime");
++ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_airtime);
++ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_airtime);
++ blobmsg_close_table(&b, r);
++ r = blobmsg_open_table(&b, "packets");
++ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_packets);
++ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_packets);
++ blobmsg_close_table(&b, r);
++ r = blobmsg_open_table(&b, "rate");
++ /* Rate in kbits */
++ blobmsg_add_u32(&b, "rx", sta_driver_data.current_rx_rate * 100);
++ blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
++ blobmsg_close_table(&b, r);
++ blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
++ }
++
++ hostapd_parse_capab_blobmsg(sta);
++
++ blobmsg_close_table(&b, c);
++ }
++ blobmsg_close_array(&b, list);
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features));
++ blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features));
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ void *airtime_table, *dfs_table, *rrm_table, *wnm_table;
++ struct os_reltime now;
++ char ssid[SSID_MAX_LEN + 1];
++ char phy_name[17];
++ size_t ssid_len = SSID_MAX_LEN;
++ u8 channel = 0, op_class = 0;
++
++ if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN)
++ ssid_len = hapd->conf->ssid.ssid_len;
++
++ ieee80211_freq_to_channel_ext(hapd->iface->freq,
++ hapd->iconf->secondary_channel,
++ hostapd_get_oper_chwidth(hapd->iconf),
++ &op_class, &channel);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
++ blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid));
++
++ memset(ssid, 0, SSID_MAX_LEN + 1);
++ memcpy(ssid, hapd->conf->ssid.ssid, ssid_len);
++ blobmsg_add_string(&b, "ssid", ssid);
++
++ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
++ blobmsg_add_u32(&b, "channel", channel);
++ blobmsg_add_u32(&b, "op_class", op_class);
++ blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
++#ifdef CONFIG_IEEE80211AX
++ blobmsg_add_u32(&b, "bss_color", hapd->iface->conf->he_op.he_bss_color_disabled ? -1 :
++ hapd->iface->conf->he_op.he_bss_color);
++#else
++ blobmsg_add_u32(&b, "bss_color", -1);
++#endif
++
++ snprintf(phy_name, 17, "%s", hapd->iface->phy);
++ blobmsg_add_string(&b, "phy", phy_name);
++
++ /* RRM */
++ rrm_table = blobmsg_open_table(&b, "rrm");
++ blobmsg_add_u64(&b, "neighbor_report_tx", hapd->openwrt_stats.rrm.neighbor_report_tx);
++ blobmsg_close_table(&b, rrm_table);
++
++ /* WNM */
++ wnm_table = blobmsg_open_table(&b, "wnm");
++ blobmsg_add_u64(&b, "bss_transition_query_rx", hapd->openwrt_stats.wnm.bss_transition_query_rx);
++ blobmsg_add_u64(&b, "bss_transition_request_tx", hapd->openwrt_stats.wnm.bss_transition_request_tx);
++ blobmsg_add_u64(&b, "bss_transition_response_rx", hapd->openwrt_stats.wnm.bss_transition_response_rx);
++ blobmsg_close_table(&b, wnm_table);
++
++ /* Airtime */
++ airtime_table = blobmsg_open_table(&b, "airtime");
++ blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
++ blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
++ blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
++ blobmsg_close_table(&b, airtime_table);
++
++ /* DFS */
++ dfs_table = blobmsg_open_table(&b, "dfs");
++ blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
++ blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
++ os_reltime_age(&hapd->iface->dfs_cac_start, &now);
++ blobmsg_add_u32(&b, "cac_seconds_left",
++ hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
++ blobmsg_close_table(&b, dfs_table);
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++enum {
++ NOTIFY_RESPONSE,
++ __NOTIFY_MAX
++};
++
++static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
++ [NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__NOTIFY_MAX];
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct wpabuf *elems;
++ const char *pos;
++ size_t len;
++
++ blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
++ blob_data(msg), blob_len(msg));
++
++ if (!tb[NOTIFY_RESPONSE])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
++
++ return UBUS_STATUS_OK;
++}
++
++enum {
++ DEL_CLIENT_ADDR,
++ DEL_CLIENT_REASON,
++ DEL_CLIENT_DEAUTH,
++ DEL_CLIENT_BAN_TIME,
++ __DEL_CLIENT_MAX
++};
++
++static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
++ [DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
++ [DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
++ [DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__DEL_CLIENT_MAX];
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct sta_info *sta;
++ bool deauth = false;
++ int reason;
++ u8 addr[ETH_ALEN];
++
++ blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[DEL_CLIENT_ADDR])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[DEL_CLIENT_REASON])
++ reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
++
++ if (tb[DEL_CLIENT_DEAUTH])
++ deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
++
++ sta = ap_get_sta(hapd, addr);
++ if (sta) {
++ if (deauth) {
++ hostapd_drv_sta_deauth(hapd, addr, reason);
++ ap_sta_deauthenticate(hapd, sta, reason);
++ } else {
++ hostapd_drv_sta_disassoc(hapd, addr, reason);
++ ap_sta_disassociate(hapd, sta, reason);
++ }
++ }
++
++ if (tb[DEL_CLIENT_BAN_TIME])
++ hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
++
++ return 0;
++}
++
++static void
++blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
++{
++ char *s;
++
++ s = blobmsg_alloc_string_buffer(buf, name, 20);
++ sprintf(s, MACSTR, MAC2STR(addr));
++ blobmsg_add_string_buffer(buf);
++}
++
++static int
++hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct ubus_banned_client *ban;
++ void *c;
++
++ blob_buf_init(&b, 0);
++ c = blobmsg_open_array(&b, "clients");
++ avl_for_each_element(&hapd->ubus.banned, ban, avl)
++ blobmsg_add_macaddr(&b, NULL, ban->addr);
++ blobmsg_close_array(&b, c);
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++#ifdef CONFIG_WPS
++static int
++hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ rc = hostapd_wps_button_pushed(hapd, NULL);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++
++
++static const char * pbc_status_enum_str(enum pbc_status status)
++{
++ switch (status) {
++ case WPS_PBC_STATUS_DISABLE:
++ return "Disabled";
++ case WPS_PBC_STATUS_ACTIVE:
++ return "Active";
++ case WPS_PBC_STATUS_TIMEOUT:
++ return "Timed-out";
++ case WPS_PBC_STATUS_OVERLAP:
++ return "Overlap";
++ default:
++ return "Unknown";
++ }
++}
++
++static int
++hostapd_bss_wps_status(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ blob_buf_init(&b, 0);
++
++ blobmsg_add_string(&b, "pbc_status", pbc_status_enum_str(hapd->wps_stats.pbc_status));
++ blobmsg_add_string(&b, "last_wps_result",
++ (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
++ "Success":
++ (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
++ "Failed" : "None")));
++
++ /* If status == Failure - Add possible Reasons */
++ if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
++ hapd->wps_stats.failure_reason > 0)
++ blobmsg_add_string(&b, "reason", wps_ei_str(hapd->wps_stats.failure_reason));
++
++ if (hapd->wps_stats.status)
++ blobmsg_printf(&b, "peer_address", MACSTR, MAC2STR(hapd->wps_stats.peer_addr));
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ rc = hostapd_wps_cancel(hapd);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++#endif /* CONFIG_WPS */
++
++static int
++hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++
++ rc = ieee802_11_set_beacon(hapd);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++
++enum {
++ CONFIG_IFACE,
++ CONFIG_FILE,
++ __CONFIG_MAX
++};
++
++enum {
++ CSA_FREQ,
++ CSA_BCN_COUNT,
++ CSA_CENTER_FREQ1,
++ CSA_CENTER_FREQ2,
++ CSA_BANDWIDTH,
++ CSA_SEC_CHANNEL_OFFSET,
++ CSA_HT,
++ CSA_VHT,
++ CSA_HE,
++ CSA_BLOCK_TX,
++ CSA_FORCE,
++ __CSA_MAX
++};
++
++static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
++ [CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
++ [CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
++ [CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 },
++ [CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 },
++ [CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 },
++ [CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 },
++ [CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL },
++ [CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL },
++ [CSA_HE] = { "he", BLOBMSG_TYPE_BOOL },
++ [CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL },
++ [CSA_FORCE] = { "force", BLOBMSG_TYPE_BOOL },
++};
++
++
++static void switch_chan_fallback_cb(void *eloop_data, void *user_ctx)
++{
++ struct hostapd_iface *iface = eloop_data;
++ struct hostapd_freq_params *freq_params = user_ctx;
++
++ hostapd_switch_channel_fallback(iface, freq_params);
++}
++
++#ifdef NEED_AP_MLME
++static int
++hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__CSA_MAX];
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_config *iconf = hapd->iface->conf;
++ struct hostapd_freq_params *freq_params;
++ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
++ struct csa_settings css = {
++ .freq_params = {
++ .ht_enabled = iconf->ieee80211n,
++ .vht_enabled = iconf->ieee80211ac,
++ .he_enabled = iconf->ieee80211ax,
++ .sec_channel_offset = iconf->secondary_channel,
++ }
++ };
++ u8 chwidth = hostapd_get_oper_chwidth(iconf);
++ u8 seg0 = 0, seg1 = 0;
++ int ret = UBUS_STATUS_OK;
++ int i;
++
++ blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[CSA_FREQ])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ switch (iconf->vht_oper_chwidth) {
++ case CHANWIDTH_USE_HT:
++ if (iconf->secondary_channel)
++ css.freq_params.bandwidth = 40;
++ else
++ css.freq_params.bandwidth = 20;
++ break;
++ case CHANWIDTH_160MHZ:
++ css.freq_params.bandwidth = 160;
++ break;
++ default:
++ css.freq_params.bandwidth = 80;
++ break;
++ }
++
++ css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
++
++#define SET_CSA_SETTING(name, field, type) \
++ do { \
++ if (tb[name]) \
++ css.field = blobmsg_get_ ## type(tb[name]); \
++ } while(0)
++
++ SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32);
++ SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32);
++ SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32);
++ SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32);
++ SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32);
++ SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool);
++ SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
++ SET_CSA_SETTING(CSA_HE, freq_params.he_enabled, bool);
++ SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
++
++ css.freq_params.channel = hostapd_hw_get_channel(hapd, css.freq_params.freq);
++ if (!css.freq_params.channel)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ switch (css.freq_params.bandwidth) {
++ case 160:
++ chwidth = CHANWIDTH_160MHZ;
++ break;
++ case 80:
++ chwidth = css.freq_params.center_freq2 ? CHANWIDTH_80P80MHZ : CHANWIDTH_80MHZ;
++ break;
++ default:
++ chwidth = CHANWIDTH_USE_HT;
++ break;
++ }
++
++ hostapd_set_freq_params(&css.freq_params, iconf->hw_mode,
++ css.freq_params.freq,
++ css.freq_params.channel, iconf->enable_edmg,
++ iconf->edmg_channel,
++ css.freq_params.ht_enabled,
++ css.freq_params.vht_enabled,
++ css.freq_params.he_enabled,
++ css.freq_params.eht_enabled,
++ css.freq_params.sec_channel_offset,
++ chwidth, seg0, seg1,
++ iconf->vht_capab,
++ mode ? &mode->he_capab[IEEE80211_MODE_AP] :
++ NULL,
++ mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
++ NULL, hostapd_get_punct_bitmap(hapd));
++
++ for (i = 0; i < hapd->iface->num_bss; i++) {
++ struct hostapd_data *bss = hapd->iface->bss[i];
++
++ if (hostapd_switch_channel(bss, &css) != 0)
++ ret = UBUS_STATUS_NOT_SUPPORTED;
++ }
++
++ if (!ret || !tb[CSA_FORCE] || !blobmsg_get_bool(tb[CSA_FORCE]))
++ return ret;
++
++ freq_params = malloc(sizeof(*freq_params));
++ memcpy(freq_params, &css.freq_params, sizeof(*freq_params));
++ eloop_register_timeout(0, 1, switch_chan_fallback_cb,
++ hapd->iface, freq_params);
++
++ return 0;
++#undef SET_CSA_SETTING
++}
++#endif
++
++enum {
++ VENDOR_ELEMENTS,
++ __VENDOR_ELEMENTS_MAX
++};
++
++static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
++ /* vendor elements are provided as hex-string */
++ [VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
++};
++
++static int
++hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_bss_config *bss = hapd->conf;
++ struct wpabuf *elems;
++ const char *pos;
++ size_t len;
++
++ blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
++ blob_data(msg), blob_len(msg));
++
++ if (!tb[VENDOR_ELEMENTS])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
++ len = os_strlen(pos);
++ if (len & 0x01)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ len /= 2;
++ if (len == 0) {
++ wpabuf_free(bss->vendor_elements);
++ bss->vendor_elements = NULL;
++ return 0;
++ }
++
++ elems = wpabuf_alloc(len);
++ if (elems == NULL)
++ return 1;
++
++ if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
++ wpabuf_free(elems);
++ return UBUS_STATUS_INVALID_ARGUMENT;
++ }
++
++ wpabuf_free(bss->vendor_elements);
++ bss->vendor_elements = elems;
++
++ /* update beacons if vendor elements were set successfully */
++ if (ieee802_11_update_beacons(hapd->iface) != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++ return UBUS_STATUS_OK;
++}
++
++static void
++hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
++{
++ const u8 *data;
++ char *str;
++ int len;
++
++ blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid));
++
++ str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1);
++ memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len);
++ str[nr->ssid.ssid_len] = 0;
++ blobmsg_add_string_buffer(&b);
++
++ len = wpabuf_len(nr->nr);
++ str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1);
++ wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len);
++ blobmsg_add_string_buffer(&b);
++}
++
++enum {
++ BSS_MGMT_EN_NEIGHBOR,
++ BSS_MGMT_EN_BEACON,
++ BSS_MGMT_EN_LINK_MEASUREMENT,
++#ifdef CONFIG_WNM_AP
++ BSS_MGMT_EN_BSS_TRANSITION,
++#endif
++ __BSS_MGMT_EN_MAX
++};
++
++static bool
++__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
++{
++ struct hostapd_bss_config *bss = hapd->conf;
++ uint32_t flags;
++
++ switch (flag) {
++ case BSS_MGMT_EN_NEIGHBOR:
++ if (bss->radio_measurements[0] &
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT)
++ return false;
++
++ bss->radio_measurements[0] |=
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT;
++ hostapd_neighbor_set_own_report(hapd);
++ return true;
++ case BSS_MGMT_EN_BEACON:
++ flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
++ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
++ WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
++
++ if (bss->radio_measurements[0] & flags == flags)
++ return false;
++
++ bss->radio_measurements[0] |= (u8) flags;
++ return true;
++ case BSS_MGMT_EN_LINK_MEASUREMENT:
++ flags = WLAN_RRM_CAPS_LINK_MEASUREMENT;
++
++ if (bss->radio_measurements[0] & flags == flags)
++ return false;
++
++ bss->radio_measurements[0] |= (u8) flags;
++ return true;
++#ifdef CONFIG_WNM_AP
++ case BSS_MGMT_EN_BSS_TRANSITION:
++ if (bss->bss_transition)
++ return false;
++
++ bss->bss_transition = 1;
++ return true;
++#endif
++ }
++}
++
++static void
++__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
++{
++ bool update = false;
++ int i;
++
++ for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
++ if (!(flags & (1 << i)))
++ continue;
++
++ update |= __hostapd_bss_mgmt_enable_f(hapd, i);
++ }
++
++ if (update)
++ ieee802_11_update_beacons(hapd->iface);
++}
++
++
++static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
++ [BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
++ [BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
++ [BSS_MGMT_EN_LINK_MEASUREMENT] = { "link_measurement", BLOBMSG_TYPE_BOOL },
++#ifdef CONFIG_WNM_AP
++ [BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
++#endif
++};
++
++static int
++hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++
++{
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct blob_attr *tb[__BSS_MGMT_EN_MAX];
++ struct blob_attr *cur;
++ uint32_t flags = 0;
++ int i;
++ bool neigh = false, beacon = false;
++
++ blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
++
++ for (i = 0; i < ARRAY_SIZE(tb); i++) {
++ if (!tb[i] || !blobmsg_get_bool(tb[i]))
++ continue;
++
++ flags |= (1 << i);
++ }
++
++ __hostapd_bss_mgmt_enable(hapd, flags);
++
++ return 0;
++}
++
++
++static void
++hostapd_rrm_nr_enable(struct hostapd_data *hapd)
++{
++ __hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
++}
++
++static int
++hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_neighbor_entry *nr;
++ void *c;
++
++ hostapd_rrm_nr_enable(hapd);
++
++ nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL);
++ if (!nr)
++ return UBUS_STATUS_NOT_FOUND;
++
++ blob_buf_init(&b, 0);
++
++ c = blobmsg_open_array(&b, "value");
++ hostapd_rrm_print_nr(nr);
++ blobmsg_close_array(&b, c);
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct hostapd_neighbor_entry *nr;
++ void *c;
++
++ hostapd_rrm_nr_enable(hapd);
++ blob_buf_init(&b, 0);
++
++ c = blobmsg_open_array(&b, "list");
++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
++ void *cur;
++
++ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
++ continue;
++
++ cur = blobmsg_open_array(&b, NULL);
++ hostapd_rrm_print_nr(nr);
++ blobmsg_close_array(&b, cur);
++ }
++ blobmsg_close_array(&b, c);
++
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++enum {
++ NR_SET_LIST,
++ __NR_SET_LIST_MAX
++};
++
++static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = {
++ [NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY },
++};
++
++
++static void
++hostapd_rrm_nr_clear(struct hostapd_data *hapd)
++{
++ struct hostapd_neighbor_entry *nr;
++
++restart:
++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
++ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
++ continue;
++
++ hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid);
++ goto restart;
++ }
++}
++
++static int
++hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ static const struct blobmsg_policy nr_e_policy[] = {
++ { .type = BLOBMSG_TYPE_STRING },
++ { .type = BLOBMSG_TYPE_STRING },
++ { .type = BLOBMSG_TYPE_STRING },
++ };
++ struct hostapd_data *hapd = get_hapd_from_object(obj);
++ struct blob_attr *tb_l[__NR_SET_LIST_MAX];
++ struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)];
++ struct blob_attr *cur;
++ int rem;
++
++ hostapd_rrm_nr_enable(hapd);
++
++ blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg));
++ if (!tb_l[NR_SET_LIST])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ hostapd_rrm_nr_clear(hapd);
++ blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) {
++ struct wpa_ssid_value ssid;
++ struct wpabuf *data;
++ u8 bssid[ETH_ALEN];
++ char *s, *nr_s;
++
++ blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
++ if (!tb[0] || !tb[1] || !tb[2])
++ goto invalid;
++
++ /* Neighbor Report binary */
++ nr_s = blobmsg_get_string(tb[2]);
++ data = wpabuf_parse_bin(nr_s);
++ if (!data)
++ goto invalid;
++
++ /* BSSID */
++ s = blobmsg_get_string(tb[0]);
++ if (strlen(s) == 0) {
++ /* Copy BSSID from neighbor report */
++ if (hwaddr_compact_aton(nr_s, bssid))
++ goto invalid;
++ } else if (hwaddr_aton(s, bssid)) {
++ goto invalid;
++ }
++
++ /* SSID */
++ s = blobmsg_get_string(tb[1]);
++ if (strlen(s) == 0) {
++ /* Copy SSID from hostapd BSS conf */
++ memcpy(&ssid, &hapd->conf->ssid, sizeof(ssid));
++ } else {
++ ssid.ssid_len = strlen(s);
++ if (ssid.ssid_len > sizeof(ssid.ssid))
++ goto invalid;
++
++ memcpy(&ssid, s, ssid.ssid_len);
++ }
++
++ hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0, 0);
++ wpabuf_free(data);
++ continue;
++
++invalid:
++ return UBUS_STATUS_INVALID_ARGUMENT;
++ }
++
++ return 0;
++}
++
++enum {
++ BEACON_REQ_ADDR,
++ BEACON_REQ_MODE,
++ BEACON_REQ_OP_CLASS,
++ BEACON_REQ_CHANNEL,
++ BEACON_REQ_DURATION,
++ BEACON_REQ_BSSID,
++ BEACON_REQ_SSID,
++ __BEACON_REQ_MAX,
++};
++
++static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = {
++ [BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 },
++ [BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING },
++ [BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING },
++};
++
++static int
++hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__BEACON_REQ_MAX];
++ struct blob_attr *cur;
++ struct wpabuf *req;
++ u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++ u8 addr[ETH_ALEN];
++ int mode, rem, ret;
++ int buf_len = 13;
++
++ blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] ||
++ !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[BEACON_REQ_SSID])
++ buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1;
++
++ mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]);
++ if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[BEACON_REQ_BSSID] &&
++ hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ req = wpabuf_alloc(buf_len);
++ if (!req)
++ return UBUS_STATUS_UNKNOWN_ERROR;
++
++ /* 1: regulatory class */
++ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS]));
++
++ /* 2: channel number */
++ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL]));
++
++ /* 3-4: randomization interval */
++ wpabuf_put_le16(req, 0);
++
++ /* 5-6: duration */
++ wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION]));
++
++ /* 7: mode */
++ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE]));
++
++ /* 8-13: BSSID */
++ wpabuf_put_data(req, bssid, ETH_ALEN);
++
++ if ((cur = tb[BEACON_REQ_SSID]) != NULL) {
++ wpabuf_put_u8(req, WLAN_EID_SSID);
++ wpabuf_put_u8(req, blobmsg_data_len(cur) - 1);
++ wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1);
++ }
++
++ ret = hostapd_send_beacon_req(hapd, addr, 0, req);
++ if (ret < 0)
++ return -ret;
++
++ return 0;
++}
++
++enum {
++ LM_REQ_ADDR,
++ LM_REQ_TX_POWER_USED,
++ LM_REQ_TX_POWER_MAX,
++ __LM_REQ_MAX,
++};
++
++static const struct blobmsg_policy lm_req_policy[__LM_REQ_MAX] = {
++ [LM_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [LM_REQ_TX_POWER_USED] = { "tx-power-used", BLOBMSG_TYPE_INT32 },
++ [LM_REQ_TX_POWER_MAX] = { "tx-power-max", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_rrm_lm_req(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__LM_REQ_MAX];
++ struct wpabuf *buf;
++ u8 addr[ETH_ALEN];
++ int ret;
++ int8_t txp_used, txp_max;
++
++ txp_used = 0;
++ txp_max = 0;
++
++ blobmsg_parse(lm_req_policy, __LM_REQ_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[LM_REQ_ADDR])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[LM_REQ_TX_POWER_USED])
++ txp_used = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_USED]);
++
++ if (tb[LM_REQ_TX_POWER_MAX])
++ txp_max = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_MAX]);
++
++ if (hwaddr_aton(blobmsg_data(tb[LM_REQ_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ buf = wpabuf_alloc(5);
++ if (!buf)
++ return UBUS_STATUS_UNKNOWN_ERROR;
++
++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
++ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
++ wpabuf_put_u8(buf, 1);
++ /* TX-Power used */
++ wpabuf_put_u8(buf, txp_used);
++ /* Max TX Power */
++ wpabuf_put_u8(buf, txp_max);
++
++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++ wpabuf_head(buf), wpabuf_len(buf));
++
++ wpabuf_free(buf);
++ if (ret < 0)
++ return -ret;
++
++ return 0;
++}
++
++
++void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
++{
++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) data;
++ const u8 *pos, *end;
++ u8 token;
++
++ end = data + len;
++ token = mgmt->u.action.u.rrm.dialog_token;
++ pos = mgmt->u.action.u.rrm.variable;
++
++ if (end - pos < 8)
++ return;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", mgmt->sa);
++ blobmsg_add_u16(&b, "dialog-token", token);
++ blobmsg_add_u16(&b, "rx-antenna-id", pos[4]);
++ blobmsg_add_u16(&b, "tx-antenna-id", pos[5]);
++ blobmsg_add_u16(&b, "rcpi", pos[6]);
++ blobmsg_add_u16(&b, "rsni", pos[7]);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "link-measurement-report", b.head, -1);
++}
++
++
++#ifdef CONFIG_WNM_AP
++
++static int
++hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
++ u16 disassoc_timer, u8 validity_period, u8 dialog_token,
++ struct blob_attr *neighbors, u8 mbo_reason, u8 cell_pref, u8 reassoc_delay)
++{
++ struct blob_attr *cur;
++ struct sta_info *sta;
++ int nr_len = 0;
++ int rem;
++ u8 *nr = NULL;
++ u8 req_mode = 0;
++ u8 mbo[10];
++ size_t mbo_len = 0;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return UBUS_STATUS_NOT_FOUND;
++
++ if (neighbors) {
++ u8 *nr_cur;
++
++ if (blobmsg_check_array(neighbors,
++ BLOBMSG_TYPE_STRING) < 0)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ blobmsg_for_each_attr(cur, neighbors, rem) {
++ int len = strlen(blobmsg_get_string(cur));
++
++ if (len % 2)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ nr_len += (len / 2) + 2;
++ }
++
++ if (nr_len) {
++ nr = os_zalloc(nr_len);
++ if (!nr)
++ return UBUS_STATUS_UNKNOWN_ERROR;
++ }
++
++ nr_cur = nr;
++ blobmsg_for_each_attr(cur, neighbors, rem) {
++ int len = strlen(blobmsg_get_string(cur)) / 2;
++
++ *nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
++ *nr_cur++ = (u8) len;
++ if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) {
++ free(nr);
++ return UBUS_STATUS_INVALID_ARGUMENT;
++ }
++
++ nr_cur += len;
++ }
++ }
++
++ if (nr)
++ req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
++
++ if (abridged)
++ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
++
++ if (disassoc_imminent)
++ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
++
++#ifdef CONFIG_MBO
++ u8 *mbo_pos = mbo;
++
++ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (reassoc_delay > 65535 || (reassoc_delay && !disassoc_imminent))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = mbo_reason;
++ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = cell_pref;
++
++ if (reassoc_delay) {
++ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
++ *mbo_pos++ = 2;
++ WPA_PUT_LE16(mbo_pos, reassoc_delay);
++ mbo_pos += 2;
++ }
++
++ mbo_len = mbo_pos - mbo;
++#endif
++
++ if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
++ dialog_token, NULL, nr, nr_len, mbo_len ? mbo : NULL, mbo_len))
++ return UBUS_STATUS_UNKNOWN_ERROR;
++
++ return 0;
++}
++
++enum {
++ BSS_TR_ADDR,
++ BSS_TR_DA_IMMINENT,
++ BSS_TR_DA_TIMER,
++ BSS_TR_VALID_PERIOD,
++ BSS_TR_NEIGHBORS,
++ BSS_TR_ABRIDGED,
++ BSS_TR_DIALOG_TOKEN,
++#ifdef CONFIG_MBO
++ BSS_TR_MBO_REASON,
++ BSS_TR_CELL_PREF,
++ BSS_TR_REASSOC_DELAY,
++#endif
++ __BSS_TR_DISASSOC_MAX
++};
++
++static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
++ [BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
++ [BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
++ [BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
++ [BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
++ [BSS_TR_DIALOG_TOKEN] = { "dialog_token", BLOBMSG_TYPE_INT32 },
++#ifdef CONFIG_MBO
++ [BSS_TR_MBO_REASON] = { "mbo_reason", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_CELL_PREF] = { "cell_pref", BLOBMSG_TYPE_INT32 },
++ [BSS_TR_REASSOC_DELAY] = { "reassoc_delay", BLOBMSG_TYPE_INT32 },
++#endif
++};
++
++static int
++hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
++ struct sta_info *sta;
++ u32 da_timer = 0;
++ u32 valid_period = 0;
++ u8 addr[ETH_ALEN];
++ u32 dialog_token = 1;
++ bool abridged;
++ bool da_imminent;
++ u8 mbo_reason;
++ u8 cell_pref;
++ u8 reassoc_delay;
++
++ blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[BSS_TR_ADDR])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ if (tb[BSS_TR_DA_TIMER])
++ da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
++
++ if (tb[BSS_TR_VALID_PERIOD])
++ valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
++
++ if (tb[BSS_TR_DIALOG_TOKEN])
++ dialog_token = blobmsg_get_u32(tb[BSS_TR_DIALOG_TOKEN]);
++
++ da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
++ abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
++
++#ifdef CONFIG_MBO
++ if (tb[BSS_TR_MBO_REASON])
++ mbo_reason = blobmsg_get_u32(tb[BSS_TR_MBO_REASON]);
++
++ if (tb[BSS_TR_CELL_PREF])
++ cell_pref = blobmsg_get_u32(tb[BSS_TR_CELL_PREF]);
++
++ if (tb[BSS_TR_REASSOC_DELAY])
++ reassoc_delay = blobmsg_get_u32(tb[BSS_TR_REASSOC_DELAY]);
++#endif
++
++ return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
++ dialog_token, tb[BSS_TR_NEIGHBORS], mbo_reason, cell_pref, reassoc_delay);
++}
++#endif
++
++#ifdef CONFIG_AIRTIME_POLICY
++enum {
++ UPDATE_AIRTIME_STA,
++ UPDATE_AIRTIME_WEIGHT,
++ __UPDATE_AIRTIME_MAX,
++};
++
++
++static const struct blobmsg_policy airtime_policy[__UPDATE_AIRTIME_MAX] = {
++ [UPDATE_AIRTIME_STA] = { "sta", BLOBMSG_TYPE_STRING },
++ [UPDATE_AIRTIME_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
++};
++
++static int
++hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *ureq, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb[__UPDATE_AIRTIME_MAX];
++ struct sta_info *sta = NULL;
++ u8 addr[ETH_ALEN];
++ int weight;
++
++ blobmsg_parse(airtime_policy, __UPDATE_AIRTIME_MAX, tb, blob_data(msg), blob_len(msg));
++
++ if (!tb[UPDATE_AIRTIME_WEIGHT])
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ weight = blobmsg_get_u32(tb[UPDATE_AIRTIME_WEIGHT]);
++
++ if (!tb[UPDATE_AIRTIME_STA]) {
++ if (!weight)
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ hapd->conf->airtime_weight = weight;
++ return 0;
++ }
++
++ if (hwaddr_aton(blobmsg_data(tb[UPDATE_AIRTIME_STA]), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return UBUS_STATUS_NOT_FOUND;
++
++ sta->dyn_airtime_weight = weight;
++ airtime_policy_new_sta(hapd, sta);
++
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_TAXONOMY
++static const struct blobmsg_policy addr_policy[] = {
++ { "address", BLOBMSG_TYPE_STRING }
++};
++
++static bool
++hostapd_add_b64_data(const char *name, const struct wpabuf *buf)
++{
++ char *str;
++
++ if (!buf)
++ return false;
++
++ str = blobmsg_alloc_string_buffer(&b, name, B64_ENCODE_LEN(wpabuf_len(buf)));
++ b64_encode(wpabuf_head(buf), wpabuf_len(buf), str, B64_ENCODE_LEN(wpabuf_len(buf)));
++ blobmsg_add_string_buffer(&b);
++
++ return true;
++}
++
++static int
++hostapd_bss_get_sta_ies(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
++ struct blob_attr *tb;
++ struct sta_info *sta;
++ u8 addr[ETH_ALEN];
++
++ blobmsg_parse(addr_policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
++
++ if (!tb || hwaddr_aton(blobmsg_data(tb), addr))
++ return UBUS_STATUS_INVALID_ARGUMENT;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta || (!sta->probe_ie_taxonomy && !sta->assoc_ie_taxonomy))
++ return UBUS_STATUS_NOT_FOUND;
++
++ blob_buf_init(&b, 0);
++ hostapd_add_b64_data("probe_ie", sta->probe_ie_taxonomy);
++ hostapd_add_b64_data("assoc_ie", sta->assoc_ie_taxonomy);
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++#endif
++
++
++static const struct ubus_method bss_methods[] = {
++ UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
++ UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
++#ifdef CONFIG_TAXONOMY
++ UBUS_METHOD("get_sta_ies", hostapd_bss_get_sta_ies, addr_policy),
++#endif
++ UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
++ UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
++#ifdef CONFIG_AIRTIME_POLICY
++ UBUS_METHOD("update_airtime", hostapd_bss_update_airtime, airtime_policy),
++#endif
++ UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
++#ifdef CONFIG_WPS
++ UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
++ UBUS_METHOD_NOARG("wps_status", hostapd_bss_wps_status),
++ UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
++#endif
++ UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
++ UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features),
++#ifdef NEED_AP_MLME
++ UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
++#endif
++ UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
++ UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
++ UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
++ UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
++ UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list),
++ UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy),
++ UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
++ UBUS_METHOD("link_measurement_req", hostapd_rrm_lm_req, lm_req_policy),
++#ifdef CONFIG_WNM_AP
++ UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
++#endif
++};
++
++static struct ubus_object_type bss_object_type =
++ UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
++
++static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
++{
++ return memcmp(k1, k2, ETH_ALEN);
++}
++
++void hostapd_ubus_add_bss(struct hostapd_data *hapd)
++{
++ struct ubus_object *obj = &hapd->ubus.obj;
++ char *name;
++ int ret;
++
++#ifdef CONFIG_MESH
++ if (hapd->conf->mesh & MESH_ENABLED)
++ return;
++#endif
++
++ if (!hostapd_ubus_init())
++ return;
++
++ if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
++ return;
++
++ avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
++ obj->name = name;
++ obj->type = &bss_object_type;
++ obj->methods = bss_object_type.methods;
++ obj->n_methods = bss_object_type.n_methods;
++ ret = ubus_add_object(ctx, obj);
++ hostapd_ubus_ref_inc();
++}
++
++void hostapd_ubus_free_bss(struct hostapd_data *hapd)
++{
++ struct ubus_object *obj = &hapd->ubus.obj;
++ char *name = (char *) obj->name;
++
++#ifdef CONFIG_MESH
++ if (hapd->conf->mesh & MESH_ENABLED)
++ return;
++#endif
++
++ if (!ctx)
++ return;
++
++ if (obj->id) {
++ ubus_remove_object(ctx, obj);
++ hostapd_ubus_ref_dec();
++ }
++
++ free(name);
++}
++
++static void
++hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++ const char *action)
++{
++ struct vlan_description *desc = &vlan->vlan_desc;
++ void *c;
++ int i;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_string(&b, "ifname", vlan->ifname);
++ blobmsg_add_string(&b, "bridge", vlan->bridge);
++ blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
++
++ if (desc->notempty) {
++ blobmsg_add_u32(&b, "untagged", desc->untagged);
++ c = blobmsg_open_array(&b, "tagged");
++ for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
++ blobmsg_add_u32(&b, "", desc->tagged[i]);
++ blobmsg_close_array(&b, c);
++ }
++
++ ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
++}
++
++void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++ hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
++}
++
++void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++ hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
++}
++
++struct ubus_event_req {
++ struct ubus_notify_request nreq;
++ int resp;
++};
++
++static void
++ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
++{
++ struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
++
++ ureq->resp = ret;
++}
++
++int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
++{
++ struct ubus_banned_client *ban;
++ const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
++ [HOSTAPD_UBUS_PROBE_REQ] = "probe",
++ [HOSTAPD_UBUS_AUTH_REQ] = "auth",
++ [HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
++ };
++ const char *type = "mgmt";
++ struct ubus_event_req ureq = {};
++ const u8 *addr;
++
++ if (req->mgmt_frame)
++ addr = req->mgmt_frame->sa;
++ else
++ addr = req->addr;
++
++ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
++ if (ban)
++ return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return WLAN_STATUS_SUCCESS;
++
++ if (req->type < ARRAY_SIZE(types))
++ type = types[req->type];
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ if (req->mgmt_frame)
++ blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
++ if (req->ssi_signal)
++ blobmsg_add_u32(&b, "signal", req->ssi_signal);
++ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
++
++ if (req->elems) {
++ if(req->elems->ht_capabilities)
++ {
++ struct ieee80211_ht_capabilities *ht_capabilities;
++ void *ht_cap, *ht_cap_mcs_set, *mcs_set;
++
++
++ ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
++ ht_cap = blobmsg_open_table(&b, "ht_capabilities");
++ blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
++ ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
++ blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
++ blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
++ blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
++ blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities);
++ mcs_set = blobmsg_open_array(&b, "supported_mcs_set");
++ for (int i = 0; i < 16; i++) {
++ blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]);
++ }
++ blobmsg_close_array(&b, mcs_set);
++ blobmsg_close_table(&b, ht_cap_mcs_set);
++ blobmsg_close_table(&b, ht_cap);
++ }
++ if(req->elems->vht_capabilities)
++ {
++ struct ieee80211_vht_capabilities *vht_capabilities;
++ void *vht_cap, *vht_cap_mcs_set;
++
++ vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities;
++ vht_cap = blobmsg_open_table(&b, "vht_capabilities");
++ blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info);
++ vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set");
++ blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map);
++ blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest);
++ blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map);
++ blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest);
++ blobmsg_close_table(&b, vht_cap_mcs_set);
++ blobmsg_close_table(&b, vht_cap);
++ }
++ }
++
++ if (!hapd->ubus.notify_response) {
++ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
++ return WLAN_STATUS_SUCCESS;
++ }
++
++ if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
++ return WLAN_STATUS_SUCCESS;
++
++ ureq.nreq.status_cb = ubus_event_cb;
++ ubus_complete_request(ctx, &ureq.nreq.req, 100);
++
++ if (ureq.resp)
++ return ureq.resp;
++
++ return WLAN_STATUS_SUCCESS;
++}
++
++void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
++{
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ if (!addr)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++
++ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
++}
++
++void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
++ const char *auth_alg)
++{
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", sta->addr);
++ if (auth_alg)
++ blobmsg_add_string(&b, "auth-alg", auth_alg);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "sta-authorized", b.head, -1);
++}
++
++void hostapd_ubus_notify_beacon_report(
++ struct hostapd_data *hapd, const u8 *addr, u8 token, u8 rep_mode,
++ struct rrm_measurement_beacon_report *rep, size_t len)
++{
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ if (!addr || !rep)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ blobmsg_add_u16(&b, "op-class", rep->op_class);
++ blobmsg_add_u16(&b, "channel", rep->channel);
++ blobmsg_add_u64(&b, "start-time", rep->start_time);
++ blobmsg_add_u16(&b, "duration", rep->duration);
++ blobmsg_add_u16(&b, "report-info", rep->report_info);
++ blobmsg_add_u16(&b, "rcpi", rep->rcpi);
++ blobmsg_add_u16(&b, "rsni", rep->rsni);
++ blobmsg_add_macaddr(&b, "bssid", rep->bssid);
++ blobmsg_add_u16(&b, "antenna-id", rep->antenna_id);
++ blobmsg_add_u16(&b, "parent-tsf", rep->parent_tsf);
++ blobmsg_add_u16(&b, "rep-mode", rep_mode);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "beacon-report", b.head, -1);
++}
++
++void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
++ int chan_width, int cf1, int cf2)
++{
++ struct hostapd_data *hapd;
++ int i;
++
++ if (!ctx)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u16(&b, "frequency", frequency);
++ blobmsg_add_u16(&b, "width", chan_width);
++ blobmsg_add_u16(&b, "center1", cf1);
++ blobmsg_add_u16(&b, "center2", cf2);
++
++ for (i = 0; i < iface->num_bss; i++) {
++ hapd = iface->bss[i];
++ ubus_notify(ctx, &hapd->ubus.obj, "radar-detected", b.head, -1);
++ }
++}
++
++#ifdef CONFIG_WNM_AP
++static void hostapd_ubus_notify_bss_transition_add_candidate_list(
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++ char *cl_str;
++ int i;
++
++ if (candidate_list_len == 0)
++ return;
++
++ cl_str = blobmsg_alloc_string_buffer(&b, "candidate-list", candidate_list_len * 2 + 1);
++ for (i = 0; i < candidate_list_len; i++)
++ snprintf(&cl_str[i*2], 3, "%02X", candidate_list[i]);
++ blobmsg_add_string_buffer(&b);
++
++}
++#endif
++
++void hostapd_ubus_notify_bss_transition_response(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
++ u8 bss_termination_delay, const u8 *target_bssid,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++#ifdef CONFIG_WNM_AP
++ u16 i;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return;
++
++ if (!addr)
++ return;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ blobmsg_add_u8(&b, "dialog-token", dialog_token);
++ blobmsg_add_u8(&b, "status-code", status_code);
++ blobmsg_add_u8(&b, "bss-termination-delay", bss_termination_delay);
++ if (target_bssid)
++ blobmsg_add_macaddr(&b, "target-bssid", target_bssid);
++
++ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
++
++ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-response", b.head, -1);
++#endif
++}
++
++int hostapd_ubus_notify_bss_transition_query(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++#ifdef CONFIG_WNM_AP
++ struct ubus_event_req ureq = {};
++ char *cl_str;
++ u16 i;
++
++ if (!hapd->ubus.obj.has_subscribers)
++ return 0;
++
++ if (!addr)
++ return 0;
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_macaddr(&b, "address", addr);
++ blobmsg_add_u8(&b, "dialog-token", dialog_token);
++ blobmsg_add_u8(&b, "reason", reason);
++ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
++
++ if (!hapd->ubus.notify_response) {
++ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, -1);
++ return 0;
++ }
++
++ if (ubus_notify_async(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, &ureq.nreq))
++ return 0;
++
++ ureq.nreq.status_cb = ubus_event_cb;
++ ubus_complete_request(ctx, &ureq.nreq.req, 100);
++
++ return ureq.resp;
++#endif
++}
+diff --git a/src/ap/ubus.h b/src/ap/ubus.h
+new file mode 100644
+index 000000000..b0f7c44ab
+--- /dev/null
++++ b/src/ap/ubus.h
+@@ -0,0 +1,154 @@
++/*
++ * hostapd / ubus support
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++#ifndef __HOSTAPD_UBUS_H
++#define __HOSTAPD_UBUS_H
++
++enum hostapd_ubus_event_type {
++ HOSTAPD_UBUS_PROBE_REQ,
++ HOSTAPD_UBUS_AUTH_REQ,
++ HOSTAPD_UBUS_ASSOC_REQ,
++ HOSTAPD_UBUS_TYPE_MAX
++};
++
++struct hostapd_ubus_request {
++ enum hostapd_ubus_event_type type;
++ const struct ieee80211_mgmt *mgmt_frame;
++ const struct ieee802_11_elems *elems;
++ int ssi_signal; /* dBm */
++ const u8 *addr;
++};
++
++struct hostapd_iface;
++struct hostapd_data;
++struct hapd_interfaces;
++struct rrm_measurement_beacon_report;
++
++#ifdef UBUS_SUPPORT
++
++#include <libubox/avl.h>
++#include <libubus.h>
++
++struct hostapd_ubus_bss {
++ struct ubus_object obj;
++ struct avl_tree banned;
++ int notify_response;
++};
++
++void hostapd_ubus_add_iface(struct hostapd_iface *iface);
++void hostapd_ubus_free_iface(struct hostapd_iface *iface);
++void hostapd_ubus_add_bss(struct hostapd_data *hapd);
++void hostapd_ubus_free_bss(struct hostapd_data *hapd);
++void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
++void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
++
++int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
++void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len);
++void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
++void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
++ const u8 *addr, u8 token, u8 rep_mode,
++ struct rrm_measurement_beacon_report *rep,
++ size_t len);
++void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
++ int chan_width, int cf1, int cf2);
++
++void hostapd_ubus_notify_bss_transition_response(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
++ u8 bss_termination_delay, const u8 *target_bssid,
++ const u8 *candidate_list, u16 candidate_list_len);
++void hostapd_ubus_add(struct hapd_interfaces *interfaces);
++void hostapd_ubus_free(struct hapd_interfaces *interfaces);
++int hostapd_ubus_notify_bss_transition_query(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
++ const u8 *candidate_list, u16 candidate_list_len);
++void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
++ const char *auth_alg);
++
++#else
++
++struct hostapd_ubus_bss {};
++
++static inline void hostapd_ubus_add_iface(struct hostapd_iface *iface)
++{
++}
++
++static inline void hostapd_ubus_free_iface(struct hostapd_iface *iface)
++{
++}
++
++static inline void hostapd_ubus_add_bss(struct hostapd_data *hapd)
++{
++}
++
++static inline void hostapd_ubus_free_bss(struct hostapd_data *hapd)
++{
++}
++
++static inline void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++}
++
++static inline void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
++{
++}
++
++static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
++{
++ return 0;
++}
++
++static inline void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
++{
++}
++
++static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
++{
++}
++
++static inline void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
++ const u8 *addr, u8 token,
++ u8 rep_mode,
++ struct rrm_measurement_beacon_report *rep,
++ size_t len)
++{
++}
++static inline void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
++ int chan_width, int cf1, int cf2)
++{
++}
++
++static inline void hostapd_ubus_notify_bss_transition_response(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
++ u8 bss_termination_delay, const u8 *target_bssid,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++}
++
++static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
++{
++}
++
++static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
++{
++}
++
++static inline int hostapd_ubus_notify_bss_transition_query(
++ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
++ const u8 *candidate_list, u16 candidate_list_len)
++{
++ return 0;
++}
++
++static inline void
++hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
++ const char *auth_alg)
++{
++}
++
++#endif
++
++#endif
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+new file mode 100644
+index 000000000..16d1b5153
+--- /dev/null
++++ b/src/ap/ucode.c
+@@ -0,0 +1,813 @@
++#include <sys/un.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "hostapd.h"
++#include "beacon.h"
++#include "hw_features.h"
++#include "ap_drv_ops.h"
++#include "dfs.h"
++#include "acs.h"
++#include <libubox/uloop.h>
++
++static uc_resource_type_t *global_type, *bss_type, *iface_type;
++static struct hapd_interfaces *interfaces;
++static uc_value_t *global, *bss_registry, *iface_registry;
++static uc_vm_t *vm;
++
++static uc_value_t *
++hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ if (hapd->ucode.idx)
++ return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
++
++ val = uc_resource_new(bss_type, hapd);
++ hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
++
++ return val;
++}
++
++static uc_value_t *
++hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
++{
++ uc_value_t *val;
++
++ if (hapd->ucode.idx)
++ return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
++
++ val = uc_resource_new(iface_type, hapd);
++ hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++ return val;
++}
++
++static void
++hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
++{
++ uc_value_t *list;
++ int i;
++
++ list = ucv_array_new(vm);
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *hapd = iface->bss[i];
++ uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
++
++ ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
++ ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
++ }
++ ucv_object_add(if_bss, iface->phy, ucv_get(list));
++}
++
++static void
++hostapd_ucode_update_interfaces(void)
++{
++ uc_value_t *ifs = ucv_object_new(vm);
++ uc_value_t *if_bss = ucv_array_new(vm);
++ uc_value_t *bss = ucv_object_new(vm);
++ int i;
++
++ for (i = 0; i < interfaces->count; i++) {
++ struct hostapd_iface *iface = interfaces->iface[i];
++
++ ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
++ hostapd_ucode_update_bss_list(iface, if_bss, bss);
++ }
++
++ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
++ ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
++ ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
++ ucv_gc(vm);
++}
++
++static uc_value_t *
++uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *iface = uc_fn_arg(0);
++ int ret;
++
++ if (ucv_type(iface) != UC_STRING)
++ return ucv_int64_new(-1);
++
++ ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
++ hostapd_ucode_update_interfaces();
++
++ return ucv_int64_new(ret);
++}
++
++static uc_value_t *
++uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *iface = uc_fn_arg(0);
++
++ if (ucv_type(iface) != UC_STRING)
++ return NULL;
++
++ hostapd_remove_iface(interfaces, ucv_string_get(iface));
++ hostapd_ucode_update_interfaces();
++
++ return NULL;
++}
++
++static struct hostapd_vlan *
++bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
++{
++ struct hostapd_vlan *vlan;
++
++ for (vlan = bss->vlan; vlan; vlan = vlan->next)
++ if (vlan->vlan_id == id)
++ return vlan;
++
++ return NULL;
++}
++
++static int
++bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++ const char *ifname)
++{
++ if (!strcmp(ifname, vlan->ifname))
++ return 0;
++
++ hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
++ os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
++
++ return 0;
++}
++
++static int
++bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
++{
++ struct hostapd_bss_config *old_bss = hapd->conf;
++ struct hostapd_vlan *vlan, *vlan_new, *wildcard;
++ char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
++ int ret;
++
++ vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
++ wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
++ if (!!vlan != !!wildcard)
++ return -1;
++
++ if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
++ strcpy(vlan->ifname, wildcard->ifname);
++ else
++ wildcard = NULL;
++
++ for (vlan = bss->vlan; vlan; vlan = vlan->next) {
++ if (vlan->vlan_id == VLAN_ID_WILDCARD ||
++ vlan->dynamic_vlan > 0)
++ continue;
++
++ if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
++ return -1;
++ }
++
++ for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
++ if (vlan->vlan_id == VLAN_ID_WILDCARD)
++ continue;
++
++ if (vlan->dynamic_vlan == 0) {
++ vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
++ if (!vlan_new)
++ return -1;
++
++ if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
++ return -1;
++
++ continue;
++ }
++
++ if (!wildcard)
++ continue;
++
++ os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
++ pos = os_strchr(ifname, '#');
++ if (!pos)
++ return -1;
++
++ *pos++ = '\0';
++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
++ ifname, vlan->vlan_id, pos);
++ if (os_snprintf_error(sizeof(vlan_ifname), ret))
++ return -1;
++
++ if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
++ return -1;
++ }
++
++ return 0;
++}
++
++static uc_value_t *
++uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ struct hostapd_bss_config *old_bss;
++ struct hostapd_iface *iface;
++ struct hostapd_config *conf;
++ uc_value_t *file = uc_fn_arg(0);
++ uc_value_t *index = uc_fn_arg(1);
++ uc_value_t *files_only = uc_fn_arg(2);
++ unsigned int i, idx = 0;
++ int ret = -1;
++
++ if (!hapd || ucv_type(file) != UC_STRING)
++ goto out;
++
++ if (ucv_type(index) == UC_INTEGER)
++ idx = ucv_int64_get(index);
++
++ iface = hapd->iface;
++ conf = interfaces->config_read_cb(ucv_string_get(file));
++ if (!conf)
++ goto out;
++
++ if (idx > conf->num_bss || !conf->bss[idx])
++ goto free;
++
++ if (ucv_boolean_get(files_only)) {
++ struct hostapd_bss_config *bss = conf->bss[idx];
++ struct hostapd_bss_config *old_bss = hapd->conf;
++
++#define swap_field(name) \
++ do { \
++ void *ptr = old_bss->name; \
++ old_bss->name = bss->name; \
++ bss->name = ptr; \
++ } while (0)
++
++ swap_field(ssid.wpa_psk_file);
++ ret = bss_reload_vlans(hapd, bss);
++ goto done;
++ }
++
++ hostapd_bss_deinit_no_free(hapd);
++ hostapd_drv_stop_ap(hapd);
++ hostapd_free_hapd_data(hapd);
++
++ old_bss = hapd->conf;
++ for (i = 0; i < iface->conf->num_bss; i++)
++ if (iface->conf->bss[i] == hapd->conf)
++ iface->conf->bss[i] = conf->bss[idx];
++ hapd->conf = conf->bss[idx];
++ conf->bss[idx] = old_bss;
++
++ hostapd_setup_bss(hapd, hapd == iface->bss[0], true);
++ hostapd_ucode_update_interfaces();
++
++done:
++ ret = 0;
++free:
++ hostapd_config_free(conf);
++out:
++ return ucv_int64_new(ret);
++}
++
++static void
++hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
++ struct hostapd_bss_config *conf)
++{
++ int i;
++
++ for (i = 0; i < iconf->num_bss; i++)
++ if (iconf->bss[i] == conf)
++ break;
++
++ if (i == iconf->num_bss)
++ return;
++
++ for (i++; i < iconf->num_bss; i++)
++ iconf->bss[i - 1] = iconf->bss[i];
++ iconf->num_bss--;
++}
++
++
++static uc_value_t *
++uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ struct hostapd_iface *iface;
++ int i, idx;
++
++ if (!hapd)
++ return NULL;
++
++ iface = hapd->iface;
++ if (iface->num_bss == 1) {
++ wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
++ return NULL;
++ }
++
++ for (idx = 0; idx < iface->num_bss; idx++)
++ if (iface->bss[idx] == hapd)
++ break;
++
++ if (idx == iface->num_bss)
++ return NULL;
++
++ for (i = idx + 1; i < iface->num_bss; i++)
++ iface->bss[i - 1] = iface->bss[i];
++
++ iface->num_bss--;
++
++ iface->bss[0]->interface_added = 0;
++ hostapd_drv_set_first_bss(iface->bss[0]);
++ hapd->interface_added = 1;
++
++ hostapd_drv_stop_ap(hapd);
++ hostapd_bss_deinit(hapd);
++ hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
++ hostapd_config_free_bss(hapd->conf);
++ os_free(hapd);
++
++ hostapd_ucode_update_interfaces();
++ ucv_gc(vm);
++
++ return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ struct hostapd_bss_config *bss;
++ struct hostapd_config *conf;
++ struct hostapd_data *hapd;
++ uc_value_t *file = uc_fn_arg(0);
++ uc_value_t *index = uc_fn_arg(1);
++ unsigned int idx = 0;
++ uc_value_t *ret = NULL;
++
++ if (!iface || ucv_type(file) != UC_STRING)
++ goto out;
++
++ if (ucv_type(index) == UC_INTEGER)
++ idx = ucv_int64_get(index);
++
++ conf = interfaces->config_read_cb(ucv_string_get(file));
++ if (!conf || idx > conf->num_bss || !conf->bss[idx])
++ goto out;
++
++ bss = conf->bss[idx];
++ hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
++ if (!hapd)
++ goto out;
++
++ hapd->driver = iface->bss[0]->driver;
++ hapd->drv_priv = iface->bss[0]->drv_priv;
++ if (interfaces->ctrl_iface_init &&
++ interfaces->ctrl_iface_init(hapd) < 0)
++ goto free_hapd;
++
++ if (iface->state == HAPD_IFACE_ENABLED &&
++ hostapd_setup_bss(hapd, -1, true))
++ goto deinit_ctrl;
++
++ iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
++ sizeof(*iface->bss));
++ iface->bss[iface->num_bss++] = hapd;
++
++ iface->conf->bss = os_realloc_array(iface->conf->bss,
++ iface->conf->num_bss + 1,
++ sizeof(*iface->conf->bss));
++ iface->conf->bss[iface->conf->num_bss] = bss;
++ conf->bss[idx] = NULL;
++ ret = hostapd_ucode_bss_get_uval(hapd);
++ hostapd_ucode_update_interfaces();
++ goto out;
++
++deinit_ctrl:
++ if (interfaces->ctrl_iface_deinit)
++ interfaces->ctrl_iface_deinit(hapd);
++free_hapd:
++ hostapd_free_hapd_data(hapd);
++ os_free(hapd);
++out:
++ hostapd_config_free(conf);
++ return ret;
++}
++
++static uc_value_t *
++uc_hostapd_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ uc_value_t *bss_list = uc_fn_arg(0);
++ struct hostapd_data **new_bss;
++ struct hostapd_bss_config **new_conf;
++
++ if (!iface)
++ return NULL;
++
++ if (ucv_type(bss_list) != UC_ARRAY ||
++ ucv_array_length(bss_list) != iface->num_bss)
++ return NULL;
++
++ new_bss = calloc(iface->num_bss, sizeof(*new_bss));
++ new_conf = calloc(iface->num_bss, sizeof(*new_conf));
++ for (size_t i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *bss;
++
++ bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
++ if (bss->iface != iface)
++ goto free;
++
++ for (size_t k = 0; k < i; k++)
++ if (new_bss[k] == bss)
++ goto free;
++
++ new_bss[i] = bss;
++ new_conf[i] = bss->conf;
++ }
++
++ new_bss[0]->interface_added = 0;
++ for (size_t i = 1; i < iface->num_bss; i++)
++ new_bss[i]->interface_added = 1;
++
++ free(iface->bss);
++ iface->bss = new_bss;
++
++ free(iface->conf->bss);
++ iface->conf->bss = new_conf;
++ iface->conf->num_bss = iface->num_bss;
++ hostapd_drv_set_first_bss(iface->bss[0]);
++
++ return ucv_boolean_new(true);
++
++free:
++ free(new_bss);
++ free(new_conf);
++ return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ uc_value_t *arg = uc_fn_arg(0);
++ struct sockaddr_storage from = {};
++ static char reply[4096];
++ int reply_len;
++
++ if (!hapd || !interfaces->ctrl_iface_recv ||
++ ucv_type(arg) != UC_STRING)
++ return NULL;
++
++ reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
++ reply, sizeof(reply),
++ &from, sizeof(from));
++ if (reply_len < 0)
++ return NULL;
++
++ if (reply_len && reply[reply_len - 1] == '\n')
++ reply_len--;
++
++ return ucv_string_new_length(reply, reply_len);
++}
++
++static void
++uc_hostapd_disable_iface(struct hostapd_iface *iface)
++{
++ switch (iface->state) {
++ case HAPD_IFACE_DISABLED:
++ break;
++#ifdef CONFIG_ACS
++ case HAPD_IFACE_ACS:
++ acs_cleanup(iface);
++ iface->scan_cb = NULL;
++ /* fallthrough */
++#endif
++ default:
++ hostapd_disable_iface(iface);
++ break;
++ }
++}
++
++static uc_value_t *
++uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ int i;
++
++ if (!iface)
++ return NULL;
++
++ if (iface->state != HAPD_IFACE_ENABLED)
++ uc_hostapd_disable_iface(iface);
++
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *hapd = iface->bss[i];
++
++ hostapd_drv_stop_ap(hapd);
++ hapd->beacon_set_done = 0;
++ }
++
++ return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ uc_value_t *info = uc_fn_arg(0);
++ struct hostapd_config *conf;
++ bool changed = false;
++ uint64_t intval;
++ int i;
++
++ if (!iface)
++ return NULL;
++
++ if (!info) {
++ iface->freq = 0;
++ goto out;
++ }
++
++ if (ucv_type(info) != UC_OBJECT)
++ return NULL;
++
++#define UPDATE_VAL(field, name) \
++ if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) && \
++ !errno && intval != conf->field) do { \
++ conf->field = intval; \
++ changed = true; \
++ } while(0)
++
++ conf = iface->conf;
++ UPDATE_VAL(op_class, "op_class");
++ UPDATE_VAL(hw_mode, "hw_mode");
++ UPDATE_VAL(channel, "channel");
++ UPDATE_VAL(secondary_channel, "sec_channel");
++ if (!changed &&
++ (iface->bss[0]->beacon_set_done ||
++ iface->state == HAPD_IFACE_DFS))
++ return ucv_boolean_new(true);
++
++ intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
++ if (!errno)
++ hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
++
++ intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
++ if (!errno)
++ hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
++
++ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++ if (!errno)
++ hostapd_set_oper_chwidth(conf, intval);
++
++ intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
++ if (!errno)
++ iface->freq = intval;
++ else
++ iface->freq = 0;
++ conf->acs = 0;
++
++out:
++ switch (iface->state) {
++ case HAPD_IFACE_ENABLED:
++ if (!hostapd_is_dfs_required(iface) ||
++ hostapd_is_dfs_chan_available(iface))
++ break;
++ wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
++ /* fallthrough */
++ default:
++ uc_hostapd_disable_iface(iface);
++ break;
++ }
++
++ if (conf->channel && !iface->freq)
++ iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
++
++ if (iface->state != HAPD_IFACE_ENABLED) {
++ hostapd_enable_iface(iface);
++ return ucv_boolean_new(true);
++ }
++
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *hapd = iface->bss[i];
++ int ret;
++
++ hapd->conf->start_disabled = 0;
++ hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
++ conf->channel,
++ conf->enable_edmg,
++ conf->edmg_channel,
++ conf->ieee80211n,
++ conf->ieee80211ac,
++ conf->ieee80211ax,
++ conf->ieee80211be,
++ conf->secondary_channel,
++ hostapd_get_oper_chwidth(conf),
++ hostapd_get_oper_centr_freq_seg0_idx(conf),
++ hostapd_get_oper_centr_freq_seg1_idx(conf));
++
++ ieee802_11_set_beacon(hapd);
++ }
++
++ return ucv_boolean_new(true);
++}
++
++static uc_value_t *
++uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ uc_value_t *info = uc_fn_arg(0);
++ struct hostapd_config *conf;
++ struct csa_settings csa = {};
++ uint64_t intval;
++ int i, ret = 0;
++
++ if (!iface || ucv_type(info) != UC_OBJECT)
++ return NULL;
++
++ conf = iface->conf;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
++ csa.cs_count = intval;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
++ csa.freq_params.sec_channel_offset = intval;
++
++ csa.freq_params.ht_enabled = conf->ieee80211n;
++ csa.freq_params.vht_enabled = conf->ieee80211ac;
++ csa.freq_params.he_enabled = conf->ieee80211ax;
++#ifdef CONFIG_IEEE80211BE
++ csa.freq_params.eht_enabled = conf->ieee80211be;
++#endif
++ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++ if (errno)
++ intval = hostapd_get_oper_chwidth(conf);
++ if (intval)
++ csa.freq_params.bandwidth = 40 << intval;
++ else
++ csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
++
++ if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
++ csa.freq_params.freq = intval;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
++ csa.freq_params.center_freq1 = intval;
++ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
++ csa.freq_params.center_freq2 = intval;
++
++ for (i = 0; i < iface->num_bss; i++)
++ ret = hostapd_switch_channel(iface->bss[i], &csa);
++
++ return ucv_boolean_new(!ret);
++}
++
++static uc_value_t *
++uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++ uc_value_t *ifname_arg = uc_fn_arg(0);
++ char prev_ifname[IFNAMSIZ + 1];
++ struct sta_info *sta;
++ const char *ifname;
++ int ret;
++
++ if (!hapd || ucv_type(ifname_arg) != UC_STRING)
++ return NULL;
++
++ os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
++ ifname = ucv_string_get(ifname_arg);
++
++ hostapd_ubus_free_bss(hapd);
++ if (interfaces->ctrl_iface_deinit)
++ interfaces->ctrl_iface_deinit(hapd);
++
++ ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
++ if (ret)
++ goto out;
++
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
++
++ if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
++ continue;
++
++ snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
++ snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
++ hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
++ }
++
++ if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
++ os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
++ os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
++ hostapd_ubus_add_bss(hapd);
++
++ hostapd_ucode_update_interfaces();
++out:
++ if (interfaces->ctrl_iface_init)
++ interfaces->ctrl_iface_init(hapd);
++
++ return ret ? NULL : ucv_boolean_new(true);
++}
++
++
++int hostapd_ucode_init(struct hapd_interfaces *ifaces)
++{
++ static const uc_function_list_t global_fns[] = {
++ { "printf", uc_wpa_printf },
++ { "getpid", uc_wpa_getpid },
++ { "sha1", uc_wpa_sha1 },
++ { "freq_info", uc_wpa_freq_info },
++ { "add_iface", uc_hostapd_add_iface },
++ { "remove_iface", uc_hostapd_remove_iface },
++ { "udebug_set", uc_wpa_udebug_set },
++ };
++ static const uc_function_list_t bss_fns[] = {
++ { "ctrl", uc_hostapd_bss_ctrl },
++ { "set_config", uc_hostapd_bss_set_config },
++ { "rename", uc_hostapd_bss_rename },
++ { "delete", uc_hostapd_bss_delete },
++ };
++ static const uc_function_list_t iface_fns[] = {
++ { "set_bss_order", uc_hostapd_iface_set_bss_order },
++ { "add_bss", uc_hostapd_iface_add_bss },
++ { "stop", uc_hostapd_iface_stop },
++ { "start", uc_hostapd_iface_start },
++ { "switch_channel", uc_hostapd_iface_switch_channel },
++ };
++ uc_value_t *data, *proto;
++
++ interfaces = ifaces;
++ vm = wpa_ucode_create_vm();
++
++ global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
++ bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
++ iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
++
++ bss_registry = ucv_array_new(vm);
++ uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
++
++ iface_registry = ucv_array_new(vm);
++ uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
++
++ global = wpa_ucode_global_init("hostapd", global_type);
++
++ if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
++ goto free_vm;
++ ucv_gc(vm);
++
++ return 0;
++
++free_vm:
++ wpa_ucode_free_vm();
++ return -1;
++}
++
++void hostapd_ucode_free(void)
++{
++ if (wpa_ucode_call_prepare("shutdown") == 0)
++ ucv_put(wpa_ucode_call(0));
++ wpa_ucode_free_vm();
++}
++
++void hostapd_ucode_free_iface(struct hostapd_iface *iface)
++{
++ wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
++}
++
++void hostapd_ucode_add_bss(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ if (wpa_ucode_call_prepare("bss_add"))
++ return;
++
++ val = hostapd_ucode_bss_get_uval(hapd);
++ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ if (wpa_ucode_call_prepare("bss_reload"))
++ return;
++
++ val = hostapd_ucode_bss_get_uval(hapd);
++ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void hostapd_ucode_free_bss(struct hostapd_data *hapd)
++{
++ uc_value_t *val;
++
++ val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
++ if (!val)
++ return;
++
++ hapd->ucode.idx = 0;
++ if (wpa_ucode_call_prepare("bss_remove"))
++ return;
++
++ uc_value_push(ucv_string_new(hapd->conf->iface));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
+diff --git a/src/ap/ucode.h b/src/ap/ucode.h
+new file mode 100644
+index 000000000..d00b78716
+--- /dev/null
++++ b/src/ap/ucode.h
+@@ -0,0 +1,54 @@
++#ifndef __HOSTAPD_AP_UCODE_H
++#define __HOSTAPD_AP_UCODE_H
++
++#include "utils/ucode.h"
++
++struct hostapd_data;
++
++struct hostapd_ucode_bss {
++#ifdef UCODE_SUPPORT
++ int idx;
++#endif
++};
++
++struct hostapd_ucode_iface {
++#ifdef UCODE_SUPPORT
++ int idx;
++#endif
++};
++
++#ifdef UCODE_SUPPORT
++
++int hostapd_ucode_init(struct hapd_interfaces *ifaces);
++
++void hostapd_ucode_free(void);
++void hostapd_ucode_free_iface(struct hostapd_iface *iface);
++void hostapd_ucode_add_bss(struct hostapd_data *hapd);
++void hostapd_ucode_free_bss(struct hostapd_data *hapd);
++void hostapd_ucode_reload_bss(struct hostapd_data *hapd);
++
++#else
++
++static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
++{
++ return -EINVAL;
++}
++static inline void hostapd_ucode_free(void)
++{
++}
++static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface)
++{
++}
++static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
++{
++}
++static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd)
++{
++}
++static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd)
++{
++}
++
++#endif
++
++#endif
+diff --git a/src/utils/build_features.h b/src/utils/build_features.h
+new file mode 100644
+index 000000000..553769ece
+--- /dev/null
++++ b/src/utils/build_features.h
+@@ -0,0 +1,65 @@
++#ifndef BUILD_FEATURES_H
++#define BUILD_FEATURES_H
++
++static inline int has_feature(const char *feat)
++{
++#if defined(IEEE8021X_EAPOL) || (defined(HOSTAPD) && !defined(CONFIG_NO_RADIUS))
++ if (!strcmp(feat, "eap"))
++ return 1;
++#endif
++#ifdef CONFIG_IEEE80211AC
++ if (!strcmp(feat, "11ac"))
++ return 1;
++#endif
++#ifdef CONFIG_IEEE80211AX
++ if (!strcmp(feat, "11ax"))
++ return 1;
++#endif
++#ifdef CONFIG_IEEE80211R
++ if (!strcmp(feat, "11r"))
++ return 1;
++#endif
++#ifdef CONFIG_ACS
++ if (!strcmp(feat, "acs"))
++ return 1;
++#endif
++#ifdef CONFIG_SAE
++ if (!strcmp(feat, "sae"))
++ return 1;
++#endif
++#ifdef CONFIG_OWE
++ if (!strcmp(feat, "owe"))
++ return 1;
++#endif
++#ifdef CONFIG_SUITEB192
++ if (!strcmp(feat, "suiteb192"))
++ return 1;
++#endif
++#ifdef CONFIG_WEP
++ if (!strcmp(feat, "wep"))
++ return 1;
++#endif
++#ifdef CONFIG_HS20
++ if (!strcmp(feat, "hs20"))
++ return 1;
++#endif
++#ifdef CONFIG_WPS
++ if (!strcmp(feat, "wps"))
++ return 1;
++#endif
++#ifdef CONFIG_FILS
++ if (!strcmp(feat, "fils"))
++ return 1;
++#endif
++#ifdef CONFIG_OCV
++ if (!strcmp(feat, "ocv"))
++ return 1;
++#endif
++#ifdef CONFIG_MESH
++ if (!strcmp(feat, "mesh"))
++ return 1;
++#endif
++ return 0;
++}
++
++#endif /* BUILD_FEATURES_H */
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+new file mode 100644
+index 000000000..29c753c32
+--- /dev/null
++++ b/src/utils/ucode.c
+@@ -0,0 +1,502 @@
++#include <unistd.h>
++#include "ucode.h"
++#include "utils/eloop.h"
++#include "crypto/crypto.h"
++#include "crypto/sha1.h"
++#include "common/ieee802_11_common.h"
++#include <linux/netlink.h>
++#include <linux/genetlink.h>
++#include <linux/nl80211.h>
++#include <libubox/uloop.h>
++#include <ucode/compiler.h>
++#include <udebug.h>
++
++static uc_value_t *registry;
++static uc_vm_t vm;
++static struct uloop_timeout gc_timer;
++static struct udebug ud;
++static struct udebug_buf ud_log, ud_nl[3];
++static const struct udebug_buf_meta meta_log = {
++ .name = "wpa_log",
++ .format = UDEBUG_FORMAT_STRING,
++};
++static const struct udebug_buf_meta meta_nl_ll = {
++ .name = "wpa_nl_ctrl",
++ .format = UDEBUG_FORMAT_PACKET,
++ .sub_format = UDEBUG_DLT_NETLINK,
++};
++static const struct udebug_buf_meta meta_nl_tx = {
++ .name = "wpa_nl_tx",
++ .format = UDEBUG_FORMAT_PACKET,
++ .sub_format = UDEBUG_DLT_NETLINK,
++};
++#define UDEBUG_FLAG_RX_FRAME (1ULL << 0)
++static const struct udebug_buf_flag rx_flags[] = {
++ { "rx_frame", UDEBUG_FLAG_RX_FRAME },
++};
++static const struct udebug_buf_meta meta_nl_rx = {
++ .name = "wpa_nl_rx",
++ .format = UDEBUG_FORMAT_PACKET,
++ .sub_format = UDEBUG_DLT_NETLINK,
++ .flags = rx_flags,
++ .n_flags = ARRAY_SIZE(rx_flags),
++};
++static struct udebug_ubus_ring udebug_rings[] = {
++ {
++ .buf = &ud_log,
++ .meta = &meta_log,
++ .default_entries = 1024,
++ .default_size = 64 * 1024
++ },
++ {
++ .buf = &ud_nl[0],
++ .meta = &meta_nl_rx,
++ .default_entries = 1024,
++ .default_size = 256 * 1024,
++ },
++ {
++ .buf = &ud_nl[1],
++ .meta = &meta_nl_tx,
++ .default_entries = 1024,
++ .default_size = 64 * 1024,
++ },
++ {
++ .buf = &ud_nl[2],
++ .meta = &meta_nl_ll,
++ .default_entries = 1024,
++ .default_size = 32 * 1024,
++ }
++};
++char *udebug_service;
++struct udebug_ubus ud_ubus;
++
++static void uc_gc_timer(struct uloop_timeout *timeout)
++{
++ ucv_gc(&vm);
++}
++
++uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *level = uc_fn_arg(0);
++ uc_value_t *ret, **args;
++ uc_cfn_ptr_t _sprintf;
++ int l = MSG_INFO;
++ int i, start = 0;
++
++ _sprintf = uc_stdlib_function("sprintf");
++ if (!sprintf)
++ return NULL;
++
++ if (ucv_type(level) == UC_INTEGER) {
++ l = ucv_int64_get(level);
++ start++;
++ }
++
++ if (nargs <= start)
++ return NULL;
++
++ ret = _sprintf(vm, nargs - start);
++ if (ucv_type(ret) != UC_STRING)
++ return NULL;
++
++ wpa_printf(l, "%s", ucv_string_get(ret));
++ ucv_put(ret);
++
++ return NULL;
++}
++
++uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *freq = uc_fn_arg(0);
++ uc_value_t *sec = uc_fn_arg(1);
++ int width = ucv_uint64_get(uc_fn_arg(2));
++ int freq_val, center_idx, center_ofs;
++ enum oper_chan_width chanwidth;
++ enum hostapd_hw_mode hw_mode;
++ u8 op_class, channel, tmp_channel;
++ const char *modestr;
++ int sec_channel = 0;
++ uc_value_t *ret;
++
++ if (ucv_type(freq) != UC_INTEGER)
++ return NULL;
++
++ freq_val = ucv_int64_get(freq);
++ if (ucv_type(sec) == UC_INTEGER)
++ sec_channel = ucv_int64_get(sec);
++ else if (sec)
++ return NULL;
++ else if (freq_val > 4000)
++ sec_channel = (freq_val / 20) & 1 ? 1 : -1;
++ else
++ sec_channel = freq_val < 2442 ? 1 : -1;
++
++ if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
++ return NULL;
++
++ switch (width) {
++ case 0:
++ chanwidth = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ case 1:
++ chanwidth = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case 2:
++ chanwidth = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ default:
++ return NULL;
++ }
++
++ hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
++ chanwidth, &op_class, &channel);
++ switch (hw_mode) {
++ case HOSTAPD_MODE_IEEE80211B:
++ modestr = "b";
++ break;
++ case HOSTAPD_MODE_IEEE80211G:
++ modestr = "g";
++ break;
++ case HOSTAPD_MODE_IEEE80211A:
++ modestr = "a";
++ break;
++ case HOSTAPD_MODE_IEEE80211AD:
++ modestr = "ad";
++ break;
++ default:
++ return NULL;
++ }
++
++ ret = ucv_object_new(vm);
++ ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
++ ucv_object_add(ret, "channel", ucv_int64_new(channel));
++ ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
++ ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
++ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
++ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++
++ if (!sec_channel)
++ return ret;
++
++ if (freq_val >= 5900)
++ center_ofs = 0;
++ else if (freq_val >= 5745)
++ center_ofs = 20;
++ else
++ center_ofs = 35;
++ tmp_channel = channel - center_ofs;
++ tmp_channel &= ~((8 << width) - 1);
++ center_idx = tmp_channel + center_ofs + (4 << width) - 1;
++
++ if (freq_val < 3000)
++ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
++ else
++ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
++ center_idx = (center_idx - channel) * 5 + freq_val;
++ ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
++
++out:
++ return ret;
++}
++
++uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
++{
++ return ucv_int64_new(getpid());
++}
++
++uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
++{
++ u8 hash[SHA1_MAC_LEN];
++ char hash_hex[2 * ARRAY_SIZE(hash) + 1];
++ uc_value_t *val;
++ size_t *lens;
++ const u8 **args;
++ int i;
++
++ if (!nargs)
++ return NULL;
++
++ args = alloca(nargs * sizeof(*args));
++ lens = alloca(nargs * sizeof(*lens));
++ for (i = 0; i < nargs; i++) {
++ val = uc_fn_arg(i);
++ if (ucv_type(val) != UC_STRING)
++ return NULL;
++
++ args[i] = ucv_string_get(val);
++ lens[i] = ucv_string_length(val);
++ }
++
++ if (sha1_vector(nargs, args, lens, hash))
++ return NULL;
++
++ for (i = 0; i < ARRAY_SIZE(hash); i++)
++ sprintf(hash_hex + 2 * i, "%02x", hash[i]);
++
++ return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
++}
++
++uc_vm_t *wpa_ucode_create_vm(void)
++{
++ static uc_parse_config_t config = {
++ .strict_declarations = true,
++ .lstrip_blocks = true,
++ .trim_blocks = true,
++ .raw_mode = true
++ };
++
++ uc_search_path_init(&config.module_search_path);
++ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
++ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
++
++ uc_vm_init(&vm, &config);
++
++ uc_stdlib_load(uc_vm_scope_get(&vm));
++ eloop_add_uloop();
++ gc_timer.cb = uc_gc_timer;
++
++ return &vm;
++}
++
++int wpa_ucode_run(const char *script)
++{
++ uc_source_t *source;
++ uc_program_t *prog;
++ uc_value_t *ops;
++ char *err;
++ int ret;
++
++ source = uc_source_new_file(script);
++ if (!source)
++ return -1;
++
++ prog = uc_compile(vm.config, source, &err);
++ uc_source_put(source);
++ if (!prog) {
++ wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
++ return -1;
++ }
++
++ ret = uc_vm_execute(&vm, prog, &ops);
++ uc_program_put(prog);
++ if (ret || !ops)
++ return -1;
++
++ registry = ucv_array_new(&vm);
++ uc_vm_registry_set(&vm, "hostap.registry", registry);
++ ucv_array_set(registry, 0, ucv_get(ops));
++
++ return 0;
++}
++
++int wpa_ucode_call_prepare(const char *fname)
++{
++ uc_value_t *obj, *func;
++
++ if (!registry)
++ return -1;
++
++ obj = ucv_array_get(registry, 0);
++ if (!obj)
++ return -1;
++
++ func = ucv_object_get(obj, fname, NULL);
++ if (!ucv_is_callable(func))
++ return -1;
++
++ uc_vm_stack_push(&vm, ucv_get(obj));
++ uc_vm_stack_push(&vm, ucv_get(func));
++
++ return 0;
++}
++
++static void udebug_printf_hook(int level, const char *fmt, va_list ap)
++{
++ udebug_entry_init(&ud_log);
++ udebug_entry_vprintf(&ud_log, fmt, ap);
++ udebug_entry_add(&ud_log);
++}
++
++static void udebug_hexdump_hook(int level, const char *title,
++ const void *data, size_t len)
++{
++ char *buf;
++
++ udebug_entry_init(&ud_log);
++ udebug_entry_printf(&ud_log, "%s - hexdump:", title);
++ buf = udebug_entry_append(&ud_log, NULL, 3 * len);
++ for (size_t i = 0; i < len; i++)
++ buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
++ udebug_entry_add(&ud_log);
++}
++
++static void udebug_netlink_hook(int tx, const void *data, size_t len)
++{
++ struct {
++ uint16_t pkttype;
++ uint16_t arphdr;
++ uint16_t _pad[5];
++ uint16_t proto;
++ } hdr = {
++ .pkttype = host_to_be16(tx ? 7 : 6),
++ .arphdr = host_to_be16(824),
++ .proto = host_to_be16(16),
++ };
++ const struct nlmsghdr *nlh = data;
++ const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
++ struct udebug_buf *buf = &ud_nl[!!tx];
++
++ if (nlh->nlmsg_type == 0x10)
++ buf = &ud_nl[2];
++ else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
++ !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
++ return;
++
++ if (!udebug_buf_valid(buf))
++ return;
++
++ udebug_entry_init(buf);
++ udebug_entry_append(buf, &hdr, sizeof(hdr));
++ udebug_entry_append(buf, data, len);
++ udebug_entry_add(buf);
++}
++
++static void
++wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
++ bool enabled)
++{
++ udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
++ data, enabled);
++
++ if (udebug_buf_valid(&ud_log)) {
++ wpa_printf_hook = udebug_printf_hook;
++ wpa_hexdump_hook = udebug_hexdump_hook;
++ } else {
++ wpa_printf_hook = NULL;
++ wpa_hexdump_hook = NULL;
++ }
++
++ if (udebug_buf_valid(&ud_nl[0]) ||
++ udebug_buf_valid(&ud_nl[1]) ||
++ udebug_buf_valid(&ud_nl[2]))
++ wpa_netlink_hook = udebug_netlink_hook;
++ else
++ wpa_netlink_hook = NULL;
++}
++
++uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *name = uc_fn_arg(0);
++ uc_value_t *ubus = uc_fn_arg(1);
++ static bool enabled = false;
++ struct ubus_context *ctx;
++ bool cur_en;
++
++ cur_en = ucv_type(name) == UC_STRING;
++ ctx = ucv_resource_data(ubus, "ubus.connection");
++ if (!ctx)
++ cur_en = false;
++
++ if (enabled == cur_en)
++ return ucv_boolean_new(true);
++
++ enabled = cur_en;
++ if (enabled) {
++ udebug_service = strdup(ucv_string_get(name));
++ udebug_init(&ud);
++ udebug_auto_connect(&ud, NULL);
++ udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
++ } else {
++ udebug_ubus_free(&ud_ubus);
++ for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
++ if (udebug_buf_valid(udebug_rings[i].buf))
++ udebug_buf_free(udebug_rings[i].buf);
++ udebug_free(&ud);
++ free(udebug_service);
++ }
++
++ return ucv_boolean_new(true);
++}
++
++uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
++{
++ uc_value_t *global = uc_resource_new(global_type, NULL);
++ uc_value_t *proto;
++
++ uc_vm_registry_set(&vm, "hostap.global", global);
++ proto = ucv_prototype_get(global);
++ ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
++
++#define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
++ ADD_CONST(MSG_EXCESSIVE);
++ ADD_CONST(MSG_MSGDUMP);
++ ADD_CONST(MSG_DEBUG);
++ ADD_CONST(MSG_INFO);
++ ADD_CONST(MSG_WARNING);
++ ADD_CONST(MSG_ERROR);
++#undef ADD_CONST
++
++ ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
++
++ return global;
++}
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
++{
++ uc_value_t *data;
++ int i = 0;
++
++ while (ucv_array_get(reg, i))
++ i++;
++
++ ucv_array_set(reg, i, ucv_get(val));
++
++ return i + 1;
++}
++
++uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
++{
++ if (!idx)
++ return NULL;
++
++ return ucv_array_get(reg, idx - 1);
++}
++
++uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
++{
++ uc_value_t *val = wpa_ucode_registry_get(reg, idx);
++ void **dataptr;
++
++ if (!val)
++ return NULL;
++
++ ucv_array_set(reg, idx - 1, NULL);
++ dataptr = ucv_resource_dataptr(val, NULL);
++ if (dataptr)
++ *dataptr = NULL;
++
++ return val;
++}
++
++
++uc_value_t *wpa_ucode_call(size_t nargs)
++{
++ if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
++ return NULL;
++
++ if (!gc_timer.pending)
++ uloop_timeout_set(&gc_timer, 10);
++
++ return uc_vm_stack_pop(&vm);
++}
++
++void wpa_ucode_free_vm(void)
++{
++ if (!vm.config)
++ return;
++
++ uc_search_path_free(&vm.config->module_search_path);
++ uc_vm_free(&vm);
++ registry = NULL;
++ vm = (uc_vm_t){};
++}
+diff --git a/src/utils/ucode.h b/src/utils/ucode.h
+new file mode 100644
+index 000000000..c083241e0
+--- /dev/null
++++ b/src/utils/ucode.h
+@@ -0,0 +1,30 @@
++#ifndef __HOSTAPD_UTILS_UCODE_H
++#define __HOSTAPD_UTILS_UCODE_H
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include <ucode/lib.h>
++#include <ucode/vm.h>
++
++#define HOSTAPD_UC_PATH "/usr/share/hostap/"
++
++extern uc_value_t *uc_registry;
++uc_vm_t *wpa_ucode_create_vm(void);
++int wpa_ucode_run(const char *script);
++int wpa_ucode_call_prepare(const char *fname);
++uc_value_t *wpa_ucode_call(size_t nargs);
++void wpa_ucode_free_vm(void);
++
++uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type);
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
++uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx);
++uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx);
++
++uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs);
++uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
++
++#endif
+diff --git a/wpa_supplicant/ubus.c b/wpa_supplicant/ubus.c
+new file mode 100644
+index 000000000..1c477f0c0
+--- /dev/null
++++ b/wpa_supplicant/ubus.c
+@@ -0,0 +1,280 @@
++/*
++ * wpa_supplicant / ubus support
++ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "utils/wpabuf.h"
++#include "common/ieee802_11_defs.h"
++#include "wpa_supplicant_i.h"
++#include "wps_supplicant.h"
++#include "ubus.h"
++
++static struct ubus_context *ctx;
++static struct blob_buf b;
++static int ctx_ref;
++
++static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
++{
++ return container_of(obj, struct wpa_global, ubus_global);
++}
++
++static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
++{
++ return container_of(obj, struct wpa_supplicant, ubus.obj);
++}
++
++static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
++{
++ if (ubus_reconnect(ctx, NULL)) {
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++ return;
++ }
++
++ ubus_add_uloop(ctx);
++}
++
++static void wpas_ubus_connection_lost(struct ubus_context *ctx)
++{
++ uloop_fd_delete(&ctx->sock);
++ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
++}
++
++static bool wpas_ubus_init(void)
++{
++ if (ctx)
++ return true;
++
++ eloop_add_uloop();
++ ctx = ubus_connect(NULL);
++ if (!ctx)
++ return false;
++
++ ctx->connection_lost = wpas_ubus_connection_lost;
++ ubus_add_uloop(ctx);
++
++ return true;
++}
++
++static void wpas_ubus_ref_inc(void)
++{
++ ctx_ref++;
++}
++
++static void wpas_ubus_ref_dec(void)
++{
++ ctx_ref--;
++ if (!ctx)
++ return;
++
++ if (ctx_ref)
++ return;
++
++ uloop_fd_delete(&ctx->sock);
++ ubus_free(ctx);
++ ctx = NULL;
++}
++
++static int
++wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++
++ blob_buf_init(&b, 0);
++ blobmsg_add_u8(&b, "ht_supported", ht_supported(wpa_s->hw.modes));
++ blobmsg_add_u8(&b, "vht_supported", vht_supported(wpa_s->hw.modes));
++ ubus_send_reply(ctx, req, b.head);
++
++ return 0;
++}
++
++static int
++wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++
++ if (wpa_supplicant_reload_configuration(wpa_s))
++ return UBUS_STATUS_UNKNOWN_ERROR;
++ else
++ return 0;
++}
++
++#ifdef CONFIG_WPS
++enum {
++ WPS_START_MULTI_AP,
++ __WPS_START_MAX
++};
++
++static const struct blobmsg_policy wps_start_policy[] = {
++ [WPS_START_MULTI_AP] = { "multi_ap", BLOBMSG_TYPE_BOOL },
++};
++
++static int
++wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++ struct blob_attr *tb[__WPS_START_MAX], *cur;
++ int multi_ap = 0;
++
++ blobmsg_parse(wps_start_policy, __WPS_START_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
++
++ if (tb[WPS_START_MULTI_AP])
++ multi_ap = blobmsg_get_bool(tb[WPS_START_MULTI_AP]);
++
++ rc = wpas_wps_start_pbc(wpa_s, NULL, 0, multi_ap);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++
++static int
++wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ int rc;
++ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
++
++ rc = wpas_wps_cancel(wpa_s);
++
++ if (rc != 0)
++ return UBUS_STATUS_NOT_SUPPORTED;
++
++ return 0;
++}
++#endif
++
++static const struct ubus_method bss_methods[] = {
++ UBUS_METHOD_NOARG("reload", wpas_bss_reload),
++ UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
++#ifdef CONFIG_WPS
++ UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
++ UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
++#endif
++};
++
++static struct ubus_object_type bss_object_type =
++ UBUS_OBJECT_TYPE("wpas_bss", bss_methods);
++
++void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
++{
++ struct ubus_object *obj = &wpa_s->ubus.obj;
++ char *name;
++ int ret;
++
++ if (!wpas_ubus_init())
++ return;
++
++ if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
++ return;
++
++ obj->name = name;
++ obj->type = &bss_object_type;
++ obj->methods = bss_object_type.methods;
++ obj->n_methods = bss_object_type.n_methods;
++ ret = ubus_add_object(ctx, obj);
++ wpas_ubus_ref_inc();
++}
++
++void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
++{
++ struct ubus_object *obj = &wpa_s->ubus.obj;
++ char *name = (char *) obj->name;
++
++ if (!ctx)
++ return;
++
++ if (obj->id) {
++ ubus_remove_object(ctx, obj);
++ wpas_ubus_ref_dec();
++ }
++
++ free(name);
++}
++
++#ifdef CONFIG_WPS
++void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
++{
++ u16 auth_type;
++ char *ifname, *encryption, *ssid, *key;
++ size_t ifname_len;
++
++ if (!cred)
++ return;
++
++ auth_type = cred->auth_type;
++
++ if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))
++ auth_type = WPS_AUTH_WPA2PSK;
++
++ if (auth_type != WPS_AUTH_OPEN &&
++ auth_type != WPS_AUTH_WPAPSK &&
++ auth_type != WPS_AUTH_WPA2PSK) {
++ wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
++ "unsupported authentication type 0x%x",
++ auth_type);
++ return;
++ }
++
++ if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
++ if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
++ wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
++ "invalid Network Key length %lu",
++ (unsigned long) cred->key_len);
++ return;
++ }
++ }
++
++ blob_buf_init(&b, 0);
++
++ ifname_len = strlen(wpa_s->ifname);
++ ifname = blobmsg_alloc_string_buffer(&b, "ifname", ifname_len + 1);
++ memcpy(ifname, wpa_s->ifname, ifname_len + 1);
++ ifname[ifname_len] = '\0';
++ blobmsg_add_string_buffer(&b);
++
++ switch (auth_type) {
++ case WPS_AUTH_WPA2PSK:
++ encryption = "psk2";
++ break;
++ case WPS_AUTH_WPAPSK:
++ encryption = "psk";
++ break;
++ default:
++ encryption = "none";
++ break;
++ }
++
++ blobmsg_add_string(&b, "encryption", encryption);
++
++ ssid = blobmsg_alloc_string_buffer(&b, "ssid", cred->ssid_len + 1);
++ memcpy(ssid, cred->ssid, cred->ssid_len);
++ ssid[cred->ssid_len] = '\0';
++ blobmsg_add_string_buffer(&b);
++
++ if (cred->key_len > 0) {
++ key = blobmsg_alloc_string_buffer(&b, "key", cred->key_len + 1);
++ memcpy(key, cred->key, cred->key_len);
++ key[cred->key_len] = '\0';
++ blobmsg_add_string_buffer(&b);
++ }
++
++// ubus_notify(ctx, &wpa_s->ubus.obj, "wps_credentials", b.head, -1);
++ ubus_send_event(ctx, "wps_credentials", b.head);
++}
++#endif /* CONFIG_WPS */
+diff --git a/wpa_supplicant/ubus.h b/wpa_supplicant/ubus.h
+new file mode 100644
+index 000000000..f6681cb26
+--- /dev/null
++++ b/wpa_supplicant/ubus.h
+@@ -0,0 +1,55 @@
++/*
++ * wpa_supplicant / ubus support
++ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
++ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++#ifndef __WPAS_UBUS_H
++#define __WPAS_UBUS_H
++
++struct wpa_supplicant;
++struct wpa_global;
++
++#include "wps_supplicant.h"
++
++#ifdef UBUS_SUPPORT
++#include <libubus.h>
++
++struct wpas_ubus_bss {
++ struct ubus_object obj;
++};
++
++void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
++void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
++
++#ifdef CONFIG_WPS
++void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
++#endif
++
++#else
++struct wpas_ubus_bss {};
++
++static inline void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
++{
++}
++
++static inline void wpas_ubus_add(struct wpa_global *global)
++{
++}
++
++static inline void wpas_ubus_free(struct wpa_global *global)
++{
++}
++#endif
++
++#endif
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+new file mode 100644
+index 000000000..397f85bde
+--- /dev/null
++++ b/wpa_supplicant/ucode.c
+@@ -0,0 +1,299 @@
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "drivers/driver.h"
++#include "ap/hostapd.h"
++#include "wpa_supplicant_i.h"
++#include "wps_supplicant.h"
++#include "bss.h"
++#include "ucode.h"
++
++static struct wpa_global *wpa_global;
++static uc_resource_type_t *global_type, *iface_type;
++static uc_value_t *global, *iface_registry;
++static uc_vm_t *vm;
++
++static uc_value_t *
++wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s)
++{
++ uc_value_t *val;
++
++ if (wpa_s->ucode.idx)
++ return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++
++ val = uc_resource_new(iface_type, wpa_s);
++ wpa_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++ return val;
++}
++
++static void
++wpas_ucode_update_interfaces(void)
++{
++ uc_value_t *ifs = ucv_object_new(vm);
++ struct wpa_supplicant *wpa_s;
++ int i;
++
++ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
++ ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
++
++ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
++{
++ uc_value_t *val;
++
++ if (wpa_ucode_call_prepare("iface_add"))
++ return;
++
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
++{
++ uc_value_t *val;
++
++ val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx);
++ if (!val)
++ return;
++
++ wpa_s->ucode.idx = 0;
++ if (wpa_ucode_call_prepare("iface_remove"))
++ return;
++
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(val));
++ ucv_put(wpa_ucode_call(2));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++ const char *state;
++ uc_value_t *val;
++
++ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++ if (!val)
++ return;
++
++ if (wpa_ucode_call_prepare("state"))
++ return;
++
++ state = wpa_supplicant_state_txt(wpa_s->wpa_state);
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(val));
++ uc_value_push(ucv_get(ucv_string_new(state)));
++ ucv_put(wpa_ucode_call(3));
++ ucv_gc(vm);
++}
++
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++ const char *state;
++ uc_value_t *val;
++
++ if (event != EVENT_CH_SWITCH_STARTED)
++ return;
++
++ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++ if (!val)
++ return;
++
++ if (wpa_ucode_call_prepare("event"))
++ return;
++
++ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++ uc_value_push(ucv_get(val));
++ uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
++ val = ucv_object_new(vm);
++ uc_value_push(ucv_get(val));
++
++ if (event == EVENT_CH_SWITCH_STARTED) {
++ ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
++ ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
++ ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
++ ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++ ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++ }
++
++ ucv_put(wpa_ucode_call(4));
++ ucv_gc(vm);
++}
++
++static const char *obj_stringval(uc_value_t *obj, const char *name)
++{
++ uc_value_t *val = ucv_object_get(obj, name, NULL);
++
++ return ucv_string_get(val);
++}
++
++static uc_value_t *
++uc_wpas_add_iface(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *info = uc_fn_arg(0);
++ uc_value_t *driver = ucv_object_get(info, "driver", NULL);
++ uc_value_t *ifname = ucv_object_get(info, "iface", NULL);
++ uc_value_t *bridge = ucv_object_get(info, "bridge", NULL);
++ uc_value_t *config = ucv_object_get(info, "config", NULL);
++ uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL);
++ struct wpa_interface iface;
++ int ret = -1;
++
++ if (ucv_type(info) != UC_OBJECT)
++ goto out;
++
++ iface = (struct wpa_interface){
++ .driver = "nl80211",
++ .ifname = ucv_string_get(ifname),
++ .bridge_ifname = ucv_string_get(bridge),
++ .confname = ucv_string_get(config),
++ .ctrl_interface = ucv_string_get(ctrl),
++ };
++
++ if (driver) {
++ const char *drvname;
++ if (ucv_type(driver) != UC_STRING)
++ goto out;
++
++ iface.driver = NULL;
++ drvname = ucv_string_get(driver);
++ for (int i = 0; wpa_drivers[i]; i++) {
++ if (!strcmp(drvname, wpa_drivers[i]->name))
++ iface.driver = wpa_drivers[i]->name;
++ }
++
++ if (!iface.driver)
++ goto out;
++ }
++
++ if (!iface.ifname || !iface.confname)
++ goto out;
++
++ ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1;
++ wpas_ucode_update_interfaces();
++
++out:
++ return ucv_int64_new(ret);
++}
++
++static uc_value_t *
++uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs)
++{
++ struct wpa_supplicant *wpa_s = NULL;
++ uc_value_t *ifname_arg = uc_fn_arg(0);
++ const char *ifname = ucv_string_get(ifname_arg);
++ int ret = -1;
++
++ if (!ifname)
++ goto out;
++
++ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
++ if (!strcmp(wpa_s->ifname, ifname))
++ break;
++
++ if (!wpa_s)
++ goto out;
++
++ ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0);
++ wpas_ucode_update_interfaces();
++
++out:
++ return ucv_int64_new(ret);
++}
++
++static uc_value_t *
++uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
++{
++ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++ struct wpa_bss *bss;
++ uc_value_t *ret, *val;
++
++ if (!wpa_s)
++ return NULL;
++
++ ret = ucv_object_new(vm);
++
++ val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
++ ucv_object_add(ret, "state", ucv_get(val));
++
++ bss = wpa_s->current_bss;
++ if (bss) {
++ int sec_chan = 0;
++ const u8 *ie;
++
++ ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
++ if (ie && ie[1] >= 2) {
++ const struct ieee80211_ht_operation *ht_oper;
++ int sec;
++
++ ht_oper = (const void *) (ie + 2);
++ sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
++ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
++ sec_chan = 1;
++ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
++ sec_chan = -1;
++ }
++
++ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
++ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++ }
++
++#ifdef CONFIG_MESH
++ if (wpa_s->ifmsh) {
++ struct hostapd_iface *ifmsh = wpa_s->ifmsh;
++
++ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
++ ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
++ }
++#endif
++
++ return ret;
++}
++
++int wpas_ucode_init(struct wpa_global *gl)
++{
++ static const uc_function_list_t global_fns[] = {
++ { "printf", uc_wpa_printf },
++ { "getpid", uc_wpa_getpid },
++ { "add_iface", uc_wpas_add_iface },
++ { "remove_iface", uc_wpas_remove_iface },
++ { "udebug_set", uc_wpa_udebug_set },
++ };
++ static const uc_function_list_t iface_fns[] = {
++ { "status", uc_wpas_iface_status },
++ };
++ uc_value_t *data, *proto;
++
++ wpa_global = gl;
++ vm = wpa_ucode_create_vm();
++
++ global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL);
++ iface_type = uc_type_declare(vm, "wpas.iface", iface_fns, NULL);
++
++ iface_registry = ucv_array_new(vm);
++ uc_vm_registry_set(vm, "wpas.iface_registry", iface_registry);
++
++ global = wpa_ucode_global_init("wpas", global_type);
++
++ if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc"))
++ goto free_vm;
++
++ ucv_gc(vm);
++ return 0;
++
++free_vm:
++ wpa_ucode_free_vm();
++ return -1;
++}
++
++void wpas_ucode_free(void)
++{
++ if (wpa_ucode_call_prepare("shutdown") == 0)
++ ucv_put(wpa_ucode_call(0));
++ wpa_ucode_free_vm();
++}
+diff --git a/wpa_supplicant/ucode.h b/wpa_supplicant/ucode.h
+new file mode 100644
+index 000000000..a429a0ed8
+--- /dev/null
++++ b/wpa_supplicant/ucode.h
+@@ -0,0 +1,49 @@
++#ifndef __WPAS_UCODE_H
++#define __WPAS_UCODE_H
++
++#include "utils/ucode.h"
++
++struct wpa_global;
++union wpa_event_data;
++struct wpa_supplicant;
++
++struct wpas_ucode_bss {
++#ifdef UCODE_SUPPORT
++ unsigned int idx;
++#endif
++};
++
++#ifdef UCODE_SUPPORT
++int wpas_ucode_init(struct wpa_global *gl);
++void wpas_ucode_free(void);
++void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s);
++void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s);
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
++#else
++static inline int wpas_ucode_init(struct wpa_global *gl)
++{
++ return -EINVAL;
++}
++static inline void wpas_ucode_free(void)
++{
++}
++static inline void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++}
++
++#endif
++
++#endif
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
new file mode 100644
index 0000000..1dff1f3
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
@@ -0,0 +1,14442 @@
+From 2eff271199b22ce44432d531e8b44809368ac5d2 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Tue, 23 Jan 2024 17:07:17 +0800
+Subject: [PATCH 028/104] hostapd: sync 2024-01-18 openwrt/trunk patch folder
+
+---
+ hostapd/Makefile | 149 +-
+ hostapd/config_file.c | 39 +-
+ hostapd/ctrl_iface.c | 4 +
+ hostapd/defconfig | 15 +-
+ hostapd/hostapd_cli.c | 10 +-
+ hostapd/main.c | 21 +-
+ src/ap/acs.c | 5 +-
+ src/ap/airtime_policy.c | 15 +-
+ src/ap/ap_config.h | 8 +
+ src/ap/ap_drv_ops.c | 5 +-
+ src/ap/ap_drv_ops.h | 21 +-
+ src/ap/beacon.c | 14 +-
+ src/ap/ctrl_iface_ap.c | 45 +-
+ src/ap/dfs.c | 22 +-
+ src/ap/drv_callbacks.c | 16 +-
+ src/ap/hostapd.c | 51 +-
+ src/ap/hostapd.h | 42 +
+ src/ap/hw_features.c | 3 +-
+ src/ap/ieee802_11.c | 50 +-
+ src/ap/ieee802_11_ht.c | 15 +
+ src/ap/ieee802_11_shared.c | 2 -
+ src/ap/ieee802_11_vht.c | 17 +
+ src/ap/ieee802_1x.c | 6 +
+ src/ap/ndisc_snoop.c | 1 +
+ src/ap/rrm.c | 8 +
+ src/ap/sta_info.c | 35 +-
+ src/ap/sta_info.h | 12 +-
+ src/ap/vlan_full.c | 4 +
+ src/ap/vlan_init.c | 8 +-
+ src/ap/wnm_ap.c | 16 +-
+ src/ap/wpa_auth.c | 3 +-
+ src/ap/wpa_auth_glue.c | 9 +-
+ src/ap/wps_hostapd.c | 6 +-
+ src/ap/x_snoop.c | 22 +-
+ src/common/dpp_crypto.c | 10 +-
+ src/common/hw_features_common.c | 1 +
+ src/common/ieee802_11_defs.h | 2 +
+ src/common/sae.c | 7 +
+ src/common/wpa_common.c | 40 +-
+ src/common/wpa_ctrl.c | 10 +-
+ src/crypto/Makefile | 129 +-
+ src/crypto/crypto_mbedtls.c | 4228 +++++++++++++++++++++
+ src/crypto/crypto_module_tests.c | 134 +
+ src/crypto/crypto_wolfssl.c | 18 +
+ src/crypto/tls_mbedtls.c | 3313 ++++++++++++++++
+ src/drivers/driver.h | 36 +-
+ src/drivers/driver_nl80211.c | 300 +-
+ src/drivers/driver_nl80211_capa.c | 4 +
+ src/drivers/driver_nl80211_event.c | 5 +
+ src/drivers/driver_nl80211_scan.c | 2 +-
+ src/drivers/drivers.c | 4 +
+ src/drivers/drivers.mak | 4 +-
+ src/drivers/rfkill.h | 16 +
+ src/radius/radius_client.c | 34 +
+ src/radius/radius_client.h | 2 +
+ src/radius/radius_das.c | 201 +-
+ src/radius/radius_das.h | 1 +
+ src/radius/radius_server.c | 61 +-
+ src/rsn_supp/wpa.c | 3 +
+ src/tls/Makefile | 11 +
+ src/utils/eloop.c | 20 +-
+ src/utils/eloop.h | 8 +
+ src/utils/uloop.c | 64 +
+ src/utils/wpa_debug.c | 49 +-
+ src/utils/wpa_debug.h | 73 +-
+ tests/Makefile | 65 +-
+ tests/hwsim/example-hostapd.config | 10 +-
+ tests/hwsim/example-wpa_supplicant.config | 11 +-
+ tests/hwsim/test_ap_eap.py | 112 +-
+ tests/hwsim/test_ap_ft.py | 4 +-
+ tests/hwsim/test_authsrv.py | 9 +-
+ tests/hwsim/test_dpp.py | 19 +-
+ tests/hwsim/test_erp.py | 16 +-
+ tests/hwsim/test_fils.py | 12 +
+ tests/hwsim/test_pmksa_cache.py | 4 +-
+ tests/hwsim/test_sae.py | 7 +
+ tests/hwsim/test_suite_b.py | 3 +
+ tests/hwsim/test_wpas_ctrl.py | 2 +-
+ tests/hwsim/utils.py | 8 +-
+ tests/test-crypto_module.c | 16 +
+ tests/test-https.c | 12 +-
+ tests/test-https_server.c | 12 +-
+ wpa_supplicant/Makefile | 144 +-
+ wpa_supplicant/ap.c | 22 +-
+ wpa_supplicant/config.c | 95 +
+ wpa_supplicant/config_file.c | 8 +-
+ wpa_supplicant/config_ssid.h | 7 +
+ wpa_supplicant/ctrl_iface.c | 12 +-
+ wpa_supplicant/defconfig | 6 +-
+ wpa_supplicant/eapol_test.c | 11 +
+ wpa_supplicant/events.c | 13 +-
+ wpa_supplicant/main.c | 14 +-
+ wpa_supplicant/mesh.c | 3 +
+ wpa_supplicant/wpa_cli.c | 9 +
+ wpa_supplicant/wpa_priv.c | 8 +-
+ wpa_supplicant/wpa_supplicant.c | 75 +-
+ wpa_supplicant/wpa_supplicant_i.h | 6 +
+ wpa_supplicant/wps_supplicant.c | 3 +
+ wpa_supplicant/wps_supplicant.h | 3 +-
+ 99 files changed, 9786 insertions(+), 464 deletions(-)
+ create mode 100644 src/crypto/crypto_mbedtls.c
+ create mode 100644 src/crypto/tls_mbedtls.c
+ create mode 100644 src/utils/uloop.c
+ create mode 100644 tests/test-crypto_module.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index 405e05e5f..f5c1dc029 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -1,6 +1,7 @@
+ ALL=hostapd hostapd_cli
+ CONFIG_FILE = .config
+
++-include $(if $(MULTICALL), ../wpa_supplicant/.config)
+ include ../src/build.rules
+
+ ifdef LIBS
+@@ -62,6 +63,10 @@ endif
+ OBJS += main.o
+ OBJS += config_file.o
+
++ifdef CONFIG_RADIUS_SERVER
++OBJS += radius.o
++endif
++
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/drv_callbacks.o
+@@ -171,6 +176,24 @@ OBJS += ../src/common/hw_features_common.o
+
+ OBJS += ../src/eapol_auth/eapol_auth_sm.o
+
++ifdef CONFIG_UBUS
++CFLAGS += -DUBUS_SUPPORT
++OBJS += ../src/ap/ubus.o
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ../src/ap/ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
++OBJS += ../src/utils/uloop.o
++LIBS += -lubox
++endif
+
+ ifdef CONFIG_CODE_COVERAGE
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+@@ -205,7 +228,8 @@ endif
+
+ ifdef CONFIG_NO_VLAN
+ CFLAGS += -DCONFIG_NO_VLAN
+-else
++endif
++ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
+ OBJS += ../src/ap/vlan_init.o
+ OBJS += ../src/ap/vlan_ifconfig.o
+ OBJS += ../src/ap/vlan.o
+@@ -225,6 +249,9 @@ endif
+ ifdef CONFIG_NO_CTRL_IFACE
+ CFLAGS += -DCONFIG_NO_CTRL_IFACE
+ else
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ ifeq ($(CONFIG_CTRL_IFACE), udp)
+ CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+ else
+@@ -332,6 +359,7 @@ ifdef CONFIG_FILS
+ CFLAGS += -DCONFIG_FILS
+ OBJS += ../src/ap/fils_hlp.o
+ NEED_SHA384=y
++NEED_HMAC_SHA384_KDF=y
+ NEED_AES_SIV=y
+ ifdef CONFIG_FILS_SK_PFS
+ CFLAGS += -DCONFIG_FILS_SK_PFS
+@@ -364,10 +392,14 @@ CFLAGS += -DCONFIG_MBO
+ OBJS += ../src/ap/mbo_ap.o
+ endif
+
++ifndef MULTICALL
++CFLAGS += -DNO_SUPPLICANT
++endif
++
+ include ../src/drivers/drivers.mak
+-OBJS += $(DRV_AP_OBJS)
+-CFLAGS += $(DRV_AP_CFLAGS)
+-LDFLAGS += $(DRV_AP_LDFLAGS)
++OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
++CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
++LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
+ LIBS += $(DRV_AP_LIBS)
+
+ ifdef CONFIG_L2_PACKET
+@@ -714,6 +746,7 @@ CFLAGS += -DCONFIG_TLSV12
+ endif
+
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ CONFIG_CRYPTO=wolfssl
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -734,6 +767,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ CONFIG_CRYPTO=openssl
+ ifdef TLS_FUNCS
+@@ -763,7 +797,39 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls
++ifndef CONFIG_DPP
++LIBS += -lmbedx509
++endif
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++ifdef CONFIG_DPP
++LIBS += -lmbedx509
++LIBS_h += -lmbedx509
++LIBS_n += -lmbedx509
++LIBS_s += -lmbedx509
++endif
++LIBS += -lmbedcrypto
++LIBS_h += -lmbedcrypto
++LIBS_n += -lmbedcrypto
++LIBS_s += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -794,6 +860,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -872,6 +939,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/crypto_internal-rsa.o
+@@ -942,9 +1010,11 @@ endif
+
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -954,38 +1024,48 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_UNWRAP
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_AES_DEC=y
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_DEC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_DEC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-dec.o
+@@ -1000,12 +1080,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1014,16 +1098,22 @@ endif
+ endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+
+ ifdef NEED_SHA1
+ OBJS += $(SHA1OBJS)
+@@ -1033,11 +1123,13 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+@@ -1076,56 +1168,81 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ OBJS += ../src/crypto/sha256-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
++CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ ifdef NEED_SHA384
+ CFLAGS += -DCONFIG_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ CFLAGS += -DCONFIG_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+
+ ifdef CONFIG_INTERNAL_SHA384
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+@@ -1170,11 +1287,13 @@ HOBJS += $(SHA1OBJS)
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+
+ ifdef CONFIG_RADIUS_SERVER
+ CFLAGS += -DRADIUS_SERVER
+@@ -1311,8 +1430,14 @@ install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
+
++hostapd_multi.a: $(BCHECK) $(OBJS)
++ $(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
++ @$(E) " CC " $<
++ @rm -f $@
++ @$(AR) cr $@ hostapd_multi.o $(OBJS)
++
+ hostapd: $(OBJS)
+- $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
++ +$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
+ @$(E) " LD " $@
+
+ ifdef CONFIG_WPA_TRACE
+@@ -1323,7 +1448,7 @@ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+
+ hostapd_cli: $(OBJS_c)
+- $(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
++ +$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
+ @$(E) " LD " $@
+
+ NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+@@ -1347,7 +1472,9 @@ NOBJS += ../src/utils/trace.o
+ endif
+
+ HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/aes-encblock.o
++endif
+ ifdef CONFIG_INTERNAL_AES
+ HOBJS += ../src/crypto/aes-internal.o
+ HOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1370,13 +1497,17 @@ SOBJS += ../src/common/sae.o
+ SOBJS += ../src/common/sae_pk.o
+ SOBJS += ../src/common/dragonfly.o
+ SOBJS += $(AESOBJS)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-prf.o
+ SOBJS += ../src/crypto/sha384-prf.o
+ SOBJS += ../src/crypto/sha512-prf.o
++endif
+ SOBJS += ../src/crypto/dh_groups.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-kdf.o
+ SOBJS += ../src/crypto/sha384-kdf.o
+ SOBJS += ../src/crypto/sha512-kdf.o
++endif
+
+ _OBJS_VAR := NOBJS
+ include ../src/objs.mk
+@@ -1385,6 +1516,12 @@ include ../src/objs.mk
+ _OBJS_VAR := SOBJS
+ include ../src/objs.mk
+
++dump_cflags:
++ @printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ nt_password_hash: $(NOBJS)
+ $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
+ @$(E) " LD " $@
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 261905368..0094db279 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1229,6 +1229,8 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
+ if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
+ conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
++ if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
++ conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
+ return 0;
+ }
+ #endif /* CONFIG_IEEE80211AC */
+@@ -1678,6 +1680,8 @@ static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
+ return 0;
+ }
+
++#endif /* CONFIG_INTERWORKING */
++
+
+ static int parse_qos_map_set(struct hostapd_bss_config *bss,
+ char *buf, int line)
+@@ -1719,8 +1723,6 @@ static int parse_qos_map_set(struct hostapd_bss_config *bss,
+ return 0;
+ }
+
+-#endif /* CONFIG_INTERWORKING */
+-
+
+ #ifdef CONFIG_HS20
+ static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
+@@ -2630,8 +2632,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ sizeof(conf->bss[0]->iface));
+ } else if (os_strcmp(buf, "bridge") == 0) {
+ os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
++ if (!bss->wds_bridge[0])
++ os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+ } else if (os_strcmp(buf, "bridge_hairpin") == 0) {
+ bss->bridge_hairpin = atoi(pos);
++ } else if (os_strcmp(buf, "snoop_iface") == 0) {
++ os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
+ } else if (os_strcmp(buf, "vlan_bridge") == 0) {
+ os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
+ } else if (os_strcmp(buf, "wds_bridge") == 0) {
+@@ -2998,6 +3004,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "iapp_interface") == 0) {
+ wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
+ #endif /* CONFIG_IAPP */
++ } else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
++ bss->dynamic_own_ip_addr = atoi(pos);
+ } else if (os_strcmp(buf, "own_ip_addr") == 0) {
+ if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+ wpa_printf(MSG_ERROR,
+@@ -3222,6 +3230,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ line, bss->max_num_sta, MAX_STA_COUNT);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
++ conf->max_num_sta = atoi(pos);
++ if (conf->max_num_sta < 0 ||
++ conf->max_num_sta > MAX_STA_COUNT) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
++ line, conf->max_num_sta, MAX_STA_COUNT);
++ return 1;
++ }
+ } else if (os_strcmp(buf, "wpa") == 0) {
+ bss->wpa = atoi(pos);
+ } else if (os_strcmp(buf, "extended_key_id") == 0) {
+@@ -3373,6 +3389,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ wpa_printf(MSG_INFO,
+ "Line %d: Obsolete peerkey parameter ignored", line);
+ #ifdef CONFIG_IEEE80211R_AP
++ } else if (os_strcmp(buf, "ft_iface") == 0) {
++ os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
+ } else if (os_strcmp(buf, "mobility_domain") == 0) {
+ if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+ hexstr2bin(pos, bss->mobility_domain,
+@@ -3742,6 +3760,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ #ifndef CONFIG_NO_VLAN
+ } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+ bss->ssid.dynamic_vlan = atoi(pos);
++ } else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
++ bss->ssid.vlan_no_bridge = atoi(pos);
+ } else if (os_strcmp(buf, "per_sta_vif") == 0) {
+ bss->ssid.per_sta_vif = atoi(pos);
+ } else if (os_strcmp(buf, "vlan_file") == 0) {
+@@ -3839,6 +3859,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ if (bss->ocv && !bss->ieee80211w)
+ bss->ieee80211w = 1;
+ #endif /* CONFIG_OCV */
++ } else if (os_strcmp(buf, "noscan") == 0) {
++ conf->noscan = atoi(pos);
++ } else if (os_strcmp(buf, "ht_coex") == 0) {
++ conf->no_ht_coex = !atoi(pos);
+ } else if (os_strcmp(buf, "ieee80211n") == 0) {
+ conf->ieee80211n = atoi(pos);
+ } else if (os_strcmp(buf, "ht_capab") == 0) {
+@@ -3887,6 +3911,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "he_bss_color") == 0) {
+ conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ conf->he_op.he_bss_color_disabled = 0;
++ if (atoi(pos) > 63)
++ conf->he_op.he_bss_color = os_random() % 63 + 1;
+ } else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
+ conf->he_op.he_bss_color_partial = atoi(pos);
+ } else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
+@@ -4520,10 +4546,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->gas_frag_limit = val;
+ } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+ bss->gas_comeback_delay = atoi(pos);
++#endif /* CONFIG_INTERWORKING */
+ } else if (os_strcmp(buf, "qos_map_set") == 0) {
+ if (parse_qos_map_set(bss, pos, line) < 0)
+ return 1;
+-#endif /* CONFIG_INTERWORKING */
+ #ifdef CONFIG_RADIUS_TEST
+ } else if (os_strcmp(buf, "dump_msk_file") == 0) {
+ os_free(bss->dump_msk_file);
+@@ -5347,7 +5373,12 @@ struct hostapd_config * hostapd_config_read(const char *fname)
+ int errors = 0;
+ size_t i;
+
+- f = fopen(fname, "r");
++ if (!strncmp(fname, "data:", 5)) {
++ f = fmemopen((void *)(fname + 5), strlen(fname + 5), "r");
++ fname = "<inline>";
++ } else {
++ f = fopen(fname, "r");
++ }
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+ "for reading.", fname);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 10cb186f1..f76226cf4 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3876,6 +3876,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_size);
+ } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
+ reply_len = hostapd_drv_status(hapd, reply, reply_size);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strcmp(buf, "MIB") == 0) {
+ reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
+ if (reply_len >= 0) {
+@@ -3917,6 +3918,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
+ reply_size);
++#endif
+ } else if (os_strcmp(buf, "ATTACH") == 0) {
+ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
+ reply_len = -1;
+@@ -5464,6 +5466,7 @@ try_again:
+ return -1;
+ }
+
++ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
+ return 0;
+@@ -5565,6 +5568,7 @@ fail:
+ os_free(fname);
+
+ interface->global_ctrl_sock = s;
++ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+ interface, NULL);
+
+diff --git a/hostapd/defconfig b/hostapd/defconfig
+index 66bf894eb..f716553bb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -6,9 +6,21 @@
+ # just setting VARIABLE=n is not disabling that variable.
+ #
+ # This file is included in Makefile, so variables like CFLAGS and LIBS can also
+-# be modified from here. In most cass, these lines should use += in order not
++# be modified from here. In most cases, these lines should use += in order not
+ # to override previous values of the variables.
+
++
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
++#CFLAGS += -I/usr/local/openssl/include
++#LIBS += -L/usr/local/openssl/lib
++
++# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
++# the kerberos files are not in the default include path. Following line can be
++# used to fix build issues on such systems (krb5.h not found).
++#CFLAGS += -I/usr/include/kerberos
++
++
+ # Driver interface for Host AP driver
+ CONFIG_DRIVER_HOSTAP=y
+
+@@ -281,6 +293,7 @@ CONFIG_IPV6=y
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index a9d326de8..a469b1f4d 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -401,7 +401,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
+-#ifdef CONFIG_TAXONOMY
+ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+@@ -414,7 +413,6 @@ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
+ return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_TAXONOMY */
+
+
+ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+@@ -431,7 +429,6 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
+-#ifdef CONFIG_WPS
+ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+@@ -657,7 +654,6 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ ssid_hex, argv[1]);
+ return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_WPS */
+
+
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+@@ -757,7 +753,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+ }
+
+ buf[len] = '\0';
+- if (memcmp(buf, "FAIL", 4) == 0)
++ if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
+ return -1;
+ if (print)
+ printf("%s", buf);
+@@ -1670,13 +1666,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ { "disassociate", hostapd_cli_cmd_disassociate,
+ hostapd_complete_stations,
+ "<addr> = disassociate a station" },
+-#ifdef CONFIG_TAXONOMY
+ { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
+ "<addr> = get taxonomy signature for a station" },
+-#endif /* CONFIG_TAXONOMY */
+ { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
+ "<addr> = send SA Query to a station" },
+-#ifdef CONFIG_WPS
+ { "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
+ "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
+ { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
+@@ -1701,7 +1694,6 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<SSID> <auth> <encr> <key> = configure AP" },
+ { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
+ "= show current WPS status" },
+-#endif /* CONFIG_WPS */
+ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
+ "= send Disassociation Imminent notification" },
+ { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
+diff --git a/hostapd/main.c b/hostapd/main.c
+index 524a10274..0ccd4a5d7 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -31,7 +31,7 @@
+ #include "config_file.h"
+ #include "eap_register.h"
+ #include "ctrl_iface.h"
+-
++#include "build_features.h"
+
+ struct hapd_global {
+ void **drv_priv;
+@@ -40,6 +40,7 @@ struct hapd_global {
+
+ static struct hapd_global global;
+
++extern int radius_main(int argc, char **argv);
+
+ #ifndef CONFIG_NO_HOSTAPD_LOGGER
+ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+@@ -690,6 +691,11 @@ fail:
+ return -1;
+ }
+
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ #ifdef CONFIG_WPS
+ static int gen_uuid(const char *txt_addr)
+@@ -781,6 +787,11 @@ int main(int argc, char *argv[])
+ if (os_program_init())
+ return -1;
+
++#ifdef RADIUS_SERVER
++ if (strstr(argv[0], "radius"))
++ return radius_main(argc, argv);
++#endif
++
+ os_memset(&interfaces, 0, sizeof(interfaces));
+ interfaces.reload_config = hostapd_reload_config;
+ interfaces.config_read_cb = hostapd_config_read;
+@@ -806,8 +817,10 @@ int main(int argc, char *argv[])
+ return -1;
+ #endif /* CONFIG_DPP */
+
++ wpa_supplicant_event = hostapd_wpa_event;
++ wpa_supplicant_event_global = hostapd_wpa_event_global;
+ for (;;) {
+- c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
++ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
+ if (c < 0)
+ break;
+ switch (c) {
+@@ -844,6 +857,8 @@ int main(int argc, char *argv[])
+ break;
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+ case 'v':
++ if (optarg)
++ exit(!has_feature(optarg));
+ show_version();
+ exit(1);
+ case 'g':
+@@ -1013,6 +1028,7 @@ int main(int argc, char *argv[])
+ }
+
+ hostapd_global_ctrl_iface_init(&interfaces);
++ hostapd_ucode_init(&interfaces);
+
+ if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
+ wpa_printf(MSG_ERROR, "Failed to start eloop");
+@@ -1022,6 +1038,7 @@ int main(int argc, char *argv[])
+ ret = 0;
+
+ out:
++ hostapd_ucode_free();
+ hostapd_global_ctrl_iface_deinit(&interfaces);
+ /* Deinitialize all interfaces */
+ for (i = 0; i < interfaces.count; i++) {
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 28b0ba71c..4c4c750ab 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -467,17 +467,17 @@ static int acs_get_bw_center_chan(int freq, enum bw_type bw)
+ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ {
+ if (!(survey->filled & SURVEY_HAS_NF)) {
++ survey->nf = -95;
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing noise floor",
+ survey->freq);
+- return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
++ survey->channel_time = 0;
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing channel time",
+ survey->freq);
+- return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+@@ -485,7 +485,6 @@ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+ survey->freq);
+- return 0;
+ }
+
+ return 1;
+diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
+index 68443115f..26f11ad98 100644
+--- a/src/ap/airtime_policy.c
++++ b/src/ap/airtime_policy.c
+@@ -112,8 +112,14 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+ {
+ struct sta_info *sta;
+
+- for (sta = hapd->sta_list; sta; sta = sta->next)
+- sta_set_airtime_weight(hapd, sta, weight);
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ unsigned int sta_weight = weight;
++
++ if (sta->dyn_airtime_weight)
++ sta_weight = (weight * sta->dyn_airtime_weight) / 256;
++
++ sta_set_airtime_weight(hapd, sta, sta_weight);
++ }
+ }
+
+
+@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ unsigned int weight;
+
+ if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+- weight = get_weight_for_sta(hapd, sta->addr);
++ if (sta->dyn_airtime_weight)
++ weight = sta->dyn_airtime_weight;
++ else
++ weight = get_weight_for_sta(hapd, sta->addr);
+ if (weight)
+ return sta_set_airtime_weight(hapd, sta, weight);
+ }
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 2330163c4..d10b00be9 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -121,6 +121,7 @@ struct hostapd_ssid {
+ #define DYNAMIC_VLAN_OPTIONAL 1
+ #define DYNAMIC_VLAN_REQUIRED 2
+ int dynamic_vlan;
++ int vlan_no_bridge;
+ #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+ #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+ #define DYNAMIC_VLAN_NAMING_END 2
+@@ -282,6 +283,8 @@ struct airtime_sta_weight {
+ struct hostapd_bss_config {
+ char iface[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
++ char ft_iface[IFNAMSIZ + 1];
++ char snoop_iface[IFNAMSIZ + 1];
+ char vlan_bridge[IFNAMSIZ + 1];
+ char wds_bridge[IFNAMSIZ + 1];
+ int bridge_hairpin; /* hairpin_mode on bridge members */
+@@ -307,6 +310,7 @@ struct hostapd_bss_config {
+ unsigned int eap_sim_db_timeout;
+ int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+ struct hostapd_ip_addr own_ip_addr;
++ int dynamic_own_ip_addr;
+ char *nas_identifier;
+ struct hostapd_radius_servers *radius;
+ int acct_interim_interval;
+@@ -1064,6 +1068,8 @@ struct hostapd_config {
+ unsigned int track_sta_max_num;
+ unsigned int track_sta_max_age;
+
++ int max_num_sta;
++
+ char country[3]; /* first two octets: country code as described in
+ * ISO/IEC 3166-1. Third octet:
+ * ' ' (ascii 32): all environments
+@@ -1101,6 +1107,8 @@ struct hostapd_config {
+
+ int ht_op_mode_fixed;
+ u16 ht_capab;
++ int noscan;
++ int no_ht_coex;
+ int ieee80211n;
+ int secondary_channel;
+ int no_pri_sec_switch;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 32722084d..527b2c984 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -387,8 +387,6 @@ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ return -1;
+ if (hapd->conf->wds_bridge[0])
+ bridge = hapd->conf->wds_bridge;
+- else if (hapd->conf->bridge[0])
+- bridge = hapd->conf->bridge;
+ return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ bridge, ifname_wds);
+ }
+@@ -1031,7 +1029,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+ {
+- if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
++ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
++ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
+ return 0;
+ return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+ qos_map_set_len);
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index d7e79c840..f8a8725be 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -371,12 +371,12 @@ static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+
+ static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+ enum drv_br_net_param param,
+- unsigned int val)
++ const char *ifname, unsigned int val)
+ {
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_set_net_param == NULL)
+ return -1;
+- return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
++ return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
+ }
+
+ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+@@ -404,6 +404,23 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+ return hapd->driver->stop_ap(hapd->drv_priv, link_id);
+ }
+
++static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
++ enum wpa_driver_if_type type,
++ const char *ifname,
++ const char *new_name)
++{
++ if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
++ return -1;
++ return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
++}
++
++static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
++ return 0;
++ return hapd->driver->set_first_bss(hapd->drv_priv);
++}
++
+ static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+ struct wpa_channel_info *ci)
+ {
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 4354dfae3..26453cb2c 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -1343,6 +1343,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ int mld_id;
+ u16 links;
+ #endif /* CONFIG_IEEE80211BE */
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_PROBE_REQ,
++ .mgmt_frame = mgmt,
++ .ssi_signal = ssi_signal,
++ .elems = &elems,
++ };
+
+ if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
+ ssi_signal < hapd->iconf->rssi_ignore_probe_request)
+@@ -1529,6 +1535,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ }
+ #endif /* CONFIG_P2P */
+
++ if (hostapd_ubus_handle_event(hapd, &req)) {
++ wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
++ MAC2STR(mgmt->sa));
++ return;
++ }
++
+ /* TODO: verify that supp_rates contains at least one matching rate
+ * with AP configuration */
+
+@@ -1547,7 +1559,7 @@ void handle_probe_req(struct hostapd_data *hapd,
+ if (hapd->conf->no_probe_resp_if_max_sta &&
+ is_multicast_ether_addr(mgmt->da) &&
+ is_multicast_ether_addr(mgmt->bssid) &&
+- hapd->num_sta >= hapd->conf->max_num_sta &&
++ hostapd_check_max_sta(hapd) &&
+ !ap_get_sta(hapd, mgmt->sa)) {
+ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ " since no room for additional STA",
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 2cfef4bd4..cd7db4fc6 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -26,6 +26,26 @@
+ #include "taxonomy.h"
+ #include "wnm_ap.h"
+
++static const char * hw_mode_str(enum hostapd_hw_mode mode)
++{
++ switch (mode) {
++ case HOSTAPD_MODE_IEEE80211B:
++ return "b";
++ case HOSTAPD_MODE_IEEE80211G:
++ return "g";
++ case HOSTAPD_MODE_IEEE80211A:
++ return "a";
++ case HOSTAPD_MODE_IEEE80211AD:
++ return "ad";
++ case HOSTAPD_MODE_IEEE80211ANY:
++ return "any";
++ case NUM_HOSTAPD_MODES:
++ return "invalid";
++ }
++ return "unknown";
++}
++
++#ifdef CONFIG_CTRL_IFACE_MIB
+
+ static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+ size_t curr_len, const u8 *mcs_set)
+@@ -212,26 +232,6 @@ static const char * timeout_next_str(int val)
+ }
+
+
+-static const char * hw_mode_str(enum hostapd_hw_mode mode)
+-{
+- switch (mode) {
+- case HOSTAPD_MODE_IEEE80211B:
+- return "b";
+- case HOSTAPD_MODE_IEEE80211G:
+- return "g";
+- case HOSTAPD_MODE_IEEE80211A:
+- return "a";
+- case HOSTAPD_MODE_IEEE80211AD:
+- return "ad";
+- case HOSTAPD_MODE_IEEE80211ANY:
+- return "any";
+- case NUM_HOSTAPD_MODES:
+- return "invalid";
+- }
+- return "unknown";
+-}
+-
+-
+ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+@@ -539,6 +539,7 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+ return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+ }
+
++#endif
+
+ #ifdef CONFIG_P2P_MANAGER
+ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+@@ -987,12 +988,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ return len;
+ len += ret;
+ }
+-
++#ifdef CONFIG_CTRL_IFACE_MIB
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ mode->mcs_set);
+ }
+-
++#endif /* CONFIG_CTRL_IFACE_MIB */
+ if (iface->current_rates && iface->num_rates) {
+ ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+ if (os_snprintf_error(buflen - len, ret))
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index fc2e8d83c..d14fad136 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -17,6 +17,7 @@
+ #include "ap_drv_ops.h"
+ #include "drivers/driver.h"
+ #include "dfs.h"
++#include "crypto/crypto.h"
+
+
+ enum dfs_channel_type {
+@@ -526,9 +527,14 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ int num_available_chandefs;
+ int chan_idx, chan_idx2;
+ int sec_chan_idx_80p80 = -1;
++ bool is_mesh = false;
+ int i;
+ u32 _rand;
+
++#ifdef CONFIG_MESH
++ is_mesh = iface->mconf;
++#endif
++
+ wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+ *secondary_channel = 0;
+ *oper_centr_freq_seg0_idx = 0;
+@@ -548,8 +554,20 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ if (num_available_chandefs == 0)
+ return NULL;
+
+- if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
++ /* try to use deterministic channel in mesh, so that both sides
++ * have a chance to switch to the same channel */
++ if (is_mesh) {
++#ifdef CONFIG_MESH
++ u64 hash[4];
++ const u8 *meshid[1] = { &iface->mconf->meshid[0] };
++ const size_t meshid_len = iface->mconf->meshid_len;
++
++ sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
++ _rand = hash[0] + hash[1] + hash[2] + hash[3];
++#endif
++ } else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ return NULL;
++
+ chan_idx = _rand % num_available_chandefs;
+ wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ chan_idx, num_available_chandefs);
+@@ -1207,6 +1225,8 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
++ hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
++
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index dc21977ff..e8796f709 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -268,6 +268,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ struct hostapd_iface *iface = hapd->iface;
+ #endif /* CONFIG_OWE */
+ bool updated = false;
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_ASSOC_REQ,
++ .addr = addr,
++ };
+
+ if (addr == NULL) {
+ /*
+@@ -412,6 +416,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ goto fail;
+ }
+
++ if (hostapd_ubus_handle_event(hapd, &req)) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++ MAC2STR(req.addr));
++ goto fail;
++ }
++
+ #ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+@@ -2342,8 +2352,8 @@ static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+ }
+
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data)
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data)
+ {
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+@@ -2675,7 +2685,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+
+
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+ {
+ struct hapd_interfaces *interfaces = ctx;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index b899c9831..7959859b0 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -247,6 +247,29 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+ return 0;
+ }
+
++static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
++{
++ int num_sta = 0;
++ int i;
++
++ for (i = 0; i < iface->num_bss; i++)
++ num_sta += iface->bss[i]->num_sta;
++
++ return num_sta;
++}
++
++
++int hostapd_check_max_sta(struct hostapd_data *hapd)
++{
++ if (hapd->num_sta >= hapd->conf->max_num_sta)
++ return 1;
++
++ if (hapd->iconf->max_num_sta &&
++ hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
++ return 1;
++
++ return 0;
++}
+
+ int hostapd_reload_config(struct hostapd_iface *iface)
+ {
+@@ -255,6 +278,8 @@ int hostapd_reload_config(struct hostapd_iface *iface)
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+
++ hostapd_ucode_reload_bss(hapd);
++
+ if (iface->config_fname == NULL) {
+ /* Only in-memory config in use - assume it has been updated */
+ hostapd_clear_old(iface);
+@@ -475,6 +500,8 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ hapd->beacon_set_done = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
++ hostapd_ucode_free_bss(hapd);
++ hostapd_ubus_free_bss(hapd);
+ accounting_deinit(hapd);
+ hostapd_deinit_wpa(hapd);
+ vlan_deinit(hapd);
+@@ -685,6 +712,7 @@ static void sta_track_deinit(struct hostapd_iface *iface)
+ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+ {
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
++ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ #ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+ #endif /* NEED_AP_MLME */
+@@ -714,7 +742,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+ {
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+- eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
++ hostapd_ucode_free_iface(iface);
+ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ NULL);
+
+@@ -1303,6 +1331,9 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ if (hapd->driver && hapd->driver->set_operstate)
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
++ hostapd_ubus_add_bss(hapd);
++ hostapd_ucode_add_bss(hapd);
++
+ return 0;
+ }
+
+@@ -1324,8 +1355,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+-static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+- bool start_beacon)
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ {
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 ssid[SSID_MAX_LEN + 1];
+@@ -1552,6 +1582,7 @@ setup_mld:
+
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = conf->radius_das_port;
++ das_conf.nas_identifier = conf->nas_identifier;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ conf->radius_das_shared_secret_len;
+@@ -1627,6 +1658,7 @@ setup_mld:
+ wpa_printf(MSG_ERROR, "GAS server initialization failed");
+ return -1;
+ }
++#endif /* CONFIG_INTERWORKING */
+
+ if (conf->qos_map_set_len &&
+ hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
+@@ -1634,7 +1666,6 @@ setup_mld:
+ wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
+ return -1;
+ }
+-#endif /* CONFIG_INTERWORKING */
+
+ if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+ wpa_printf(MSG_ERROR, "BSS Load initialization failed");
+@@ -2447,6 +2478,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ if (err)
+ goto fail;
+
++ hostapd_ubus_add_iface(iface);
+ wpa_printf(MSG_DEBUG, "Completing interface initialization");
+ if (iface->freq) {
+ #ifdef NEED_AP_MLME
+@@ -2676,6 +2708,7 @@ dfs_offload:
+
+ fail:
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
++ hostapd_ubus_free_iface(iface);
+
+ if (iface->is_no_ir) {
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+@@ -2875,7 +2908,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ }
+
+
+-static void hostapd_bss_deinit(struct hostapd_data *hapd)
++void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
+ if (!hapd)
+ return;
+@@ -3395,6 +3428,7 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+ (unsigned int) iface->conf->num_bss);
+ driver = iface->bss[0]->driver;
+ drv_priv = iface->bss[0]->drv_priv;
++ hostapd_ubus_free_iface(iface);
+ hostapd_interface_deinit(iface);
+ wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ __func__, driver, drv_priv);
+@@ -3926,7 +3960,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ hapd_iface = interfaces->iface[i];
+ if (hapd_iface == NULL)
+ return -1;
+- if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
++ if (!os_strcmp(hapd_iface->phy, buf) ||
++ !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
+ wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+@@ -3972,6 +4007,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc)
+ {
++ int mld_assoc_link_id = -1;
++
+ if (hapd->tkip_countermeasures) {
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+@@ -3983,6 +4020,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ sta->mld_assoc_link_id != hapd->mld_link_id)
+ return;
+ #endif /* CONFIG_IEEE80211BE */
++ if (mld_assoc_link_id != -2)
++ hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
+
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ sta->post_csa_sa_query = 0;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 594866fbb..1e4113459 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -13,11 +13,14 @@
+ #include <sqlite3.h>
+ #endif /* CONFIG_SQLITE */
+
++#include "ap/sta_info.h"
+ #include "common/defs.h"
+ #include "common/dpp.h"
+ #include "utils/list.h"
+ #include "ap_config.h"
+ #include "drivers/driver.h"
++#include "ubus.h"
++#include "ucode.h"
+
+ #define OCE_STA_CFON_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_STA_CFON) && \
+@@ -51,6 +54,10 @@ struct hapd_interfaces {
+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
++ int (*ctrl_iface_recv)(struct hostapd_data *hapd,
++ char *buf, char *reply, int reply_size,
++ struct sockaddr_storage *from,
++ socklen_t fromlen);
+ int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+@@ -167,6 +174,21 @@ struct hostapd_sae_commit_queue {
+ u8 msg[];
+ };
+
++/**
++ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
++ */
++struct hostapd_openwrt_stats {
++ struct {
++ u64 neighbor_report_tx;
++ } rrm;
++
++ struct {
++ u64 bss_transition_query_rx;
++ u64 bss_transition_request_tx;
++ u64 bss_transition_response_rx;
++ } wnm;
++};
++
+ /**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+@@ -174,6 +196,8 @@ struct hostapd_data {
+ struct hostapd_iface *iface;
+ struct hostapd_config *iconf;
+ struct hostapd_bss_config *conf;
++ struct hostapd_ubus_bss ubus;
++ struct hostapd_ucode_bss ucode;
+ int interface_added; /* virtual interface added for this BSS */
+ unsigned int started:1;
+ unsigned int disabled:1;
+@@ -181,6 +205,9 @@ struct hostapd_data {
+
+ u8 own_addr[ETH_ALEN];
+
++ /* OpenWrt specific statistics */
++ struct hostapd_openwrt_stats openwrt_stats;
++
+ int num_sta; /* number of entries in sta_list */
+ struct sta_info *sta_list; /* STA info list head */
+ #define STA_HASH_SIZE 256
+@@ -523,6 +550,7 @@ struct hostapd_mld {
+ */
+ struct hostapd_iface {
+ struct hapd_interfaces *interfaces;
++ struct hostapd_ucode_iface ucode;
+ void *owner;
+ char *config_fname;
+ struct hostapd_config *conf;
+@@ -745,6 +773,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_bss_config *bss);
+ int hostapd_setup_interface(struct hostapd_iface *iface);
+ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
++void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
+ void hostapd_interface_deinit(struct hostapd_iface *iface);
+ void hostapd_interface_free(struct hostapd_iface *iface);
+ struct hostapd_iface * hostapd_alloc_iface(void);
+@@ -753,6 +782,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ struct hostapd_iface *
+ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug);
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
++void hostapd_bss_deinit(struct hostapd_data *hapd);
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+@@ -780,6 +811,7 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+ void hostapd_periodic_iface(struct hostapd_iface *iface);
+ int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
+ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
++int hostapd_check_max_sta(struct hostapd_data *hapd);
+
+ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+ void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+@@ -865,4 +897,14 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+
+ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
+
++static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++#ifdef CONFIG_IEEE80211BE
++ return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
++#else /* CONFIG_IEEE80211BE */
++ return false;
++#endif /* CONFIG_IEEE80211BE */
++}
++
+ #endif /* HOSTAPD_H */
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 222f3dc05..672e43a10 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -546,7 +546,8 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+ int ret;
+
+ /* Check that HT40 is used and PRI / SEC switch is allowed */
+- if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
++ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
++ iface->conf->noscan)
+ return 0;
+
+ hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 179af5e28..bda61b998 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -2804,7 +2804,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ u16 auth_alg, auth_transaction, status_code;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct sta_info *sta = NULL;
+- int res, reply_res;
++ int res, reply_res, ubus_resp;
+ u16 fc;
+ const u8 *challenge = NULL;
+ u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+@@ -2815,6 +2815,11 @@ static void handle_auth(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ bool mld_sta = false;
+ #endif /* CONFIG_IEEE80211BE */
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_AUTH_REQ,
++ .mgmt_frame = mgmt,
++ .ssi_signal = rssi,
++ };
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+@@ -3008,6 +3013,13 @@ static void handle_auth(struct hostapd_data *hapd,
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
++ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++ if (ubus_resp) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
++ MAC2STR(mgmt->sa));
++ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
+ if (res == HOSTAPD_ACL_PENDING)
+ return;
+
+@@ -3042,15 +3054,6 @@ static void handle_auth(struct hostapd_data *hapd,
+ seq_ctrl);
+ return;
+ }
+-#ifdef CONFIG_MESH
+- if ((hapd->conf->mesh & MESH_ENABLED) &&
+- sta->plink_state == PLINK_BLOCKED) {
+- wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+- " is blocked - drop Authentication frame",
+- MAC2STR(sa));
+- return;
+- }
+-#endif /* CONFIG_MESH */
+ #ifdef CONFIG_PASN
+ if (auth_alg == WLAN_AUTH_PASN &&
+ (sta->flags & WLAN_STA_ASSOC)) {
+@@ -4698,6 +4701,13 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ * drivers to accept the STA parameter configuration. Since this is
+ * after a new FT-over-DS exchange, a new TK has been derived, so key
+ * reinstallation is not a concern for this case.
++ *
++ * If the STA was associated and authorized earlier, but came for a new
++ * connection (!added_unassoc + !reassoc), remove the existing STA entry
++ * so that it can be re-added. This case is rarely seen when the AP could
++ * not receive the deauth/disassoc frame from the STA. And the STA comes
++ * back with new connection within a short period or before the inactive
++ * STA entry is removed from the list.
+ */
+ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+ " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+@@ -4711,7 +4721,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ (!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
+ (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+- !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
++ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
++ (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
+ set = 0;
+@@ -5273,7 +5284,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ int resp = WLAN_STATUS_SUCCESS;
+ u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ const u8 *pos;
+- int left, i;
++ int left, i, ubus_resp;
+ struct sta_info *sta;
+ u8 *tmp = NULL;
+ #ifdef CONFIG_FILS
+@@ -5515,6 +5526,11 @@ static void handle_assoc(struct hostapd_data *hapd,
+ left = res;
+ }
+ #endif /* CONFIG_FILS */
++ struct hostapd_ubus_request req = {
++ .type = HOSTAPD_UBUS_ASSOC_REQ,
++ .mgmt_frame = mgmt,
++ .ssi_signal = rssi,
++ };
+
+ /* followed by SSID and Supported rates; and HT capabilities if 802.11n
+ * is used */
+@@ -5617,6 +5633,13 @@ static void handle_assoc(struct hostapd_data *hapd,
+ if (set_beacon)
+ ieee802_11_set_beacons(hapd->iface);
+
++ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++ if (ubus_resp) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++ MAC2STR(mgmt->sa));
++ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
+ fail:
+
+ /*
+@@ -5848,6 +5871,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
+ (unsigned long) len);
+ return;
+ }
++ hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+@@ -5879,6 +5903,8 @@ static void handle_deauth(struct hostapd_data *hapd,
+ /* Clear the PTKSA cache entries for PASN */
+ ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
+
++ hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
++
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
+index f90f1254e..7f0a00f95 100644
+--- a/src/ap/ieee802_11_ht.c
++++ b/src/ap/ieee802_11_ht.c
+@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ struct ieee80211_ht_operation *oper;
++ le32 vht_capabilities_info;
+ u8 *pos = eid;
++ u8 chwidth;
+
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+ is_6ghz_op_class(hapd->iconf->op_class))
+@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+
++ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
++ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
++ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++ oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
++ }
++
+ pos += sizeof(*oper);
+
+ return pos;
+@@ -230,6 +239,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ return;
+ }
+
++ if (iface->conf->noscan || iface->conf->no_ht_coex)
++ return;
++
+ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore too short 20/40 BSS Coexistence Management frame");
+@@ -390,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return;
+
++ if (iface->conf->noscan || iface->conf->no_ht_coex)
++ return;
++
+ wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+ " in Association Request", MAC2STR(sta->addr));
+
+diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
+index a5716f037..85790c7ed 100644
+--- a/src/ap/ieee802_11_shared.c
++++ b/src/ap/ieee802_11_shared.c
+@@ -1138,13 +1138,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
+ u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+ {
+-#ifdef CONFIG_INTERWORKING
+ /* check for QoS Map support */
+ if (ext_capab_ie_len >= 5) {
+ if (ext_capab_ie[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+-#endif /* CONFIG_INTERWORKING */
+
+ if (ext_capab_ie_len > 0) {
+ sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
+index 4dc325ce8..68880ab64 100644
+--- a/src/ap/ieee802_11_vht.c
++++ b/src/ap/ieee802_11_vht.c
+@@ -26,6 +26,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ struct ieee80211_vht_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ u8 *pos = eid;
++ u8 chwidth;
+
+ if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+@@ -63,6 +64,17 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ }
+
++ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++ if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++ cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
++ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
++ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
++ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
++ } else {
++ cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
++ }
++
+ /* Supported MCS set comes from hw */
+ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+
+@@ -75,6 +87,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ struct ieee80211_vht_operation *oper;
++ le32 vht_capabilities_info;
+ u8 *pos = eid;
+ enum oper_chan_width oper_chwidth =
+ hostapd_get_oper_chwidth(hapd->iconf);
+@@ -110,6 +123,7 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
+
+ oper->vht_op_info_chwidth = oper_chwidth;
++ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
+ /*
+ * Convert 160 MHz channel width to new style as interop
+@@ -123,6 +137,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
+ else
+ oper->vht_op_info_chan_center_freq_seg0_idx += 8;
++
++ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++ oper->vht_op_info_chan_center_freq_seg1_idx = 0;
+ } else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
+ /*
+ * Convert 80+80 MHz channel width to new style as interop
+diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
+index 8e98b6521..8abebbf34 100644
+--- a/src/ap/ieee802_1x.c
++++ b/src/ap/ieee802_1x.c
+@@ -600,6 +600,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *attr;
+ int len;
+
++ if (hapd->conf->dynamic_own_ip_addr)
++ radius_client_get_local_addr(hapd->radius,
++ &hapd->conf->own_ip_addr);
++
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IP_ADDRESS) &&
+ hapd->conf->own_ip_addr.af == AF_INET &&
+@@ -2845,6 +2849,7 @@ static const char * bool_txt(bool val)
+ return val ? "TRUE" : "FALSE";
+ }
+
++#ifdef CONFIG_CTRL_IFACE_MIB
+
+ int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+ {
+@@ -3031,6 +3036,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ return len;
+ }
+
++#endif
+
+ #ifdef CONFIG_HS20
+ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
+diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
+index 788c12fdc..bc1eb6251 100644
+--- a/src/ap/ndisc_snoop.c
++++ b/src/ap/ndisc_snoop.c
+@@ -61,6 +61,7 @@ void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+ dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+ list) {
+ hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
++ dl_list_del(&ip6addr->list);
+ os_free(ip6addr);
+ }
+ }
+diff --git a/src/ap/rrm.c b/src/ap/rrm.c
+index f2d5cd16e..8220590a0 100644
+--- a/src/ap/rrm.c
++++ b/src/ap/rrm.c
+@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+ MAC2STR(addr), token, rep_mode, report);
++ if (len < sizeof(struct rrm_measurement_beacon_report))
++ return;
++ hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
+ }
+
+
+@@ -269,6 +272,8 @@ static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+ }
+ }
+
++ hapd->openwrt_stats.rrm.neighbor_report_tx++;
++
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+@@ -350,6 +355,9 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+ mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+
+ switch (mgmt->u.action.u.rrm.action) {
++ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
++ hostapd_ubus_handle_link_measurement(hapd, buf, len);
++ break;
+ case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+ hostapd_handle_radio_msmt_report(hapd, buf, len);
+ break;
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index d483aa9d3..ee6e20538 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -539,6 +539,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "local deauth request");
++ hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
+ ap_free_sta(hapd, sta);
+ return;
+ }
+@@ -694,6 +695,7 @@ skip_poll:
+ mlme_deauthenticate_indication(
+ hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
++ hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
+ ap_free_sta(hapd, sta);
+ break;
+ }
+@@ -1476,9 +1478,6 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ mld_assoc_link_id = -2;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+- if (mld_assoc_link_id != -2)
+- hostapd_prune_associations(hapd, sta->addr,
+- mld_assoc_link_id);
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ } else {
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+@@ -1515,15 +1514,28 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+
+ if (authorized) {
++ static const char * const auth_algs[] = {
++ [WLAN_AUTH_OPEN] = "open",
++ [WLAN_AUTH_SHARED_KEY] = "shared",
++ [WLAN_AUTH_FT] = "ft",
++ [WLAN_AUTH_SAE] = "sae",
++ [WLAN_AUTH_FILS_SK] = "fils-sk",
++ [WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
++ [WLAN_AUTH_FILS_PK] = "fils-pk",
++ [WLAN_AUTH_PASN] = "pasn",
++ };
++ const char *auth_alg = NULL;
+ const u8 *dpp_pkhash;
+ const char *keyid;
+ char dpp_pkhash_buf[100];
+ char keyid_buf[100];
+ char ip_addr[100];
++ char alg_buf[100];
+
+ dpp_pkhash_buf[0] = '\0';
+ keyid_buf[0] = '\0';
+ ip_addr[0] = '\0';
++ alg_buf[0] = '\0';
+ #ifdef CONFIG_P2P
+ if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+ os_snprintf(ip_addr, sizeof(ip_addr),
+@@ -1534,6 +1546,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ }
+ #endif /* CONFIG_P2P */
+
++ if (sta->auth_alg < ARRAY_SIZE(auth_algs))
++ auth_alg = auth_algs[sta->auth_alg];
++
++ if (auth_alg)
++ os_snprintf(alg_buf, sizeof(alg_buf),
++ " auth_alg=%s", auth_alg);
++
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ os_snprintf(keyid_buf, sizeof(keyid_buf),
+@@ -1552,17 +1571,19 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ dpp_pkhash, SHA256_MAC_LEN);
+ }
+
+- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+- buf, ip_addr, keyid_buf, dpp_pkhash_buf);
++ hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
++ buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
+
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+- AP_STA_CONNECTED "%s%s%s%s",
++ AP_STA_CONNECTED "%s%s%s%s%s",
+ buf, ip_addr, keyid_buf,
+- dpp_pkhash_buf);
++ dpp_pkhash_buf, alg_buf);
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
++ hostapd_ubus_notify(hapd, "disassoc", sta->addr);
+
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 153e4a000..38b80903d 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -17,7 +17,6 @@
+ #include "common/sae.h"
+ #include "crypto/sha384.h"
+ #include "pasn/pasn_common.h"
+-#include "hostapd.h"
+
+ /* STA flags */
+ #define WLAN_STA_AUTH BIT(0)
+@@ -323,6 +322,7 @@ struct sta_info {
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #ifdef CONFIG_AIRTIME_POLICY
+ unsigned int airtime_weight;
++ unsigned int dyn_airtime_weight;
+ struct os_reltime backlogged_until;
+ #endif /* CONFIG_AIRTIME_POLICY */
+
+@@ -420,16 +420,6 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
+
+ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+
+-static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
+- struct sta_info *sta)
+-{
+-#ifdef CONFIG_IEEE80211BE
+- return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
+-#else /* CONFIG_IEEE80211BE */
+- return false;
+-#endif /* CONFIG_IEEE80211BE */
+-}
+-
+ static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
+ {
+ #ifdef CONFIG_IEEE80211BE
+diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
+index 19aa3c649..053d6338e 100644
+--- a/src/ap/vlan_full.c
++++ b/src/ap/vlan_full.c
+@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ if (!vlan)
+ return;
+
++ if (hapd->conf->ssid.vlan_no_bridge)
++ goto out;
++
+ vlan->configured = 1;
+
+ notempty = vlan->vlan_desc.notempty;
+@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ ifname, br_name, tagged[i], hapd);
+ }
+
++out:
+ ifconfig_up(ifname);
+ }
+
+diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
+index 53eacfb45..b69f3de41 100644
+--- a/src/ap/vlan_init.c
++++ b/src/ap/vlan_init.c
+@@ -22,6 +22,7 @@
+ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ int existsok)
+ {
++ bool vlan_exists = iface_exists(vlan->ifname);
+ int ret;
+ #ifdef CONFIG_WEP
+ int i;
+@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ }
+ #endif /* CONFIG_WEP */
+
+- if (!iface_exists(vlan->ifname))
++ if (!vlan_exists)
+ ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+ else if (!existsok)
+ return -1;
+@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ if (hapd->wpa_auth)
+ ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
++ if (!ret && !vlan_exists)
++ hostapd_ubus_add_vlan(hapd, vlan);
++
+ if (ret == 0)
+ return ret;
+
+@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+ "WPA deinitialization for VLAN %d failed (%d)",
+ vlan->vlan_id, ret);
+
++ hostapd_ubus_remove_vlan(hapd, vlan);
++
+ return hostapd_vlan_if_remove(hapd, vlan->ifname);
+ }
+
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index af8cccaef..d259200c9 100644
+--- a/src/ap/wnm_ap.c
++++ b/src/ap/wnm_ap.c
+@@ -410,6 +410,7 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
++ 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 "
+ "validity_interval=%u",
+@@ -478,7 +479,8 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+ MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+ os_free(hex);
+
+- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
++ if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
++ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+ }
+
+
+@@ -500,7 +502,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ size_t len)
+ {
+ u8 dialog_token, status_code, bss_termination_delay;
+- const u8 *pos, *end;
++ const u8 *pos, *end, *target_bssid = NULL;
+ int enabled = hapd->conf->bss_transition;
+ struct sta_info *sta;
+
+@@ -547,6 +549,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+ return;
+ }
++ target_bssid = pos;
+ sta->agreed_to_steer = 1;
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+@@ -566,6 +569,10 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ MAC2STR(addr), status_code, bss_termination_delay);
+ }
+
++ hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
++ status_code, bss_termination_delay,
++ target_bssid, pos, end - pos);
++
+ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ pos, end - pos);
+ }
+@@ -814,10 +821,12 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ plen);
+ return 0;
+ case WNM_BSS_TRANS_MGMT_QUERY:
++ hapd->openwrt_stats.wnm.bss_transition_query_rx++;
+ ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+ case WNM_BSS_TRANS_MGMT_RESP:
++ hapd->openwrt_stats.wnm.bss_transition_response_rx++;
+ ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+@@ -865,6 +874,7 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+ MACSTR, disassoc_timer, MAC2STR(sta->addr));
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+@@ -947,6 +957,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ return -1;
+ }
+
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ if (disassoc_timer) {
+ /* send disassociation frame after time-out */
+ set_disassoc_timer(hapd, sta, disassoc_timer);
+@@ -1028,6 +1039,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+ os_free(buf);
+
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ if (disassoc_timer) {
+ #ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 7a07dcc4c..b23d75444 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -5865,6 +5865,7 @@ static const char * wpa_bool_txt(int val)
+ return val ? "TRUE" : "FALSE";
+ }
+
++#ifdef CONFIG_CTRL_IFACE_MIB
+
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+@@ -6017,7 +6018,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+
+ return len;
+ }
+-
++#endif
+
+ void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+ {
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index 1726c7201..5a9ec6975 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -275,6 +275,7 @@ static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+ struct hostapd_data *hapd = ctx;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+ MAC2STR(addr));
++ hostapd_ubus_notify(hapd, "key-mismatch", addr);
+ }
+
+
+@@ -1812,8 +1813,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+ wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+ const char *ft_iface;
+
+- ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+- hapd->conf->iface;
++ if (hapd->conf->ft_iface[0])
++ ft_iface = hapd->conf->ft_iface;
++ else if (hapd->conf->bridge[0])
++ ft_iface = hapd->conf->bridge;
++ else
++ ft_iface = hapd->conf->iface;
+ hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
+ hostapd_rrb_receive, hapd, 1);
+ if (!hapd->l2) {
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index 82d4d5fdd..dfc5c3ecb 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+ bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+ else
+ bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+- }
+ #ifndef CONFIG_NO_TKIP
+- if (cred->encr_type & WPS_ENCR_TKIP)
++ } else if (cred->encr_type & WPS_ENCR_TKIP)
+ bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+ #endif /* CONFIG_NO_TKIP */
+ bss->rsn_pairwise = bss->wpa_pairwise;
+@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
+ WPA_CIPHER_GCMP_256)) {
+ wps->encr_types |= WPS_ENCR_AES;
+ wps->encr_types_rsn |= WPS_ENCR_AES;
+- }
+- if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
++ } else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+ #ifdef CONFIG_NO_TKIP
+ wpa_printf(MSG_INFO, "WPS: TKIP not supported");
+ goto fail;
+diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
+index 029f4de23..4c20f137f 100644
+--- a/src/ap/x_snoop.c
++++ b/src/ap/x_snoop.c
+@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *hapd)
+
+ hapd->x_snoop_initialized = true;
+
+- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
++ if (!conf->snoop_iface[0] &&
++ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+ 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable hairpin_mode on the bridge port");
+ return -1;
+ }
+
+- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
++ if (!conf->snoop_iface[0] &&
++ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable proxyarp on the bridge port");
+ return -1;
+ }
+
+ if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+- 1)) {
++ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+ return -1;
+ }
+
+ #ifdef CONFIG_IPV6
+- if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
++ if (!conf->snoop_iface[0] &&
++ hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable multicast snooping on the bridge");
+ return -1;
+@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ {
+ struct hostapd_bss_config *conf = hapd->conf;
+ struct l2_packet_data *l2;
++ const char *ifname = conf->bridge;
++
++ if (conf->snoop_iface[0])
++ ifname = conf->snoop_iface;
+
+- l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
++ l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
+ if (l2 == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to initialize L2 packet processing %s",
+@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+
+ void x_snoop_deinit(struct hostapd_data *hapd)
+ {
++ struct hostapd_bss_config *conf = hapd->conf;
++
+ if (!hapd->x_snoop_initialized)
+ return;
+- hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
++ hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
++ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+ hapd->x_snoop_initialized = false;
+diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
+index f17f95a2c..39d39f429 100644
+--- a/src/common/dpp_crypto.c
++++ b/src/common/dpp_crypto.c
+@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
+
+ struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
+ {
++ if (curve == NULL) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: %s curve must be initialized", __func__);
++ return NULL;
++ }
++
+ struct crypto_ec_key *key;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+@@ -1582,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
+ Pr = crypto_ec_key_get_public_key(Pr_key);
+ Qr = crypto_ec_point_init(ec);
+ hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
+- if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
++ if (!Pr || !Qr || !hash_bn ||
++ crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
++ crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
+ goto fail;
+
+ if (crypto_ec_point_is_at_infinity(ec, Qr)) {
+diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
+index 2c47bf812..8bd6e994d 100644
+--- a/src/common/hw_features_common.c
++++ b/src/common/hw_features_common.c
+@@ -898,6 +898,7 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+ VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+ VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
++ VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
+
+ #undef VHT_CAP_CHECK
+ #undef VHT_CAP_CHECK_MAX
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index 5b39a61e1..7a1da3252 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -1397,6 +1397,8 @@ struct ieee80211_ampe_ie {
+ #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27))
+ #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28))
+ #define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT ((u32) BIT(30))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK ((u32) BIT(30) | BIT(31))
+
+ #define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1))
+ #define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \
+diff --git a/src/common/sae.c b/src/common/sae.c
+index f1c164e13..05de737e5 100644
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
+ static int sae_derive_commit_element_ecc(struct sae_data *sae,
+ struct crypto_bignum *mask)
+ {
++ if (sae->tmp->pwe_ecc == NULL) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: %s sae->tmp->pwe_ecc must be initialized",
++ __func__);
++ return -1;
++ }
++
+ /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+ if (!sae->tmp->own_commit_element_ecc) {
+ sae->tmp->own_commit_element_ecc =
+diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
+index 6ea3311ce..7a608c30e 100644
+--- a/src/common/wpa_common.c
++++ b/src/common/wpa_common.c
+@@ -2856,6 +2856,31 @@ u32 wpa_akm_to_suite(int akm)
+ }
+
+
++static void wpa_fixup_wpa_ie_rsn(u8 *assoc_ie, const u8 *wpa_msg_ie,
++ size_t rsn_ie_len)
++{
++ int pos, count;
++
++ pos = sizeof(struct rsn_ie_hdr) + RSN_SELECTOR_LEN;
++ if (rsn_ie_len < pos + 2)
++ return;
++
++ count = WPA_GET_LE16(wpa_msg_ie + pos);
++ pos += 2 + count * RSN_SELECTOR_LEN;
++ if (rsn_ie_len < pos + 2)
++ return;
++
++ count = WPA_GET_LE16(wpa_msg_ie + pos);
++ pos += 2 + count * RSN_SELECTOR_LEN;
++ if (rsn_ie_len < pos + 2)
++ return;
++
++ if (!assoc_ie[pos] && !assoc_ie[pos + 1] &&
++ (wpa_msg_ie[pos] || wpa_msg_ie[pos + 1]))
++ memcpy(&assoc_ie[pos], &wpa_msg_ie[pos], 2);
++}
++
++
+ int wpa_compare_rsn_ie(int ft_initial_assoc,
+ const u8 *ie1, size_t ie1len,
+ const u8 *ie2, size_t ie2len)
+@@ -2863,8 +2888,19 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
+ if (ie1 == NULL || ie2 == NULL)
+ return -1;
+
+- if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
+- return 0; /* identical IEs */
++ if (ie1len == ie2len) {
++ u8 *ie_tmp;
++
++ if (os_memcmp(ie1, ie2, ie1len) == 0)
++ return 0; /* identical IEs */
++
++ ie_tmp = alloca(ie1len);
++ memcpy(ie_tmp, ie1, ie1len);
++ wpa_fixup_wpa_ie_rsn(ie_tmp, ie2, ie1len);
++
++ if (os_memcmp(ie_tmp, ie2, ie1len) == 0)
++ return 0; /* only mismatch in RSN capabilties */
++ }
+
+ #ifdef CONFIG_IEEE80211R
+ if (ft_initial_assoc) {
+diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
+index 7e197f094..791fdbf93 100644
+--- a/src/common/wpa_ctrl.c
++++ b/src/common/wpa_ctrl.c
+@@ -135,7 +135,7 @@ try_again:
+ return NULL;
+ }
+ tries++;
+-#ifdef ANDROID
++
+ /* Set client socket file permissions so that bind() creates the client
+ * socket with these permissions and there is no need to try to change
+ * them with chmod() after bind() which would have potential issues with
+@@ -147,7 +147,7 @@ try_again:
+ * operations to allow the response to go through. Those are using the
+ * no-deference-symlinks version to avoid races. */
+ fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+-#endif /* ANDROID */
++
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local)) < 0) {
+ if (errno == EADDRINUSE && tries < 2) {
+@@ -165,7 +165,11 @@ try_again:
+ return NULL;
+ }
+
+-#ifdef ANDROID
++#ifndef ANDROID
++ /* Set group even if we do not have privileges to change owner */
++ lchown(ctrl->local.sun_path, -1, 101);
++ lchown(ctrl->local.sun_path, 101, 101);
++#else
+ /* Set group even if we do not have privileges to change owner */
+ lchown(ctrl->local.sun_path, -1, AID_WIFI);
+ lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+diff --git a/src/crypto/Makefile b/src/crypto/Makefile
+index ce0997091..96bac9476 100644
+--- a/src/crypto/Makefile
++++ b/src/crypto/Makefile
+@@ -1,10 +1,121 @@
+-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+-CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ #CFLAGS += -DALL_DH_GROUPS
+ CFLAGS += -DCONFIG_SHA256
+ CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++
++# crypto_module_tests.c
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
++ifeq ($(CONFIG_TLS),mbedtls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DCONFIG_DES
++CFLAGS += -DEAP_IKEV2
++CFLAGS += -DEAP_MSCHAPv2
++CFLAGS += -DEAP_SIM
++
++LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
++LIB_OBJS+= \
++ aes-eax.o \
++ aes-siv.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o
++
++else
++ifeq ($(CONFIG_TLS),openssl)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
++ifndef CONFIG_TLS_DEFAULT_CIPHERS
++CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
++endif
++CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DEAP_TLS_OPENSSL
++
++LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
++LIB_OBJS+= \
++ aes-ctr.o \
++ aes-eax.o \
++ aes-encblock.o \
++ aes-siv.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o \
++ sha1-prf.o \
++ sha1-tlsprf.o \
++ sha1-tprf.o \
++ sha256-kdf.o \
++ sha256-prf.o \
++ sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++
++# (wolfssl libraries must be built with ./configure --enable-wpas)
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
++CFLAGS += -DWOLFSSL_DER_LOAD
++CFLAGS += -DCONFIG_DES
++
++LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
++LIB_OBJS+= \
++ aes-ctr.o \
++ aes-eax.o \
++ aes-encblock.o \
++ aes-siv.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o \
++ sha1-prf.o \
++ sha1-tlsprf.o \
++ sha1-tprf.o \
++ sha256-kdf.o \
++ sha256-prf.o \
++ sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),gnutls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
++LIB_OBJS = tls_gnutls.o crypto_gnutls.o
++LIB_OBJS+= \
++ aes-cbc.o \
++ aes-ctr.o \
++ aes-eax.o \
++ aes-encblock.o \
++ aes-omac1.o \
++ aes-siv.o \
++ aes-unwrap.o \
++ aes-wrap.o \
++ dh_group5.o \
++ dh_groups.o \
++ milenage.o \
++ ms_funcs.o \
++ rc4.o \
++ sha1-pbkdf2.o \
++ sha1-prf.o \
++ fips_prf_internal.o \
++ sha1-internal.o \
++ sha1-tlsprf.o \
++ sha1-tprf.o \
++ sha256-kdf.o \
++ sha256-prf.o \
++ sha256-tlsprf.o
++
++else
++
++CFLAGS += -DCONFIG_CRYPTO_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+
+ LIB_OBJS= \
+@@ -13,7 +124,6 @@ LIB_OBJS= \
+ aes-ctr.o \
+ aes-eax.o \
+ aes-encblock.o \
+- aes-gcm.o \
+ aes-internal.o \
+ aes-internal-dec.o \
+ aes-internal-enc.o \
+@@ -37,6 +147,7 @@ LIB_OBJS= \
+ sha1-tlsprf.o \
+ sha1-tprf.o \
+ sha256.o \
++ sha256-kdf.o \
+ sha256-prf.o \
+ sha256-tlsprf.o \
+ sha256-internal.o \
+@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
+ LIB_OBJS += crypto_internal-rsa.o
+ LIB_OBJS += tls_internal.o
+ LIB_OBJS += fips_prf_internal.o
++
++endif
++endif
++endif
++endif
++
++
++# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
++LIB_OBJS += aes-gcm.o
++
+ ifndef TEST_FUZZ
+ LIB_OBJS += random.o
+ endif
+diff --git a/src/crypto/crypto_mbedtls.c b/src/crypto/crypto_mbedtls.c
+new file mode 100644
+index 000000000..7a91c965f
+--- /dev/null
++++ b/src/crypto/crypto_mbedtls.c
+@@ -0,0 +1,4228 @@
++/*
++ * crypto wrapper functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/entropy.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/asn1.h>
++#include <mbedtls/asn1write.h>
++#include <mbedtls/aes.h>
++#include <mbedtls/md.h>
++#include <mbedtls/md5.h>
++#include <mbedtls/sha1.h>
++#include <mbedtls/sha256.h>
++#include <mbedtls/sha512.h>
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__ __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__ __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++#include "crypto.h"
++#include "aes_wrap.h"
++#include "aes.h"
++#include "md5.h"
++#include "sha1.h"
++#include "sha256.h"
++#include "sha384.h"
++#include "sha512.h"
++
++
++/*
++ * selective code inclusion based on preprocessor defines
++ *
++ * future: additional code could be wrapped with preprocessor checks if
++ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
++ * setting preprocessor defines for named groups of functionality
++ */
++
++#if defined(CONFIG_FIPS)
++#undef MBEDTLS_MD4_C /* omit md4_vector() */
++#undef MBEDTLS_MD5_C /* omit md5_vector() hmac_md5_vector() hmac_md5() */
++#undef MBEDTLS_DES_C /* omit des_encrypt() */
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#define CRYPTO_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if !defined(CONFIG_FIPS)
++#if defined(EAP_PWD) \
++ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
++ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
++ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
++ || defined(EAP_SERVER_MSCHAPV2)
++#ifndef MBEDTLS_MD4_C /* (MD4 not in mbedtls 3.x) */
++#include "md4-internal.c"/* pull in hostap local implementation */
++#endif /* md4_vector() */
++#else
++#undef MBEDTLS_MD4_C /* omit md4_vector() */
++#endif
++#endif
++
++#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
++#ifndef MBEDTLS_ARC4_C /* (RC4 not in mbedtls 3.x) */
++#include "rc4.c" /* pull in hostap local implementation */
++#endif /* rc4_skip() */
++#else
++#undef MBEDTLS_ARC4_C /* omit rc4_skip() */
++#endif
++
++#if defined(CONFIG_MACSEC) \
++ || defined(CONFIG_NO_RADIUS) \
++ || defined(CONFIG_IEEE80211R) \
++ || defined(EAP_SERVER_FAST) \
++ || defined(EAP_SERVER_TEAP) \
++ || !defined(CONFIG_NO_WPA)
++ /* aes_wrap() aes_unwrap() */
++#else
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#endif
++
++#if !defined(CONFIG_SHA256)
++#undef MBEDTLS_SHA256_C
++#endif
++
++#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
++#undef MBEDTLS_SHA512_C
++#endif
++
++#if defined(CONFIG_HMAC_SHA256_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++#endif
++#if defined(CONFIG_HMAC_SHA384_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++#endif
++#if defined(CONFIG_HMAC_SHA512_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++#endif
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
++/* EAP_SIM=y EAP_AKA=y */
++#define CRYPTO_MBEDTLS_FIPS186_2_PRF
++#endif
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define CRYPTO_MBEDTLS_SHA1_T_PRF
++#endif
++
++#if defined(CONFIG_DES)
++#define CRYPTO_MBEDTLS_DES_ENCRYPT
++#endif /* des_encrypt() */
++
++#if !defined(CONFIG_NO_PBKDF2)
++#define CRYPTO_MBEDTLS_PBKDF2_SHA1
++#endif /* pbkdf2_sha1() */
++
++#if defined(EAP_IKEV2) \
++ || defined(EAP_IKEV2_DYNAMIC) \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
++#endif /* crypto_cipher_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HASH
++#endif /* crypto_hash_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#endif /* crypto_bignum_*() */
++
++#if defined(EAP_PWD) /* CONFIG_EAP_PWD=y */ \
++ || defined(EAP_EKE) /* CONFIG_EAP_EKE=y */ \
++ || defined(EAP_EKE_DYNAMIC) /* CONFIG_EAP_EKE=y */ \
++ || defined(EAP_SERVER_EKE) /* CONFIG_EAP_EKE=y */ \
++ || defined(EAP_IKEV2) /* CONFIG_EAP_IKEV2y */ \
++ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */ \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */ \
++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */ \
++ || defined(CONFIG_WPS) /* CONFIG_WPS=y */
++#define CRYPTO_MBEDTLS_CRYPTO_DH
++#if defined(CONFIG_WPS_NFC)
++#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
++#endif /* dh5_init_fixed() */
++#endif /* crypto_dh_*() */
++
++#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
++#define CRYPTO_MBEDTLS_CRYPTO_ECDH
++#endif /* crypto_ecdh_*() */
++
++#if defined(CONFIG_ECC)
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#define CRYPTO_MBEDTLS_CRYPTO_EC
++#endif /* crypto_ec_*() crypto_ec_key_*() */
++
++#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
++#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
++#define CRYPTO_MBEDTLS_CRYPTO_CSR
++#endif /* crypto_csr_*() */
++
++#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HPKE
++#endif
++
++#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
++#endif /* crypto_pkcs7_*() */
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
++ || defined(CONFIG_AP) || defined(HOSTAPD)
++/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
++#if defined(CRYPTO_RSA_OAEP_SHA256)
++#define CRYPTO_MBEDTLS_CRYPTO_RSA
++#endif
++#endif /* crypto_rsa_*() */
++
++
++static int ctr_drbg_init_state;
++static mbedtls_ctr_drbg_context ctr_drbg;
++static mbedtls_entropy_context entropy;
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#include <mbedtls/bignum.h>
++static mbedtls_mpi mpi_sw_A;
++#endif
++
++__attribute_cold__
++__attribute_noinline__
++static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
++{
++ mbedtls_ctr_drbg_init(&ctr_drbg);
++ mbedtls_entropy_init(&entropy);
++ if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
++ NULL, 0)) {
++ wpa_printf(MSG_ERROR, "Init of random number generator failed");
++ /* XXX: abort? */
++ }
++ else
++ ctr_drbg_init_state = 1;
++
++ return &ctr_drbg;
++}
++
++__attribute_cold__
++void crypto_unload(void)
++{
++ if (ctr_drbg_init_state) {
++ mbedtls_ctr_drbg_free(&ctr_drbg);
++ mbedtls_entropy_free(&entropy);
++ #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++ mbedtls_mpi_free(&mpi_sw_A);
++ #endif
++ ctr_drbg_init_state = 0;
++ }
++}
++
++/* init ctr_drbg on first use
++ * crypto_global_init() and crypto_global_deinit() are not available here
++ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++inline
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
++{
++ return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
++}
++
++#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
++int crypto_get_random(void *buf, size_t len)
++{
++ return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
++}
++#endif
++
++
++#if 1
++
++/* tradeoff: slightly smaller code size here at cost of slight increase
++ * in instructions and function calls at runtime versus the expanded
++ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
++
++__attribute_noinline__
++static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac, mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_starts(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md_update(&ctx, addr[i], len[i]);
++ mbedtls_md_finish(&ctx, mac);
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
++}
++#endif
++
++#else /* expanded per-message-digest functions */
++
++#ifdef MBEDTLS_SHA512_C
++#include <mbedtls/sha512.h>
++__attribute_noinline__
++static int sha384_512_vector(size_t num_elem, const u8 *addr[],
++ const size_t *len, u8 *mac, int is384)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_sha512_context ctx;
++ mbedtls_sha512_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_sha512_starts(&ctx, is384);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha512_update(&ctx, addr[i], len[i]);
++ mbedtls_sha512_finish(&ctx, mac);
++ #else
++ mbedtls_sha512_starts_ret(&ctx, is384);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_sha512_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_sha512_free(&ctx);
++ return 0;
++}
++
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return sha384_512_vector(num_elem, addr, len, mac, 0);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return sha384_512_vector(num_elem, addr, len, mac, 1);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#include <mbedtls/sha256.h>
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_sha256_context ctx;
++ mbedtls_sha256_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_sha256_starts(&ctx, 0);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha256_update(&ctx, addr[i], len[i]);
++ mbedtls_sha256_finish(&ctx, mac);
++ #else
++ mbedtls_sha256_starts_ret(&ctx, 0);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_sha256_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_sha256_free(&ctx);
++ return 0;
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++#include <mbedtls/sha1.h>
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_sha1_context ctx;
++ mbedtls_sha1_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_sha1_starts(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha1_update(&ctx, addr[i], len[i]);
++ mbedtls_sha1_finish(&ctx, mac);
++ #else
++ mbedtls_sha1_starts_ret(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_sha1_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_sha1_free(&ctx);
++ return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++#include <mbedtls/md5.h>
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_md5_context ctx;
++ mbedtls_md5_init(&ctx);
++ #if MBEDTLS_VERSION_MAJOR >= 3
++ mbedtls_md5_starts(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md5_update(&ctx, addr[i], len[i]);
++ mbedtls_md5_finish(&ctx, mac);
++ #else
++ mbedtls_md5_starts_ret(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_md5_finish_ret(&ctx, mac);
++ #endif
++ mbedtls_md5_free(&ctx);
++ return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ struct mbedtls_md4_context ctx;
++ mbedtls_md4_init(&ctx);
++ mbedtls_md4_starts_ret(&ctx);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
++ mbedtls_md4_finish_ret(&ctx, mac);
++ mbedtls_md4_free(&ctx);
++ return 0;
++}
++#endif
++
++#endif /* expanded per-message-digest functions */
++
++
++__attribute_noinline__
++static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac,
++ mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_hmac_starts(&ctx, key, key_len);
++ for (size_t i = 0; i < num_elem; ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, mac);
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA384);
++}
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA256);
++}
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_SHA1);
++}
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return hmac_vector(key, key_len, num_elem, addr, len, mac,
++ MBEDTLS_MD_MD5);
++}
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++ MBEDTLS_MD_MD5);
++}
++#endif
++
++
++#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
++
++#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
++
++#include <mbedtls/hkdf.h>
++
++/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
++
++/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
++/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
++/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
++__attribute_noinline__
++static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
++ const char *label, const u8 *info, size_t info_len,
++ u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++ #ifdef MBEDTLS_HKDF_C
++ if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */
++ return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
++ info_len, okm, okm_len) ? -1 : 0;
++ #endif
++
++ const size_t mac_len = mbedtls_md_get_size(md_info);
++ /* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
++ if (okm_len > ((mac_len << 8) - mac_len))
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_hmac_starts(&ctx, prk, prk_len);
++
++ u8 iter = 1;
++ const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
++ size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
++
++ for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, okm);
++ mbedtls_md_hmac_reset(&ctx);
++ addr[0] = okm;
++ okm += mac_len;
++ len[0] = mac_len; /*(include digest in subsequent rounds)*/
++ }
++
++ if (okm_len) {
++ u8 hash[MBEDTLS_MD_MAX_SIZE];
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, hash);
++ os_memcpy(okm, hash, okm_len);
++ forced_memzero(hash, mac_len);
++ }
++
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++ out, outlen, MBEDTLS_MD_SHA512);
++}
++#endif
++
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++ out, outlen, MBEDTLS_MD_SHA384);
++}
++#endif
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++ out, outlen, MBEDTLS_MD_SHA256);
++}
++#endif
++#endif
++
++#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
++
++
++/* sha256-prf.c sha384-prf.c sha512-prf.c */
++
++/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
++__attribute_noinline__
++static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits, mbedtls_md_type_t md_type)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++ mbedtls_md_free(&ctx);
++ return -1;
++ }
++ mbedtls_md_hmac_starts(&ctx, key, key_len);
++
++ u16 ctr, n_le = host_to_le16(buf_len_bits);
++ const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
++ const size_t len[] = { 2, os_strlen(label), data_len, 2 };
++ const size_t mac_len = mbedtls_md_get_size(md_info);
++ size_t buf_len = (buf_len_bits + 7) / 8;
++ for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
++ #if __BYTE_ORDER == __BIG_ENDIAN
++ ctr = host_to_le16(ctr);
++ #endif
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, buf);
++ mbedtls_md_hmac_reset(&ctx);
++ buf += mac_len;
++ #if __BYTE_ORDER == __BIG_ENDIAN
++ ctr = le_to_host16(ctr);
++ #endif
++ }
++
++ if (buf_len) {
++ u8 hash[MBEDTLS_MD_MAX_SIZE];
++ #if __BYTE_ORDER == __BIG_ENDIAN
++ ctr = host_to_le16(ctr);
++ #endif
++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++ mbedtls_md_hmac_finish(&ctx, hash);
++ os_memcpy(buf, hash, buf_len);
++ buf += buf_len;
++ forced_memzero(hash, mac_len);
++ }
++
++ /* Mask out unused bits in last octet if it does not use all the bits */
++ if ((buf_len_bits &= 0x7))
++ buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
++
++ mbedtls_md_free(&ctx);
++ return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8, MBEDTLS_MD_SHA512);
++}
++
++int sha384_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8, MBEDTLS_MD_SHA256);
++}
++
++int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits)
++{
++ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len_bits, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
++
++
++#ifdef MBEDTLS_SHA1_C
++
++/* sha1-prf.c */
++
++/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
++
++int sha1_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ /*(note: algorithm differs from hmac_prf_bits() */
++ /*(note: smaller code size instead of expanding hmac_sha1_vector()
++ * as is done in hmac_prf_bits(); not expecting large num of loops) */
++ u8 counter = 0;
++ const u8 *addr[] = { (u8 *)label, data, &counter };
++ const size_t len[] = { os_strlen(label)+1, data_len, 1 };
++
++ for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
++ if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
++ return -1;
++ buf += SHA1_MAC_LEN;
++ }
++
++ if (buf_len) {
++ u8 hash[SHA1_MAC_LEN];
++ if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
++ return -1;
++ os_memcpy(buf, hash, buf_len);
++ forced_memzero(hash, sizeof(hash));
++ }
++
++ return 0;
++}
++
++#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
++
++/* sha1-tprf.c */
++
++/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
++
++int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
++{
++ /*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
++ /*(note: smaller code size instead of expanding hmac_sha1_vector()
++ * as is done in hmac_prf_bits(); not expecting large num of loops) */
++ u8 ctr;
++ u16 olen = host_to_be16(buf_len);
++ const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
++ size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
++
++ for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
++ if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
++ return -1;
++ addr[0] = buf;
++ buf += SHA1_MAC_LEN;
++ len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
++ }
++
++ if (buf_len) {
++ u8 hash[SHA1_MAC_LEN];
++ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
++ return -1;
++ os_memcpy(buf, hash, buf_len);
++ forced_memzero(hash, sizeof(hash));
++ }
++
++ return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
++
++#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
++
++/* fips_prf_internal.c sha1-internal.c */
++
++/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
++ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
++ * where xlen is 160 */
++
++int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
++{
++ /* FIPS 186-2 + change notice 1 */
++
++ mbedtls_sha1_context ctx;
++ u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
++ u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
++ const u32 xstate_init[] =
++ { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
++
++ mbedtls_sha1_init(&ctx);
++ os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
++
++ /* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
++ for (; xlen >= 20; xlen -= 20) {
++ /* XSEED_j = 0 */
++ /* XVAL = (XKEY + XSEED_j) mod 2^b */
++
++ /* w_i = G(t, XVAL) */
++ os_memcpy(xstate, xstate_init, sizeof(xstate_init));
++ mbedtls_internal_sha1_process(&ctx, xkey);
++
++ #if __BYTE_ORDER == __LITTLE_ENDIAN
++ xstate[0] = host_to_be32(xstate[0]);
++ xstate[1] = host_to_be32(xstate[1]);
++ xstate[2] = host_to_be32(xstate[2]);
++ xstate[3] = host_to_be32(xstate[3]);
++ xstate[4] = host_to_be32(xstate[4]);
++ #endif
++ os_memcpy(x, xstate, 20);
++ if (xlen == 20) /*(done; skip prep for next loop)*/
++ break;
++
++ /* XKEY = (1 + XKEY + w_i) mod 2^b */
++ for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
++ xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
++ x += 20;
++ /* x_j = w_0|w_1 (each pair of iterations through loop)*/
++ }
++
++ mbedtls_sha1_free(&ctx);
++ return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
++
++#endif /* MBEDTLS_SHA1_C */
++
++
++#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
++#ifdef MBEDTLS_DES_C
++#include <mbedtls/des.h>
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
++ u8 pkey[8], next, tmp;
++ int i;
++
++ /* Add parity bits to the key */
++ next = 0;
++ for (i = 0; i < 7; i++) {
++ tmp = key[i];
++ pkey[i] = (tmp >> i) | next | 1;
++ next = tmp << (7 - i);
++ }
++ pkey[i] = next | 1;
++
++ mbedtls_des_context des;
++ mbedtls_des_init(&des);
++ int ret = mbedtls_des_setkey_enc(&des, pkey)
++ || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
++ mbedtls_des_free(&des);
++ return ret;
++}
++#else
++#include "des-internal.c"/* pull in hostap local implementation */
++#endif
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
++/* sha1-pbkdf2.c */
++#include <mbedtls/pkcs5.h>
++int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
++ int iterations, u8 *buf, size_t buflen)
++{
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
++ return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
++ (const u8 *)passphrase, os_strlen(passphrase),
++ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++ #else
++ const mbedtls_md_info_t *md_info;
++ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
++ if (md_info == NULL)
++ return -1;
++ mbedtls_md_context_t ctx;
++ mbedtls_md_init(&ctx);
++ int ret = mbedtls_md_setup(&ctx, md_info, 1)
++ || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
++ (const u8 *)passphrase, os_strlen(passphrase),
++ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++ mbedtls_md_free(&ctx);
++ return ret;
++ #endif
++}
++#endif
++
++
++/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
++
++static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
++ if (!aes)
++ return NULL;
++
++ mbedtls_aes_init(aes);
++ if ((mode == MBEDTLS_AES_ENCRYPT
++ ? mbedtls_aes_setkey_enc(aes, key, len * 8)
++ : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
++ return aes;
++
++ mbedtls_aes_free(aes);
++ os_free(aes);
++ return NULL;
++}
++
++void *aes_encrypt_init(const u8 *key, size_t len)
++{
++ return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++{
++ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
++}
++
++void aes_encrypt_deinit(void *ctx)
++{
++ mbedtls_aes_free(ctx);
++ os_free(ctx);
++}
++
++void *aes_decrypt_init(const u8 *key, size_t len)
++{
++ return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
++}
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++{
++ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
++}
++
++void aes_decrypt_deinit(void *ctx)
++{
++ mbedtls_aes_free(ctx);
++ os_free(ctx);
++}
++
++
++#include "aes_wrap.h"
++
++
++#ifdef MBEDTLS_NIST_KW_C
++
++#include <mbedtls/nist_kw.h>
++
++/* aes-wrap.c */
++int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_nist_kw_context ctx;
++ mbedtls_nist_kw_init(&ctx);
++ size_t olen;
++ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++ kek, kek_len*8, 1)
++ || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
++ cipher, &olen, (n+1)*8) ? -1 : 0;
++ mbedtls_nist_kw_free(&ctx);
++ return ret;
++}
++
++/* aes-unwrap.c */
++int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_nist_kw_context ctx;
++ mbedtls_nist_kw_init(&ctx);
++ size_t olen;
++ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++ kek, kek_len*8, 0)
++ || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
++ (n+1)*8, plain, &olen, n*8) ? -1 : 0;
++ mbedtls_nist_kw_free(&ctx);
++ return ret;
++}
++
++#else
++
++#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
++#include "aes-wrap.c" /* pull in hostap local implementation */
++#include "aes-unwrap.c" /* pull in hostap local implementation */
++#endif
++
++#endif /* MBEDTLS_NIST_KW_C */
++
++
++#ifdef MBEDTLS_CMAC_C
++
++/* aes-omac1.c */
++
++#include <mbedtls/cmac.h>
++
++int omac1_aes_vector(
++ const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
++ const size_t *len, u8 *mac)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_cipher_type_t cipher_type;
++ switch (key_len) {
++ case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
++ case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
++ case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
++ default: return -1;
++ }
++ const mbedtls_cipher_info_t *cipher_info;
++ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++ if (cipher_info == NULL)
++ return -1;
++
++ mbedtls_cipher_context_t ctx;
++ mbedtls_cipher_init(&ctx);
++ int ret = -1;
++ if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
++ && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
++ ret = 0;
++ for (size_t i = 0; i < num_elem && ret == 0; ++i)
++ ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
++ }
++ if (ret == 0)
++ ret = mbedtls_cipher_cmac_finish(&ctx, mac);
++ mbedtls_cipher_free(&ctx);
++ return ret ? -1 : 0;
++}
++
++int omac1_aes_128_vector(const u8 *key, size_t num_elem,
++ const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
++}
++
++int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
++}
++
++int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
++}
++
++#else
++
++#include "aes-omac1.c" /* pull in hostap local implementation */
++
++#ifndef MBEDTLS_AES_BLOCK_SIZE
++#define MBEDTLS_AES_BLOCK_SIZE 16
++#endif
++
++#endif /* MBEDTLS_CMAC_C */
++
++
++/* These interfaces can be inefficient when used in loops, as the overhead of
++ * initialization each call is large for each block input (e.g. 16 bytes) */
++
++
++/* aes-encblock.c */
++int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_aes_context aes;
++ mbedtls_aes_init(&aes);
++ int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
++ || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
++ ? -1
++ : 0;
++ mbedtls_aes_free(&aes);
++ return ret;
++}
++
++
++/* aes-ctr.c */
++int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
++ u8 *data, size_t data_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
++ unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
++ os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
++
++ mbedtls_aes_context ctx;
++ mbedtls_aes_init(&ctx);
++ size_t nc_off = 0;
++ int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
++ || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
++ counter, stream_block,
++ data, data) ? -1 : 0;
++ forced_memzero(stream_block, sizeof(stream_block));
++ mbedtls_aes_free(&ctx);
++ return ret;
++}
++
++int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
++ u8 *data, size_t data_len)
++{
++ return aes_ctr_encrypt(key, 16, nonce, data, data_len);
++}
++
++
++/* aes-cbc.c */
++static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
++ u8 *data, size_t data_len, int mode)
++{
++ unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
++ os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
++
++ mbedtls_aes_context ctx;
++ mbedtls_aes_init(&ctx);
++ int ret = (mode == MBEDTLS_AES_ENCRYPT
++ ? mbedtls_aes_setkey_enc(&ctx, key, 128)
++ : mbedtls_aes_setkey_dec(&ctx, key, 128))
++ || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
++ mbedtls_aes_free(&ctx);
++ return ret ? -1 : 0;
++}
++
++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
++}
++
++
++/*
++ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
++ * but such comments are not accurate:
++ *
++ * "This function is only used with internal TLSv1 implementation
++ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
++ * to implement this."
++ */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
++
++#include <mbedtls/cipher.h>
++
++struct crypto_cipher
++{
++ mbedtls_cipher_context_t ctx_enc;
++ mbedtls_cipher_context_t ctx_dec;
++};
++
++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
++ const u8 *iv, const u8 *key,
++ size_t key_len)
++{
++ /* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
++ * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
++
++ mbedtls_cipher_type_t cipher_type;
++ size_t iv_len;
++ switch (alg) {
++ #ifdef MBEDTLS_ARC4_C
++ #if 0
++ case CRYPTO_CIPHER_ALG_RC4:
++ cipher_type = MBEDTLS_CIPHER_ARC4_128;
++ iv_len = 0;
++ break;
++ #endif
++ #endif
++ #ifdef MBEDTLS_AES_C
++ case CRYPTO_CIPHER_ALG_AES:
++ if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
++ if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
++ if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
++ iv_len = 16;
++ break;
++ #endif
++ #ifdef MBEDTLS_DES_C
++ case CRYPTO_CIPHER_ALG_3DES:
++ cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
++ iv_len = 8;
++ break;
++ #if 0
++ case CRYPTO_CIPHER_ALG_DES:
++ cipher_type = MBEDTLS_CIPHER_DES_CBC;
++ iv_len = 8;
++ break;
++ #endif
++ #endif
++ default:
++ return NULL;
++ }
++
++ const mbedtls_cipher_info_t *cipher_info;
++ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++ if (cipher_info == NULL)
++ return NULL;
++
++ key_len *= 8; /* key_bitlen */
++ #if 0 /*(were key_bitlen not already available)*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++ key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
++ #else
++ key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
++ #endif
++ #endif
++
++ #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
++ iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
++ #endif
++
++ struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ mbedtls_cipher_init(&ctx->ctx_enc);
++ mbedtls_cipher_init(&ctx->ctx_dec);
++ if ( mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
++ && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
++ && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
++ && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
++ && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
++ && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
++ && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
++ && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
++ return ctx;
++ }
++
++ mbedtls_cipher_free(&ctx->ctx_enc);
++ mbedtls_cipher_free(&ctx->ctx_dec);
++ os_free(ctx);
++ return NULL;
++}
++
++int crypto_cipher_encrypt(struct crypto_cipher *ctx,
++ const u8 *plain, u8 *crypt, size_t len)
++{
++ size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
++ return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
++ || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
++}
++
++int crypto_cipher_decrypt(struct crypto_cipher *ctx,
++ const u8 *crypt, u8 *plain, size_t len)
++{
++ size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
++ return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
++ || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
++}
++
++void crypto_cipher_deinit(struct crypto_cipher *ctx)
++{
++ mbedtls_cipher_free(&ctx->ctx_enc);
++ mbedtls_cipher_free(&ctx->ctx_dec);
++ os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
++
++struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
++ size_t key_len)
++{
++ mbedtls_md_type_t md_type;
++ int is_hmac = 0;
++
++ switch (alg) {
++ #ifdef MBEDTLS_MD5_C
++ case CRYPTO_HASH_ALG_MD5:
++ md_type = MBEDTLS_MD_MD5;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA1_C
++ case CRYPTO_HASH_ALG_SHA1:
++ md_type = MBEDTLS_MD_SHA1;
++ break;
++ #endif
++ #ifdef MBEDTLS_MD5_C
++ case CRYPTO_HASH_ALG_HMAC_MD5:
++ md_type = MBEDTLS_MD_MD5;
++ is_hmac = 1;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA1_C
++ case CRYPTO_HASH_ALG_HMAC_SHA1:
++ md_type = MBEDTLS_MD_SHA1;
++ is_hmac = 1;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA256_C
++ case CRYPTO_HASH_ALG_SHA256:
++ md_type = MBEDTLS_MD_SHA256;
++ break;
++ case CRYPTO_HASH_ALG_HMAC_SHA256:
++ md_type = MBEDTLS_MD_SHA256;
++ is_hmac = 1;
++ break;
++ #endif
++ #ifdef MBEDTLS_SHA512_C
++ case CRYPTO_HASH_ALG_SHA384:
++ md_type = MBEDTLS_MD_SHA384;
++ break;
++ case CRYPTO_HASH_ALG_SHA512:
++ md_type = MBEDTLS_MD_SHA512;
++ break;
++ #endif
++ default:
++ return NULL;
++ }
++
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++ if (!md_info)
++ return NULL;
++
++ mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
++ if (mctx == NULL)
++ return NULL;
++
++ mbedtls_md_init(mctx);
++ if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
++ os_free(mctx);
++ return NULL;
++ }
++
++ if (is_hmac)
++ mbedtls_md_hmac_starts(mctx, key, key_len);
++ else
++ mbedtls_md_starts(mctx);
++ return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
++}
++
++void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
++{
++ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++ #if 0
++ /*(mbedtls_md_hmac_update() and mbedtls_md_update()
++ * make same modifications under the hood in mbedtls)*/
++ if ((uintptr_t)ctx & 1uL)
++ mbedtls_md_hmac_update(mctx, data, len);
++ else
++ #endif
++ mbedtls_md_update(mctx, data, len);
++}
++
++int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
++{
++ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++ if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
++ #else
++ const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
++ #endif
++ size_t maclen = mbedtls_md_get_size(md_info);
++ if (*len < maclen) {
++ *len = maclen;
++ /*(note: ctx not freed; can call again with larger *len)*/
++ return -1;
++ }
++ *len = maclen;
++ if ((uintptr_t)ctx & 1uL)
++ mbedtls_md_hmac_finish(mctx, mac);
++ else
++ mbedtls_md_finish(mctx, mac);
++ }
++ mbedtls_md_free(mctx);
++ os_free(mctx);
++
++ if (TEST_FAIL())
++ return -1;
++
++ return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++
++#include <mbedtls/bignum.h>
++
++/* crypto.h bignum interfaces */
++
++struct crypto_bignum *crypto_bignum_init(void)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn)
++ mbedtls_mpi_init(bn);
++ return (struct crypto_bignum *)bn;
++}
++
++struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn) {
++ mbedtls_mpi_init(bn);
++ if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
++ return (struct crypto_bignum *)bn;
++ }
++
++ os_free(bn);
++ return NULL;
++}
++
++struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ #if 0 /*(hostap use of this interface passes int, not uint)*/
++ val = host_to_be32(val);
++ return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
++ #else
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn) {
++ mbedtls_mpi_init(bn);
++ if (mbedtls_mpi_lset(bn, (int)val) == 0)
++ return (struct crypto_bignum *)bn;
++ }
++
++ os_free(bn);
++ return NULL;
++ #endif
++}
++
++void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
++{
++ mbedtls_mpi_free((mbedtls_mpi *)n);
++ os_free(n);
++}
++
++int crypto_bignum_to_bin(const struct crypto_bignum *a,
++ u8 *buf, size_t buflen, size_t padlen)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
++ if (n < padlen)
++ n = padlen;
++ return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
++ ? -1
++ : (int)(n);
++}
++
++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
++ return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++ #else
++ /* (needed by EAP_PWD, SAE, DPP) */
++ wpa_printf(MSG_ERROR,
++ "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
++ return -1;
++ #endif
++}
++
++int crypto_bignum_add(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_mod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_exptmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ const struct crypto_bignum *c,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /* (check if input params match d; d is the result) */
++ /* (a == d) is ok in current mbedtls implementation */
++ if (b == d || c == d) { /*(not ok; store result in intermediate)*/
++ mbedtls_mpi R;
++ mbedtls_mpi_init(&R);
++ int rc = mbedtls_mpi_exp_mod(&R,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b,
++ (const mbedtls_mpi *)c,
++ NULL)
++ || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
++ mbedtls_mpi_free(&R);
++ return rc;
++ }
++ else {
++ return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b,
++ (const mbedtls_mpi *)c,
++ NULL) ? -1 : 0;
++ }
++}
++
++int crypto_bignum_inverse(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_sub(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_div(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /*(most current use of this crypto.h interface has a == c (result),
++ * so store result in an intermediate to avoid overwritten input)*/
++ mbedtls_mpi R;
++ mbedtls_mpi_init(&R);
++ int rc = mbedtls_mpi_div_mpi(&R, NULL,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b)
++ || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
++ mbedtls_mpi_free(&R);
++ return rc;
++}
++
++int crypto_bignum_addmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ const struct crypto_bignum *c,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b)
++ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++ (mbedtls_mpi *)d,
++ (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_mulmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ const struct crypto_bignum *c,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
++ (const mbedtls_mpi *)a,
++ (const mbedtls_mpi *)b)
++ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++ (mbedtls_mpi *)d,
++ (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_sqrmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ #if 1
++ return crypto_bignum_mulmod(a, a, b, c);
++ #else
++ mbedtls_mpi bn;
++ mbedtls_mpi_init(&bn);
++ if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
++ return -1;
++ int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
++ (const mbedtls_mpi *)a, &bn,
++ (const mbedtls_mpi *)b, NULL) ? -1 : 0;
++ mbedtls_mpi_free(&bn);
++ return ret;
++ #endif
++}
++
++int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
++ struct crypto_bignum *r)
++{
++ return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
++ || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
++}
++
++int crypto_bignum_cmp(const struct crypto_bignum *a,
++ const struct crypto_bignum *b)
++{
++ return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
++}
++
++int crypto_bignum_is_zero(const struct crypto_bignum *a)
++{
++ /* XXX: src/common/sae.c:sswu() contains comment:
++ * "TODO: Make sure crypto_bignum_is_zero() is constant time"
++ * Note: mbedtls_mpi_cmp_int() *is not* constant time */
++ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
++}
++
++int crypto_bignum_is_one(const struct crypto_bignum *a)
++{
++ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
++}
++
++int crypto_bignum_is_odd(const struct crypto_bignum *a)
++{
++ return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
++}
++
++#include "utils/const_time.h"
++int crypto_bignum_legendre(const struct crypto_bignum *a,
++ const struct crypto_bignum *p)
++{
++ if (TEST_FAIL())
++ return -2;
++
++ /* Security Note:
++ * mbedtls_mpi_exp_mod() is not documented to run in constant time,
++ * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
++ * Compare to crypto_openssl.c:crypto_bignum_legendre()
++ * which uses openssl BN_mod_exp_mont_consttime()
++ * mbedtls/library/ecp.c has further countermeasures to timing attacks,
++ * (but ecp.c funcs are not used here) */
++
++ mbedtls_mpi exp, tmp;
++ mbedtls_mpi_init(&exp);
++ mbedtls_mpi_init(&tmp);
++
++ /* exp = (p-1) / 2 */
++ int res;
++ if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
++ && mbedtls_mpi_shift_r(&exp, 1) == 0
++ && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
++ (const mbedtls_mpi *)p, NULL) == 0) {
++ /*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
++ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
++ * to use constant time selection to avoid branches here. */
++ unsigned int mask;
++ res = -1;
++ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
++ res = const_time_select_int(mask, 1, res);
++ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
++ res = const_time_select_int(mask, 0, res);
++ } else {
++ res = -2;
++ }
++
++ mbedtls_mpi_free(&tmp);
++ mbedtls_mpi_free(&exp);
++ return res;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
++
++/* crypto_internal-modexp.c */
++
++#include <mbedtls/bignum.h>
++#include <mbedtls/dhm.h>
++
++#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
++int crypto_mod_exp(const u8 *base, size_t base_len,
++ const u8 *power, size_t power_len,
++ const u8 *modulus, size_t modulus_len,
++ u8 *result, size_t *result_len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
++ mbedtls_mpi_init(&bn_base);
++ mbedtls_mpi_init(&bn_exp);
++ mbedtls_mpi_init(&bn_modulus);
++ mbedtls_mpi_init(&bn_result);
++
++ size_t len;
++ int ret = mbedtls_mpi_read_binary(&bn_base, base, base_len)
++ || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
++ || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
++ || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
++ || (len = mbedtls_mpi_size(&bn_result)) > *result_len
++ || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
++ ? -1
++ : 0;
++
++ mbedtls_mpi_free(&bn_base);
++ mbedtls_mpi_free(&bn_exp);
++ mbedtls_mpi_free(&bn_modulus);
++ mbedtls_mpi_free(&bn_result);
++ return ret;
++}
++#endif
++
++static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
++ const u8 *prime, size_t prime_len)
++{
++ /*(could set these directly in MBEDTLS_PRIVATE members)*/
++ mbedtls_mpi P, G;
++ mbedtls_mpi_init(&P);
++ mbedtls_mpi_init(&G);
++ int ret = mbedtls_mpi_lset(&G, generator)
++ || mbedtls_mpi_read_binary(&P, prime, prime_len)
++ || mbedtls_dhm_set_group(ctx, &P, &G);
++ mbedtls_mpi_free(&P);
++ mbedtls_mpi_free(&G);
++ return ret;
++}
++
++__attribute_noinline__
++static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
++ const u8 *prime, size_t prime_len,
++ u8 *privkey, u8 *pubkey)
++{
++ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
++ || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()))
++ return -1;
++
++ /*(enable later when upstream mbedtls interface changes require)*/
++ #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ mbedtls_mpi X;
++ mbedtls_mpi_init(&X);
++ int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
++ || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
++ mbedtls_mpi_free(&X);
++ return ret;
++ #else
++ return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
++ privkey, prime_len) ? -1 : 0;
++ #endif
++}
++
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
++ size_t pubkey_len, pad;
++
++ if (os_get_random(privkey, prime_len) < 0)
++ return -1;
++ if (os_memcmp(privkey, prime, prime_len) > 0) {
++ /* Make sure private value is smaller than prime */
++ privkey[0] = 0;
++ }
++
++ pubkey_len = prime_len;
++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++ pubkey, &pubkey_len) < 0)
++ return -1;
++ if (pubkey_len < prime_len) {
++ pad = prime_len - pubkey_len;
++ os_memmove(pubkey + pad, pubkey, pubkey_len);
++ os_memset(pubkey, 0, pad);
++ }
++
++ return 0;
++ #else
++ /* Prefer to use mbedtls to derive our public/private key, as doing so
++ * leverages mbedtls to properly format output and to perform blinding*/
++ mbedtls_dhm_context ctx;
++ mbedtls_dhm_init(&ctx);
++ int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
++ prime_len, privkey, pubkey);
++ mbedtls_dhm_free(&ctx);
++ return ret;
++ #endif
++}
++
++/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
++ * instead of being reimplemented in each crypto_*.c)*/
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ #if 0
++ if (pubkey_len > prime_len ||
++ (pubkey_len == prime_len &&
++ os_memcmp(pubkey, prime, prime_len) >= 0))
++ return -1;
++
++ int res = 0;
++ mbedtls_mpi pub;
++ mbedtls_mpi_init(&pub);
++ if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
++ || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
++ res = -1;
++ } else if (order) {
++ mbedtls_mpi p, q, tmp;
++ mbedtls_mpi_init(&p);
++ mbedtls_mpi_init(&q);
++ mbedtls_mpi_init(&tmp);
++
++ /* verify: pubkey^q == 1 mod p */
++ res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
++ || mbedtls_mpi_read_binary(&q, order, order_len)
++ || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
++ || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
++
++ mbedtls_mpi_free(&p);
++ mbedtls_mpi_free(&q);
++ mbedtls_mpi_free(&tmp);
++ }
++ mbedtls_mpi_free(&pub);
++
++ return (res == 0)
++ ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++ prime, prime_len, secret, len)
++ : -1;
++ #else
++ /* Prefer to use mbedtls to derive DH shared secret, as doing so
++ * leverages mbedtls to validate params and to perform blinding.
++ *
++ * Attempt to reconstitute DH context to derive shared secret
++ * (due to limitations of the interface, which ought to pass context).
++ * Force provided G (our private key) into context without validation.
++ * Regenerating GX (our public key) not needed to derive shared secret.
++ */
++ /*(older compilers might not support VLAs)*/
++ /*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
++ unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
++ unsigned char *p = buf + 2 + prime_len;
++ if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
++ return -1;
++ WPA_PUT_BE16(buf, prime_len); /*(2-byte big-endian size of prime)*/
++ p[0] = 0; /*(2-byte big-endian size of generator)*/
++ p[1] = 1;
++ p[2] = generator;
++ WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
++ os_memcpy(p+5, pubkey, pubkey_len);
++ os_memcpy(buf+2, prime, prime_len);
++
++ mbedtls_dhm_context ctx;
++ mbedtls_dhm_init(&ctx);
++ p = buf;
++ int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
++ || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
++ privkey, privkey_len)
++ || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++ mbedtls_dhm_free(&ctx);
++ return ret;
++ #endif
++}
++
++/* dh_group5.c */
++
++#include "dh_group5.h"
++
++/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
++
++static const unsigned char RFC3526_PRIME_1536[] = {
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
++ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
++ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
++ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
++ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
++ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
++ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
++ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
++ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
++ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
++ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
++ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
++ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
++ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
++ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
++ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++};
++
++static const unsigned char RFC3526_GENERATOR_1536[] = {
++ 0x02
++};
++
++void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
++{
++ const unsigned char * const prime = RFC3526_PRIME_1536;
++ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++ const u8 generator = *RFC3526_GENERATOR_1536;
++ struct wpabuf *wpubl = NULL, *wpriv = NULL;
++
++ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_dhm_init(ctx);
++
++ if ( (wpubl = wpabuf_alloc(prime_len))
++ && (wpriv = wpabuf_alloc(prime_len))
++ && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
++ wpabuf_put(wpriv, prime_len),
++ wpabuf_put(wpubl, prime_len))==0) {
++ wpabuf_free(*publ);
++ wpabuf_clear_free(*priv);
++ *publ = wpubl;
++ *priv = wpriv;
++ return ctx;
++ }
++
++ wpabuf_clear_free(wpriv);
++ wpabuf_free(wpubl);
++ mbedtls_dhm_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
++void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
++{
++ const unsigned char * const prime = RFC3526_PRIME_1536;
++ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++ const u8 generator = *RFC3526_GENERATOR_1536;
++
++ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_dhm_init(ctx);
++
++ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
++ #if 0 /*(ignore; not required to derive shared secret)*/
++ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
++ wpabuf_head(publ),wpabuf_len(publ))==0
++ #endif
++ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
++ wpabuf_head(priv),wpabuf_len(priv))==0) {
++ return ctx;
++ }
++
++ mbedtls_dhm_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++#endif
++
++struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
++ const struct wpabuf *own_private)
++{
++ /*((mbedtls_dhm_context *)ctx must already contain own_private)*/
++ /* mbedtls 2.x: prime_len = ctx->len; */
++ /* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
++ size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
++ struct wpabuf *buf = wpabuf_alloc(olen);
++ if (buf == NULL)
++ return NULL;
++ if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
++ wpabuf_head(peer_public),
++ wpabuf_len(peer_public)) == 0
++ && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0) {
++ wpabuf_put(buf, olen);
++ return buf;
++ }
++
++ wpabuf_free(buf);
++ return NULL;
++}
++
++void dh5_free(void *ctx)
++{
++ mbedtls_dhm_free(ctx);
++ os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
++
++#include <mbedtls/ecp.h>
++
++#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
++#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
++#define CRYPTO_EC_P(e) (&((mbedtls_ecp_group *)(e))->P)
++#define CRYPTO_EC_N(e) (&((mbedtls_ecp_group *)(e))->N)
++#define CRYPTO_EC_A(e) (&((mbedtls_ecp_group *)(e))->A)
++#define CRYPTO_EC_B(e) (&((mbedtls_ecp_group *)(e))->B)
++#define CRYPTO_EC_G(e) (&((mbedtls_ecp_group *)(e))->G)
++
++static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
++{
++ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++ switch (group) {
++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++ case 19: return MBEDTLS_ECP_DP_SECP256R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++ case 20: return MBEDTLS_ECP_DP_SECP384R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++ case 21: return MBEDTLS_ECP_DP_SECP521R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++ case 25: return MBEDTLS_ECP_DP_SECP192R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++ case 26: return MBEDTLS_ECP_DP_SECP224R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++ case 28: return MBEDTLS_ECP_DP_BP256R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++ case 29: return MBEDTLS_ECP_DP_BP384R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++ case 30: return MBEDTLS_ECP_DP_BP512R1;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++ case 31: return MBEDTLS_ECP_DP_CURVE25519;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++ case 32: return MBEDTLS_ECP_DP_CURVE448;
++ #endif
++ default: return MBEDTLS_ECP_DP_NONE;
++ }
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
++{
++ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++ /*(for crypto_ec_key_group())*/
++ switch (grp_id) {
++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP256R1: return 19;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP384R1: return 20;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP521R1: return 21;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP192R1: return 25;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP224R1: return 26;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++ case MBEDTLS_ECP_DP_BP256R1: return 28;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++ case MBEDTLS_ECP_DP_BP384R1: return 29;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++ case MBEDTLS_ECP_DP_BP512R1: return 30;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++ case MBEDTLS_ECP_DP_CURVE25519: return 31;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++ case MBEDTLS_ECP_DP_CURVE448: return 32;
++ #endif
++ default: return -1;
++ }
++}
++#endif
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
++
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return -1;
++ const mbedtls_pk_info_t *pk_info =
++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++ if (pk_info == NULL)
++ return -1;
++ return mbedtls_pk_setup(pk, pk_info)
++ || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
++
++#include <mbedtls/ecdh.h>
++#include <mbedtls/ecdsa.h>
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++/* wrap mbedtls_ecdh_context for more future-proof direct access to components
++ * (mbedtls_ecdh_context internal implementation may change between releases)
++ *
++ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
++ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
++ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
++ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
++ * wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
++struct crypto_ecdh {
++ mbedtls_ecdh_context ctx;
++ mbedtls_ecp_group grp;
++ mbedtls_ecp_point Q;
++};
++
++struct crypto_ecdh * crypto_ecdh_init(int group)
++{
++ mbedtls_pk_context pk;
++ mbedtls_pk_init(&pk);
++ struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
++ ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
++ : NULL;
++ mbedtls_pk_free(&pk);
++ return ecdh;
++}
++
++struct crypto_ecdh * crypto_ecdh_init2(int group,
++ struct crypto_ec_key *own_key)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
++ struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
++ if (ecdh == NULL)
++ return NULL;
++ mbedtls_ecdh_init(&ecdh->ctx);
++ mbedtls_ecp_group_init(&ecdh->grp);
++ mbedtls_ecp_point_init(&ecdh->Q);
++ if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
++ && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
++ /* copy grp and Q for later use
++ * (retrieving this info later is more convoluted
++ * even if mbedtls_ecdh_make_public() is considered)*/
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ mbedtls_mpi d;
++ mbedtls_mpi_init(&d);
++ if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
++ mbedtls_mpi_free(&d);
++ return ecdh;
++ }
++ mbedtls_mpi_free(&d);
++ #else
++ if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
++ && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
++ return ecdh;
++ #endif
++ }
++
++ mbedtls_ecp_point_free(&ecdh->Q);
++ mbedtls_ecp_group_free(&ecdh->grp);
++ mbedtls_ecdh_free(&ecdh->ctx);
++ os_free(ecdh);
++ return NULL;
++}
++
++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
++{
++ mbedtls_ecp_group *grp = &ecdh->grp;
++ size_t prime_len = CRYPTO_EC_plen(grp);
++ size_t output_len = prime_len;
++ u8 output_offset = 0;
++ u8 buf[256];
++
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ /* len */
++ #endif
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
++ output_offset = 1;
++ }
++ #endif
++
++ if (output_len > sizeof(buf))
++ return NULL;
++
++ inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
++ if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
++ buf, output_len) == 0) {
++ return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
++ }
++
++ return NULL;
++}
++
++#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
++ mbedtls_mpi *bn,
++ int parity_bit)
++{
++ /* y^2 = x^3 + ax + b
++ * sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) */
++ mbedtls_mpi *cy2 = (mbedtls_mpi *)
++ crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
++ (const struct crypto_bignum *)bn); /*x*/
++ if (cy2 == NULL)
++ return -1;
++
++ /*mbedtls_mpi_free(bn);*/
++ /*(reuse bn to store result (y))*/
++
++ mbedtls_mpi exp;
++ mbedtls_mpi_init(&exp);
++ int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
++ || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
++ || mbedtls_mpi_add_int(&exp, &grp->P, 1)
++ || mbedtls_mpi_shift_r(&exp, 2)
++ || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
++ || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
++ && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
++ mbedtls_mpi_free(&exp);
++ mbedtls_mpi_free(cy2);
++ os_free(cy2);
++ return ret;
++}
++#endif
++
++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
++ const u8 *key, size_t len)
++{
++ if (len == 0) /*(invalid peer key)*/
++ return NULL;
++
++ mbedtls_ecp_group *grp = &ecdh->grp;
++
++ #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ /* add header for mbedtls_ecdh_read_public() */
++ u8 buf[256];
++ if (sizeof(buf)-1 < len)
++ return NULL;
++ buf[0] = (u8)(len);
++ os_memcpy(buf+1, key, len);
++
++ if (inc_y) {
++ if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
++ if (sizeof(buf)-2 < len)
++ return NULL;
++ buf[0] = (u8)(1+len);
++ buf[1] = 0x04;
++ os_memcpy(buf+2, key, len);
++ }
++ len >>= 1; /*(repurpose len to prime_len)*/
++ } else { /* (inc_y == 0) */
++ /* mbedtls_ecp_point_read_binary() does not currently support
++ * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
++ * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
++
++ /* derive y, amend buf[] with y for UNCOMPRESSED format */
++ if (sizeof(buf)-2 < len*2 || len == 0)
++ return NULL;
++
++ buf[0] = (u8)(1+len*2);
++ buf[1] = 0x04;
++ os_memcpy(buf+2, key, len);
++
++ mbedtls_mpi bn;
++ mbedtls_mpi_init(&bn);
++ int ret = mbedtls_mpi_read_binary(&bn, key, len)
++ || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
++ || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
++ mbedtls_mpi_free(&bn);
++ if (ret != 0)
++ return NULL;
++ }
++
++ if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
++ return NULL;
++ }
++ #endif
++ #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
++ return NULL;
++ }
++ #endif
++
++ struct wpabuf *buf = wpabuf_alloc(len);
++ if (buf == NULL)
++ return NULL;
++
++ if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
++ wpabuf_mhead(buf), len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0) {
++ wpabuf_put(buf, len);
++ return buf;
++ }
++
++ wpabuf_clear_free(buf);
++ return NULL;
++}
++
++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
++{
++ if (ecdh == NULL)
++ return;
++ mbedtls_ecp_point_free(&ecdh->Q);
++ mbedtls_ecp_group_free(&ecdh->grp);
++ mbedtls_ecdh_free(&ecdh->ctx);
++ os_free(ecdh);
++}
++
++size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
++{
++ return CRYPTO_EC_plen(&ecdh->grp);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++
++#include <mbedtls/ecp.h>
++
++struct crypto_ec *crypto_ec_init(int group)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ mbedtls_ecp_group *e = os_malloc(sizeof(*e));
++ if (e == NULL)
++ return NULL;
++ mbedtls_ecp_group_init(e);
++ if (mbedtls_ecp_group_load(e, grp_id) == 0)
++ return (struct crypto_ec *)e;
++
++ mbedtls_ecp_group_free(e);
++ os_free(e);
++ return NULL;
++}
++
++void crypto_ec_deinit(struct crypto_ec *e)
++{
++ mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
++ os_free(e);
++}
++
++size_t crypto_ec_prime_len(struct crypto_ec *e)
++{
++ return CRYPTO_EC_plen(e);
++}
++
++size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
++{
++ return CRYPTO_EC_pbits(e);
++}
++
++size_t crypto_ec_order_len(struct crypto_ec *e)
++{
++ return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
++}
++
++const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *)CRYPTO_EC_P(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *)CRYPTO_EC_N(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
++{
++ static const uint8_t secp256r1_a[] =
++ {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++ static const uint8_t secp384r1_a[] =
++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
++ static const uint8_t secp521r1_a[] =
++ {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xfc};
++ static const uint8_t secp192r1_a[] =
++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++ static const uint8_t secp224r1_a[] =
++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++ 0xff,0xff,0xff,0xfe};
++
++ const uint8_t *bin = NULL;
++ size_t len = 0;
++
++ /* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
++ switch (((mbedtls_ecp_group *)e)->id) {
++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP256R1:
++ bin = secp256r1_a;
++ len = sizeof(secp256r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP384R1:
++ bin = secp384r1_a;
++ len = sizeof(secp384r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP521R1:
++ bin = secp521r1_a;
++ len = sizeof(secp521r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP192R1:
++ bin = secp192r1_a;
++ len = sizeof(secp192r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++ case MBEDTLS_ECP_DP_SECP224R1:
++ bin = secp224r1_a;
++ len = sizeof(secp224r1_a);
++ break;
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++ case MBEDTLS_ECP_DP_BP256R1:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++ case MBEDTLS_ECP_DP_BP384R1:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++ case MBEDTLS_ECP_DP_BP512R1:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++ case MBEDTLS_ECP_DP_CURVE25519:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++ case MBEDTLS_ECP_DP_CURVE448:
++ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++ #endif
++ default:
++ return NULL;
++ }
++
++ /*(note: not thread-safe; returns file-scoped static storage)*/
++ if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
++ return (const struct crypto_bignum *)&mpi_sw_A;
++ return NULL;
++}
++
++const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *)CRYPTO_EC_B(e);
++}
++
++const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
++{
++ return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
++}
++
++struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++ if (p != NULL)
++ mbedtls_ecp_point_init(p);
++ return (struct crypto_ec_point *)p;
++}
++
++void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
++{
++ mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
++ os_free(p);
++}
++
++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
++ struct crypto_bignum *x)
++{
++ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++ return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
++ ? -1
++ : 0;
++}
++
++int crypto_ec_point_to_bin(struct crypto_ec *e,
++ const struct crypto_ec_point *point, u8 *x, u8 *y)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
++ size_t len = CRYPTO_EC_plen(e);
++ if (x) {
++ mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
++ if (mbedtls_mpi_write_binary(px, x, len))
++ return -1;
++ }
++ if (y) {
++ #if 0 /*(should not be necessary; py mpi should be in initial state)*/
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ os_memset(y, 0, len);
++ return 0;
++ }
++ #endif
++ #endif
++ mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
++ if (mbedtls_mpi_write_binary(py, y, len))
++ return -1;
++ }
++ return 0;
++}
++
++struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
++ const u8 *val)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ size_t len = CRYPTO_EC_plen(e);
++ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++ if (p == NULL)
++ return NULL;
++ mbedtls_ecp_point_init(p);
++
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
++ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++ mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
++
++ if (mbedtls_mpi_read_binary(px, val, len) == 0
++ && mbedtls_mpi_read_binary(py, val + len, len) == 0
++ && mbedtls_mpi_lset(pz, 1) == 0)
++ return (struct crypto_ec_point *)p;
++ #else
++ buf[0] = 0x04;
++ os_memcpy(buf+1, val, len*2);
++ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++ buf, 1+len*2) == 0)
++ return (struct crypto_ec_point *)p;
++ #endif
++ }
++ #endif
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ /* crypto.h interface documents crypto_ec_point_from_bin()
++ * val is length: prime_len * 2 and is big-endian
++ * (Short Weierstrass is assumed by hostap)
++ * Reverse to little-endian format for Montgomery */
++ for (unsigned int i = 0; i < len; ++i)
++ buf[i] = val[len-1-i];
++ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++ buf, len) == 0)
++ return (struct crypto_ec_point *)p;
++ }
++ #endif
++
++ mbedtls_ecp_point_free(p);
++ os_free(p);
++ return NULL;
++}
++
++int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
++ const struct crypto_ec_point *b,
++ struct crypto_ec_point *c)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ /* mbedtls does not provide an mbedtls_ecp_point add function */
++ mbedtls_mpi one;
++ mbedtls_mpi_init(&one);
++ int ret = mbedtls_mpi_lset(&one, 1)
++ || mbedtls_ecp_muladd(
++ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
++ &one, (const mbedtls_ecp_point *)a,
++ &one, (const mbedtls_ecp_point *)b) ? -1 : 0;
++ mbedtls_mpi_free(&one);
++ return ret;
++}
++
++int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
++ const struct crypto_bignum *b,
++ struct crypto_ec_point *res)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mbedtls_ecp_mul(
++ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
++ (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
++ mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++ /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
++ wpa_printf(MSG_ERROR,
++ "%s not implemented for Montgomery curves",__func__);
++ return -1;
++ }
++
++ /* mbedtls does not provide an mbedtls_ecp_point invert function */
++ /* below works for Short Weierstrass; incorrect for Montgomery curves */
++ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
++ || mbedtls_mpi_cmp_int(py, 0) == 0 /*point is its own inverse*/
++ || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
++}
++
++#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++static int
++crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++ mbedtls_mpi *y2)
++{
++ /* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS y^2 = x^3 + a x + b */
++
++ /* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
++ /* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
++ * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
++ * treated as if A = -3. */
++
++ #if 0
++ /* y^2 = x^3 + ax + b */
++ mbedtls_mpi *A = &e->A;
++ mbedtls_mpi t, A_neg3;
++ if (&e->A.p == NULL) {
++ mbedtls_mpi_init(&A_neg3);
++ if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
++ mbedtls_mpi_free(&A_neg3);
++ return -1;
++ }
++ A = &A_neg3;
++ }
++ mbedtls_mpi_init(&t);
++ int ret = /* x^3 */
++ mbedtls_mpi_lset(&t, 3)
++ || mbedtls_mpi_exp_mod(y2, x, &t, &e->P, NULL)
++ /* ax */
++ || mbedtls_mpi_mul_mpi(y2, y2, A)
++ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++ /* ax + b */
++ || mbedtls_mpi_add_mpi(&t, &t, &e->B)
++ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++ /* x^3 + ax + b */
++ || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
++ || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
++ mbedtls_mpi_free(&t);
++ if (A == &A_neg3)
++ mbedtls_mpi_free(&A_neg3);
++ return ret; /* 0: success, non-zero: failure */
++ #else
++ /* y^2 = x^3 + ax + b = (x^2 + a)x + b */
++ return /* x^2 */
++ mbedtls_mpi_mul_mpi(y2, x, x)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* x^2 + a */
++ || (e->A.MBEDTLS_PRIVATE(p)
++ ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
++ : mbedtls_mpi_sub_int(y2, y2, 3))
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x^2 + a)x */
++ || mbedtls_mpi_mul_mpi(y2, y2, x)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x^2 + a)x + b */
++ || mbedtls_mpi_add_mpi(y2, y2, &e->B)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++ #endif
++}
++#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
++
++#if 0 /* not used by hostap */
++#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++static int
++crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++ mbedtls_mpi *y2)
++{
++ /* XXX: !!! must be reviewed and audited for correctness !!! */
++
++ /* MBEDTLS_ECP_TYPE_MONTGOMERY y^2 = x^3 + a x^2 + x */
++
++ /* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
++ mbedtls_mpi x2;
++ mbedtls_mpi_init(&x2);
++ int ret = /* x^2 */
++ mbedtls_mpi_mul_mpi(&x2, x, x)
++ || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
++ /* x + a */
++ || mbedtls_mpi_add_mpi(y2, x, &e->A)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x + a)x^2 */
++ || mbedtls_mpi_mul_mpi(y2, y2, &x2)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++ /* (x + a)x^2 + x */
++ || mbedtls_mpi_add_mpi(y2, y2, x)
++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++ mbedtls_mpi_free(&x2);
++ return ret; /* 0: success, non-zero: failure */
++}
++#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
++#endif
++
++struct crypto_bignum *
++crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
++ const struct crypto_bignum *x)
++{
++ if (TEST_FAIL())
++ return NULL;
++
++ mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
++ if (y2 == NULL)
++ return NULL;
++ mbedtls_mpi_init(y2);
++
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++ && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
++ (const mbedtls_mpi *)x,
++ y2) == 0)
++ return (struct crypto_bignum *)y2;
++ #endif
++ #if 0 /* not used by hostap */
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++ == MBEDTLS_ECP_TYPE_MONTGOMERY
++ && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
++ (const mbedtls_mpi *)x,
++ y2) == 0)
++ return (struct crypto_bignum *)y2;
++ #endif
++ #endif
++
++ mbedtls_mpi_free(y2);
++ os_free(y2);
++ return NULL;
++}
++
++int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
++ const struct crypto_ec_point *p)
++{
++ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
++}
++
++int crypto_ec_point_is_on_curve(struct crypto_ec *e,
++ const struct crypto_ec_point *p)
++{
++ #if 1
++ return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
++ (const mbedtls_ecp_point *)p) == 0;
++ #else
++ /* compute y^2 mod P and compare to y^2 mod P */
++ /*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
++ const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++ mbedtls_mpi *cy2 = (mbedtls_mpi *)
++ crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
++ if (cy2 == NULL)
++ return 0;
++
++ mbedtls_mpi y2;
++ mbedtls_mpi_init(&y2);
++ const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++ int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
++ || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
++ || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
++
++ mbedtls_mpi_free(&y2);
++ mbedtls_mpi_free(cy2);
++ os_free(cy2);
++ return is_on_curve;
++ #endif
++}
++
++int crypto_ec_point_cmp(const struct crypto_ec *e,
++ const struct crypto_ec_point *a,
++ const struct crypto_ec_point *b)
++{
++ return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
++ (const mbedtls_ecp_point *)b);
++}
++
++#if !defined(CONFIG_NO_STDOUT_DEBUG)
++void crypto_ec_point_debug_print(const struct crypto_ec *e,
++ const struct crypto_ec_point *p,
++ const char *title)
++{
++ u8 x[MBEDTLS_MPI_MAX_SIZE];
++ u8 y[MBEDTLS_MPI_MAX_SIZE];
++ size_t len = CRYPTO_EC_plen(e);
++ /* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
++ struct crypto_ec *ee;
++ *(const struct crypto_ec **)&ee = e; /*(cast away const)*/
++ if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
++ if (title)
++ wpa_printf(MSG_DEBUG, "%s", title);
++ wpa_hexdump(MSG_DEBUG, "x:", x, len);
++ wpa_hexdump(MSG_DEBUG, "y:", y, len);
++ }
++}
++#endif
++
++
++struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
++{
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
++ #else
++ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0)
++ #endif
++ return (struct crypto_ec_key *)ctx;
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++#ifdef CONFIG_MODULE_TESTS
++/*(for crypto_module_tests.c)*/
++struct crypto_ec_key * crypto_ec_key_set_priv(int group,
++ const u8 *raw, size_t raw_len)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ const mbedtls_pk_info_t *pk_info =
++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++ if (pk_info == NULL)
++ return NULL;
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ if (mbedtls_pk_setup(ctx, pk_info) == 0
++ && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
++ return (struct crypto_ec_key *)ctx;
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++#endif
++#endif
++
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
++{
++ /* The following is modified from:
++ * mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
++ * mbedtls/library/pkparse.c:pk_get_pk_alg()
++ * mbedtls/library/pkparse.c:pk_use_ecparams()
++ */
++ mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
++ const mbedtls_pk_info_t *pk_info;
++ int ret;
++ size_t len;
++ const unsigned char *end = der+der_len;
++ unsigned char *p;
++ *(const unsigned char **)&p = der;
++
++ if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
++ {
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
++ }
++
++ end = p + len;
++
++ /*
++ if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
++ return( ret );
++ */
++ mbedtls_asn1_buf alg_oid, params;
++ memset( ¶ms, 0, sizeof(mbedtls_asn1_buf) );
++ if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, ¶ms ) ) != 0 )
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
++ if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
++ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++ if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
++
++ if( p + len != end )
++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
++ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
++
++ if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
++ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
++ return( ret );
++
++ /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
++ * has already run with ctx initialized up to pk_get_ecpubkey(),
++ * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
++ *
++ * mbedtls mbedtls_ecp_point_read_binary()
++ * does not handle point in COMPRESSED format
++ *
++ * (validate assumption that algorithm is EC) */
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++ if (ecp_kp == NULL)
++ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ mbedtls_ecp_group_id grp_id;
++
++
++ /* mbedtls/library/pkparse.c:pk_use_ecparams() */
++
++ if( params.tag == MBEDTLS_ASN1_OID )
++ {
++ if( mbedtls_oid_get_ec_grp( ¶ms, &grp_id ) != 0 )
++ return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
++ }
++ else
++ {
++#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
++ /*(large code block not copied from mbedtls; unsupported)*/
++ #if 0
++ if( ( ret = pk_group_id_from_specified( ¶ms, &grp_id ) ) != 0 )
++ return( ret );
++ #endif
++#endif
++ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++ }
++
++ /*
++ * grp may already be initialized; if so, make sure IDs match
++ */
++ if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
++ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++
++ if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
++ return( ret );
++
++
++ /* (validate assumption that EC point is in COMPRESSED format) */
++ len = CRYPTO_EC_plen(ecp_kp_grp);
++ if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++ || (end - p) != 1+len
++ || (*p != 0x02 && *p != 0x03) )
++ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++
++ /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
++ * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
++ mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
++ mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
++ mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
++ ret = mbedtls_mpi_lset(Z, 1);
++ if (ret != 0)
++ return( ret );
++ ret = mbedtls_mpi_read_binary(X, p+1, len);
++ if (ret != 0)
++ return( ret );
++ /* derive Y
++ * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
++ ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
++ || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
++ if (ret != 0)
++ return( ret );
++
++ return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
++}
++
++struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
++{
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ /*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
++ int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
++ if (rc == 0)
++ return (struct crypto_ec_key *)ctx;
++ else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
++ /* mbedtls mbedtls_ecp_point_read_binary()
++ * does not handle point in COMPRESSED format; parse internally */
++ rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
++ if (rc == 0)
++ return (struct crypto_ec_key *)ctx;
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++static struct crypto_ec_key *
++crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
++ const mbedtls_ecp_point *pub,
++ const u8 *buf, size_t len)
++{
++ const mbedtls_pk_info_t *pk_info =
++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++ if (pk_info == NULL)
++ return NULL;
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ if (mbedtls_pk_setup(ctx, pk_info) == 0) {
++ /* (Is private key generation necessary for callers?)
++ * alt: gen key then overwrite Q
++ * mbedtls_ecp_gen_key(grp_id, ecp_kp,
++ * mbedtls_ctr_drbg_random,
++ * crypto_mbedtls_ctr_drbg()) == 0
++ */
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++ if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
++ && (pub
++ ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
++ : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
++ buf, len) == 0)
++ && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0){
++ return (struct crypto_ec_key *)ctx;
++ }
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
++ const u8 *y, size_t len)
++{
++ mbedtls_ecp_group_id grp_id =
++ crypto_mbedtls_ecp_group_id_from_ike_id(group);
++ if (grp_id == MBEDTLS_ECP_DP_NONE)
++ return NULL;
++ if (len > MBEDTLS_MPI_MAX_SIZE)
++ return NULL;
++ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++ buf[0] = 0x04; /* assume x,y for Short Weierstrass */
++ os_memcpy(buf+1, x, len);
++ os_memcpy(buf+1+len, y, len);
++
++ return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
++}
++
++struct crypto_ec_key *
++crypto_ec_key_set_pub_point(struct crypto_ec *e,
++ const struct crypto_ec_point *pub)
++{
++ mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
++ mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
++ return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
++}
++
++
++struct crypto_ec_key * crypto_ec_key_gen(int group)
++{
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return NULL;
++ mbedtls_pk_init(ctx);
++ if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
++ return (struct crypto_ec_key *)ctx;
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++void crypto_ec_key_deinit(struct crypto_ec_key *key)
++{
++ mbedtls_pk_free((mbedtls_pk_context *)key);
++ os_free(key);
++}
++
++struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
++{
++ /* (similar to crypto_ec_key_get_pubkey_point(),
++ * but compressed point format and ASN.1 DER wrapping)*/
++#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++ unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
++ int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
++ buf, sizeof(buf));
++ if (len < 0)
++ return NULL;
++ /* Note: data is written at the end of the buffer! Use the
++ * return value to determine where you should start
++ * using the buffer */
++ unsigned char *p = buf+sizeof(buf)-len;
++
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ /* Note: sae_pk.c expects pubkey point in compressed format,
++ * but mbedtls_pk_write_pubkey_der() writes uncompressed format.
++ * Manually translate format and update lengths in DER format */
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++ unsigned char *end = buf+sizeof(buf);
++ size_t n;
++ /* SubjectPublicKeyInfo SEQUENCE */
++ mbedtls_asn1_get_tag(&p, end, &n,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ /* algorithm AlgorithmIdentifier */
++ unsigned char *a = p;
++ size_t alen;
++ mbedtls_asn1_get_tag(&p, end, &alen,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ p += alen;
++ alen = (size_t)(p - a);
++ /* subjectPublicKey BIT STRING */
++ mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
++ /* rewrite into compressed point format and rebuild ASN.1 */
++ p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
++ n = 1 + 1 + (n-2)/2;
++ len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
++ len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
++ os_memmove(p-alen, a, alen);
++ len += alen;
++ p -= alen;
++ len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
++ len += mbedtls_asn1_write_tag(&p, buf,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ }
++ #endif
++ return wpabuf_alloc_copy(p, (size_t)len);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
++ bool include_pub)
++{
++#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++ unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
++ int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
++ priv, sizeof(priv));
++ if (privlen < 0)
++ return NULL;
++
++ struct wpabuf *wbuf;
++
++ /* Note: data is written at the end of the buffer! Use the
++ * return value to determine where you should start
++ * using the buffer */
++ /* mbedtls_pk_write_key_der() includes publicKey in DER */
++ if (include_pub)
++ wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
++ else {
++ /* calculate publicKey offset and skip from end of buffer */
++ unsigned char *p = priv+sizeof(priv)-privlen;
++ unsigned char *end = priv+sizeof(priv);
++ size_t len;
++ /* ECPrivateKey SEQUENCE */
++ mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ /* version INTEGER */
++ unsigned char *v = p;
++ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
++ p += len;
++ /* privateKey OCTET STRING */
++ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
++ p += len;
++ /* parameters ECParameters */
++ mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
++ p += len;
++
++ /* write new SEQUENCE header (we know that it fits in priv[]) */
++ len = (size_t)(p - v);
++ p = v;
++ len += mbedtls_asn1_write_len(&p, priv, len);
++ len += mbedtls_asn1_write_tag(&p, priv,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ wbuf = wpabuf_alloc_copy(p, len);
++ }
++
++ forced_memzero(priv, sizeof(priv));
++ return wbuf;
++}
++
++struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
++ int prefix)
++{
++ /*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ size_t len = CRYPTO_EC_plen(grp);
++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++ /* len */
++ #endif
++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
++ len = len*2+1;
++ #endif
++ struct wpabuf *buf = wpabuf_alloc(len);
++ if (buf == NULL)
++ return NULL;
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
++ MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
++ wpabuf_mhead_u8(buf), len) == 0) {
++ if (!prefix) /* Remove 0x04 prefix if requested */
++ os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
++ wpabuf_put(buf, len);
++ return buf;
++ }
++
++ wpabuf_free(buf);
++ return NULL;
++}
++
++struct crypto_ec_point *
++crypto_ec_key_get_public_key(struct crypto_ec_key *key)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++ if (p != NULL) {
++ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++ mbedtls_ecp_point_init(p);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++ if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
++ mbedtls_ecp_point_free(p);
++ os_free(p);
++ p = NULL;
++ }
++ }
++ return (struct crypto_ec_point *)p;
++}
++
++struct crypto_bignum *
++crypto_ec_key_get_private_key(struct crypto_ec_key *key)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++ if (bn) {
++ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++ mbedtls_mpi_init(bn);
++ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++ if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
++ mbedtls_mpi_free(bn);
++ os_free(bn);
++ bn = NULL;
++ }
++ }
++ return (struct crypto_bignum *)bn;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
++{
++ /* get mbedtls_md_type_t from length of hash data to be signed */
++ switch (len) {
++ case 64: return MBEDTLS_MD_SHA512;
++ case 48: return MBEDTLS_MD_SHA384;
++ case 32: return MBEDTLS_MD_SHA256;
++ case 20: return MBEDTLS_MD_SHA1;
++ case 16: return MBEDTLS_MD_MD5;
++ default: return MBEDTLS_MD_NONE;
++ }
++}
++
++struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
++ size_t len)
++{
++ #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
++ #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
++ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
++ #else
++ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
++ #endif
++ #endif
++ size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
++ struct wpabuf *buf = wpabuf_alloc(sig_len);
++ if (buf == NULL)
++ return NULL;
++ if (mbedtls_pk_sign((mbedtls_pk_context *)key,
++ crypto_ec_key_sign_md(len), data, len,
++ wpabuf_mhead_u8(buf),
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ sig_len,
++ #endif
++ &sig_len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) == 0) {
++ wpabuf_put(buf, sig_len);
++ return buf;
++ }
++
++ wpabuf_free(buf);
++ return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
++ const u8 *data, size_t len)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return NULL;
++
++ size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
++ u8 buf[MBEDTLS_ECDSA_MAX_LEN];
++ if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
++ data, len, buf,
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ sig_len,
++ #endif
++ &sig_len,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg())) {
++ return NULL;
++ }
++
++ /*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
++ /* parse ASN.1 to get r and s and lengths */
++ u8 *p = buf, *r, *s;
++ u8 *end = p + sig_len;
++ size_t rlen, slen;
++ mbedtls_asn1_get_tag(&p, end, &rlen,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
++ r = p;
++ p += rlen;
++ mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
++ s = p;
++
++ /* write raw r and s into out
++ * (including removal of leading 0 if added for ASN.1 integer)
++ * note: DPP caller expects raw r, s each padded to prime len */
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
++ if (rlen > plen) {
++ r += (rlen - plen);
++ rlen = plen;
++ }
++ if (slen > plen) {
++ s += (slen - plen);
++ slen = plen;
++ }
++ struct wpabuf *out = wpabuf_alloc(plen*2);
++ if (out) {
++ wpabuf_put(out, plen*2);
++ p = wpabuf_mhead_u8(out);
++ os_memset(p, 0, plen*2);
++ os_memcpy(p+plen*1-rlen, r, rlen);
++ os_memcpy(p+plen*2-slen, s, slen);
++ }
++ return out;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
++ size_t len, const u8 *sig, size_t sig_len)
++{
++ switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
++ crypto_ec_key_sign_md(len), data, len,
++ sig, sig_len)) {
++ case 0:
++ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++ return 1;
++ case MBEDTLS_ERR_ECP_VERIFY_FAILED:
++ return 0;
++ default:
++ return -1;
++ }
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
++ const u8 *data, size_t len,
++ const u8 *r, size_t r_len,
++ const u8 *s, size_t s_len)
++{
++ /* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
++ * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return -1;
++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++
++ mbedtls_mpi mpi_r;
++ mbedtls_mpi mpi_s;
++ mbedtls_mpi_init(&mpi_r);
++ mbedtls_mpi_init(&mpi_s);
++ int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
++ || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
++ if (ret == 0) {
++ ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
++ ecp_kp_Q, &mpi_r, &mpi_s);
++ ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
++ }
++ mbedtls_mpi_free(&mpi_r);
++ mbedtls_mpi_free(&mpi_s);
++ return ret;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_group(struct crypto_ec_key *key)
++{
++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++ if (ecp_kp == NULL)
++ return -1;
++ mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
++ return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
++{
++#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++ (const mbedtls_pk_context *)key2) ? -1 : 0;
++ #else
++ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++ (const mbedtls_pk_context *)key2,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++ #endif
++#else
++ mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
++ mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
++ if (ecp_kp1 == NULL || ecp_kp2 == NULL)
++ return -1;
++ mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
++ mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
++ mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
++ return ecp_kp1_grp->id != ecp_kp2_grp->id
++ || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
++#endif
++}
++
++void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
++ const char *title)
++{
++ /* TBD: what info is desirable here and in what human readable format?*/
++ /*(crypto_openssl.c prints a human-readably public key and attributes)*/
++ #if 0
++ struct mbedtls_pk_debug_item debug_item;
++ if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
++ return;
++ /* ... */
++ #endif
++ wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
++
++#include <mbedtls/x509_csr.h>
++#include <mbedtls/oid.h>
++
++struct crypto_csr * crypto_csr_init(void)
++{
++ mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
++ if (csr != NULL)
++ mbedtls_x509write_csr_init(csr);
++ return (struct crypto_csr *)csr;
++}
++
++struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
++{
++ /* future: look for alternatives to MBEDTLS_PRIVATE() access */
++
++ /* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
++ * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
++ * so allocate different object (mbedtls_x509_csr *) and special-case
++ * object when used in crypto_csr_get_attribute() and when free()d in
++ * crypto_csr_deinit(). */
++
++ mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
++ if (csr == NULL)
++ return NULL;
++ mbedtls_x509_csr_init(csr);
++ const mbedtls_md_info_t *md_info;
++ unsigned char digest[MBEDTLS_MD_MAX_SIZE];
++ if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
++ && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
++ != NULL
++ && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
++ switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
++ digest, mbedtls_md_get_size(md_info),
++ csr->MBEDTLS_PRIVATE(sig).p,
++ csr->MBEDTLS_PRIVATE(sig).len)) {
++ case 0:
++ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++ return (struct crypto_csr *)((uintptr_t)csr | 1uL);
++ default:
++ break;
++ }
++ }
++
++ mbedtls_x509_csr_free(csr);
++ os_free(csr);
++ return NULL;
++}
++
++void crypto_csr_deinit(struct crypto_csr *csr)
++{
++ if ((uintptr_t)csr & 1uL) {
++ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++ mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
++ }
++ else
++ mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
++ os_free(csr);
++}
++
++int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
++ struct crypto_ec_key *key)
++{
++ mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
++ (mbedtls_pk_context *)key);
++ return 0;
++}
++
++int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
++ const char *name)
++{
++ /* specialized for src/common/dpp_crypto.c */
++
++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr()
++ * calls this function only once, using type == CSR_NAME_CN
++ * (If called more than once, this code would need to append
++ * components to the subject name, which we could do by
++ * appending to (mbedtls_x509write_csr *) private member
++ * mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
++
++ const char *label;
++ switch (type) {
++ case CSR_NAME_CN: label = "CN="; break;
++ case CSR_NAME_SN: label = "SN="; break;
++ case CSR_NAME_C: label = "C="; break;
++ case CSR_NAME_O: label = "O="; break;
++ case CSR_NAME_OU: label = "OU="; break;
++ default: return -1;
++ }
++
++ size_t len = strlen(name);
++ struct wpabuf *buf = wpabuf_alloc(3+len+1);
++ if (buf == NULL)
++ return -1;
++ wpabuf_put_data(buf, label, strlen(label));
++ wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
++ /* Note: 'name' provided is set as given and should be backslash-escaped
++ * by caller when necessary, e.g. literal ',' which are not separating
++ * components should be backslash-escaped */
++
++ int ret =
++ mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
++ wpabuf_head(buf)) ? -1 : 0;
++ wpabuf_free(buf);
++ return ret;
++}
++
++/* OBJ_pkcs9_challengePassword 1 2 840 113549 1 9 7 */
++static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
++
++int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
++ int attr_type, const u8 *value, size_t len)
++{
++ /* specialized for src/common/dpp_crypto.c */
++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++ * attr == CSR_ATTR_CHALLENGE_PASSWORD
++ * attr_type == ASN1_TAG_UTF8STRING */
++
++ const char *oid;
++ size_t oid_len;
++ switch (attr) {
++ case CSR_ATTR_CHALLENGE_PASSWORD:
++ oid = OBJ_pkcs9_challengePassword;
++ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++ break;
++ default:
++ return -1;
++ }
++
++ #if 0 /*(incorrect; sets an extension, not an attribute)*/
++ return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
++ oid, oid_len,
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ 0, /*(critical flag)*/
++ #endif
++ value, len) ? -1 : 0;
++ #else
++ (void)oid;
++ (void)oid_len;
++ #endif
++
++ /* mbedtls does not currently provide way to set an attribute in a CSR:
++ * https://github.com/Mbed-TLS/mbedtls/issues/4886 */
++ wpa_printf(MSG_ERROR,
++ "mbedtls does not currently support setting challengePassword "
++ "attribute in CSR");
++ return -1;
++}
++
++const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
++ const char *oid, size_t oid_len,
++ size_t *vlen, int *vtype)
++{
++ /* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
++ * so validation checks are not repeated here
++ *
++ * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
++ * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
++ * already parsed the rest of CertificationRequestInfo, some of which is
++ * repeated here to step to Attributes. Since csr->subject_raw.p points
++ * into csr->cri.p, which points into csr->raw.p, step over version and
++ * subject of CertificationRequestInfo (SEQUENCE) */
++ unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
++ unsigned char *end = csr->cri.p + csr->cri.len, *ext;
++ size_t len;
++
++ /* step over SubjectPublicKeyInfo */
++ mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++ p += len;
++
++ /* Attributes
++ * { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
++ */
++ if (mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
++ return NULL;
++ }
++ while (p < end) {
++ if (mbedtls_asn1_get_tag(&p, end, &len,
++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
++ return NULL;
++ }
++ ext = p;
++ p += len;
++
++ if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
++ return NULL;
++ if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
++ continue;
++
++ /* found oid; return value */
++ *vtype = *ext++; /* tag */
++ return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
++ }
++
++ return NULL;
++}
++
++const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
++ enum crypto_csr_attr attr,
++ size_t *len, int *type)
++{
++ /* specialized for src/common/dpp_crypto.c */
++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++ * attr == CSR_ATTR_CHALLENGE_PASSWORD */
++
++ const char *oid;
++ size_t oid_len;
++ switch (attr) {
++ case CSR_ATTR_CHALLENGE_PASSWORD:
++ oid = OBJ_pkcs9_challengePassword;
++ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++ break;
++ default:
++ return NULL;
++ }
++
++ /* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
++ if (!((uintptr_t)csr & 1uL))
++ return NULL;
++ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++
++ return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
++ oid, oid_len, len, type);
++}
++
++struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
++ struct crypto_ec_key *key,
++ enum crypto_hash_alg algo)
++{
++ mbedtls_md_type_t sig_md;
++ switch (algo) {
++ #ifdef MBEDTLS_SHA256_C
++ case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
++ #endif
++ #ifdef MBEDTLS_SHA512_C
++ case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
++ case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
++ #endif
++ default:
++ return NULL;
++ }
++ mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
++
++ #if 0
++ unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
++ | MBEDTLS_X509_KU_KEY_CERT_SIGN;
++ if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
++ key_usage))
++ return NULL;
++ #endif
++
++ #if 0
++ unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
++ | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
++ if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
++ ns_cert_type))
++ return NULL;
++ #endif
++
++ #if 0
++ /* mbedtls does not currently provide way to set an attribute in a CSR:
++ * https://github.com/Mbed-TLS/mbedtls/issues/4886
++ * XXX: hwsim dpp_enterprise test fails due to this limitation.
++ *
++ * Current usage of this function is solely by dpp_build_csr(),
++ * so as a kludge, might consider custom (struct crypto_csr *)
++ * containing (mbedtls_x509write_csr *) and a list of attributes
++ * (i.e. challengePassword). Might have to totally reimplement
++ * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
++ * handles signing the CSR. (This is more work that appending an
++ * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
++ */
++ #endif
++
++ unsigned char buf[4096]; /* XXX: large enough? too large? */
++ int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
++ buf, sizeof(buf),
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg());
++ if (len < 0)
++ return NULL;
++ /* Note: data is written at the end of the buffer! Use the
++ * return value to determine where you should start
++ * using the buffer */
++ return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
++
++#if 0
++#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
++#include <mbedtls/pem.h>
++#endif
++
++struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
++{
++ /* PKCS7 is not currently supported in mbedtls */
++ return NULL;
++
++#if 0
++ /* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
++ * (??? potential future contribution to mbedtls ???) */
++
++ /* Note: PKCS7 signature *is not* verified by this function.
++ * The function interface does not provide for passing a certificate */
++
++ mbedtls_pkcs7 mpkcs7;
++ mbedtls_pkcs7_init(&mpkcs7);
++ int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
++ wpabuf_len(pkcs7),
++ &mpkcs7);
++ wpabuf *buf = NULL;
++ do {
++ if (pkcs7_type < 0)
++ break;
++
++ /* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
++ * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
++ * (? are adding certificate headers and footers desired ?) */
++
++ /* development-pkcs7 branch does not currently provide
++ * additional interfaces to retrieve the parsed data */
++
++ mbedtls_x509_crt *certs =
++ &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
++ int ncerts =
++ mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
++
++ /* allocate buffer for PEM (base64-encoded DER)
++ * plus header, footer, newlines, and some extra */
++ buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
++ if (buf == NULL)
++ break;
++
++ #define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
++ #define PEM_END_CRT "-----END CERTIFICATE-----\n"
++ size_t olen;
++ for (int i = 0; i < ncerts; ++i) {
++ int ret = mbedtls_pem_write_buffer(
++ PEM_BEGIN_CRT, PEM_END_CRT,
++ certs[i].raw.p, certs[i].raw.len,
++ wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
++ &olen));
++ if (ret == 0)
++ wpabuf_put(buf, olen);
++ } else {
++ if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
++ ret = wpabuf_resize(
++ &buf,olen-wpabuf_tailroom(buf));
++ if (ret == 0) {
++ --i;/*(adjust loop iterator for retry)*/
++ continue;
++ }
++ wpabuf_free(buf);
++ buf = NULL;
++ break;
++ }
++ }
++ } while (0);
++
++ mbedtls_pkcs7_free(&mpkcs7);
++ return buf;
++#endif
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
++
++
++#ifdef MBEDTLS_ARC4_C
++#include <mbedtls/arc4.h>
++int rc4_skip(const u8 *key, size_t keylen, size_t skip,
++ u8 *data, size_t data_len)
++{
++ mbedtls_arc4_context ctx;
++ mbedtls_arc4_init(&ctx);
++ mbedtls_arc4_setup(&ctx, key, keylen);
++
++ if (skip) {
++ /*(prefer [16] on ancient hardware with smaller cache lines)*/
++ unsigned char skip_buf[64]; /*('skip' is generally small)*/
++ /*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
++ size_t len;
++ do {
++ len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
++ mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
++ } while ((skip -= len));
++ }
++
++ int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
++ mbedtls_arc4_free(&ctx);
++ return ret;
++}
++#endif
++
++
++/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
++__attribute_noinline__
++static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++ #if 0 /* #ifdef MBEDTLS_FS_IO */
++ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++ return -1;
++ }
++ #else
++ /*(use os_readfile() so that we can use os_free()
++ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++ * on buf aborts in tests if buf not allocated via os_malloc())*/
++ *buf = (u8 *)os_readfile(path, n);
++ if (!*buf) {
++ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++ return -1;
++ }
++ u8 *buf0 = os_realloc(*buf, *n+1);
++ if (!buf0) {
++ bin_clear_free(*buf, *n);
++ *buf = NULL;
++ return -1;
++ }
++ buf0[(*n)++] = '\0';
++ *buf = buf0;
++ #endif
++ return 0;
++}
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
++#ifdef MBEDTLS_RSA_C
++
++#include <mbedtls/pk.h>
++#include <mbedtls/rsa.h>
++
++struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
++{
++ /* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
++ * require #ifdef MBEDTLS_FS_IO in mbedtls library. Prefer to use
++ * crypto_mbedtls_readfile(), which wraps os_readfile() */
++ u8 *data;
++ size_t len;
++ if (crypto_mbedtls_readfile(file, &data, &len) != 0)
++ return NULL;
++
++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++ if (ctx == NULL) {
++ bin_clear_free(data, len);
++ return NULL;
++ }
++ mbedtls_pk_init(ctx);
++
++ int rc;
++ rc = (private_key
++ ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ ,mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg()
++ #endif
++ )
++ : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
++ && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
++
++ bin_clear_free(data, len);
++
++ if (rc) {
++ /* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
++ /* use MBEDTLS_MD_SHA256 for these hostap interfaces */
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ /*(no return value in mbedtls 2.x)*/
++ mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++ MBEDTLS_RSA_PKCS_V21,
++ MBEDTLS_MD_SHA256);
++ #else
++ if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++ MBEDTLS_RSA_PKCS_V21,
++ MBEDTLS_MD_SHA256) == 0)
++ #endif
++ return (struct crypto_rsa_key *)ctx;
++ }
++
++ mbedtls_pk_free(ctx);
++ os_free(ctx);
++ return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
++ const struct wpabuf *in)
++{
++ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++ size_t olen = mbedtls_rsa_get_len(pk_rsa);
++ struct wpabuf *buf = wpabuf_alloc(olen);
++ if (buf == NULL)
++ return NULL;
++
++ /* mbedtls_pk_encrypt() takes a few more hops to get to same func */
++ if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg(),
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ MBEDTLS_RSA_PRIVATE,
++ #endif
++ NULL, 0,
++ wpabuf_len(in), wpabuf_head(in),
++ wpabuf_put(buf, olen)) == 0) {
++ return buf;
++ }
++
++ wpabuf_clear_free(buf);
++ return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
++ const struct wpabuf *in)
++{
++ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++ size_t olen = mbedtls_rsa_get_len(pk_rsa);
++ struct wpabuf *buf = wpabuf_alloc(olen);
++ if (buf == NULL)
++ return NULL;
++
++ /* mbedtls_pk_decrypt() takes a few more hops to get to same func */
++ if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
++ mbedtls_ctr_drbg_random,
++ crypto_mbedtls_ctr_drbg(),
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ MBEDTLS_RSA_PUBLIC,
++ #endif
++ NULL, 0, &olen, wpabuf_head(in),
++ wpabuf_mhead(buf), olen) == 0) {
++ wpabuf_put(buf, olen);
++ return buf;
++ }
++
++ wpabuf_clear_free(buf);
++ return NULL;
++}
++
++void crypto_rsa_key_free(struct crypto_rsa_key *key)
++{
++ mbedtls_pk_free((mbedtls_pk_context *)key);
++ os_free(key);
++}
++
++#endif /* MBEDTLS_RSA_C */
++#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++
++struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
++ enum hpke_kdf_id kdf_id,
++ enum hpke_aead_id aead_id,
++ struct crypto_ec_key *peer_pub,
++ const u8 *info, size_t info_len,
++ const u8 *aad, size_t aad_len,
++ const u8 *pt, size_t pt_len)
++{
++ /* not yet implemented */
++ return NULL;
++}
++
++struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
++ enum hpke_kdf_id kdf_id,
++ enum hpke_aead_id aead_id,
++ struct crypto_ec_key *own_priv,
++ const u8 *info, size_t info_len,
++ const u8 *aad, size_t aad_len,
++ const u8 *enc_ct, size_t enc_ct_len)
++{
++ /* not yet implemented */
++ return NULL;
++}
++
++#endif
+diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
+index ffeddbadd..07c36d850 100644
+--- a/src/crypto/crypto_module_tests.c
++++ b/src/crypto/crypto_module_tests.c
+@@ -2470,6 +2470,139 @@ static int test_hpke(void)
+ }
+
+
++static int test_ecc(void)
++{
++#ifdef CONFIG_ECC
++#ifndef CONFIG_TLS_INTERNAL
++#ifndef CONFIG_TLS_GNUTLS
++#if defined(CONFIG_TLS_MBEDTLS) \
++ || defined(CONFIG_TLS_OPENSSL) \
++ || defined(CONFIG_TLS_WOLFSSL)
++ wpa_printf(MSG_INFO, "Testing ECC");
++ /* Note: some tests below are valid on supported Short Weierstrass
++ * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
++ * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
++ */
++#ifdef CONFIG_TLS_MBEDTLS
++ const int grps[] = {19, 20, 21, 25, 26, 28};
++#endif
++#ifdef CONFIG_TLS_OPENSSL
++ const int grps[] = {19, 20, 21, 26};
++#endif
++#ifdef CONFIG_TLS_WOLFSSL
++ const int grps[] = {19, 20, 21, 26};
++#endif
++ uint32_t i;
++ struct crypto_ec *e = NULL;
++ struct crypto_ec_point *p = NULL, *q = NULL;
++ struct crypto_bignum *x = NULL, *y = NULL;
++#ifdef CONFIG_DPP
++ u8 bin[4096];
++#endif
++ for (i = 0; i < ARRAY_SIZE(grps); ++i) {
++ e = crypto_ec_init(grps[i]);
++ if (e == NULL
++ || crypto_ec_prime_len(e) == 0
++ || crypto_ec_prime_len_bits(e) == 0
++ || crypto_ec_order_len(e) == 0
++ || crypto_ec_get_prime(e) == NULL
++ || crypto_ec_get_order(e) == NULL
++ || crypto_ec_get_a(e) == NULL
++ || crypto_ec_get_b(e) == NULL
++ || crypto_ec_get_generator(e) == NULL) {
++ break;
++ }
++#ifdef CONFIG_DPP
++ struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
++ if (key == NULL)
++ break;
++ p = crypto_ec_key_get_public_key(key);
++ q = crypto_ec_key_get_public_key(key);
++ crypto_ec_key_deinit(key);
++ if (p == NULL || q == NULL)
++ break;
++ if (!crypto_ec_point_is_on_curve(e, p))
++ break;
++
++ /* inverted point should not match original;
++ * double-invert should match */
++ if (crypto_ec_point_invert(e, q) != 0
++ || crypto_ec_point_cmp(e, p, q) == 0
++ || crypto_ec_point_invert(e, q) != 0
++ || crypto_ec_point_cmp(e, p, q) != 0) {
++ break;
++ }
++
++ /* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
++ * imbalanced interfaces? */
++ size_t prime_len = crypto_ec_prime_len(e);
++ if (prime_len * 2 > sizeof(bin))
++ break;
++ if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
++ break;
++ struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
++ if (tmp == NULL)
++ break;
++ if (crypto_ec_point_cmp(e, p, tmp) != 0) {
++ crypto_ec_point_deinit(tmp, 0);
++ break;
++ }
++ crypto_ec_point_deinit(tmp, 0);
++
++ x = crypto_bignum_init();
++ y = crypto_bignum_init_set(bin+prime_len, prime_len);
++ if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
++ break;
++ struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
++ if (y2 == NULL)
++ break;
++ if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
++ || crypto_bignum_cmp(y, y2) != 0) {
++ crypto_bignum_deinit(y2, 0);
++ break;
++ }
++ crypto_bignum_deinit(y2, 0);
++ crypto_bignum_deinit(x, 0);
++ crypto_bignum_deinit(y, 0);
++ x = NULL;
++ y = NULL;
++
++ x = crypto_bignum_init();
++ if (x == NULL)
++ break;
++ if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
++ break;
++ crypto_bignum_deinit(x, 0);
++ x = NULL;
++
++ crypto_ec_point_deinit(p, 0);
++ p = NULL;
++ crypto_ec_point_deinit(q, 0);
++ q = NULL;
++#endif /* CONFIG_DPP */
++ crypto_ec_deinit(e);
++ e = NULL;
++ }
++ if (i != ARRAY_SIZE(grps)) {
++ crypto_bignum_deinit(x, 0);
++ crypto_bignum_deinit(y, 0);
++ crypto_ec_point_deinit(p, 0);
++ crypto_ec_point_deinit(q, 0);
++ crypto_ec_deinit(e);
++ wpa_printf(MSG_INFO,
++ "ECC test case failed tls_id:%d", grps[i]);
++ return -1;
++ }
++
++ wpa_printf(MSG_INFO, "ECC test cases passed");
++#endif
++#endif /* !CONFIG_TLS_GNUTLS */
++#endif /* !CONFIG_TLS_INTERNAL */
++#endif /* CONFIG_ECC */
++ return 0;
++}
++
++
+ static int test_ms_funcs(void)
+ {
+ #ifndef CONFIG_FIPS
+@@ -2591,6 +2724,7 @@ int crypto_module_tests(void)
+ test_fips186_2_prf() ||
+ test_extract_expand_hkdf() ||
+ test_hpke() ||
++ test_ecc() ||
+ test_ms_funcs())
+ ret = -1;
+
+diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
+index 269174321..a554fd8a8 100644
+--- a/src/crypto/crypto_wolfssl.c
++++ b/src/crypto/crypto_wolfssl.c
+@@ -1633,6 +1633,7 @@ struct crypto_ec {
+ #ifdef CONFIG_DPP
+ ecc_point *g; /* Only used in DPP for now */
+ #endif /* CONFIG_DPP */
++ WC_RNG rng;
+ mp_int a;
+ mp_int prime;
+ mp_int order;
+@@ -1666,6 +1667,20 @@ struct crypto_ec * crypto_ec_init(int group)
+ e->key = ecc_key_init();
+ if (!e->key) {
+ LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
++
++ if (wc_ecc_init(e->key) != 0 ||
++ wc_InitRng(e->rng) != 0 ||
++ wc_ecc_set_rng(e->key, e->rng) != 0 ||
++ wc_ecc_set_curve(e->key, 0, curve_id) != 0 ||
++ mp_init(e->a) != MP_OKAY ||
++ mp_init(e->prime) != MP_OKAY ||
++ mp_init(e->order) != MP_OKAY ||
++ mp_init(e->b) != MP_OKAY ||
++ mp_read_radix(e->a, e->key.dp->Af, 16) != MP_OKAY ||
++ mp_read_radix(e->b, e->key.dp->Bf, 16) != MP_OKAY ||
++ mp_read_radix(e->prime, e->key.dp->prime, 16) != MP_OKAY ||
++ mp_read_radix(e->order, e->key.dp->order, 16) != MP_OKAY ||
++ mp_montgomery_setup(e->prime, e->mont_b) != MP_OKAY)
+ goto done;
+ }
+
+@@ -1764,6 +1779,9 @@ void crypto_ec_deinit(struct crypto_ec* e)
+ #endif /* CONFIG_DPP */
+ if (e->own_key)
+ ecc_key_deinit(e->key);
++
++ wc_FreeRng(e->rng);
++ wc_ecc_free(e->key);
+ os_free(e);
+ }
+
+diff --git a/src/crypto/tls_mbedtls.c b/src/crypto/tls_mbedtls.c
+new file mode 100644
+index 000000000..2580a3a27
+--- /dev/null
++++ b/src/crypto/tls_mbedtls.c
+@@ -0,0 +1,3313 @@
++/*
++ * SSL/TLS interface functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ *
++ * template: src/crypto/tls_none.c
++ * reference: src/crypto/tls_*.c
++ *
++ * Known Limitations:
++ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
++ * - no OCSP (not yet available in mbedtls)
++ * - mbedtls does not support all certificate encodings used by hwsim tests
++ * PCKS#5 v1.5
++ * PCKS#12
++ * DH DSA
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - mbedtls does not currently provide way to set an attribute in a CSR
++ * https://github.com/Mbed-TLS/mbedtls/issues/4886
++ * so tests/hwsim dpp_enterprise tests fail
++ * - DPP2 not supported
++ * PKCS#7 parsing is not supported in mbedtls
++ * See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
++ * - DPP3 not supported
++ * hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
++ *
++ * Status:
++ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
++ * (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
++ * (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
++ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
++ * - passes all tests/ crypto module tests (incomplete coverage)
++ * ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
++ * - passes almost all tests/hwsim tests
++ * (hwsim tests skipped for missing features)
++ *
++ * RFE:
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - client/server session resumption, and/or save client session ticket
++ */
++
++#include "includes.h"
++#include "common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++#include <mbedtls/pem.h>
++#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/ssl.h>
++#include <mbedtls/ssl_ticket.h>
++#include <mbedtls/x509.h>
++#include <mbedtls/x509_crt.h>
++
++#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
++#include <mbedtls/net_sockets.h>
++#else
++#include <mbedtls/net.h>
++#endif
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
++ ((ssl)->MBEDTLS_PRIVATE(session) \
++ ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
++ : 0)
++#define mbedtls_ssl_ciphersuite_get_name(info) \
++ (info)->MBEDTLS_PRIVATE(name)
++#endif
++
++#include "crypto.h" /* sha256_vector() */
++#include "tls.h"
++
++#ifndef SHA256_DIGEST_LENGTH
++#define SHA256_DIGEST_LENGTH 32
++#endif
++
++#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
++#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
++#endif
++
++#ifndef MBEDTLS_EXPKEY_RAND_LEN
++#define MBEDTLS_EXPKEY_RAND_LEN 32
++#endif
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
++#else /*(not implemented; return error)*/
++#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
++typedef mbedtls_tls_prf_types int;
++#endif
++
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#ifdef MBEDTLS_SSL_SESSION_TICKETS
++#ifdef MBEDTLS_SSL_TICKET_C
++#define TLS_MBEDTLS_SESSION_TICKETS
++#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#define TLS_MBEDTLS_EAP_TEAP
++#endif
++#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define TLS_MBEDTLS_EAP_FAST
++#endif
++#endif
++#endif
++#endif
++#endif
++
++
++struct tls_conf {
++ mbedtls_ssl_config conf;
++
++ unsigned int verify_peer:1;
++ unsigned int verify_depth0_only:1;
++ unsigned int check_crl:2; /*(needs :2 bits for 0, 1, 2)*/
++ unsigned int check_crl_strict:1; /*(needs :1 bit for 0, 1)*/
++ unsigned int ca_cert_probe:1;
++ unsigned int has_ca_cert:1;
++ unsigned int has_client_cert:1;
++ unsigned int has_private_key:1;
++ unsigned int suiteb128:1;
++ unsigned int suiteb192:1;
++ mbedtls_x509_crl *crl;
++ mbedtls_x509_crt ca_cert;
++ mbedtls_x509_crt client_cert;
++ mbedtls_pk_context private_key;
++
++ uint32_t refcnt;
++
++ unsigned int flags;
++ char *subject_match;
++ char *altsubject_match;
++ char *suffix_match;
++ char *domain_match;
++ char *check_cert_subject;
++ u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
++
++ int *ciphersuites; /* list of ciphersuite ids for mbedtls_ssl_config */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++ mbedtls_ecp_group_id *curves;
++#else
++ uint16_t *curves; /* list of curve ids for mbedtls_ssl_config */
++#endif
++};
++
++
++struct tls_global {
++ struct tls_conf *tls_conf;
++ char *ocsp_stapling_response;
++ mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ mbedtls_ssl_ticket_context ticket_ctx;
++ #endif
++ char *ca_cert_file;
++ struct os_reltime crl_reload_previous;
++ unsigned int crl_reload_interval;
++ uint32_t refcnt;
++ struct tls_config init_conf;
++};
++
++static struct tls_global tls_ctx_global;
++
++
++struct tls_connection {
++ struct tls_conf *tls_conf;
++ struct wpabuf *push_buf;
++ struct wpabuf *pull_buf;
++ size_t pull_buf_offset;
++
++ unsigned int established:1;
++ unsigned int resumed:1;
++ unsigned int verify_peer:1;
++ unsigned int is_server:1;
++
++ mbedtls_ssl_context ssl;
++
++ mbedtls_tls_prf_types tls_prf_type;
++ size_t expkey_keyblock_size;
++ size_t expkey_secret_len;
++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++ unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
++ #else
++ unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
++ #endif
++ unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
++
++ int read_alerts, write_alerts, failed;
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ tls_session_ticket_cb session_ticket_cb;
++ void *session_ticket_cb_ctx;
++ unsigned char *clienthello_session_ticket;
++ size_t clienthello_session_ticket_len;
++ #endif
++ char *peer_subject; /* peer subject info for authenticated peer */
++ struct wpabuf *success_data;
++};
++
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__ __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__ __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsg(int level, const char * const msg)
++{
++ wpa_printf(level, "MTLS: %s", msg);
++}
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsgrc(int level, const char * const msg, int rc)
++{
++ #ifdef MBEDTLS_ERROR_C
++ /* error logging convenience function that decodes mbedtls result codes */
++ char buf[256];
++ mbedtls_strerror(rc, buf, sizeof(buf));
++ wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
++ #else
++ wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
++ #endif
++}
++
++
++#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
++#define ilog(rc, msg) emsgrc(MSG_INFO, (msg), (rc))
++
++
++struct tls_conf * tls_conf_init(void *tls_ctx)
++{
++ struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
++ if (tls_conf == NULL)
++ return NULL;
++ tls_conf->refcnt = 1;
++
++ mbedtls_ssl_config_init(&tls_conf->conf);
++ mbedtls_ssl_conf_rng(&tls_conf->conf,
++ mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
++ mbedtls_x509_crt_init(&tls_conf->ca_cert);
++ mbedtls_x509_crt_init(&tls_conf->client_cert);
++ mbedtls_pk_init(&tls_conf->private_key);
++
++ return tls_conf;
++}
++
++
++void tls_conf_deinit(struct tls_conf *tls_conf)
++{
++ if (tls_conf == NULL || --tls_conf->refcnt != 0)
++ return;
++
++ mbedtls_x509_crt_free(&tls_conf->ca_cert);
++ mbedtls_x509_crt_free(&tls_conf->client_cert);
++ if (tls_conf->crl) {
++ mbedtls_x509_crl_free(tls_conf->crl);
++ os_free(tls_conf->crl);
++ }
++ mbedtls_pk_free(&tls_conf->private_key);
++ mbedtls_ssl_config_free(&tls_conf->conf);
++ os_free(tls_conf->curves);
++ os_free(tls_conf->ciphersuites);
++ os_free(tls_conf->subject_match);
++ os_free(tls_conf->altsubject_match);
++ os_free(tls_conf->suffix_match);
++ os_free(tls_conf->domain_match);
++ os_free(tls_conf->check_cert_subject);
++ os_free(tls_conf);
++}
++
++
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++
++__attribute_cold__
++void * tls_init(const struct tls_config *conf)
++{
++ /* RFE: review struct tls_config *conf (different from tls_conf) */
++
++ if (++tls_ctx_global.refcnt > 1)
++ return &tls_ctx_global;
++
++ tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
++ mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
++ mbedtls_ctr_drbg_random,
++ tls_ctx_global.ctr_drbg,
++ MBEDTLS_CIPHER_AES_256_GCM,
++ 43200); /* ticket timeout: 12 hours */
++ #endif
++ /* copy struct for future use */
++ tls_ctx_global.init_conf = *conf;
++ if (conf->openssl_ciphers)
++ tls_ctx_global.init_conf.openssl_ciphers =
++ os_strdup(conf->openssl_ciphers);
++
++ tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
++ os_get_reltime(&tls_ctx_global.crl_reload_previous);
++
++ return &tls_ctx_global;
++}
++
++
++__attribute_cold__
++void tls_deinit(void *tls_ctx)
++{
++ if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
++ return;
++
++ tls_conf_deinit(tls_ctx_global.tls_conf);
++ os_free(tls_ctx_global.ca_cert_file);
++ os_free(tls_ctx_global.ocsp_stapling_response);
++ char *openssl_ciphers; /*(allocated in tls_init())*/
++ *(const char **)&openssl_ciphers =
++ tls_ctx_global.init_conf.openssl_ciphers;
++ os_free(openssl_ciphers);
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
++ #endif
++ os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
++}
++
++
++int tls_get_errors(void *tls_ctx)
++{
++ return 0;
++}
++
++
++static void tls_connection_deinit_expkey(struct tls_connection *conn)
++{
++ conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
++ conn->expkey_keyblock_size = 0;
++ conn->expkey_secret_len = 0;
++ forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
++ forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
++{
++ if (conn->clienthello_session_ticket) {
++ mbedtls_platform_zeroize(conn->clienthello_session_ticket,
++ conn->clienthello_session_ticket_len);
++ mbedtls_free(conn->clienthello_session_ticket);
++ conn->clienthello_session_ticket = NULL;
++ conn->clienthello_session_ticket_len = 0;
++ }
++}
++#endif
++
++
++void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
++{
++ if (conn == NULL)
++ return;
++
++ #if 0 /*(good intention, but never sent since we destroy self below)*/
++ if (conn->established)
++ mbedtls_ssl_close_notify(&conn->ssl);
++ #endif
++
++ if (conn->tls_prf_type)
++ tls_connection_deinit_expkey(conn);
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ if (conn->clienthello_session_ticket)
++ tls_connection_deinit_clienthello_session_ticket(conn);
++ #endif
++
++ os_free(conn->peer_subject);
++ wpabuf_free(conn->success_data);
++ wpabuf_free(conn->push_buf);
++ wpabuf_free(conn->pull_buf);
++ mbedtls_ssl_free(&conn->ssl);
++ tls_conf_deinit(conn->tls_conf);
++ os_free(conn);
++}
++
++
++static void tls_mbedtls_refresh_crl(void);
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
++
++struct tls_connection * tls_connection_init(void *tls_ctx)
++{
++ struct tls_connection *conn = os_zalloc(sizeof(*conn));
++ if (conn == NULL)
++ return NULL;
++
++ mbedtls_ssl_init(&conn->ssl);
++
++ conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
++ if (conn->tls_conf) {
++ ++conn->tls_conf->refcnt;
++ /* check for CRL refresh if inheriting from global config */
++ tls_mbedtls_refresh_crl();
++
++ conn->verify_peer = conn->tls_conf->verify_peer;
++ if (tls_mbedtls_ssl_setup(conn) != 0) {
++ tls_connection_deinit(&tls_ctx_global, conn);
++ return NULL;
++ }
++ }
++
++ return conn;
++}
++
++
++int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? conn->established : 0;
++}
++
++
++__attribute_noinline__
++char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
++{
++ /* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
++ * colons, so generate the hex serial number here. The func
++ * wpa_snprintf_hex_uppercase() is similarly inefficient. */
++ size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
++ while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
++ if (i == crt->serial.len) --i;
++
++ const unsigned char *s = crt->serial.p + i;
++ const size_t e = (crt->serial.len - i) * 2;
++ if (e >= len)
++ return NULL;
++ #if 0
++ wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
++ #else
++ for (i = 0; i < e; i+=2, ++s) {
++ serial_num[i+0] = "0123456789ABCDEF"[(*s >> 4)];
++ serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
++ }
++ serial_num[e] = '\0';
++ #endif
++ return serial_num;
++}
++
++
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
++ if (crt == NULL)
++ return NULL;
++ size_t len = crt->serial.len * 2 + 1;
++ char *serial_num = os_malloc(len);
++ if (!serial_num)
++ return NULL;
++ return tls_mbedtls_peer_serial_num(crt, serial_num, len);
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn);
++
++int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
++{
++ /* Note: this function called from eap_peer_tls_reauth_init()
++ * for session resumption, not for connection shutdown */
++
++ if (conn == NULL)
++ return -1;
++
++ tls_pull_buf_reset(conn);
++ wpabuf_free(conn->push_buf);
++ conn->push_buf = NULL;
++ conn->established = 0;
++ conn->resumed = 0;
++ if (conn->tls_prf_type)
++ tls_connection_deinit_expkey(conn);
++
++ /* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
++
++ return mbedtls_ssl_session_reset(&conn->ssl);
++}
++
++
++static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
++ const unsigned char *data, size_t dlen)
++{
++ if (wpabuf_resize(buf, dlen) < 0)
++ return 0;
++ wpabuf_put_data(*buf, data, dlen);
++ return 1;
++}
++
++
++static int tls_pull_buf_append(struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ /*(interface does not lend itself to move semantics)*/
++ return tls_wpabuf_resize_put_data(&conn->pull_buf,
++ wpabuf_head(in_data),
++ wpabuf_len(in_data));
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn)
++{
++ /*(future: might consider reusing conn->pull_buf)*/
++ wpabuf_free(conn->pull_buf);
++ conn->pull_buf = NULL;
++ conn->pull_buf_offset = 0;
++}
++
++
++__attribute_cold__
++static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
++{
++ size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++ if (discard)
++ wpa_printf(MSG_DEBUG,
++ "%s - %zu bytes remaining in pull_buf; discarding",
++ func, discard);
++ tls_pull_buf_reset(conn);
++}
++
++
++static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
++{
++ struct tls_connection *conn = (struct tls_connection *) ptr;
++ if (conn->pull_buf == NULL)
++ return MBEDTLS_ERR_SSL_WANT_READ;
++ const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++ if (dlen == 0)
++ return MBEDTLS_ERR_SSL_WANT_READ;
++
++ if (len > dlen)
++ len = dlen;
++ os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
++
++ if (len == dlen) {
++ tls_pull_buf_reset(conn);
++ /*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
++ }
++ else {
++ conn->pull_buf_offset += len;
++ /*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
++ __func__, dlen - len);*/
++ }
++ return (int)len;
++}
++
++
++static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
++{
++ struct tls_connection *conn = (struct tls_connection *) ptr;
++ return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
++ ? (int)len
++ : MBEDTLS_ERR_SSL_ALLOC_FAILED;
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
++
++
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
++{
++ #if 0
++ /* mbedtls_ssl_setup() must be called only once */
++ /* If this func might be called multiple times (e.g. via set_params),
++ * then we should set a flag in conn that ssl was initialized */
++ if (conn->ssl_is_init) {
++ mbedtls_ssl_free(&conn->ssl);
++ mbedtls_ssl_init(&conn->ssl);
++ }
++ #endif
++
++ int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
++ if (ret != 0) {
++ elog(ret, "mbedtls_ssl_setup");
++ return -1;
++ }
++
++ mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ mbedtls_ssl_set_export_keys_cb(
++ &conn->ssl, tls_connection_export_keys_cb, conn);
++ #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++ mbedtls_ssl_conf_export_keys_ext_cb(
++ &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
++ #endif
++ if (conn->verify_peer)
++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++ return 0;
++}
++
++
++static int tls_mbedtls_data_is_pem(const u8 *data)
++{
++ return (NULL != os_strstr((char *)data, "-----"));
++}
++
++
++static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
++ mbedtls_ssl_config *conf)
++{
++ #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
++ #endif
++
++ /* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
++ if (tls_conf->flags & TLS_CONN_SUITEB) {
++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
++ }
++
++ const unsigned int flags = tls_conf->flags;
++
++ /* attempt to map flags to min and max TLS protocol version */
++
++ int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_3)
++ ? 4
++ : 3
++ : 2
++ : 1
++ : 0;
++
++ int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
++ ? (flags & TLS_CONN_DISABLE_TLSv1_0)
++ ? -1
++ : 0
++ : 1
++ : 2
++ : 3;
++
++ if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
++ if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
++ if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
++ if (max < min) {
++ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++ return;
++ }
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ /* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
++ if (min < 2 || max < 2) {
++ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++ if (min < 2) min = 2;
++ if (max < 2) max = 2;
++ }
++ #endif
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ /* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
++ /* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
++ min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++ max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++ mbedtls_ssl_conf_min_tls_version(conf, min);
++ mbedtls_ssl_conf_max_tls_version(conf, max);
++ #else
++ #ifndef MBEDTLS_SSL_MINOR_VERSION_4
++ if (min == 3) min = 2;
++ if (max == 3) max = 2;
++ #endif
++ /* MBEDTLS_SSL_MINOR_VERSION_0 0 *//*!< SSL v3.0 */
++ /* MBEDTLS_SSL_MINOR_VERSION_1 1 *//*!< TLS v1.0 */
++ /* MBEDTLS_SSL_MINOR_VERSION_2 2 *//*!< TLS v1.1 */
++ /* MBEDTLS_SSL_MINOR_VERSION_3 3 *//*!< TLS v1.2 */
++ /* MBEDTLS_SSL_MINOR_VERSION_4 4 *//*!< TLS v1.3 */
++ mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
++ mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
++ #endif
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
++
++
++static int
++tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
++{
++ size_t len;
++ u8 *data;
++ if (tls_mbedtls_readfile(dh_file, &data, &len))
++ return 0;
++
++ /* parse only if DH parameters if in PEM format */
++ if (tls_mbedtls_data_is_pem(data)
++ && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
++ if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
++ wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
++ else
++ wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
++ forced_memzero(data, len);
++ os_free(data);
++ return 0;
++ }
++
++ /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
++ mbedtls_dhm_context dhm;
++ mbedtls_dhm_init(&dhm);
++ int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
++ if (0 == rc)
++ rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
++ if (0 != rc)
++ elog(rc, dh_file);
++ mbedtls_dhm_free(&dhm);
++
++ forced_memzero(data, len);
++ os_free(data);
++ return (0 == rc);
++}
++
++
++/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
++ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++static int
++tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
++{
++ if (1 >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR, "error: too many curves during list expand");
++ return -1;
++ }
++ ids[++nids] = id;
++ return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++ mbedtls_ecp_group_id ids[512];
++ int nids = -1;
++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++ for (const char *e = curvelist-1; e; ) {
++ const char * const n = e+1;
++ e = os_strchr(n, ':');
++ size_t len = e ? (size_t)(e - n) : os_strlen(n);
++ mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
++ switch (len) {
++ case 5:
++ if (0 == os_memcmp("P-521", n, 5))
++ grp_id = MBEDTLS_ECP_DP_SECP521R1;
++ else if (0 == os_memcmp("P-384", n, 5))
++ grp_id = MBEDTLS_ECP_DP_SECP384R1;
++ else if (0 == os_memcmp("P-256", n, 5))
++ grp_id = MBEDTLS_ECP_DP_SECP256R1;
++ break;
++ case 6:
++ if (0 == os_memcmp("BP-521", n, 6))
++ grp_id = MBEDTLS_ECP_DP_BP512R1;
++ else if (0 == os_memcmp("BP-384", n, 6))
++ grp_id = MBEDTLS_ECP_DP_BP384R1;
++ else if (0 == os_memcmp("BP-256", n, 6))
++ grp_id = MBEDTLS_ECP_DP_BP256R1;
++ break;
++ default:
++ break;
++ }
++ if (grp_id != MBEDTLS_ECP_DP_NONE) {
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
++ if (-1 == nids) return 0;
++ continue;
++ }
++ /* similar to mbedtls_ecp_curve_info_from_name() */
++ const mbedtls_ecp_curve_info *info;
++ for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
++ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++ break;
++ }
++ if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
++ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++ return 0;
++ }
++
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
++ if (-1 == nids) return 0;
++ }
++
++ /* mod_openssl configures "prime256v1" if curve list not specified,
++ * but mbedtls provides a list of supported curves if not explicitly set */
++ if (-1 == nids) return 1; /* empty list; no-op */
++
++ ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
++ ++nids;
++
++ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++ tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
++ if (tls_conf->curves == NULL)
++ return 0;
++ os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
++
++ mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
++ return 1;
++}
++#else
++static int
++tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
++{
++ if (1 >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR, "error: too many curves during list expand");
++ return -1;
++ }
++ ids[++nids] = id;
++ return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++ /* TLS Supported Groups (renamed from "EC Named Curve Registry")
++ * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
++ */
++ uint16_t ids[512];
++ int nids = -1;
++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++ for (const char *e = curvelist-1; e; ) {
++ const char * const n = e+1;
++ e = os_strchr(n, ':');
++ size_t len = e ? (size_t)(e - n) : os_strlen(n);
++ uint16_t tls_id = 0;
++ switch (len) {
++ case 5:
++ if (0 == os_memcmp("P-521", n, 5))
++ tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
++ else if (0 == os_memcmp("P-384", n, 5))
++ tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
++ else if (0 == os_memcmp("P-256", n, 5))
++ tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
++ break;
++ case 6:
++ if (0 == os_memcmp("BP-521", n, 6))
++ tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
++ else if (0 == os_memcmp("BP-384", n, 6))
++ tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
++ else if (0 == os_memcmp("BP-256", n, 6))
++ tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
++ break;
++ default:
++ break;
++ }
++ if (tls_id != 0) {
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
++ if (-1 == nids) return 0;
++ continue;
++ }
++ /* similar to mbedtls_ecp_curve_info_from_name() */
++ const mbedtls_ecp_curve_info *info;
++ for (info = curve_info; info->tls_id != 0; ++info) {
++ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++ break;
++ }
++ if (info->tls_id == 0) {
++ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++ return 0;
++ }
++
++ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
++ if (-1 == nids) return 0;
++ }
++
++ /* mod_openssl configures "prime256v1" if curve list not specified,
++ * but mbedtls provides a list of supported curves if not explicitly set */
++ if (-1 == nids) return 1; /* empty list; no-op */
++
++ ids[++nids] = 0; /* terminate list */
++ ++nids;
++
++ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++ tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
++ if (tls_conf->curves == NULL)
++ return 0;
++ os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
++
++ mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
++ return 1;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
++
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_256_ephemeral[] = {
++ /* All AES-256 ephemeral suites */
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_128_ephemeral[] = {
++ /* All AES-128 ephemeral suites */
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++/* HIGH cipher list (mapped from openssl list to mbedtls) */
++static const int suite_HIGH[] = {
++ MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
++ MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
++ MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
++ MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
++ MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
++ MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
++ MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++ MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
++ MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
++};
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
++{
++ if (xsz >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR, "error: too many ciphers during list expand");
++ return -1;
++ }
++
++ for (int i = 0; i < xsz; ++i)
++ ids[++nids] = x[i];
++
++ return nids;
++}
++
++
++static int
++tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
++{
++ const mbedtls_ssl_ciphersuite_t *info =
++ mbedtls_ssl_ciphersuite_from_id(id);
++ if (info == NULL)
++ return 0;
++ const char *name = mbedtls_ssl_ciphersuite_get_name(info);
++ const size_t len = os_strlen(name);
++ if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
++ return 0;
++ if (len >= buflen)
++ return 0;
++ os_strlcpy(buf, name, buflen);
++
++ /* attempt to translate mbedtls string to openssl string
++ * (some heuristics; incomplete) */
++ size_t i = 0, j = 0;
++ if (buf[0] == 'T') {
++ if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
++ buf[3] = '-';
++ j = 4; /* remove "1-3" from "TLS1-3-" prefix */
++ i = 7;
++ }
++ else if (os_strncmp(buf, "TLS-", 4) == 0)
++ i = 4; /* remove "TLS-" prefix */
++ }
++ for (; buf[i]; ++i) {
++ if (buf[i] == '-') {
++ if (i >= 3) {
++ if (0 == os_memcmp(buf+i-3, "AES", 3))
++ continue; /* "AES-" -> "AES" */
++ }
++ if (i >= 4) {
++ if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
++ j -= 4; /* remove "WITH-" */
++ continue;
++ }
++ }
++ }
++ buf[j++] = buf[i];
++ }
++ buf[j] = '\0';
++
++ return j;
++}
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
++{
++ /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
++ os_free(tls_conf->ciphersuites);
++ tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
++ if (tls_conf->ciphersuites == NULL)
++ return 0;
++ os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
++ mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
++ return 1;
++}
++
++
++static int
++tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
++{
++ char buf[64];
++ int ids[512];
++ int nids = -1;
++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++ const char *next;
++ size_t blen, clen;
++ do {
++ next = os_strchr(ciphers, ':');
++ clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
++ if (!clen)
++ continue;
++
++ /* special-case a select set of openssl group names for hwsim tests */
++ /* (review; remove excess code if tests are not run for non-OpenSSL?) */
++ if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
++ static int ssl_preset_suiteb192_ciphersuites[] = {
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++ 0
++ };
++ return tls_mbedtls_set_ciphersuites(tls_conf,
++ ssl_preset_suiteb192_ciphersuites,
++ 2);
++ }
++ if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
++ static int ssl_preset_suiteb128_ciphersuites[] = {
++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++ 0
++ };
++ return tls_mbedtls_set_ciphersuites(tls_conf,
++ ssl_preset_suiteb128_ciphersuites,
++ 2);
++ }
++ if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
++ continue;
++ if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++ suite_AES_128_ephemeral,
++ (int)ARRAY_SIZE(suite_AES_128_ephemeral));
++ if (nids == -1)
++ return 0;
++ continue;
++ }
++ if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++ suite_AES_256_ephemeral,
++ (int)ARRAY_SIZE(suite_AES_256_ephemeral));
++ if (nids == -1)
++ return 0;
++ continue;
++ }
++ if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
++ (int)ARRAY_SIZE(suite_HIGH));
++ if (nids == -1)
++ return 0;
++ continue;
++ }
++ /* ignore anonymous cipher group names (?not supported by mbedtls?) */
++ if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
++ continue;
++ if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
++ continue;
++ if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
++ continue;
++
++ /* attempt to match mbedtls cipher names
++ * nb: does not support openssl group names or list manipulation syntax
++ * (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
++ * mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
++ * note: not efficient to rewrite list for each ciphers entry,
++ * but this code is expected to run only at startup
++ */
++ const int *list = mbedtls_ssl_list_ciphersuites();
++ for (; *list; ++list) {
++ blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
++ if (!blen)
++ continue;
++
++ /* matching heuristics additional to translate_ciphername above */
++ if (blen == clen+4) {
++ char *cbc = os_strstr(buf, "CBC-");
++ if (cbc) {
++ os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
++ blen -= 4;
++ }
++ }
++ if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
++ && (blen == clen
++ || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
++ if (1 >= idsz - (nids + 1)) {
++ emsg(MSG_ERROR,
++ "error: too many ciphers during list expand");
++ return 0;
++ }
++ ids[++nids] = *list;
++ break;
++ }
++ }
++ if (*list == 0) {
++ wpa_printf(MSG_ERROR,
++ "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
++ return 0;
++ }
++ } while ((ciphers = next ? next+1 : NULL));
++
++ if (-1 == nids) return 1; /* empty list; no-op */
++
++ ids[++nids] = 0; /* terminate list */
++ ++nids;
++
++ return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_set_item(char **config_item, const char *item)
++{
++ os_free(*config_item);
++ *config_item = NULL;
++ return item ? (*config_item = os_strdup(item)) != NULL : 1;
++}
++
++
++static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ int rc = 1;
++ rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
++ params->subject_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
++ params->altsubject_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
++ params->suffix_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
++ params->domain_match);
++ rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
++ params->check_cert_subject);
++ return rc;
++}
++
++
++/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++ #if 0 /* #ifdef MBEDTLS_FS_IO */
++ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++ return -1;
++ }
++ #else
++ /*(use os_readfile() so that we can use os_free()
++ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++ * on buf aborts in tests if buf not allocated via os_malloc())*/
++ *buf = (u8 *)os_readfile(path, n);
++ if (!*buf) {
++ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++ return -1;
++ }
++ u8 *buf0 = os_realloc(*buf, *n+1);
++ if (!buf0) {
++ bin_clear_free(*buf, *n);
++ *buf = NULL;
++ return -1;
++ }
++ buf0[(*n)++] = '\0';
++ *buf = buf0;
++ #endif
++ return 0;
++}
++
++
++static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
++{
++ /* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
++ if (len && data[len-1] == '\0'
++ && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
++ && tls_mbedtls_data_is_pem(data))
++ return 0;
++
++ mbedtls_x509_crl crl;
++ mbedtls_x509_crl_init(&crl);
++ int rc = mbedtls_x509_crl_parse(&crl, data, len);
++ if (rc < 0) {
++ mbedtls_x509_crl_free(&crl);
++ return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
++ }
++
++ mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
++ if (crl_new == NULL) {
++ mbedtls_x509_crl_free(&crl);
++ return MBEDTLS_ERR_X509_ALLOC_FAILED;
++ }
++ os_memcpy(crl_new, &crl, sizeof(crl));
++
++ mbedtls_x509_crl *crl_old = tls_conf->crl;
++ tls_conf->crl = crl_new;
++ if (crl_old) {
++ mbedtls_x509_crl_free(crl_old);
++ os_free(crl_old);
++ }
++ return 0;
++}
++
++
++static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
++{
++ /* load crt struct onto stack and then copy into tls_conf in
++ * order to preserve existing tls_conf value if error occurs
++ *
++ * hostapd is not threaded, or else should allocate memory and swap in
++ * pointer reduce race condition. (If threaded, would also need to
++ * keep reference count of use to avoid freeing while still in use.) */
++
++ mbedtls_x509_crt crt;
++ mbedtls_x509_crt_init(&crt);
++ int rc = mbedtls_x509_crt_parse(&crt, data, len);
++ if (rc < 0) {
++ mbedtls_x509_crt_free(&crt);
++ return rc;
++ }
++
++ mbedtls_x509_crt_free(&tls_conf->ca_cert);
++ os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
++ return 0;
++}
++
++
++static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
++{
++ size_t len;
++ u8 *data;
++ if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
++ return -1;
++
++ int rc;
++ if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
++ && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
++ || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
++ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++ &tls_conf->ca_cert,
++ tls_conf->crl);
++ }
++ else {
++ elog(rc, __func__);
++ emsg(MSG_ERROR, ca_cert_file);
++ }
++
++ forced_memzero(data, len);
++ os_free(data);
++ return rc;
++}
++
++
++static void tls_mbedtls_refresh_crl(void)
++{
++ /* check for CRL refresh
++ * continue even if error occurs; continue with previous cert, CRL */
++ unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
++ const char *ca_cert_file = tls_ctx_global.ca_cert_file;
++ if (!crl_reload_interval || !ca_cert_file)
++ return;
++
++ struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
++ struct os_reltime now;
++ if (os_get_reltime(&now) != 0
++ || !os_reltime_expired(&now, previous, crl_reload_interval))
++ return;
++
++ /* Note: modifying global state is not thread-safe
++ * if in use by existing connections
++ *
++ * src/utils/os.h does not provide a portable stat()
++ * or else it would be a good idea to check mtime and size,
++ * and avoid reloading if file has not changed */
++
++ if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
++ *previous = now;
++}
++
++
++static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ if (params->ca_cert) {
++ if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
++ tls_conf->ca_cert_probe = 1;
++ tls_conf->has_ca_cert = 1;
++ return 0;
++ }
++
++ if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
++ const char *pos = params->ca_cert + 7;
++ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
++ emsg(MSG_ERROR, "unsupported ca_cert hash value");
++ return -1;
++ }
++ pos += 14;
++ if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
++ emsg(MSG_ERROR, "unexpected ca_cert hash length");
++ return -1;
++ }
++ if (hexstr2bin(pos, tls_conf->ca_cert_hash,
++ SHA256_DIGEST_LENGTH) < 0) {
++ emsg(MSG_ERROR, "invalid ca_cert hash value");
++ return -1;
++ }
++ emsg(MSG_DEBUG, "checking only server certificate match");
++ tls_conf->verify_depth0_only = 1;
++ tls_conf->has_ca_cert = 1;
++ return 0;
++ }
++
++ if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
++ return -1;
++ }
++ if (params->ca_cert_blob) {
++ size_t len = params->ca_cert_blob_len;
++ int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
++ if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
++ ++len; /*(include '\0' in len for PEM)*/
++ int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
++ params->ca_cert_blob, len);
++ if (ret != 0) {
++ elog(ret, "mbedtls_x509_crt_parse");
++ return -1;
++ }
++ if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
++ ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
++ if (ret != 0) {
++ elog(ret, "mbedtls_x509_crl_parse");
++ return -1;
++ }
++ }
++ }
++
++ if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
++ || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
++ emsg(MSG_WARNING, "ca_cert expired or not yet valid");
++ if (params->ca_cert)
++ emsg(MSG_WARNING, params->ca_cert);
++ }
++
++ tls_conf->has_ca_cert = 1;
++ return 0;
++}
++
++
++static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ int ret;
++
++ if (params->ca_cert || params->ca_cert_blob) {
++ if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
++ return -1;
++ }
++ else if (params->ca_path) {
++ emsg(MSG_INFO, "ca_path support not implemented");
++ return -1;
++ }
++
++ if (!tls_conf->has_ca_cert)
++ mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
++ else {
++ /* Initial setting: REQUIRED for client, OPTIONAL for server
++ * (see also tls_connection_set_verify()) */
++ tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
++ int authmode = tls_conf->verify_peer
++ ? MBEDTLS_SSL_VERIFY_REQUIRED
++ : MBEDTLS_SSL_VERIFY_OPTIONAL;
++ mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
++ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++ &tls_conf->ca_cert,
++ tls_conf->crl);
++
++ if (!tls_connection_set_subject_match(tls_conf, params))
++ return -1;
++ }
++
++ if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
++ emsg(MSG_INFO, "server_cert2 support not implemented");
++
++ if (params->client_cert) {
++ size_t len;
++ u8 *data;
++ if (tls_mbedtls_readfile(params->client_cert, &data, &len))
++ return -1;
++ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
++ forced_memzero(data, len);
++ os_free(data);
++ }
++ if (params->client_cert_blob) {
++ size_t len = params->client_cert_blob_len;
++ if (len && params->client_cert_blob[len-1] != '\0'
++ && tls_mbedtls_data_is_pem(params->client_cert_blob))
++ ++len; /*(include '\0' in len for PEM)*/
++ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
++ params->client_cert_blob, len);
++ }
++ if (params->client_cert || params->client_cert_blob) {
++ if (ret < 0) {
++ elog(ret, "mbedtls_x509_crt_parse");
++ if (params->client_cert)
++ emsg(MSG_ERROR, params->client_cert);
++ return -1;
++ }
++ if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
++ || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
++ emsg(MSG_WARNING, "cert expired or not yet valid");
++ if (params->client_cert)
++ emsg(MSG_WARNING, params->client_cert);
++ }
++ tls_conf->has_client_cert = 1;
++ }
++
++ if (params->private_key || params->private_key_blob) {
++ size_t len = params->private_key_blob_len;
++ u8 *data;
++ *(const u8 **)&data = params->private_key_blob;
++ if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
++ ++len; /*(include '\0' in len for PEM)*/
++ if (params->private_key
++ && tls_mbedtls_readfile(params->private_key, &data, &len)) {
++ return -1;
++ }
++ const char *pwd = params->private_key_passwd;
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++ data, len,
++ (const unsigned char *)pwd,
++ pwd ? os_strlen(pwd) : 0,
++ mbedtls_ctr_drbg_random,
++ tls_ctx_global.ctr_drbg);
++ #else
++ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++ data, len,
++ (const unsigned char *)pwd,
++ pwd ? os_strlen(pwd) : 0);
++ #endif
++ if (params->private_key) {
++ forced_memzero(data, len);
++ os_free(data);
++ }
++ if (ret < 0) {
++ elog(ret, "mbedtls_pk_parse_key");
++ return -1;
++ }
++ tls_conf->has_private_key = 1;
++ }
++
++ if (tls_conf->has_client_cert && tls_conf->has_private_key) {
++ ret = mbedtls_ssl_conf_own_cert(
++ &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
++ if (ret < 0) {
++ elog(ret, "mbedtls_ssl_conf_own_cert");
++ return -1;
++ }
++ }
++
++ return 0;
++}
++
++
++/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
++{
++ /* Only SHA-256 and 384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++ /* Only ECDSA */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++ /* Only NIST P-256 and P-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++ 0,
++#endif
++ 2048,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
++{
++ /* Only SHA-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++ /* Only ECDSA */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++ /* Only NIST P-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++ 0,
++#endif
++ 3072,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
++{
++ /* Only SHA-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++ 0xFFFFFFF, /* Any PK alg */
++#if defined(MBEDTLS_ECP_C)
++ /* Only NIST P-384 */
++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++ 0,
++#endif
++ 3072,
++};
++
++
++static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
++ const struct tls_connection_params *params)
++{
++ tls_conf->flags = params->flags;
++
++ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
++ emsg(MSG_INFO, "ocsp=3 not supported");
++ return -1;
++ }
++
++ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
++ emsg(MSG_INFO, "ocsp not supported");
++ return -1;
++ }
++
++ int suiteb128 = 0;
++ int suiteb192 = 0;
++ if (params->openssl_ciphers) {
++ if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
++ suiteb192 = 1;
++ tls_conf->flags |= TLS_CONN_SUITEB;
++ }
++ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
++ suiteb128 = 1;
++ tls_conf->flags |= TLS_CONN_SUITEB;
++ }
++ }
++
++ int ret = mbedtls_ssl_config_defaults(
++ &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
++ : MBEDTLS_SSL_IS_CLIENT,
++ MBEDTLS_SSL_TRANSPORT_STREAM,
++ (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
++ : MBEDTLS_SSL_PRESET_DEFAULT);
++ if (ret != 0) {
++ elog(ret, "mbedtls_ssl_config_defaults");
++ return -1;
++ }
++
++ if (suiteb128) {
++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++ &tls_mbedtls_crt_profile_suiteb128);
++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
++ }
++ else if (suiteb192) {
++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++ &tls_mbedtls_crt_profile_suiteb192);
++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++ }
++ else if (tls_conf->flags & TLS_CONN_SUITEB) {
++ /* treat as suiteb192 while allowing any PK algorithm */
++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++ &tls_mbedtls_crt_profile_suiteb192_anypk);
++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++ }
++
++ tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
++ ret = tls_mbedtls_set_certs(tls_conf, params);
++ if (ret != 0)
++ return -1;
++
++ if (params->dh_file
++ && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
++ return -1;
++ }
++
++ if (params->openssl_ecdh_curves
++ && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
++ return -1;
++ }
++
++ if (params->openssl_ciphers) {
++ if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
++ return -1;
++ }
++ else if (tls_conf->flags & TLS_CONN_SUITEB) {
++ /* special-case a select set of ciphers for hwsim tests */
++ if (!tls_mbedtls_set_ciphers(tls_conf,
++ (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
++ ? "DHE-RSA-AES256-GCM-SHA384"
++ : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
++ return -1;
++ }
++
++ return 0;
++}
++
++
++int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
++ const struct tls_connection_params *params)
++{
++ if (conn == NULL || params == NULL)
++ return -1;
++
++ tls_conf_deinit(conn->tls_conf);
++ struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
++ if (tls_conf == NULL)
++ return -1;
++
++ if (tls_ctx_global.tls_conf) {
++ tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
++ tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
++ /*(tls_openssl.c inherits check_cert_subject from global conf)*/
++ if (tls_ctx_global.tls_conf->check_cert_subject) {
++ tls_conf->check_cert_subject =
++ os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
++ if (tls_conf->check_cert_subject == NULL)
++ return -1;
++ }
++ }
++
++ if (tls_mbedtls_set_params(tls_conf, params) != 0)
++ return -1;
++ conn->verify_peer = tls_conf->verify_peer;
++
++ return tls_mbedtls_ssl_setup(conn);
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
++ const u8 *data, size_t len)
++{
++ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++ return -1;
++ if (conn->clienthello_session_ticket)
++ tls_connection_deinit_clienthello_session_ticket(conn);
++ if (len) {
++ conn->clienthello_session_ticket = mbedtls_calloc(1, len);
++ if (conn->clienthello_session_ticket == NULL)
++ return -1;
++ conn->clienthello_session_ticket_len = len;
++ os_memcpy(conn->clienthello_session_ticket, data, len);
++ }
++ return 0;
++}
++
++
++static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
++{
++ mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
++ if (sess->MBEDTLS_PRIVATE(ticket)) {
++ mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
++ sess->MBEDTLS_PRIVATE(ticket_len));
++ mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
++ }
++ sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
++ sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
++ sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
++
++ conn->clienthello_session_ticket = NULL;
++ conn->clienthello_session_ticket_len = 0;
++}
++
++
++static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
++ const mbedtls_ssl_session *session,
++ unsigned char *start,
++ const unsigned char *end,
++ size_t *tlen,
++ uint32_t *lifetime)
++{
++ struct tls_connection *conn = p_ticket;
++ if (conn && conn->session_ticket_cb) {
++ /* see tls_mbedtls_clienthello_session_ticket_prep() */
++ /* see tls_mbedtls_clienthello_session_ticket_set() */
++ return 0;
++ }
++
++ return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
++ session, start, end, tlen, lifetime);
++}
++
++
++static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
++ mbedtls_ssl_session *session,
++ unsigned char *buf,
++ size_t len)
++{
++ /* XXX: TODO: not implemented in client;
++ * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
++
++ if (len == 0)
++ return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
++
++ struct tls_connection *conn = p_ticket;
++ if (conn && conn->session_ticket_cb) {
++ /* XXX: have random and secret been initialized yet?
++ * or must keys first be exported?
++ * EAP-FAST uses all args, EAP-TEAP only uses secret */
++ struct tls_random data;
++ if (tls_connection_get_random(NULL, conn, &data) != 0)
++ return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
++ int ret =
++ conn->session_ticket_cb(conn->session_ticket_cb_ctx,
++ buf, len,
++ data.client_random,
++ data.server_random,
++ conn->expkey_secret);
++ if (ret == 1) {
++ conn->resumed = 1;
++ return 0;
++ }
++ emsg(MSG_ERROR, "EAP session ticket ext not implemented");
++ return MBEDTLS_ERR_SSL_INVALID_MAC;
++ /*(non-zero return used for mbedtls debug logging)*/
++ }
++
++ /* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
++ int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
++ session, buf, len);
++ if (conn)
++ conn->resumed = (rc == 0);
++ return rc;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++__attribute_cold__
++int tls_global_set_params(void *tls_ctx,
++ const struct tls_connection_params *params)
++{
++ /* XXX: why might global_set_params be called more than once? */
++ if (tls_ctx_global.tls_conf)
++ tls_conf_deinit(tls_ctx_global.tls_conf);
++ tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
++ if (tls_ctx_global.tls_conf == NULL)
++ return -1;
++
++ #ifdef MBEDTLS_SSL_SESSION_TICKETS
++ #ifdef MBEDTLS_SSL_TICKET_C
++ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++ tls_mbedtls_ssl_ticket_write,
++ tls_mbedtls_ssl_ticket_parse,
++ NULL);
++ #else
++ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++ mbedtls_ssl_ticket_write,
++ mbedtls_ssl_ticket_parse,
++ &tls_ctx_global.ticket_ctx);
++ #endif
++ #endif
++ #endif
++
++ os_free(tls_ctx_global.ocsp_stapling_response);
++ tls_ctx_global.ocsp_stapling_response = NULL;
++ if (params->ocsp_stapling_response)
++ tls_ctx_global.ocsp_stapling_response =
++ os_strdup(params->ocsp_stapling_response);
++
++ os_free(tls_ctx_global.ca_cert_file);
++ tls_ctx_global.ca_cert_file = NULL;
++ if (params->ca_cert)
++ tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
++ return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
++}
++
++
++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
++{
++ tls_ctx_global.tls_conf->check_crl = check_crl;
++ tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
++ return 0;
++}
++
++
++int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
++ int verify_peer, unsigned int flags,
++ const u8 *session_ctx, size_t session_ctx_len)
++{
++ /*(EAP server-side calls this from eap_server_tls_ssl_init())*/
++ if (conn == NULL)
++ return -1;
++
++ conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
++
++ int authmode;
++ switch (verify_peer) {
++ case 2: authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
++ case 1: authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
++ default: authmode = MBEDTLS_SSL_VERIFY_NONE; break;
++ }
++ mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
++
++ if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++ else
++ mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
++
++ return 0;
++}
++
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static void tls_connection_export_keys_cb(
++ void *p_expkey, mbedtls_ssl_key_export_type secret_type,
++ const unsigned char *secret, size_t secret_len,
++ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++ mbedtls_tls_prf_types tls_prf_type)
++{
++ struct tls_connection *conn = p_expkey;
++ conn->tls_prf_type = tls_prf_type;
++ if (!tls_prf_type)
++ return;
++ if (secret_len > sizeof(conn->expkey_secret)) {
++ emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
++ conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
++ return;
++ }
++ conn->expkey_secret_len = secret_len;
++ os_memcpy(conn->expkey_secret, secret, secret_len);
++ os_memcpy(conn->expkey_randbytes,
++ client_random, MBEDTLS_EXPKEY_RAND_LEN);
++ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++ server_random, MBEDTLS_EXPKEY_RAND_LEN);
++}
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static int tls_connection_export_keys_cb(
++ void *p_expkey,
++ const unsigned char *ms,
++ const unsigned char *kb,
++ size_t maclen,
++ size_t keylen,
++ size_t ivlen,
++ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++ mbedtls_tls_prf_types tls_prf_type )
++{
++ struct tls_connection *conn = p_expkey;
++ conn->tls_prf_type = tls_prf_type;
++ if (!tls_prf_type)
++ return -1; /*(return value ignored by mbedtls)*/
++ conn->expkey_keyblock_size = maclen + keylen + ivlen;
++ conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
++ os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
++ os_memcpy(conn->expkey_randbytes,
++ client_random, MBEDTLS_EXPKEY_RAND_LEN);
++ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++ server_random, MBEDTLS_EXPKEY_RAND_LEN);
++ return 0;
++}
++#endif
++
++
++int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
++ struct tls_random *data)
++{
++ if (!conn || !conn->tls_prf_type)
++ return -1;
++ data->client_random = conn->expkey_randbytes;
++ data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++ data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
++ data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++ return 0;
++}
++
++
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, u8 *out, size_t out_len)
++{
++ /* (EAP-PEAP EAP-TLS EAP-TTLS) */
++ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++ return (conn && conn->established && conn->tls_prf_type)
++ ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
++ conn->expkey_secret, conn->expkey_secret_len, label,
++ conn->expkey_randbytes,
++ sizeof(conn->expkey_randbytes), out, out_len)
++ : -1;
++ #else
++ /* not implemented here for mbedtls < 2.18.0 */
++ return -1;
++ #endif
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++/* keyblock size info is not exposed in mbed TLS 3.0.0 */
++/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
++#include <mbedtls/ssl_ciphersuites.h>
++#include <mbedtls/cipher.h>
++static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
++{
++ #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
++ #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
++ if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3)
++ return 0; /* (calculation not extracted) */
++ #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
++
++ int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
++ const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
++ mbedtls_ssl_ciphersuite_from_id(ciphersuite);
++ if (ciphersuite_info == NULL)
++ return 0;
++
++ const mbedtls_cipher_info_t *cipher_info =
++ mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
++ if (cipher_info == NULL)
++ return 0;
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++ size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
++ mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
++ #else
++ size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
++ mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
++ #endif
++ #if defined(MBEDTLS_GCM_C) || \
++ defined(MBEDTLS_CCM_C) || \
++ defined(MBEDTLS_CHACHAPOLY_C)
++ if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
++ return keylen + 4;
++ else if (mode == MBEDTLS_MODE_CHACHAPOLY)
++ return keylen + 12;
++ else
++ #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
++ #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
++ {
++ const mbedtls_md_info_t *md_info =
++ mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
++ if (md_info == NULL)
++ return 0;
++ size_t mac_key_len = mbedtls_md_get_size(md_info);
++ size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
++ return keylen + mac_key_len + ivlen;
++ }
++ #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
++ #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
++ return 0;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
++
++
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++ /* XXX: has export keys callback been run? */
++ if (!conn || !conn->tls_prf_type)
++ return -1;
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
++ if (conn->expkey_keyblock_size == 0)
++ return -1;
++ #endif
++ size_t skip = conn->expkey_keyblock_size * 2;
++ unsigned char *tmp_out = os_malloc(skip + out_len);
++ if (!tmp_out)
++ return -1;
++
++ /* server_random and then client_random */
++ unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
++ os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++ MBEDTLS_EXPKEY_RAND_LEN);
++ os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
++ MBEDTLS_EXPKEY_RAND_LEN);
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++ int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
++ conn->expkey_secret, conn->expkey_secret_len,
++ "key expansion", seed, sizeof(seed),
++ tmp_out, skip + out_len);
++ if (ret == 0)
++ os_memcpy(out, tmp_out + skip, out_len);
++ #else
++ int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
++ #endif
++
++ bin_clear_free(tmp_out, skip + out_len);
++ forced_memzero(seed, sizeof(seed));
++ return ret;
++}
++
++#endif /* TLS_MBEDTLS_EAP_FAST */
++
++
++__attribute_cold__
++static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
++{
++ /* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
++ if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
++ return;
++ if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
++ return;
++ #if 0
++ /*(info not available on client;
++ * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
++ if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
++ #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++ mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
++ #else
++ mbedtls_ssl_get_ciphersuite_id(
++ mbedtls_ssl_get_ciphersuite(&conn->ssl))
++ #endif
++ && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
++ < 384 /*(3072/8)*/)
++ #endif
++ {
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (init_conf->event_cb) {
++ union tls_event_data ev;
++ os_memset(&ev, 0, sizeof(ev));
++ ev.alert.is_local = 1;
++ ev.alert.type = "fatal";
++ /*"internal error" string for tests/hwsim/test_suiteb.py */
++ ev.alert.description = "internal error: handshake failure";
++ /*ev.alert.description = "insufficient security";*/
++ init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
++ }
++ }
++}
++
++
++struct wpabuf * tls_connection_handshake(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ struct wpabuf **appl_data)
++{
++ if (appl_data)
++ *appl_data = NULL;
++
++ if (in_data && wpabuf_len(in_data)) {
++ /*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
++ if (conn->pull_buf && 0) /* disable; appears unwise */
++ tls_pull_buf_discard(conn, __func__);
++ if (!tls_pull_buf_append(conn, in_data))
++ return NULL;
++ }
++
++ if (conn->tls_conf == NULL) {
++ struct tls_connection_params params;
++ os_memset(¶ms, 0, sizeof(params));
++ params.openssl_ciphers =
++ tls_ctx_global.init_conf.openssl_ciphers;
++ params.flags = tls_ctx_global.tls_conf->flags;
++ if (tls_connection_set_params(tls_ctx, conn, ¶ms) != 0)
++ return NULL;
++ }
++
++ if (conn->verify_peer) /*(call here might be redundant; nbd)*/
++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ if (conn->clienthello_session_ticket)
++ /*(starting handshake for EAP-FAST and EAP-TEAP)*/
++ tls_mbedtls_clienthello_session_ticket_set(conn);
++
++ /* (not thread-safe due to need to set userdata 'conn' for callback) */
++ /* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
++ * since ticket write and parse callbacks take (mbedtls_ssl_session *)
++ * param instead of (mbedtls_ssl_context *) param) */
++ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++ NULL, NULL, NULL);
++ else
++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++ tls_mbedtls_ssl_ticket_write,
++ tls_mbedtls_ssl_ticket_parse,
++ conn);
++ #endif
++
++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++ int ret = mbedtls_ssl_handshake(&conn->ssl);
++ #else
++ int ret = 0;
++ while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
++ ret = mbedtls_ssl_handshake_step(&conn->ssl);
++ if (ret != 0)
++ break;
++ }
++ #endif
++
++ #ifdef TLS_MBEDTLS_SESSION_TICKETS
++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++ tls_mbedtls_ssl_ticket_write,
++ tls_mbedtls_ssl_ticket_parse,
++ NULL);
++ #endif
++
++ switch (ret) {
++ case 0:
++ conn->established = 1;
++ if (conn->push_buf == NULL)
++ /* Need to return something to get final TLS ACK. */
++ conn->push_buf = wpabuf_alloc(0);
++
++ if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
++ *appl_data = NULL; /* RFE: check for application data */
++ break;
++ case MBEDTLS_ERR_SSL_WANT_WRITE:
++ case MBEDTLS_ERR_SSL_WANT_READ:
++ case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
++ case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
++ if (tls_ctx_global.tls_conf /*(is server)*/
++ && conn->established && conn->push_buf == NULL)
++ /* Need to return something to trigger completion of EAP-TLS. */
++ conn->push_buf = wpabuf_alloc(0);
++ break;
++ default:
++ ++conn->failed;
++ switch (ret) {
++ case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
++ case MBEDTLS_ERR_NET_CONN_RESET:
++ case MBEDTLS_ERR_NET_SEND_FAILED:
++ ++conn->write_alerts;
++ break;
++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++ case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
++ #else
++ case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
++ #endif
++ tls_mbedtls_suiteb_handshake_alert(conn);
++ /* fall through */
++ case MBEDTLS_ERR_NET_RECV_FAILED:
++ case MBEDTLS_ERR_SSL_CONN_EOF:
++ case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
++ case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
++ ++conn->read_alerts;
++ break;
++ default:
++ break;
++ }
++
++ ilog(ret, "mbedtls_ssl_handshake");
++ break;
++ }
++
++ struct wpabuf *out_data = conn->push_buf;
++ conn->push_buf = NULL;
++ return out_data;
++}
++
++
++struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ struct wpabuf **appl_data)
++{
++ conn->is_server = 1;
++ return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
++}
++
++
++struct wpabuf * tls_connection_encrypt(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ int res = mbedtls_ssl_write(&conn->ssl,
++ wpabuf_head_u8(in_data), wpabuf_len(in_data));
++ if (res < 0) {
++ elog(res, "mbedtls_ssl_write");
++ return NULL;
++ }
++
++ struct wpabuf *buf = conn->push_buf;
++ conn->push_buf = NULL;
++ return buf;
++}
++
++
++struct wpabuf * tls_connection_decrypt(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ int res;
++ struct wpabuf *out;
++
++ /*assert(in_data != NULL);*/
++ if (!tls_pull_buf_append(conn, in_data))
++ return NULL;
++
++ #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
++ /* Add extra buffer space to handle the possibility of decrypted
++ * data being longer than input data due to TLS compression. */
++ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
++ #else /* TLS compression is disabled in mbedtls 3.x */
++ out = wpabuf_alloc(wpabuf_len(in_data));
++ #endif
++ if (out == NULL)
++ return NULL;
++
++ res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
++ if (res < 0) {
++ #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
++ if (res == MBEDTLS_ERR_SSL_WANT_READ)
++ return out;
++ #endif
++ elog(res, "mbedtls_ssl_read");
++ wpabuf_free(out);
++ return NULL;
++ }
++ wpabuf_put(out, res);
++
++ return out;
++}
++
++
++int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
++{
++ /* XXX: might need to detect if session resumed from TLS session ticket
++ * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
++ /* (?ssl->handshake->resume during session ticket validation?) */
++ return conn && conn->resumed;
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
++ u8 *ciphers)
++{
++ /* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
++ int ids[7];
++ const int idsz = (int)sizeof(ids);
++ int nids = -1, id;
++ for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
++ switch (*ciphers) {
++ case TLS_CIPHER_RC4_SHA:
++ #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
++ id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
++ break;
++ #else
++ continue; /*(not supported in mbedtls 3.x; ignore)*/
++ #endif
++ case TLS_CIPHER_AES128_SHA:
++ id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
++ break;
++ case TLS_CIPHER_RSA_DHE_AES128_SHA:
++ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
++ break;
++ case TLS_CIPHER_ANON_DH_AES128_SHA:
++ continue; /*(not supported in mbedtls; ignore)*/
++ case TLS_CIPHER_RSA_DHE_AES256_SHA:
++ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
++ break;
++ case TLS_CIPHER_AES256_SHA:
++ id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
++ break;
++ default:
++ return -1; /* should not happen */
++ }
++ if (++nids == idsz)
++ return -1; /* should not happen */
++ ids[nids] = id;
++ }
++ if (nids < 0)
++ return 0; /* nothing to do */
++ if (++nids == idsz)
++ return -1; /* should not happen */
++ ids[nids] = 0; /* terminate list */
++ ++nids;
++
++ return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
++}
++#endif
++
++
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++ char *buf, size_t buflen)
++{
++ if (conn == NULL)
++ return -1;
++ os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
++ return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
++{
++ if (conn == NULL)
++ return 0;
++ return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++}
++#endif
++
++
++int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
++ char *buf, size_t buflen)
++{
++ if (conn == NULL)
++ return -1;
++ const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++ return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++int tls_connection_enable_workaround(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ /* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
++ /* XXX: is there a relevant setting for this in mbed TLS? */
++ /* (do we even care that much about older CBC ciphers?) */
++ return 0;
++}
++
++
++int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
++ int ext_type, const u8 *data,
++ size_t data_len)
++{
++ /* (EAP-FAST and EAP-TEAP) */
++ if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
++ return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
++ data_len);
++
++ return -1;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? conn->failed : -1;
++}
++
++
++int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? conn->read_alerts : -1;
++}
++
++
++int tls_connection_get_write_alerts(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ return conn ? conn->write_alerts : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++int tls_connection_set_session_ticket_cb(
++ void *tls_ctx, struct tls_connection *conn,
++ tls_session_ticket_cb cb, void *ctx)
++{
++ if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
++ /* (EAP-FAST and EAP-TEAP) */
++ conn->session_ticket_cb = cb;
++ conn->session_ticket_cb_ctx = ctx;
++ return 0;
++ }
++ return -1;
++}
++#endif
++
++
++int tls_get_library_version(char *buf, size_t buf_len)
++{
++ #ifndef MBEDTLS_VERSION_C
++ const char * const ver = "n/a";
++ #else
++ char ver[9];
++ mbedtls_version_get_string(ver);
++ #endif
++ return os_snprintf(buf, buf_len,
++ "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
++}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++ struct wpabuf *data)
++{
++ wpabuf_free(conn->success_data);
++ conn->success_data = data;
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++ return conn->success_data;
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
++{
++ #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
++ /* data from TLS handshake Finished message */
++ size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
++ char *verify_data = (conn->is_server ^ conn->resumed)
++ ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
++ : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
++ if (verify_len && verify_len <= max_len) {
++ os_memcpy(buf, verify_data, verify_len);
++ return (int)verify_len;
++ }
++ #endif
++ return -1;
++}
++#endif
++
++
++__attribute_noinline__
++static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
++{
++ if (conn->peer_subject)
++ return;
++ char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++ int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
++ if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
++ os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++const char * tls_connection_get_peer_subject(struct tls_connection *conn)
++{
++ if (!conn)
++ return NULL;
++ if (!conn->peer_subject) { /*(if not set during cert verify)*/
++ const mbedtls_x509_crt *peer_cert =
++ mbedtls_ssl_get_peer_cert(&conn->ssl);
++ if (peer_cert)
++ tls_mbedtls_set_peer_subject(conn, peer_cert);
++ }
++ return conn->peer_subject;
++}
++#endif
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++bool tls_connection_get_own_cert_used(struct tls_connection *conn)
++{
++ /* XXX: availability of cert does not necessary mean that client
++ * received certificate request from server and then sent cert.
++ * ? step handshake in tls_connection_handshake() looking for
++ * MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
++ const struct tls_conf * const tls_conf = conn->tls_conf;
++ return (tls_conf->has_client_cert && tls_conf->has_private_key);
++}
++#endif
++
++
++#if defined(CONFIG_FIPS)
++#define TLS_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if defined(CONFIG_SHA256)
++#define TLS_MBEDTLS_TLS_PRF_SHA256
++#endif
++
++#if defined(CONFIG_SHA384)
++#define TLS_MBEDTLS_TLS_PRF_SHA384
++#endif
++
++
++#ifndef TLS_MBEDTLS_CONFIG_FIPS
++#if defined(CONFIG_MODULE_TESTS)
++/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
++ && MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++/* sha1-tlsprf.c */
++#include "sha1.h"
++int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
++ secret, secret_len, label,
++ seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha1-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
++/* sha256-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha256.h"
++int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
++ secret, secret_len, label,
++ seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha256-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
++/* sha384-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha384.h"
++int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
++ secret, secret_len, label,
++ seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha384-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
++ ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
++#endif
++
++struct mlist { const char *p; size_t n; };
++
++
++static int
++tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
++{
++ /* RFE: this could be pre-parsed into structured data at config time */
++ struct mlist list[256]; /*(much larger than expected)*/
++ int nlist = 0;
++ if ( os_strncmp(match, "EMAIL:", 6) != 0
++ && os_strncmp(match, "DNS:", 4) != 0
++ && os_strncmp(match, "URI:", 4) != 0 ) {
++ wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
++ return 0;
++ }
++ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++ do { } while ((tok = os_strchr(s, ';'))
++ && os_strncmp(tok+1, "EMAIL:", 6) != 0
++ && os_strncmp(tok+1, "DNS:", 4) != 0
++ && os_strncmp(tok+1, "URI:", 4) != 0);
++ list[nlist].p = s;
++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++ wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
++ match);
++ break; /* truncate huge list and continue */
++ }
++ }
++
++ if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++ return 0;
++
++ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++ for (; cur != NULL; cur = cur->next) {
++ const unsigned char san_type = (unsigned char)cur->buf.tag
++ & MBEDTLS_ASN1_TAG_VALUE_MASK;
++ char t;
++ size_t step = 4;
++ switch (san_type) { /* "EMAIL:" or "DNS:" or "URI:" */
++ case MBEDTLS_X509_SAN_RFC822_NAME: step = 6; t = 'E'; break;
++ case MBEDTLS_X509_SAN_DNS_NAME: t = 'D'; break;
++ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
++ default: continue;
++ }
++
++ for (int i = 0; i < nlist; ++i) {
++ /* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
++ /* Note: v is not '\0'-terminated, but is a known length vlen,
++ * so okay to pass to os_strncasecmp() even though not z-string */
++ if (cur->buf.len == list[i].n - step && t == *list[i].p
++ && 0 == os_strncasecmp((char *)cur->buf.p,
++ list[i].p+step, cur->buf.len)) {
++ return 1; /* match */
++ }
++ }
++ }
++ return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffix(const char *v, size_t vlen,
++ const struct mlist *list, int nlist, int full)
++{
++ /* Note: v is not '\0'-terminated, but is a known length vlen,
++ * so okay to pass to os_strncasecmp() even though not z-string */
++ for (int i = 0; i < nlist; ++i) {
++ size_t n = list[i].n;
++ if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
++ && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
++ return 1; /* match */
++ }
++ return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
++{
++ /* RFE: this could be pre-parsed into structured data at config time */
++ struct mlist list[256]; /*(much larger than expected)*/
++ int nlist = 0;
++ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++ tok = os_strchr(s, ';');
++ list[nlist].p = s;
++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++ wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
++ break; /* truncate huge list and continue */
++ }
++ }
++
++ /* check subjectAltNames */
++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
++ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++ for (; cur != NULL; cur = cur->next) {
++ const unsigned char san_type = (unsigned char)cur->buf.tag
++ & MBEDTLS_ASN1_TAG_VALUE_MASK;
++ if (san_type == MBEDTLS_X509_SAN_DNS_NAME
++ && tls_mbedtls_match_suffix((char *)cur->buf.p,
++ cur->buf.len,
++ list, nlist, full)) {
++ return 1; /* match */
++ }
++ }
++ }
++
++ /* check subject CN */
++ const mbedtls_x509_name *name = &crt->subject;
++ for (; name != NULL; name = name->next) {
++ if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
++ break;
++ }
++ if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
++ list, nlist, full)) {
++ return 1; /* match */
++ }
++
++ return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
++{
++ /* RFE: this could be pre-parsed into structured data at config time */
++ struct mlistoid { const char *p; size_t n;
++ const char *oid; size_t olen;
++ int prefix; };
++ struct mlistoid list[32]; /*(much larger than expected)*/
++ int nlist = 0;
++ for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
++ tok = os_strchr(s, '/');
++ list[nlist].oid = NULL;
++ list[nlist].olen = 0;
++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++ e = memchr(s, '=', list[nlist].n);
++ if (e == NULL) {
++ if (list[nlist].n == 0)
++ continue; /* skip consecutive, repeated '/' */
++ if (list[nlist].n == 1 && *s == '*') {
++ /* special-case "*" to match any OID and value */
++ s = e = "=*";
++ list[nlist].n = 2;
++ list[nlist].oid = "";
++ }
++ else {
++ wpa_printf(MSG_INFO,
++ "MTLS: invalid check_cert_subject '%s' missing '='",
++ match);
++ return 0;
++ }
++ }
++ switch (e - s) {
++ case 1:
++ if (*s == 'C') {
++ list[nlist].oid = MBEDTLS_OID_AT_COUNTRY;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
++ }
++ else if (*s == 'L') {
++ list[nlist].oid = MBEDTLS_OID_AT_LOCALITY;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
++ }
++ else if (*s == 'O') {
++ list[nlist].oid = MBEDTLS_OID_AT_ORGANIZATION;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
++ }
++ break;
++ case 2:
++ if (s[0] == 'C' && s[1] == 'N') {
++ list[nlist].oid = MBEDTLS_OID_AT_CN;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
++ }
++ else if (s[0] == 'S' && s[1] == 'T') {
++ list[nlist].oid = MBEDTLS_OID_AT_STATE;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
++ }
++ else if (s[0] == 'O' && s[1] == 'U') {
++ list[nlist].oid = MBEDTLS_OID_AT_ORG_UNIT;
++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
++ }
++ break;
++ case 12:
++ if (os_memcmp(s, "emailAddress", 12) == 0) {
++ list[nlist].oid = MBEDTLS_OID_PKCS9_EMAIL;
++ list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
++ }
++ break;
++ default:
++ break;
++ }
++ if (list[nlist].oid == NULL) {
++ wpa_printf(MSG_INFO,
++ "MTLS: Unknown field in check_cert_subject '%s'",
++ match);
++ return 0;
++ }
++ list[nlist].n -= (size_t)(++e - s);
++ list[nlist].p = e;
++ if (list[nlist].n && e[list[nlist].n-1] == '*') {
++ --list[nlist].n;
++ list[nlist].prefix = 1;
++ }
++ /*(could easily add support for suffix matches if value begins with '*',
++ * but suffix match is not currently supported by other TLS modules)*/
++
++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++ wpa_printf(MSG_INFO,
++ "MTLS: excessive check_cert_subject match '%s'",
++ match);
++ break; /* truncate huge list and continue */
++ }
++ }
++
++ /* each component in match string must match cert Subject in order listed
++ * The behavior below preserves ordering but is slightly different than
++ * the grossly inefficient contortions implemented in tls_openssl.c */
++ const mbedtls_x509_name *name = &crt->subject;
++ for (int i = 0; i < nlist; ++i) {
++ int found = 0;
++ for (; name != NULL && !found; name = name->next) {
++ if (!name->oid.p)
++ continue;
++ /* special-case "*" to match any OID and value */
++ if (list[i].olen == 0) {
++ found = 1;
++ continue;
++ }
++ /* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
++ if (list[i].olen != name->oid.len
++ || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
++ continue;
++ /* Note: v is not '\0'-terminated, but is a known length vlen,
++ * so okay to pass to os_strncasecmp() even though not z-string */
++ if ((list[i].prefix
++ ? list[i].n <= name->val.len /* prefix match */
++ : list[i].n == name->val.len) /* full match */
++ && 0 == os_strncasecmp((char *)name->val.p,
++ list[i].p, list[i].n)) {
++ found = 1;
++ continue;
++ }
++ }
++ if (!found)
++ return 0; /* no match */
++ }
++ return 1; /* match */
++}
++
++
++__attribute_cold__
++static void
++tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
++ const char *errmsg, enum tls_fail_reason reason)
++{
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (init_conf->event_cb == NULL)
++ return;
++
++ struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++ subject[0] = '\0';
++ union tls_event_data ev;
++ os_memset(&ev, 0, sizeof(ev));
++ ev.cert_fail.reason = reason;
++ ev.cert_fail.depth = depth;
++ ev.cert_fail.subject = subject;
++ ev.cert_fail.reason_txt = errmsg;
++ ev.cert_fail.cert = certbuf;
++
++ init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
++
++ wpabuf_free(certbuf);
++}
++
++
++__attribute_noinline__
++static void
++tls_mbedtls_verify_cert_event (struct tls_connection *conn,
++ mbedtls_x509_crt *crt, int depth)
++{
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (init_conf->event_cb == NULL)
++ return;
++
++ struct wpabuf *certbuf = NULL;
++ union tls_event_data ev;
++ os_memset(&ev, 0, sizeof(ev));
++
++ #ifdef MBEDTLS_SHA256_C
++ u8 hash[SHA256_DIGEST_LENGTH];
++ const u8 *addr[] = { (u8 *)crt->raw.p };
++ if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
++ ev.peer_cert.hash = hash;
++ ev.peer_cert.hash_len = sizeof(hash);
++ }
++ #endif
++ ev.peer_cert.depth = depth;
++ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++ if (depth == 0)
++ ev.peer_cert.subject = conn->peer_subject;
++ if (ev.peer_cert.subject == NULL) {
++ ev.peer_cert.subject = subject;
++ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++ subject[0] = '\0';
++ }
++
++ char serial_num[128+1];
++ ev.peer_cert.serial_num =
++ tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
++
++ const mbedtls_x509_sequence *cur;
++
++ cur = NULL;
++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++ cur = &crt->subject_alt_names;
++ for (; cur != NULL; cur = cur->next) {
++ const unsigned char san_type = (unsigned char)cur->buf.tag
++ & MBEDTLS_ASN1_TAG_VALUE_MASK;
++ size_t prelen = 4;
++ const char *pre;
++ switch (san_type) {
++ case MBEDTLS_X509_SAN_RFC822_NAME: prelen = 6; pre = "EMAIL:";break;
++ case MBEDTLS_X509_SAN_DNS_NAME: pre = "DNS:"; break;
++ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:"; break;
++ default: continue;
++ }
++
++ char *pos = os_malloc(prelen + cur->buf.len + 1);
++ if (pos == NULL)
++ break;
++ ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
++ os_memcpy(pos, pre, prelen);
++ /* data should be properly backslash-escaped if needed,
++ * so code below does not re-escape, but does replace CTLs */
++ /*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
++ /*pos[prelen+cur->buf.len] = '\0';*/
++ pos += prelen;
++ for (size_t i = 0; i < cur->buf.len; ++i) {
++ unsigned char c = cur->buf.p[i];
++ *pos++ = (c >= 32 && c != 127) ? c : '?';
++ }
++ *pos = '\0';
++
++ if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
++ break;
++ }
++
++ cur = NULL;
++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
++ cur = &crt->certificate_policies;
++ for (; cur != NULL; cur = cur->next) {
++ if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
++ continue;
++ /* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
++ /* TOD-TOFU "1.3.6.1.4.1.40808.1.3.2" */
++ #define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
++ #define OID_TOD_TOFU "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
++ if (os_memcmp(cur->buf.p,
++ OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
++ ev.peer_cert.tod = 1; /* TOD-STRICT */
++ break;
++ }
++ if (os_memcmp(cur->buf.p,
++ OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
++ ev.peer_cert.tod = 2; /* TOD-TOFU */
++ break;
++ }
++ }
++
++ struct tls_conf *tls_conf = conn->tls_conf;
++ if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
++ || init_conf->cert_in_cb) {
++ certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++ ev.peer_cert.cert = certbuf;
++ }
++
++ init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
++
++ wpabuf_free(certbuf);
++ char **altsubject;
++ *(const char ***)&altsubject = ev.peer_cert.altsubject;
++ for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
++ os_free(altsubject[i]);
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
++{
++ /* XXX: N.B. verify code not carefully tested besides hwsim tests
++ *
++ * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
++ * RFE: review and add support for additional TLS_CONN_* flags
++ * not handling OCSP (not available in mbedtls)
++ * ... */
++
++ struct tls_connection *conn = (struct tls_connection *)arg;
++ struct tls_conf *tls_conf = conn->tls_conf;
++ uint32_t flags_in = *flags;
++
++ if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
++ emsg(MSG_WARNING, "client cert chain too long");
++ *flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "client cert chain too long",
++ TLS_FAIL_BAD_CERTIFICATE);
++ }
++ else if (tls_conf->verify_depth0_only) {
++ if (depth > 0)
++ *flags = 0;
++ else {
++ #ifdef MBEDTLS_SHA256_C
++ u8 hash[SHA256_DIGEST_LENGTH];
++ const u8 *addr[] = { (u8 *)crt->raw.p };
++ if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
++ || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
++ *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "cert hash mismatch",
++ TLS_FAIL_UNTRUSTED);
++ }
++ else /* hash matches; ignore other issues *except* if revoked)*/
++ *flags &= MBEDTLS_X509_BADCERT_REVOKED;
++ #endif
++ }
++ }
++ else if (depth == 0) {
++ if (!conn->peer_subject)
++ tls_mbedtls_set_peer_subject(conn, crt);
++ /*(use same labels to tls_mbedtls_verify_fail_event() as used in
++ * other TLS modules so that hwsim tests find exact string match)*/
++ if (!conn->peer_subject) { /* error copying subject string */
++ *flags |= MBEDTLS_X509_BADCERT_OTHER;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "internal error",
++ TLS_FAIL_UNSPECIFIED);
++ }
++ /*(use os_strstr() for subject match as is done in tls_mbedtls.c
++ * to follow the same behavior, even though a suffix match would
++ * make more sense. Also, note that strstr match does not
++ * normalize whitespace (between components) for comparison)*/
++ else if (tls_conf->subject_match
++ && os_strstr(conn->peer_subject,
++ tls_conf->subject_match) == NULL) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: Subject '%s' did not match with '%s'",
++ conn->peer_subject, tls_conf->subject_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Subject mismatch",
++ TLS_FAIL_SUBJECT_MISMATCH);
++ }
++ if (tls_conf->altsubject_match
++ && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: altSubjectName match '%s' not found",
++ tls_conf->altsubject_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "AltSubject mismatch",
++ TLS_FAIL_ALTSUBJECT_MISMATCH);
++ }
++ if (tls_conf->suffix_match
++ && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: Domain suffix match '%s' not found",
++ tls_conf->suffix_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Domain suffix mismatch",
++ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
++ }
++ if (tls_conf->domain_match
++ && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
++ wpa_printf(MSG_WARNING,
++ "MTLS: Domain match '%s' not found",
++ tls_conf->domain_match);
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Domain mismatch",
++ TLS_FAIL_DOMAIN_MISMATCH);
++ }
++ if (tls_conf->check_cert_subject
++ && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Distinguished Name",
++ TLS_FAIL_DN_MISMATCH);
++ }
++ if (tls_conf->flags & TLS_CONN_SUITEB) {
++ /* check RSA modulus size (public key bitlen) */
++ const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
++ if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
++ && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
++ /* hwsim suite_b RSA tests expect 3072
++ * suite_b_192_rsa_ecdhe_radius_rsa2048_client
++ * suite_b_192_rsa_dhe_radius_rsa2048_client */
++ *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "Insufficient RSA modulus size",
++ TLS_FAIL_INSUFFICIENT_KEY_LEN);
++ }
++ }
++ if (tls_conf->check_crl && tls_conf->crl == NULL) {
++ /* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
++ emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
++ *flags |= MBEDTLS_X509_BADCERT_OTHER;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "check_crl set but no CRL loaded; "
++ "reject all?",
++ TLS_FAIL_BAD_CERTIFICATE);
++ }
++ }
++ else {
++ if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
++ *flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
++ }
++
++ if (!tls_conf->check_crl_strict) {
++ *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
++ *flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
++ }
++
++ if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
++ *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
++ *flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
++ }
++
++ tls_mbedtls_verify_cert_event(conn, crt, depth);
++
++ if (*flags) {
++ if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
++ |MBEDTLS_X509_BADCERT_CN_MISMATCH
++ |MBEDTLS_X509_BADCERT_REVOKED)) {
++ emsg(MSG_WARNING, "client cert not trusted");
++ }
++ /* report event if flags set but no additional flags set above */
++ /* (could translate flags to more detailed TLS_FAIL_* if needed) */
++ if (!(*flags & ~flags_in)) {
++ enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
++ const char *errmsg = "cert verify fail unspecified";
++ if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
++ reason = TLS_FAIL_UNTRUSTED;
++ errmsg = "certificate not trusted";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
++ reason = TLS_FAIL_REVOKED;
++ errmsg = "certificate has been revoked";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
++ reason = TLS_FAIL_NOT_YET_VALID;
++ errmsg = "certificate not yet valid";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
++ reason = TLS_FAIL_EXPIRED;
++ errmsg = "certificate has expired";
++ }
++ if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
++ reason = TLS_FAIL_BAD_CERTIFICATE;
++ errmsg = "certificate uses insecure algorithm";
++ }
++ tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
++ }
++ #if 0
++ /* ??? send (again) cert events for all certs in chain ???
++ * (should already have been called for greater depths) */
++ /* tls_openssl.c:tls_verify_cb() sends cert events for all certs
++ * in chain if certificate validation fails, but sends all events
++ * with depth set to 0 (might be a bug) */
++ if (depth > 0) {
++ int pdepth = depth + 1;
++ for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
++ tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
++ }
++ }
++ #endif
++ /*(do not preserve subject if verification failed but was optional)*/
++ if (depth == 0 && conn->peer_subject) {
++ os_free(conn->peer_subject);
++ conn->peer_subject = NULL;
++ }
++ }
++ else if (depth == 0) {
++ struct tls_config *init_conf = &tls_ctx_global.init_conf;
++ if (tls_conf->ca_cert_probe) {
++ /* reject server certificate on probe-only run */
++ *flags |= MBEDTLS_X509_BADCERT_OTHER;
++ tls_mbedtls_verify_fail_event(crt, depth,
++ "server chain probe",
++ TLS_FAIL_SERVER_CHAIN_PROBE);
++ }
++ else if (init_conf->event_cb) {
++ /* ??? send event as soon as depth == 0 is verified ???
++ * What about rest of chain?
++ * Follows tls_mbedtls.c behavior: */
++ init_conf->event_cb(init_conf->cb_ctx,
++ TLS_CERT_CHAIN_SUCCESS, NULL);
++ }
++ }
++
++ return 0;
++}
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index e672a1787..3e3e309f4 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -971,6 +971,9 @@ struct wpa_driver_associate_params {
+ * responsible for selecting with which BSS to associate. */
+ const u8 *bssid;
+
++ unsigned char rates[32];
++ int mcast_rate;
++
+ /**
+ * bssid_hint - BSSID of a proposed AP
+ *
+@@ -1873,6 +1876,7 @@ struct wpa_driver_mesh_join_params {
+ #define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008
+ unsigned int flags;
+ bool handle_dfs;
++ int mcast_rate;
+ };
+
+ struct wpa_driver_set_key_params {
+@@ -2340,6 +2344,9 @@ struct wpa_driver_capa {
+ /** Maximum number of iterations in a single scan plan */
+ u32 max_sched_scan_plan_iterations;
+
++ /** Maximum number of extra IE bytes for scans */
++ u16 max_scan_ie_len;
++
+ /** Whether sched_scan (offloaded scanning) is supported */
+ int sched_scan_supported;
+
+@@ -3861,6 +3868,25 @@ struct wpa_driver_ops {
+ int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname);
+
++ /**
++ * if_rename - Rename a virtual interface
++ * @priv: Private driver interface data
++ * @type: Interface type
++ * @ifname: Interface name of the virtual interface to be renamed
++ * (NULL when renaming the AP BSS interface)
++ * @new_name: New interface name of the virtual interface
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*if_rename)(void *priv, enum wpa_driver_if_type type,
++ const char *ifname, const char *new_name);
++
++ /**
++ * set_first_bss - Make a virtual interface the first (primary) bss
++ * @priv: Private driver interface data
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*set_first_bss)(void *priv);
++
+ /**
+ * set_sta_vlan - Bind a station into a specific interface (AP only)
+ * @priv: Private driver interface data
+@@ -4265,7 +4291,7 @@ struct wpa_driver_ops {
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+- unsigned int val);
++ const char *ifname, unsigned int val);
+
+ /**
+ * get_wowlan - Get wake-on-wireless status
+@@ -6559,6 +6585,7 @@ union wpa_event_data {
+
+ /**
+ * struct ch_switch
++ * @count: Count until channel switch activates
+ * @freq: Frequency of new channel in MHz
+ * @ht_enabled: Whether this is an HT channel
+ * @ch_offset: Secondary channel offset
+@@ -6569,6 +6596,7 @@ union wpa_event_data {
+ * @punct_bitmap: Puncturing bitmap
+ */
+ struct ch_switch {
++ int count;
+ int freq;
+ int ht_enabled;
+ int ch_offset;
+@@ -6816,8 +6844,8 @@ union wpa_event_data {
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data);
++extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ /**
+ * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
+@@ -6829,7 +6857,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ * Same as wpa_supplicant_event(), but we search for the interface in
+ * wpa_global.
+ */
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+
+ /*
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 9ac621ae6..6778ad369 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -78,6 +78,16 @@ enum nlmsgerr_attrs {
+
+ #endif /* ANDROID */
+
++static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
++{
++ const struct nlmsghdr *nlh;
++
++ if (!wpa_netlink_hook)
++ return;
++
++ nlh = nlmsg_hdr(msg);
++ wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
++}
+
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+@@ -432,6 +442,11 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
+ return NL_OK;
+ }
+
++static int debug_handler(struct nl_msg *msg, void *arg)
++{
++ handle_nl_debug_hook(msg, 0);
++ return NL_OK;
++}
+
+ static void nl80211_nlmsg_clear(struct nl_msg *msg)
+ {
+@@ -505,6 +520,7 @@ int send_and_recv(struct nl80211_global *global,
+ if (!msg)
+ return -ENOMEM;
+
++ handle_nl_debug_hook(msg, 1);
+ err.err = -ENOMEM;
+
+ s_nl_cb = nl_socket_get_cb(nl_handle);
+@@ -539,6 +555,7 @@ int send_and_recv(struct nl80211_global *global,
+ err.orig_msg = msg;
+ err.err_info = err_info;
+
++ nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
+ if (ack_handler_custom) {
+@@ -942,6 +959,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+ os_free(w);
+ return NULL;
+ }
++ nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -1356,7 +1374,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+ namebuf, ifname);
+- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++ if (drv->first_bss->ifindex != ifi->ifi_index) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface down",
+ drv->first_bss->ifname);
+@@ -1392,7 +1410,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+ namebuf, ifname);
+- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++ if (drv->first_bss->ifindex != ifi->ifi_index) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface up",
+ drv->first_bss->ifname);
+@@ -2038,6 +2056,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+ genl_family_put(family);
+ nl_cache_free(cache);
+
++ nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -2208,6 +2227,7 @@ static int nl80211_init_bss(struct i802_bss *bss)
+ if (!bss->nl_cb)
+ return -1;
+
++ nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -5485,7 +5505,7 @@ static int nl80211_set_channel(struct i802_bss *bss,
+ freq->he_enabled, freq->eht_enabled, freq->bandwidth,
+ freq->center_freq1, freq->center_freq2);
+
+- msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
++ msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+ NL80211_CMD_SET_WIPHY);
+ if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+ nlmsg_free(msg);
+@@ -5858,26 +5878,29 @@ fail:
+
+ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+ {
+-#ifdef CONFIG_LIBNL3_ROUTE
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+- struct rtnl_neigh *rn;
+- struct nl_addr *nl_addr;
++ struct ndmsg nhdr = {
++ .ndm_state = NUD_PERMANENT,
++ .ndm_ifindex = bss->ifindex,
++ .ndm_family = AF_BRIDGE,
++ };
++ struct nl_msg *msg;
+ int err;
+
+- rn = rtnl_neigh_alloc();
+- if (!rn)
++ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
++ if (!msg)
+ return;
+
+- rtnl_neigh_set_family(rn, AF_BRIDGE);
+- rtnl_neigh_set_ifindex(rn, bss->ifindex);
+- nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
+- if (!nl_addr) {
+- rtnl_neigh_put(rn);
+- return;
+- }
+- rtnl_neigh_set_lladdr(rn, nl_addr);
++ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
++ goto errout;
+
+- err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
++ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
++ goto errout;
++
++ if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
++ goto errout;
++
++ err = nl_wait_for_ack(drv->rtnl_sk);
+ if (err < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
+ MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
+@@ -5887,9 +5910,8 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+ MACSTR, MAC2STR(addr));
+ }
+
+- nl_addr_put(nl_addr);
+- rtnl_neigh_put(rn);
+-#endif /* CONFIG_LIBNL3_ROUTE */
++errout:
++ nlmsg_free(msg);
+ }
+
+
+@@ -6178,6 +6200,8 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
+ nl80211_put_wiphy_data_ap(bss);
+ if (bss->flink)
+ bss->flink->beacon_set = 0;
++
++ wpa_driver_nl80211_del_beacon_all(bss);
+ }
+
+
+@@ -8566,6 +8590,7 @@ static void *i802_init(struct hostapd_data *hapd,
+ char master_ifname[IFNAMSIZ];
+ int ifindex, br_ifindex = 0;
+ int br_added = 0;
++ int err;
+
+ bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+ params->global_priv, 1,
+@@ -8625,24 +8650,18 @@ static void *i802_init(struct hostapd_data *hapd,
+ (params->num_bridge == 0 || !params->bridge[0]))
+ add_ifidx(drv, br_ifindex, drv->ifindex);
+
+-#ifdef CONFIG_LIBNL3_ROUTE
+- if (bss->added_if_into_bridge || bss->already_in_bridge) {
+- int err;
+-
+- drv->rtnl_sk = nl_socket_alloc();
+- if (drv->rtnl_sk == NULL) {
+- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+- goto failed;
+- }
++ drv->rtnl_sk = nl_socket_alloc();
++ if (drv->rtnl_sk == NULL) {
++ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
++ goto failed;
++ }
+
+- err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+- if (err) {
+- wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+- nl_geterror(err));
+- goto failed;
+- }
++ err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
++ if (err) {
++ wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
++ nl_geterror(err));
++ goto failed;
+ }
+-#endif /* CONFIG_LIBNL3_ROUTE */
+
+ if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
+ wpa_printf(MSG_DEBUG,
+@@ -9000,6 +9019,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ if (drv->first_bss->next) {
+ drv->first_bss = drv->first_bss->next;
+ drv->ctx = drv->first_bss->ctx;
++ drv->ifindex = drv->first_bss->ifindex;
+ os_free(bss);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
+@@ -9009,6 +9029,50 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ return 0;
+ }
+
++static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
++ enum wpa_driver_if_type type,
++ const char *ifname, const char *new_name)
++{
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct ifinfomsg ifi = {
++ .ifi_family = AF_UNSPEC,
++ .ifi_index = bss->ifindex,
++ };
++ struct nl_msg *msg;
++ int res = -ENOMEM;
++
++ if (ifname)
++ ifi.ifi_index = if_nametoindex(ifname);
++
++ msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
++ if (!msg)
++ return res;
++
++ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
++ goto out;
++
++ if (nla_put_string(msg, IFLA_IFNAME, new_name))
++ goto out;
++
++ res = nl_send_auto_complete(drv->rtnl_sk, msg);
++ if (res < 0)
++ goto out;
++
++ res = nl_wait_for_ack(drv->rtnl_sk);
++ if (res) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Renaming device %s to %s failed: %s",
++ ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
++ goto out;
++ }
++
++ if (type == WPA_IF_AP_BSS && !ifname)
++ os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
++
++out:
++ nlmsg_free(msg);
++ return res;
++}
+
+ static int cookie_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -10792,6 +10856,37 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+ #endif /* CONFIG_IEEE80211BE */
+
+
++static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
++ const char *ifname, const char *new_name)
++{
++ struct i802_bss *bss = priv;
++ return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
++}
++
++
++static int driver_nl80211_set_first_bss(void *priv)
++{
++ struct i802_bss *bss = priv, *tbss;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++
++ if (drv->first_bss == bss)
++ return 0;
++
++ for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
++ if (tbss->next != bss)
++ continue;
++
++ tbss->next = bss->next;
++ bss->next = drv->first_bss;
++ drv->first_bss = bss;
++ drv->ctx = bss->ctx;
++ return 0;
++ }
++
++ return -1;
++}
++
++
+ static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq,
+@@ -11294,6 +11389,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ if (ret)
+ goto error;
+
++ if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
++ nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
++ }
++
+ /* beacon_csa params */
+ beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+ if (!beacon_csa)
+@@ -11940,6 +12039,18 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+ }
+
+
++static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
++{
++ if (mcast_rate > 0) {
++ wpa_printf(MSG_DEBUG, " * mcast_rate=%.1f",
++ (double)mcast_rate / 10);
++ return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
++ }
++
++ return 0;
++}
++
++
+ static int nl80211_put_mesh_config(struct nl_msg *msg,
+ struct wpa_driver_mesh_bss_params *params)
+ {
+@@ -12001,6 +12112,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
+ nl80211_put_basic_rates(msg, params->basic_rates) ||
+ nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+ nl80211_put_beacon_int(msg, params->beacon_int) ||
++ nl80211_put_mcast_rate(msg, params->mcast_rate) ||
+ nl80211_put_dtim_period(msg, params->dtim_period))
+ goto fail;
+
+@@ -12156,13 +12268,14 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+ const u8 *ipaddr, int prefixlen,
+ const u8 *addr)
+ {
+-#ifdef CONFIG_LIBNL3_ROUTE
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+- struct rtnl_neigh *rn;
+- struct nl_addr *nl_ipaddr = NULL;
+- struct nl_addr *nl_lladdr = NULL;
+- int family, addrsize;
++ struct ndmsg nhdr = {
++ .ndm_state = NUD_PERMANENT,
++ .ndm_ifindex = bss->br_ifindex,
++ };
++ struct nl_msg *msg;
++ int addrsize;
+ int res;
+
+ if (!ipaddr || prefixlen == 0 || !addr)
+@@ -12181,85 +12294,66 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+ }
+
+ if (version == 4) {
+- family = AF_INET;
++ nhdr.ndm_family = AF_INET;
+ addrsize = 4;
+ } else if (version == 6) {
+- family = AF_INET6;
++ nhdr.ndm_family = AF_INET6;
+ addrsize = 16;
+ } else {
+ return -EINVAL;
+ }
+
+- rn = rtnl_neigh_alloc();
+- if (rn == NULL)
++ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
++ if (!msg)
+ return -ENOMEM;
+
+- /* set the destination ip address for neigh */
+- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+- if (nl_ipaddr == NULL) {
+- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+- res = -ENOMEM;
++ res = -ENOMEM;
++ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+ goto errout;
+- }
+- nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
+- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+- if (res) {
+- wpa_printf(MSG_DEBUG,
+- "nl80211: neigh set destination addr failed");
++
++ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+ goto errout;
+- }
+
+- /* set the corresponding lladdr for neigh */
+- nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
+- if (nl_lladdr == NULL) {
+- wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
+- res = -ENOMEM;
++ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
+ goto errout;
+- }
+- rtnl_neigh_set_lladdr(rn, nl_lladdr);
+
+- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+- rtnl_neigh_set_state(rn, NUD_PERMANENT);
++ res = nl_send_auto_complete(drv->rtnl_sk, msg);
++ if (res < 0)
++ goto errout;
+
+- res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
++ res = nl_wait_for_ack(drv->rtnl_sk);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Adding bridge ip neigh failed: %s",
+ nl_geterror(res));
+ }
+ errout:
+- if (nl_lladdr)
+- nl_addr_put(nl_lladdr);
+- if (nl_ipaddr)
+- nl_addr_put(nl_ipaddr);
+- if (rn)
+- rtnl_neigh_put(rn);
++ nlmsg_free(msg);
+ return res;
+-#else /* CONFIG_LIBNL3_ROUTE */
+- return -1;
+-#endif /* CONFIG_LIBNL3_ROUTE */
+ }
+
+
+ static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+ const u8 *ipaddr)
+ {
+-#ifdef CONFIG_LIBNL3_ROUTE
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+- struct rtnl_neigh *rn;
+- struct nl_addr *nl_ipaddr;
+- int family, addrsize;
++ struct ndmsg nhdr = {
++ .ndm_state = NUD_PERMANENT,
++ .ndm_ifindex = bss->br_ifindex,
++ };
++ struct nl_msg *msg;
++ int addrsize;
+ int res;
+
+ if (!ipaddr)
+ return -EINVAL;
+
+ if (version == 4) {
+- family = AF_INET;
++ nhdr.ndm_family = AF_INET;
+ addrsize = 4;
+ } else if (version == 6) {
+- family = AF_INET6;
++ nhdr.ndm_family = AF_INET6;
+ addrsize = 16;
+ } else {
+ return -EINVAL;
+@@ -12277,41 +12371,30 @@ static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+ return -1;
+ }
+
+- rn = rtnl_neigh_alloc();
+- if (rn == NULL)
++ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
++ if (!msg)
+ return -ENOMEM;
+
+- /* set the destination ip address for neigh */
+- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+- if (nl_ipaddr == NULL) {
+- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+- res = -ENOMEM;
++ res = -ENOMEM;
++ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+ goto errout;
+- }
+- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+- if (res) {
+- wpa_printf(MSG_DEBUG,
+- "nl80211: neigh set destination addr failed");
++
++ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+ goto errout;
+- }
+
+- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
++ res = nl_send_auto_complete(drv->rtnl_sk, msg);
++ if (res < 0)
++ goto errout;
+
+- res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
++ res = nl_wait_for_ack(drv->rtnl_sk);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Deleting bridge ip neigh failed: %s",
+ nl_geterror(res));
+ }
+ errout:
+- if (nl_ipaddr)
+- nl_addr_put(nl_ipaddr);
+- if (rn)
+- rtnl_neigh_put(rn);
++ nlmsg_free(msg);
+ return res;
+-#else /* CONFIG_LIBNL3_ROUTE */
+- return -1;
+-#endif /* CONFIG_LIBNL3_ROUTE */
+ }
+
+
+@@ -12389,7 +12472,7 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param)
+
+
+ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+- unsigned int val)
++ const char *ifname, unsigned int val)
+ {
+ struct i802_bss *bss = priv;
+ char path[128];
+@@ -12415,8 +12498,11 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+ return -EINVAL;
+ }
+
++ if (!ifname)
++ ifname = bss->brname;
++
+ os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+- ip_version, bss->brname, param_txt);
++ ip_version, ifname, param_txt);
+
+ set_val:
+ if (linux_write_system_file(path, val))
+@@ -14019,6 +14105,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .set_acl = wpa_driver_nl80211_set_acl,
+ .if_add = wpa_driver_nl80211_if_add,
+ .if_remove = driver_nl80211_if_remove,
++ .if_rename = driver_nl80211_if_rename,
++ .set_first_bss = driver_nl80211_set_first_bss,
+ .send_mlme = driver_nl80211_send_mlme,
+ .get_hw_feature_data = nl80211_get_hw_feature_data,
+ .sta_add = wpa_driver_nl80211_sta_add,
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 65389d206..d6a887cef 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -976,6 +976,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+ }
+
++ if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
++ capa->max_scan_ie_len =
++ nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
++
+ if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+ capa->max_match_sets =
+ nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index f5778cdaf..4a12d749c 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1196,6 +1196,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *bw, struct nlattr *cf1,
+ struct nlattr *cf2,
+ struct nlattr *punct_bitmap,
++ struct nlattr *count,
+ int finished)
+ {
+ struct i802_bss *bss;
+@@ -1259,6 +1260,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ data.ch_switch.cf1 = nla_get_u32(cf1);
+ if (cf2)
+ data.ch_switch.cf2 = nla_get_u32(cf2);
++ if (count)
++ data.ch_switch.count = nla_get_u32(count);
+
+ if (link)
+ data.ch_switch.link_id = nla_get_u8(link);
+@@ -3972,6 +3975,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
++ tb[NL80211_ATTR_CH_SWITCH_COUNT],
+ 0);
+ break;
+ case NL80211_CMD_CH_SWITCH_NOTIFY:
+@@ -3984,6 +3988,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
++ NULL,
+ 1);
+ break;
+ case NL80211_CMD_DISCONNECT:
+diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
+index 577f84fef..c352a88bc 100644
+--- a/src/drivers/driver_nl80211_scan.c
++++ b/src/drivers/driver_nl80211_scan.c
+@@ -221,7 +221,7 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+ wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
+ }
+
+- if (params->extra_ies) {
++ if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ params->extra_ies, params->extra_ies_len);
+ if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
+index e95df6ddb..9071da3cf 100644
+--- a/src/drivers/drivers.c
++++ b/src/drivers/drivers.c
+@@ -10,6 +10,10 @@
+ #include "utils/common.h"
+ #include "driver.h"
+
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ const struct wpa_driver_ops *const wpa_drivers[] =
+ {
+diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
+index a03d4a034..8da44d9f5 100644
+--- a/src/drivers/drivers.mak
++++ b/src/drivers/drivers.mak
+@@ -54,7 +54,6 @@ NEED_SME=y
+ NEED_AP_MLME=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ NEED_RADIOTAP=y
+ NEED_LIBNL=y
+ endif
+@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+ CONFIG_WIRELESS_EXTENSION=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ endif
+
+ ifdef CONFIG_DRIVER_NDIS
+@@ -137,7 +135,6 @@ endif
+ ifdef CONFIG_WIRELESS_EXTENSION
+ DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+ DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+-NEED_RFKILL=y
+ endif
+
+ ifdef NEED_NETLINK
+@@ -146,6 +143,7 @@ endif
+
+ ifdef NEED_RFKILL
+ DRV_OBJS += ../src/drivers/rfkill.o
++DRV_WPA_CFLAGS += -DCONFIG_RFKILL
+ endif
+
+ ifdef NEED_RADIOTAP
+diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
+index 0412ac330..e27565375 100644
+--- a/src/drivers/rfkill.h
++++ b/src/drivers/rfkill.h
+@@ -18,8 +18,24 @@ struct rfkill_config {
+ void (*unblocked_cb)(void *ctx);
+ };
+
++#ifdef CONFIG_RFKILL
+ struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+ void rfkill_deinit(struct rfkill_data *rfkill);
+ int rfkill_is_blocked(struct rfkill_data *rfkill);
++#else
++static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
++{
++ return (void *) 1;
++}
++
++static inline void rfkill_deinit(struct rfkill_data *rfkill)
++{
++}
++
++static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
++{
++ return 0;
++}
++#endif
+
+ #endif /* RFKILL_H */
+diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
+index 2a7f36170..8e8903051 100644
+--- a/src/radius/radius_client.c
++++ b/src/radius/radius_client.c
+@@ -165,6 +165,8 @@ struct radius_client_data {
+ */
+ void *ctx;
+
++ struct hostapd_ip_addr local_ip;
++
+ /**
+ * conf - RADIUS client configuration (list of RADIUS servers to use)
+ */
+@@ -818,6 +820,30 @@ static void radius_close_acct_socket(struct radius_client_data *radius)
+ }
+
+
++/**
++ * radius_client_send - Get local address for the RADIUS auth socket
++ * @radius: RADIUS client context from radius_client_init()
++ * @addr: pointer to store the address
++ *
++ * This function returns the local address for the connection to the RADIUS
++ * auth server. It also opens the socket if it's not available yet.
++ */
++int radius_client_get_local_addr(struct radius_client_data *radius,
++ struct hostapd_ip_addr *addr)
++{
++ struct hostapd_radius_servers *conf = radius->conf;
++
++ if (conf->auth_server && radius->auth_sock < 0)
++ radius_client_init_auth(radius);
++
++ if (radius->auth_sock < 0)
++ return -1;
++
++ memcpy(addr, &radius->local_ip, sizeof(*addr));
++
++ return 0;
++}
++
+ /**
+ * radius_client_send - Send a RADIUS request
+ * @radius: RADIUS client context from radius_client_init()
+@@ -1711,6 +1737,10 @@ radius_change_server(struct radius_client_data *radius,
+ wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+ inet_ntoa(claddr.sin_addr),
+ ntohs(claddr.sin_port));
++ if (auth) {
++ radius->local_ip.af = AF_INET;
++ radius->local_ip.u.v4 = claddr.sin_addr;
++ }
+ }
+ break;
+ #ifdef CONFIG_IPV6
+@@ -1722,6 +1752,10 @@ radius_change_server(struct radius_client_data *radius,
+ inet_ntop(AF_INET6, &claddr6.sin6_addr,
+ abuf, sizeof(abuf)),
+ ntohs(claddr6.sin6_port));
++ if (auth) {
++ radius->local_ip.af = AF_INET6;
++ radius->local_ip.u.v6 = claddr6.sin6_addr;
++ }
+ }
+ break;
+ }
+diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
+index db40637ea..9a89b0382 100644
+--- a/src/radius/radius_client.h
++++ b/src/radius/radius_client.h
+@@ -274,6 +274,8 @@ int radius_client_register(struct radius_client_data *radius,
+ void radius_client_set_interim_error_cb(struct radius_client_data *radius,
+ void (*cb)(const u8 *addr, void *ctx),
+ void *ctx);
++int radius_client_get_local_addr(struct radius_client_data *radius,
++ struct hostapd_ip_addr * addr);
+ int radius_client_send(struct radius_client_data *radius,
+ struct radius_msg *msg,
+ RadiusType msg_type, const u8 *addr);
+diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
+index aaa3fc267..327782f62 100644
+--- a/src/radius/radius_das.c
++++ b/src/radius/radius_das.c
+@@ -12,13 +12,26 @@
+ #include "utils/common.h"
+ #include "utils/eloop.h"
+ #include "utils/ip_addr.h"
++#include "utils/list.h"
+ #include "radius.h"
+ #include "radius_das.h"
+
+
+-struct radius_das_data {
++static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
++
++struct radius_das_port {
++ struct dl_list list;
++ struct dl_list das_data;
++
++ int port;
+ int sock;
++};
++
++struct radius_das_data {
++ struct dl_list list;
++ struct radius_das_port *port;
+ u8 *shared_secret;
++ u8 *nas_identifier;
+ size_t shared_secret_len;
+ struct hostapd_ip_addr client_addr;
+ unsigned int time_window;
+@@ -378,56 +391,17 @@ fail:
+ }
+
+
+-static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++static void
++radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
++ struct sockaddr *from, socklen_t fromlen,
++ char *abuf, int from_port)
+ {
+- struct radius_das_data *das = eloop_ctx;
+- u8 buf[1500];
+- union {
+- struct sockaddr_storage ss;
+- struct sockaddr_in sin;
+-#ifdef CONFIG_IPV6
+- struct sockaddr_in6 sin6;
+-#endif /* CONFIG_IPV6 */
+- } from;
+- char abuf[50];
+- int from_port = 0;
+- socklen_t fromlen;
+- int len;
+- struct radius_msg *msg, *reply = NULL;
++ struct radius_msg *reply = NULL;
+ struct radius_hdr *hdr;
+ struct wpabuf *rbuf;
++ struct os_time now;
+ u32 val;
+ int res;
+- struct os_time now;
+-
+- fromlen = sizeof(from);
+- len = recvfrom(sock, buf, sizeof(buf), 0,
+- (struct sockaddr *) &from.ss, &fromlen);
+- if (len < 0) {
+- wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+- return;
+- }
+-
+- os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+- from_port = ntohs(from.sin.sin_port);
+-
+- wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+- len, abuf, from_port);
+- if (das->client_addr.u.v4.s_addr &&
+- das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+- wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+- return;
+- }
+-
+- msg = radius_msg_parse(buf, len);
+- if (msg == NULL) {
+- wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+- "from %s:%d failed", abuf, from_port);
+- return;
+- }
+-
+- if (wpa_debug_level <= MSG_MSGDUMP)
+- radius_msg_dump(msg);
+
+ if (radius_msg_verify_das_req(msg, das->shared_secret,
+ das->shared_secret_len,
+@@ -494,9 +468,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+ radius_msg_dump(reply);
+
+ rbuf = radius_msg_get_buf(reply);
+- res = sendto(das->sock, wpabuf_head(rbuf),
+- wpabuf_len(rbuf), 0,
+- (struct sockaddr *) &from.ss, fromlen);
++ res = sendto(das->port->sock, wpabuf_head(rbuf),
++ wpabuf_len(rbuf), 0, from, fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+ abuf, from_port, strerror(errno));
+@@ -508,6 +481,72 @@ fail:
+ radius_msg_free(reply);
+ }
+
++static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++{
++ struct radius_das_port *p = eloop_ctx;
++ struct radius_das_data *das;
++ u8 buf[1500];
++ union {
++ struct sockaddr_storage ss;
++ struct sockaddr_in sin;
++#ifdef CONFIG_IPV6
++ struct sockaddr_in6 sin6;
++#endif /* CONFIG_IPV6 */
++ } from;
++ struct radius_msg *msg;
++ size_t nasid_len = 0;
++ u8 *nasid_buf = NULL;
++ char abuf[50];
++ int from_port = 0;
++ socklen_t fromlen;
++ int found = 0;
++ int len;
++
++ fromlen = sizeof(from);
++ len = recvfrom(sock, buf, sizeof(buf), 0,
++ (struct sockaddr *) &from.ss, &fromlen);
++ if (len < 0) {
++ wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
++ return;
++ }
++
++ os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
++ from_port = ntohs(from.sin.sin_port);
++
++ msg = radius_msg_parse(buf, len);
++ if (msg == NULL) {
++ wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
++ "from %s:%d failed", abuf, from_port);
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
++ len, abuf, from_port);
++
++ if (wpa_debug_level <= MSG_MSGDUMP)
++ radius_msg_dump(msg);
++
++ radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
++ &nasid_buf, &nasid_len, NULL);
++ dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
++ if (das->client_addr.u.v4.s_addr &&
++ das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
++ continue;
++
++ if (das->nas_identifier && nasid_buf &&
++ (nasid_len != os_strlen(das->nas_identifier) ||
++ os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
++ continue;
++
++ found = 1;
++ radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
++ fromlen, abuf, from_port);
++ }
++
++ if (!found)
++ wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
++}
++
+
+ static int radius_das_open_socket(int port)
+ {
+@@ -533,6 +572,49 @@ static int radius_das_open_socket(int port)
+ }
+
+
++static struct radius_das_port *
++radius_das_open_port(int port)
++{
++ struct radius_das_port *p;
++
++ dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
++ if (p->port == port)
++ return p;
++ }
++
++ p = os_zalloc(sizeof(*p));
++ if (p == NULL)
++ return NULL;
++
++ dl_list_init(&p->das_data);
++ p->port = port;
++ p->sock = radius_das_open_socket(port);
++ if (p->sock < 0)
++ goto free_port;
++
++ if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
++ goto close_port;
++
++ dl_list_add(&das_ports, &p->list);
++
++ return p;
++
++close_port:
++ close(p->sock);
++free_port:
++ os_free(p);
++
++ return NULL;
++}
++
++static void radius_das_close_port(struct radius_das_port *p)
++{
++ dl_list_del(&p->list);
++ eloop_unregister_read_sock(p->sock);
++ close(p->sock);
++ free(p);
++}
++
+ struct radius_das_data *
+ radius_das_init(struct radius_das_conf *conf)
+ {
+@@ -553,6 +635,8 @@ radius_das_init(struct radius_das_conf *conf)
+ das->ctx = conf->ctx;
+ das->disconnect = conf->disconnect;
+ das->coa = conf->coa;
++ if (conf->nas_identifier)
++ das->nas_identifier = os_strdup(conf->nas_identifier);
+
+ os_memcpy(&das->client_addr, conf->client_addr,
+ sizeof(das->client_addr));
+@@ -565,19 +649,15 @@ radius_das_init(struct radius_das_conf *conf)
+ }
+ das->shared_secret_len = conf->shared_secret_len;
+
+- das->sock = radius_das_open_socket(conf->port);
+- if (das->sock < 0) {
++ das->port = radius_das_open_port(conf->port);
++ if (!das->port) {
+ wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+ "DAS");
+ radius_das_deinit(das);
+ return NULL;
+ }
+
+- if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+- {
+- radius_das_deinit(das);
+- return NULL;
+- }
++ dl_list_add(&das->port->das_data, &das->list);
+
+ return das;
+ }
+@@ -588,11 +668,14 @@ void radius_das_deinit(struct radius_das_data *das)
+ if (das == NULL)
+ return;
+
+- if (das->sock >= 0) {
+- eloop_unregister_read_sock(das->sock);
+- close(das->sock);
++ if (das->port) {
++ dl_list_del(&das->list);
++
++ if (dl_list_empty(&das->port->das_data))
++ radius_das_close_port(das->port);
+ }
+
++ os_free(das->nas_identifier);
+ os_free(das->shared_secret);
+ os_free(das);
+ }
+diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
+index 233d662f6..80dc13fc8 100644
+--- a/src/radius/radius_das.h
++++ b/src/radius/radius_das.h
+@@ -44,6 +44,7 @@ struct radius_das_attrs {
+ struct radius_das_conf {
+ int port;
+ const u8 *shared_secret;
++ const u8 *nas_identifier;
+ size_t shared_secret_len;
+ const struct hostapd_ip_addr *client_addr;
+ unsigned int time_window;
+diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
+index e02c21540..57a47263e 100644
+--- a/src/radius/radius_server.c
++++ b/src/radius/radius_server.c
+@@ -63,6 +63,12 @@ struct radius_server_counters {
+ u32 unknown_acct_types;
+ };
+
++struct radius_accept_attr {
++ u8 type;
++ u16 len;
++ void *data;
++};
++
+ /**
+ * struct radius_session - Internal RADIUS server data for a session
+ */
+@@ -90,7 +96,7 @@ struct radius_session {
+ unsigned int macacl:1;
+ unsigned int t_c_filtering:1;
+
+- struct hostapd_radius_attr *accept_attr;
++ struct radius_accept_attr *accept_attr;
+
+ u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
+ };
+@@ -394,6 +400,7 @@ static void radius_server_session_free(struct radius_server_data *data,
+ radius_msg_free(sess->last_reply);
+ os_free(sess->username);
+ os_free(sess->nas_ip);
++ os_free(sess->accept_attr);
+ os_free(sess);
+ data->num_sess--;
+ }
+@@ -554,6 +561,36 @@ radius_server_erp_find_key(struct radius_server_data *data, const char *keyname)
+ }
+ #endif /* CONFIG_ERP */
+
++static struct radius_accept_attr *
++radius_server_copy_attr(const struct hostapd_radius_attr *data)
++{
++ const struct hostapd_radius_attr *attr;
++ struct radius_accept_attr *attr_new;
++ size_t data_size = 0;
++ void *data_buf;
++ int n_attr = 1;
++
++ for (attr = data; attr; attr = attr->next) {
++ n_attr++;
++ data_size += wpabuf_len(attr->val);
++ }
++
++ attr_new = os_zalloc(n_attr * sizeof(*attr) + data_size);
++ if (!attr_new)
++ return NULL;
++
++ data_buf = &attr_new[n_attr];
++ for (n_attr = 0, attr = data; attr; attr = attr->next) {
++ struct radius_accept_attr *cur = &attr_new[n_attr++];
++
++ cur->type = attr->type;
++ cur->len = wpabuf_len(attr->val);
++ cur->data = memcpy(data_buf, wpabuf_head(attr->val), cur->len);
++ data_buf += cur->len;
++ }
++
++ return attr_new;
++}
+
+ static struct radius_session *
+ radius_server_get_new_session(struct radius_server_data *data,
+@@ -607,7 +644,7 @@ radius_server_get_new_session(struct radius_server_data *data,
+ eap_user_free(tmp);
+ return NULL;
+ }
+- sess->accept_attr = tmp->accept_attr;
++ sess->accept_attr = radius_server_copy_attr(tmp->accept_attr);
+ sess->macacl = tmp->macacl;
+ eap_user_free(tmp);
+
+@@ -1118,11 +1155,10 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+- struct hostapd_radius_attr *attr;
+- for (attr = sess->accept_attr; attr; attr = attr->next) {
+- if (!radius_msg_add_attr(msg, attr->type,
+- wpabuf_head(attr->val),
+- wpabuf_len(attr->val))) {
++ struct radius_accept_attr *attr;
++ for (attr = sess->accept_attr; attr->data; attr++) {
++ if (!radius_msg_add_attr(msg, attr->type, attr->data,
++ attr->len)) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ radius_msg_free(msg);
+ return NULL;
+@@ -1211,11 +1247,10 @@ radius_server_macacl(struct radius_server_data *data,
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+- struct hostapd_radius_attr *attr;
+- for (attr = sess->accept_attr; attr; attr = attr->next) {
+- if (!radius_msg_add_attr(msg, attr->type,
+- wpabuf_head(attr->val),
+- wpabuf_len(attr->val))) {
++ struct radius_accept_attr *attr;
++ for (attr = sess->accept_attr; attr->data; attr++) {
++ if (!radius_msg_add_attr(msg, attr->type, attr->data,
++ attr->len)) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ radius_msg_free(msg);
+ return NULL;
+@@ -2512,7 +2547,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+ ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+ phase2, user);
+ if (ret == 0 && user) {
+- sess->accept_attr = user->accept_attr;
++ sess->accept_attr = radius_server_copy_attr(user->accept_attr);
+ sess->remediation = user->remediation;
+ sess->macacl = user->macacl;
+ sess->t_c_timestamp = user->t_c_timestamp;
+diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
+index 8956c4072..e669858d8 100644
+--- a/src/rsn_supp/wpa.c
++++ b/src/rsn_supp/wpa.c
+@@ -3943,6 +3943,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+ }
+
+
++#ifdef CONFIG_CTRL_IFACE_MIB
++
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+@@ -4024,6 +4026,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+
+ return (int) len;
+ }
++#endif
+ #endif /* CONFIG_CTRL_IFACE */
+
+
+diff --git a/src/tls/Makefile b/src/tls/Makefile
+index c84fbe859..e974a41f0 100644
+--- a/src/tls/Makefile
++++ b/src/tls/Makefile
+@@ -1,3 +1,10 @@
++LIB_OBJS= asn1.o
++
++ifneq ($(CONFIG_TLS),gnutls)
++ifneq ($(CONFIG_TLS),mbedtls)
++ifneq ($(CONFIG_TLS),openssl)
++ifneq ($(CONFIG_TLS),wolfssl)
++
+ CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ CFLAGS += -DCONFIG_TLSV11
+@@ -21,5 +28,9 @@ LIB_OBJS= \
+ tlsv1_server_read.o \
+ tlsv1_server_write.o \
+ x509v3.o
++endif
++endif
++endif
++endif
+
+ include ../lib.rules
+diff --git a/src/utils/eloop.c b/src/utils/eloop.c
+index 00b0beff0..50dd1beda 100644
+--- a/src/utils/eloop.c
++++ b/src/utils/eloop.c
+@@ -77,6 +77,9 @@ struct eloop_sock_table {
+ struct eloop_data {
+ int max_sock;
+
++ eloop_timeout_poll_handler timeout_poll_cb;
++ eloop_poll_handler poll_cb;
++
+ size_t count; /* sum of all table counts */
+ #ifdef CONFIG_ELOOP_POLL
+ size_t max_pollfd_map; /* number of pollfds_map currently allocated */
+@@ -1121,6 +1124,12 @@ void eloop_run(void)
+ os_reltime_sub(&timeout->time, &now, &tv);
+ else
+ tv.sec = tv.usec = 0;
++ }
++
++ if (eloop.timeout_poll_cb && eloop.timeout_poll_cb(&tv, !!timeout))
++ timeout = (void *)1;
++
++ if (timeout) {
+ #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
+ timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+ #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+@@ -1190,7 +1199,8 @@ void eloop_run(void)
+ eloop.exceptions.changed = 0;
+
+ eloop_process_pending_signals();
+-
++ if (eloop.poll_cb)
++ eloop.poll_cb();
+
+ /* check if some registered timeouts have occurred */
+ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+@@ -1252,6 +1262,14 @@ out:
+ return;
+ }
+
++int eloop_register_cb(eloop_poll_handler poll_cb,
++ eloop_timeout_poll_handler timeout_cb)
++{
++ eloop.poll_cb = poll_cb;
++ eloop.timeout_poll_cb = timeout_cb;
++
++ return 0;
++}
+
+ void eloop_terminate(void)
+ {
+diff --git a/src/utils/eloop.h b/src/utils/eloop.h
+index 04ee6d183..5452ea589 100644
+--- a/src/utils/eloop.h
++++ b/src/utils/eloop.h
+@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
+ */
+ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
++typedef bool (*eloop_timeout_poll_handler)(struct os_reltime *tv, bool tv_set);
++typedef void (*eloop_poll_handler)(void);
++
+ /**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+@@ -73,6 +76,9 @@ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+ */
+ int eloop_init(void);
+
++int eloop_register_cb(eloop_poll_handler poll_cb,
++ eloop_timeout_poll_handler timeout_cb);
++
+ /**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+@@ -320,6 +326,8 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ */
+ int eloop_sock_requeue(void);
+
++void eloop_add_uloop(void);
++
+ /**
+ * eloop_run - Start the event loop
+ *
+diff --git a/src/utils/uloop.c b/src/utils/uloop.c
+new file mode 100644
+index 000000000..c0d26db93
+--- /dev/null
++++ b/src/utils/uloop.c
+@@ -0,0 +1,64 @@
++#include <libubox/uloop.h>
++#include "includes.h"
++#include "common.h"
++#include "eloop.h"
++
++static void eloop_uloop_event_cb(int sock, void *eloop_ctx, void *sock_ctx)
++{
++}
++
++static void eloop_uloop_fd_cb(struct uloop_fd *fd, unsigned int events)
++{
++ unsigned int changed = events ^ fd->flags;
++
++ if (changed & ULOOP_READ) {
++ if (events & ULOOP_READ)
++ eloop_register_sock(fd->fd, EVENT_TYPE_READ, eloop_uloop_event_cb, fd, fd);
++ else
++ eloop_unregister_sock(fd->fd, EVENT_TYPE_READ);
++ }
++
++ if (changed & ULOOP_WRITE) {
++ if (events & ULOOP_WRITE)
++ eloop_register_sock(fd->fd, EVENT_TYPE_WRITE, eloop_uloop_event_cb, fd, fd);
++ else
++ eloop_unregister_sock(fd->fd, EVENT_TYPE_WRITE);
++ }
++}
++
++static bool uloop_timeout_poll_handler(struct os_reltime *tv, bool tv_set)
++{
++ struct os_reltime tv_uloop;
++ int timeout_ms = uloop_get_next_timeout();
++
++ if (timeout_ms < 0)
++ return false;
++
++ tv_uloop.sec = timeout_ms / 1000;
++ tv_uloop.usec = (timeout_ms % 1000) * 1000;
++
++ if (!tv_set || os_reltime_before(&tv_uloop, tv)) {
++ *tv = tv_uloop;
++ return true;
++ }
++
++ return false;
++}
++
++static void uloop_poll_handler(void)
++{
++ uloop_run_timeout(0);
++}
++
++void eloop_add_uloop(void)
++{
++ static bool init_done = false;
++
++ if (!init_done) {
++ uloop_init();
++ uloop_fd_set_cb = eloop_uloop_fd_cb;
++ init_done = true;
++ }
++
++ eloop_register_cb(uloop_poll_handler, uloop_timeout_poll_handler);
++}
+diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
+index 7f3dd185f..627575e39 100644
+--- a/src/utils/wpa_debug.c
++++ b/src/utils/wpa_debug.c
+@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NULL;
+ #define WPAS_TRACE_PFX "wpas <%d>: "
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+
++void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
++ size_t len);
++void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+
+ int wpa_debug_level = MSG_INFO;
+ int wpa_debug_show_keys = 0;
+@@ -206,10 +210,16 @@ void wpa_debug_close_linux_tracing(void)
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ {
+ va_list ap;
+
++ if (wpa_printf_hook) {
++ va_start(ap, fmt);
++ wpa_printf_hook(level, fmt, ap);
++ va_end(ap);
++ }
++
+ if (level >= wpa_debug_level) {
+ #ifdef CONFIG_ANDROID_LOG
+ va_start(ap, fmt);
+@@ -255,11 +265,14 @@ void wpa_printf(int level, const char *fmt, ...)
+ }
+
+
+-static void _wpa_hexdump(int level, const char *title, const u8 *buf,
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ size_t len, int show, int only_syslog)
+ {
+ size_t i;
+
++ if (wpa_hexdump_hook)
++ wpa_hexdump_hook(level, title, buf, len);
++
+ #ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+@@ -382,19 +395,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ #endif /* CONFIG_ANDROID_LOG */
+ }
+
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
+-{
+- _wpa_hexdump(level, title, buf, len, 1, 0);
+-}
+-
+-
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
+-{
+- _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
+-}
+-
+-
+-static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len, int show)
+ {
+ size_t i, llen;
+@@ -507,20 +508,6 @@ file_done:
+ }
+
+
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+- size_t len)
+-{
+- _wpa_hexdump_ascii(level, title, buf, len, 1);
+-}
+-
+-
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+- size_t len)
+-{
+- _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+-}
+-
+-
+ #ifdef CONFIG_DEBUG_FILE
+ static char *last_path = NULL;
+ #endif /* CONFIG_DEBUG_FILE */
+@@ -644,7 +631,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+ }
+
+
+-void wpa_msg(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg(void *ctx, int level, const char *fmt, ...)
+ {
+ va_list ap;
+ char *buf;
+@@ -682,7 +669,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
+ }
+
+
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ {
+ va_list ap;
+ char *buf;
+diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
+index 4c02ad3c7..854520bfe 100644
+--- a/src/utils/wpa_debug.h
++++ b/src/utils/wpa_debug.h
+@@ -11,6 +11,10 @@
+
+ #include "wpabuf.h"
+
++extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++extern void (*wpa_hexdump_hook)(int level, const char *title,
++ const void *buf, size_t len);
++extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+ extern int wpa_debug_level;
+ extern int wpa_debug_show_keys;
+ extern int wpa_debug_timestamp;
+@@ -51,6 +55,17 @@ void wpa_debug_close_file(void);
+ void wpa_debug_setup_stdout(void);
+ void wpa_debug_stop_log(void);
+
++/* internal */
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
++ size_t len, int show, int only_syslog);
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++ size_t len, int show);
++extern int wpa_debug_show_keys;
++
++#ifndef CONFIG_MSG_MIN_PRIORITY
++#define CONFIG_MSG_MIN_PRIORITY 0
++#endif
++
+ /**
+ * wpa_debug_printf_timestamp - Print timestamp for debug output
+ *
+@@ -71,9 +86,15 @@ void wpa_debug_print_timestamp(void);
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ PRINTF_FORMAT(2, 3);
+
++#define wpa_printf(level, ...) \
++ do { \
++ if (level >= CONFIG_MSG_MIN_PRIORITY) \
++ _wpa_printf(level, __VA_ARGS__); \
++ } while(0)
++
+ /**
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+@@ -85,7 +106,13 @@ PRINTF_FORMAT(2, 3);
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump.
+ */
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump(level, title, buf, len, 1, 1);
++}
+
+ static inline void wpa_hexdump_buf(int level, const char *title,
+ const struct wpabuf *buf)
+@@ -107,7 +134,13 @@ static inline void wpa_hexdump_buf(int level, const char *title,
+ * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+ * etc.) in debug output.
+ */
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
++}
+
+ static inline void wpa_hexdump_buf_key(int level, const char *title,
+ const struct wpabuf *buf)
+@@ -129,8 +162,14 @@ static inline void wpa_hexdump_buf_key(int level, const char *title,
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown.
+ */
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+- size_t len);
++static inline void wpa_hexdump_ascii(int level, const char *title,
++ const u8 *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump_ascii(level, title, buf, len, 1);
++}
+
+ /**
+ * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+@@ -146,8 +185,14 @@ void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
+ * default, does not include secret keys (passwords, etc.) in debug output.
+ */
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+- size_t len);
++static inline void wpa_hexdump_ascii_key(int level, const char *title,
++ const u8 *buf, size_t len)
++{
++ if (level < CONFIG_MSG_MIN_PRIORITY)
++ return;
++
++ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
++}
+
+ /*
+ * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+@@ -184,7 +229,12 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+-void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++#define wpa_msg(ctx, level, ...) \
++ do { \
++ if (level >= CONFIG_MSG_MIN_PRIORITY) \
++ _wpa_msg(ctx, level, __VA_ARGS__); \
++ } while(0)
+
+ /**
+ * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
+@@ -198,8 +248,13 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+ * attached ctrl_iface monitors. In other words, it can be used for frequent
+ * events that do not need to be sent to syslog.
+ */
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ PRINTF_FORMAT(3, 4);
++#define wpa_msg_ctrl(ctx, level, ...) \
++ do { \
++ if (level >= CONFIG_MSG_MIN_PRIORITY) \
++ _wpa_msg_ctrl(ctx, level, __VA_ARGS__); \
++ } while(0)
+
+ /**
+ * wpa_msg_global - Global printf for ctrl_iface monitors
+diff --git a/tests/Makefile b/tests/Makefile
+index 8ec154bb3..58287f56f 100644
+--- a/tests/Makefile
++++ b/tests/Makefile
+@@ -5,6 +5,14 @@ ALL=test-base64 test-md4 test-milenage \
+ test-sha256 test-aes test-x509v3 test-list test-rc4 \
+ test-bss
+
++RUN_TESTS= \
++ test-list \
++ test-md4 test-rc4 test-sha1 test-sha256 \
++ test-milenage test-aes \
++ test-crypto_module
++
++ALL=$(RUN_TESTS) test-base64 test-https test-https_server
++
+ include ../src/build.rules
+
+ ifdef LIBFUZZER
+@@ -25,13 +33,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
+ CFLAGS += -DCONFIG_IEEE80211R
+ CFLAGS += -DCONFIG_TDLS
+
++# test-crypto_module
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DCONFIG_SHA256
++CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
+ CFLAGS += -I../src
+ CFLAGS += -I../src/utils
+
+ SLIBS = ../src/utils/libutils.a
+
+-DLIBS = ../src/crypto/libcrypto.a \
+- ../src/tls/libtls.a
++DLIBS = ../src/tls/libtls.a \
++ ../src/crypto/libcrypto.a
+
+ _OBJS_VAR := LLIBS
+ include ../src/objs.mk
+@@ -43,12 +65,43 @@ include ../src/objs.mk
+ LIBS = $(SLIBS) $(DLIBS)
+ LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
+
++ifeq ($(CONFIG_TLS),mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
++else
++ifeq ($(CONFIG_TLS),openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
++LLIBS += -lssl -lcrypto
++else
++ifeq ($(CONFIG_TLS),gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
++LLIBS += -lgnutls -lgpg-error -lgcrypt
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
++LLIBS += -lwolfssl -lm
++else
++CFLAGS += -DCONFIG_TLS_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
++ALL += test-rsa-sig-ver
++ALL += test-x509v3
++clean-config_tls_internal:
++ rm -f test_x509v3_nist.out.*
++ rm -f test_x509v3_nist2.out.*
++endif
++endif
++endif
++endif
++
+ # glibc < 2.17 needs -lrt for clock_gettime()
+ LLIBS += -lrt
+
+ test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
++test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
++ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
++
+ test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+@@ -149,10 +202,12 @@ run-tests: $(ALL)
+ ./test-sha1
+ ./test-sha256
+ ./test-bss
++
++ @set -ex; for i in $(RUN_TESTS); do ./$$i; done
+ @echo
+ @echo All tests completed successfully.
+
+-clean: common-clean
++clean: common-clean clean-config_tls_internal
+ rm -f *~
+- rm -f test_x509v3_nist.out.*
+- rm -f test_x509v3_nist2.out.*
++
++.PHONY: run-tests clean-config_tls_internal
+diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
+index 210b7fb86..608e20eed 100644
+--- a/tests/hwsim/example-hostapd.config
++++ b/tests/hwsim/example-hostapd.config
+@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
+ CONFIG_DRIVER_NL80211=y
+ CONFIG_RSN_PREAUTH=y
+
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -33,12 +34,7 @@ CONFIG_EAP_TNC=y
+ CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+ LIBS += -rdynamic
+ CONFIG_EAP_UNAUTH_TLS=y
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+ CONFIG_EAP_EKE=y
+ CONFIG_PKCS12=y
+ CONFIG_RADIUS_SERVER=y
+@@ -88,7 +84,7 @@ CFLAGS += -DCONFIG_RADIUS_TEST
+ CONFIG_MODULE_TESTS=y
+
+ CONFIG_SUITEB=y
+-CONFIG_SUITEB192=y
++CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
+
+ # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+ # This can be used as a more efficient memory error detector than valgrind
+diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
+index 123f397e3..da0dde659 100644
+--- a/tests/hwsim/example-wpa_supplicant.config
++++ b/tests/hwsim/example-wpa_supplicant.config
+@@ -2,6 +2,7 @@
+
+ CONFIG_TLS=openssl
+ #CONFIG_TLS=wolfssl
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -34,13 +35,7 @@ LIBS += -rdynamic
+ CONFIG_EAP_FAST=y
+ CONFIG_EAP_TEAP=y
+ CONFIG_EAP_IKEV2=y
+-
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+
+ CONFIG_USIM_SIMULATOR=y
+ CONFIG_SIM_SIMULATOR=y
+@@ -136,7 +131,7 @@ CONFIG_TESTING_OPTIONS=y
+ CONFIG_MODULE_TESTS=y
+
+ CONFIG_SUITEB=y
+-CONFIG_SUITEB192=y
++CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
+
+ # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+ # This can be used as a more efficient memory error detector than valgrind
+diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py
+index a20140316..027a60b25 100644
+--- a/tests/hwsim/test_ap_eap.py
++++ b/tests/hwsim/test_ap_eap.py
+@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
+ res = dev.get_capability("eap")
+ if method not in res:
+ raise HwsimSkip("EAP method %s not supported in the build" % method)
++ if method == "FAST" or method == "TEAP":
++ tls = dev.request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
+
+ def check_subject_match_support(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
+
+ def check_check_cert_subject_support(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
+
+ def check_altsubject_match_support(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
+
+ def check_domain_match(dev):
+@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
+
+ def check_domain_match_full(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("wolfSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
+
+ def check_cert_probe_support(dev):
+@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
+ raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
+
+ def check_ext_cert_check_support(dev):
++ if not openssl_imported:
++ raise HwsimSkip("OpenSSL python method not available")
++
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
+
+ def check_ocsp_support(dev):
+@@ -91,10 +126,12 @@ def check_ocsp_support(dev):
+ # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+ #if tls.startswith("wolfSSL"):
+ # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+
+ def check_pkcs5_v15_support(dev):
+ tls = dev.request("GET tls_library")
+- if "BoringSSL" in tls or "GnuTLS" in tls:
++ if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
+ raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
+
+ def check_tls13_support(dev):
+@@ -122,11 +159,15 @@ def check_pkcs12_support(dev):
+ # raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+ if tls.startswith("wolfSSL"):
+ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+
+ def check_dh_dsa_support(dev):
+ tls = dev.request("GET tls_library")
+ if tls.startswith("internal"):
+ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
+
+ def check_ec_support(dev):
+ tls = dev.request("GET tls_library")
+@@ -1741,7 +1782,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+- subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
++ check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
+ altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
+ eap_reauth(dev[0], "TTLS")
+
+@@ -2976,6 +3017,7 @@ def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
+
+ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+ """WPA2-Enterprise negative test - subject mismatch"""
++ check_subject_match_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+@@ -3036,6 +3078,7 @@ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+
+ def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
+ """WPA2-Enterprise negative test - altsubject mismatch"""
++ check_altsubject_match_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+@@ -3582,7 +3625,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
+ dev[0].request("REMOVE_NETWORK all")
+
+ tls = dev[0].request("GET tls_library")
+- if not tls.startswith("wolfSSL"):
++ if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
+ tests = [(1, "os_get_random;dh_init")]
+ else:
+ tests = [(1, "crypto_dh_init;dh_init")]
+@@ -4896,7 +4939,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
+ params["private_key"] = "auth_serv/iCA-server/server.key"
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -4962,6 +5005,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
+ run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
+
+ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
++ check_ocsp_support(dev[0])
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+ params["server_cert"] = "auth_serv/iCA-server/server.pem"
+@@ -4971,7 +5015,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5007,7 +5051,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5057,7 +5101,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, par
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5124,7 +5168,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
+
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+- if "GnuTLS" in tls or "wolfSSL" in tls:
++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+@@ -5382,6 +5426,7 @@ def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
+
+ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
++ check_pkcs12_support(dev[0])
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ del params["server_cert"]
+@@ -5394,6 +5439,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+
+ def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
+ """EAP-TTLS and server PKCS#12 file with extra certs"""
++ check_pkcs12_support(dev[0])
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ del params["server_cert"]
+@@ -5416,6 +5462,7 @@ def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
+
+ def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
++ check_dh_dsa_support(dev[0])
+ params = int_eap_server_params()
+ params["dh_file"] = "auth_serv/dsaparam.pem"
+ hapd = hostapd.add_ap(apdev[0], params)
+@@ -5727,8 +5774,8 @@ def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
+ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+ """OpenSSL cipher suite configuration on wpa_supplicant"""
+ tls = dev[0].request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
+- raise HwsimSkip("TLS library is not OpenSSL: " + tls)
++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++ raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+@@ -5754,14 +5801,14 @@ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+ def test_openssl_cipher_suite_config_hapd(dev, apdev):
+ """OpenSSL cipher suite configuration on hostapd"""
+ tls = dev[0].request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
+- raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++ raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
+ params = int_eap_server_params()
+ params['openssl_ciphers'] = "AES256"
+ hapd = hostapd.add_ap(apdev[0], params)
+ tls = hapd.request("GET tls_library")
+- if not tls.startswith("OpenSSL"):
+- raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++ raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+@@ -6207,14 +6254,26 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev):
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
+ "TLSv1.2")
+- elif tls.startswith("internal"):
++ elif tls.startswith("internal") or tls.startswith("mbed TLS"):
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
++<<<<<<< HEAD
+ check_tls_ver(dev[1], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
+ check_tls_ver(dev[2], hapd,
+ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
+ if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls:
++=======
++ if tls.startswith("mbed TLS"):
++ check_tls_ver(dev[2], hapd,
++ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
++ else:
++ check_tls_ver(dev[1], hapd,
++ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
++ check_tls_ver(dev[2], hapd,
++ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
++ if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3.0" in tls:
++>>>>>>> 585bc9ada (hostapd: sync 2024-01-18 openwrt/trunk patch folder)
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
+
+@@ -6235,6 +6294,11 @@ def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
+ tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+ ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++ #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
+ for exp, flags in tests:
+ hapd.disable()
+ hapd.set("tls_flags", flags)
+@@ -7305,6 +7369,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
+ def test_eap_tls_ext_cert_check(dev, apdev):
+ """EAP-TLS and external server certification validation"""
+ # With internal server certificate chain validation
++ check_ext_cert_check_support(dev[0])
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert="auth_serv/ca.pem",
+@@ -7317,6 +7382,7 @@ def test_eap_tls_ext_cert_check(dev, apdev):
+ def test_eap_ttls_ext_cert_check(dev, apdev):
+ """EAP-TTLS and external server certification validation"""
+ # Without internal server certificate chain validation
++ check_ext_cert_check_support(dev[0])
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+@@ -7327,6 +7393,7 @@ def test_eap_ttls_ext_cert_check(dev, apdev):
+ def test_eap_peap_ext_cert_check(dev, apdev):
+ """EAP-PEAP and external server certification validation"""
+ # With internal server certificate chain validation
++ check_ext_cert_check_support(dev[0])
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user", anonymous_identity="peap",
+ ca_cert="auth_serv/ca.pem",
+@@ -7337,6 +7404,7 @@ def test_eap_peap_ext_cert_check(dev, apdev):
+
+ def test_eap_fast_ext_cert_check(dev, apdev):
+ """EAP-FAST and external server certification validation"""
++ check_ext_cert_check_support(dev[0])
+ check_eap_capa(dev[0], "FAST")
+ # With internal server certificate chain validation
+ dev[0].request("SET blob fast_pac_auth_ext ")
+@@ -7351,10 +7419,6 @@ def test_eap_fast_ext_cert_check(dev, apdev):
+ run_ext_cert_check(dev, apdev, id)
+
+ def run_ext_cert_check(dev, apdev, net_id):
+- check_ext_cert_check_support(dev[0])
+- if not openssl_imported:
+- raise HwsimSkip("OpenSSL python method not available")
+-
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
+index 3d07d21f7..a708412de 100644
+--- a/tests/hwsim/test_ap_ft.py
++++ b/tests/hwsim/test_ap_ft.py
+@@ -2486,11 +2486,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+- with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++ with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+- with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++ with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+diff --git a/tests/hwsim/test_authsrv.py b/tests/hwsim/test_authsrv.py
+index e0665bcb2..02ec301e5 100644
+--- a/tests/hwsim/test_authsrv.py
++++ b/tests/hwsim/test_authsrv.py
+@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+- with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
+- if "FAIL" not in authsrv.request("ENABLE"):
+- raise Exception("ENABLE succeeded during OOM")
++ # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
++ tls = dev[0].request("GET tls_library")
++ if not tls.startswith("mbed TLS"):
++ with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
++ if "FAIL" not in authsrv.request("ENABLE"):
++ raise Exception("ENABLE succeeded during OOM")
+
+ for count in range(1, 3):
+ with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
+diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py
+index 518983bd0..077de58c9 100644
+--- a/tests/hwsim/test_dpp.py
++++ b/tests/hwsim/test_dpp.py
+@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False, min_ver=1):
+ raise HwsimSkip("DPP not supported")
+ if brainpool:
+ tls = dev.request("GET tls_library")
+- if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
++ if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
++ and not tls.startswith("mbed TLS"):
+ raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
+ capa = dev.request("GET_CAPABILITY dpp")
+ ver = 1
+@@ -3902,6 +3903,9 @@ def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
+
+ def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
+ """DPP protocol testing - invalid I-proto key in Auth Req"""
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+ run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
+
+ def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
+@@ -3997,7 +4001,12 @@ def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
+
+ def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
+ """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
+- run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
++ run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
++ else:
++ run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
+
+ def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
+ """DPP protocol testing - no R-nonce in Auth Resp"""
+@@ -4359,11 +4368,17 @@ def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
+
+ def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+ run_dpp_proto_pkex_req_missing(dev, 47,
+ "Peer bootstrapping key is invalid")
+
+ def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+ run_dpp_proto_pkex_resp_missing(dev, 48,
+ "Peer bootstrapping key is invalid")
+
+diff --git a/tests/hwsim/test_erp.py b/tests/hwsim/test_erp.py
+index d083993e8..262e9f095 100644
+--- a/tests/hwsim/test_erp.py
++++ b/tests/hwsim/test_erp.py
+@@ -12,7 +12,7 @@ import time
+
+ import hostapd
+ from utils import *
+-from test_ap_eap import int_eap_server_params, check_tls13_support
++from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
+ from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+ def test_erp_initiate_reauth_start(dev, apdev):
+@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+ params['erp_domain'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
++ tls = dev[0].request("GET tls_library")
+
+ erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+ erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
+ password="hello")
+- if "FAST" in eap_methods:
++ if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
+ erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=GTC",
+@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apdev):
+ password="password")
+ erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+- if "MSCHAPV2" in eap_methods:
++ if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
+ erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=MSCHAPV2")
+- erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
+- password="password", ca_cert="auth_serv/ca.pem",
+- phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
++ if check_eap_capa(dev[0], "TEAP"):
++ erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
++ password="password", ca_cert="auth_serv/ca.pem",
++ phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
+ erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ if "PWD" in eap_methods:
+@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+- for count in range(1, 6):
++ for count in range(1, 4):
+ dev[0].request("ERP_FLUSH")
+ with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py
+index 5cdc28734..17110c5c2 100644
+--- a/tests/hwsim/test_fils.py
++++ b/tests/hwsim/test_fils.py
+@@ -1484,6 +1484,18 @@ def run_fils_sk_pfs(dev, apdev, group, params):
+ check_erp_capa(dev[0])
+ check_ec_group(dev[0], group)
+
++ tls = dev[0].request("GET tls_library")
++ if tls.startswith("mbed TLS"):
++ if int(group) == 27:
++ raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
++ elif not tls.startswith("wolfSSL"):
++ if int(group) in [25]:
++ if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
++ raise HwsimSkip("EC group not supported")
++ if int(group) in [27, 28, 29, 30]:
++ if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
++ raise HwsimSkip("Brainpool EC group not supported")
++
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+diff --git a/tests/hwsim/test_pmksa_cache.py b/tests/hwsim/test_pmksa_cache.py
+index 4a3b444ff..4f7f7f760 100644
+--- a/tests/hwsim/test_pmksa_cache.py
++++ b/tests/hwsim/test_pmksa_cache.py
+@@ -958,7 +958,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ bssid=apdev[0]['bssid'])
+- for i in range(1, 11):
++ for i in range(1, 10):
+ with alloc_fail(dev[0], i, "rsn_preauth_init"):
+ res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
+ logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
+@@ -966,7 +966,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ break
+- time.sleep(0.05)
++ time.sleep(0.10)
+
+ def test_pmksa_cache_ctrl(dev, apdev):
+ """PMKSA cache control interface operations"""
+diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
+index aceb92751..6f9ee5669 100644
+--- a/tests/hwsim/test_sae.py
++++ b/tests/hwsim/test_sae.py
+@@ -178,6 +178,11 @@ def test_sae_groups(dev, apdev):
+ if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
+ logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+ sae_groups += [27, 28, 29, 30]
++ if tls.startswith("mbed TLS"):
++ # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
++ # does not have code to derive y from compressed format for those curves
++ sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
++ sae_groups += [27, 28, 29, 30]
+ heavy_groups = [14, 15, 16]
+ suitable_groups = [15, 16, 17, 18, 19, 20, 21]
+ groups = [str(g) for g in sae_groups]
+@@ -2194,6 +2199,8 @@ def run_sae_pwe_group(dev, apdev, group):
+ logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+ elif tls.startswith("wolfSSL"):
+ logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
++ elif tls.startswith("mbed TLS"):
++ logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
+ else:
+ raise HwsimSkip("Brainpool curve not supported")
+ start_sae_pwe_ap(apdev[0], group, 2)
+diff --git a/tests/hwsim/test_suite_b.py b/tests/hwsim/test_suite_b.py
+index d03a39dee..d703dee95 100644
+--- a/tests/hwsim/test_suite_b.py
++++ b/tests/hwsim/test_suite_b.py
+@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False, level128=False):
+ return
+ if tls.startswith("wolfSSL"):
+ return
++ if tls.startswith("mbed TLS"):
++ return
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("TLS library not supported for Suite B: " + tls)
+ supported = False
+@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev):
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
++ openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
+ phase1="tls_suiteb=1",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/rsa3072-ca.pem",
+diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py
+index 44eb00444..fbe0fb794 100644
+--- a/tests/hwsim/test_wpas_ctrl.py
++++ b/tests/hwsim/test_wpas_ctrl.py
+@@ -1856,7 +1856,7 @@ def _test_wpas_ctrl_oom(dev):
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("internal"):
+ tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+- 4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
++ 3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
+ for cmd, exp, count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ res = dev[0].request(cmd)
+diff --git a/tests/hwsim/utils.py b/tests/hwsim/utils.py
+index 7e3608284..b23c1ee0b 100644
+--- a/tests/hwsim/utils.py
++++ b/tests/hwsim/utils.py
+@@ -145,7 +145,13 @@ def check_imsi_privacy_support(dev):
+
+ def check_tls_tod(dev):
+ tls = dev.request("GET tls_library")
+- if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
++ if tls.startswith("OpenSSL"):
++ return
++ elif tls.startswith("internal"):
++ return
++ elif tls.startswith("mbed TLS"):
++ return
++ else:
+ raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
+
+ def vht_supported():
+diff --git a/tests/test-crypto_module.c b/tests/test-crypto_module.c
+new file mode 100644
+index 000000000..0f1156142
+--- /dev/null
++++ b/tests/test-crypto_module.c
+@@ -0,0 +1,16 @@
++/*
++ * crypto module tests - test program
++ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/module_tests.h"
++#include "crypto/crypto_module_tests.c"
++
++int main(int argc, char *argv[])
++{
++ return crypto_module_tests();
++}
+diff --git a/tests/test-https.c b/tests/test-https.c
+index a72e56f9d..e9df82f1d 100644
+--- a/tests/test-https.c
++++ b/tests/test-https.c
+@@ -75,7 +75,7 @@ static int https_client(int s, const char *path)
+ struct tls_connection *conn;
+ struct wpabuf *in, *out, *appl;
+ int res = -1;
+- int need_more_data;
++ int need_more_data = 0;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.event_cb = https_tls_event_cb;
+@@ -93,8 +93,12 @@ static int https_client(int s, const char *path)
+
+ for (;;) {
+ appl = NULL;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ out = tls_connection_handshake2(tls, conn, in, &appl,
+ &need_more_data);
++#else
++ out = tls_connection_handshake(tls, conn, in, &appl);
++#endif
+ wpabuf_free(in);
+ in = NULL;
+ if (out == NULL) {
+@@ -152,11 +156,15 @@ static int https_client(int s, const char *path)
+
+ wpa_printf(MSG_INFO, "Reading HTTP response");
+ for (;;) {
+- int need_more_data;
++ int need_more_data = 0;
+ in = https_recv(s);
+ if (in == NULL)
+ goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++ out = tls_connection_decrypt(tls, conn, in);
++#endif
+ if (need_more_data)
+ wpa_printf(MSG_DEBUG, "HTTP: Need more data");
+ wpabuf_free(in);
+diff --git a/tests/test-https_server.c b/tests/test-https_server.c
+index 33b448682..9dcca5596 100644
+--- a/tests/test-https_server.c
++++ b/tests/test-https_server.c
+@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s, int timeout_ms)
+ }
+
+
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ static void https_tls_log_cb(void *ctx, const char *msg)
+ {
+ wpa_printf(MSG_DEBUG, "TLS: %s", msg);
+ }
++#endif
+
+
+ static int https_server(int s)
+@@ -79,7 +81,7 @@ static int https_server(int s)
+ void *tls;
+ struct tls_connection_params params;
+ struct tls_connection *conn;
+- struct wpabuf *in, *out, *appl;
++ struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
+ int res = -1;
+
+ os_memset(&conf, 0, sizeof(conf));
+@@ -106,7 +108,9 @@ static int https_server(int s)
+ return -1;
+ }
+
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
++#endif
+
+ for (;;) {
+ in = https_recv(s, 5000);
+@@ -147,12 +151,16 @@ static int https_server(int s)
+
+ wpa_printf(MSG_INFO, "Reading HTTP request");
+ for (;;) {
+- int need_more_data;
++ int need_more_data = 0;
+
+ in = https_recv(s, 5000);
+ if (!in)
+ goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++ out = tls_connection_decrypt(tls, conn, in);
++#endif
+ wpabuf_free(in);
+ in = NULL;
+ if (need_more_data) {
+diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
+index dd13308f7..c65acab94 100644
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.service
+ EXTRA_TARGETS=dynamic_eap_methods
+
+ CONFIG_FILE=.config
++-include $(if $(MULTICALL),../hostapd/.config)
+ include ../src/build.rules
+
+ ifdef CONFIG_BUILD_PASN_SO
+@@ -188,6 +189,25 @@ ifdef CONFIG_EAPOL_TEST
+ CFLAGS += -Werror -DEAPOL_TEST
+ endif
+
++ifdef CONFIG_UBUS
++CFLAGS += -DUBUS_SUPPORT
++OBJS += ubus.o
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
++OBJS += ../src/utils/uloop.o
++LIBS += -lubox
++endif
++
+ ifdef CONFIG_CODE_COVERAGE
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+ LIBS += -lgcov
+@@ -334,6 +354,7 @@ endif
+ ifdef CONFIG_FILS
+ CFLAGS += -DCONFIG_FILS
+ NEED_SHA384=y
++NEED_HMAC_SHA384_KDF=y
+ NEED_AES_SIV=y
+ ifdef CONFIG_FILS_SK_PFS
+ CFLAGS += -DCONFIG_FILS_SK_PFS
+@@ -388,7 +409,9 @@ endif
+ ifdef CONFIG_IBSS_RSN
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_IBSS_RSN
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ibss_rsn.o
+ endif
+
+@@ -980,6 +1003,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
+ CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+ LIBS += -ldl -rdynamic
+ endif
++else
++ ifdef MULTICALL
++ OBJS += ../src/eap_common/eap_common.o
++ endif
+ endif
+
+ ifdef CONFIG_AP
+@@ -987,9 +1014,11 @@ NEED_EAP_COMMON=y
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_AP
+ OBJS += ap.o
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
+ CFLAGS += -DCONFIG_NO_ACCOUNTING
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/utils.o
+@@ -1029,7 +1058,16 @@ ifdef CONFIG_FILS
+ OBJS += ../src/ap/fils_hlp.o
+ endif
+ ifdef CONFIG_CTRL_IFACE
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ OBJS += ../src/ap/ctrl_iface_ap.o
++ifdef CONFIG_UBUS
++OBJS += ../src/ap/ubus.o
++endif
++ifdef CONFIG_UCODE
++OBJS += ../src/ap/ucode.o
++endif
+ endif
+
+ CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+@@ -1080,6 +1118,12 @@ endif
+ ifdef CONFIG_HS20
+ OBJS += ../src/ap/hs20.o
+ endif
++else
++ ifdef MULTICALL
++ OBJS += ../src/eap_server/eap_server.o
++ OBJS += ../src/eap_server/eap_server_identity.o
++ OBJS += ../src/eap_server/eap_server_methods.o
++ endif
+ endif
+
+ ifdef CONFIG_MBO
+@@ -1089,7 +1133,9 @@ NEED_GAS=y
+ endif
+
+ ifdef NEED_RSN_AUTHENTICATOR
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
++endif
+ NEED_AES_WRAP=y
+ OBJS += ../src/ap/wpa_auth.o
+ OBJS += ../src/ap/wpa_auth_ie.o
+@@ -1188,6 +1234,7 @@ TLS_FUNCS=y
+ endif
+
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ ifdef TLS_FUNCS
+ CFLAGS += -DWOLFSSL_DER_LOAD
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -1203,6 +1250,7 @@ LIBS_p += -lwolfssl -lm
+ endif
+
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ ifdef TLS_FUNCS
+ CFLAGS += -DEAP_TLS_OPENSSL
+@@ -1229,7 +1277,28 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls -lmbedx509
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++LIBS += -lmbedcrypto
++LIBS_p += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -1260,6 +1329,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -1340,6 +1410,7 @@ endif
+ endif
+
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ OBJS_p += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+@@ -1421,9 +1492,11 @@ endif
+
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_INTERNAL_AES_WRAP=y
+ endif
+ endif
++endif
+ ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+ # Seems to be needed at least with BoringSSL
+ NEED_INTERNAL_AES_WRAP=y
+@@ -1437,9 +1510,11 @@ endif
+
+ ifdef NEED_INTERNAL_AES_WRAP
+ ifneq ($(CONFIG_TLS), linux)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -1449,35 +1524,45 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_WRAP
+ NEED_AES_ENC=y
+ ifdef NEED_INTERNAL_AES_WRAP
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_ENC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1492,12 +1577,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1509,29 +1598,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
+ else
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+
+ ifndef CONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ MD5OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+ MD5OBJS += ../src/crypto/md5-internal.o
+@@ -1586,12 +1683,17 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ SHA256OBJS += ../src/crypto/sha256-internal.o
+ endif
+@@ -1604,50 +1706,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
+ SHA256OBJS += ../src/crypto/sha512-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ OBJS += $(SHA256OBJS)
+ ifdef NEED_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA512
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+
+ ifdef NEED_ASN1
+ OBJS += ../src/tls/asn1.o
+@@ -1822,10 +1942,12 @@ ifdef CONFIG_FIPS
+ CFLAGS += -DCONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
+ endif
+ endif
+ endif
++endif
+
+ OBJS += $(SHA1OBJS) $(DESOBJS)
+
+@@ -2003,32 +2125,38 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
+
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
++wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
++ $(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
++ @$(E) " CC " $<
++ @rm -f $@
++ @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
++
+ wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
+- $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_t
+ include ../src/objs.mk
+ eapol_test: $(OBJS_t)
+- $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_t2
+ include ../src/objs.mk
+ preauth_test: $(OBJS_t2)
+- $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_p
+ include ../src/objs.mk
+ wpa_passphrase: $(OBJS_p)
+- $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
++ +$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
+ @$(E) " LD " $@
+
+ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+ wpa_cli: $(OBJS_c)
+- $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
++ +$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+ @$(E) " LD " $@
+
+ LIBCTRL += ../src/common/wpa_ctrl.o
+@@ -2135,6 +2263,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
+ $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+ @$(E) " sed" $<
+
++dump_cflags:
++ @printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ wpa_supplicant.exe: wpa_supplicant
+ mv -f $< $@
+ wpa_cli.exe: wpa_cli
+diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
+index 69a0e5ee1..43c39d7ce 100644
+--- a/wpa_supplicant/ap.c
++++ b/wpa_supplicant/ap.c
+@@ -1520,7 +1520,7 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ #endif /* CONFIG_WPS */
+
+
+-#ifdef CONFIG_CTRL_IFACE
++#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
+
+ int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+@@ -1846,11 +1846,31 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s,
+
+
+ #ifdef CONFIG_CTRL_IFACE
++
++static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
++ struct csa_settings *settings)
++{
++#ifdef NEED_AP_MLME
++ if (!iface || !iface->bss[0])
++ return 0;
++
++ return hostapd_switch_channel(iface->bss[0], settings);
++#else
++ return -1;
++#endif
++}
++
++
+ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
+ {
+ struct csa_settings settings;
+ int ret = hostapd_parse_csa_settings(pos, &settings);
+
++ if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
++ !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
++ return -1;
++
++ ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
+ if (ret)
+ return ret;
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index 2c756136c..c3943355d 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -18,6 +18,7 @@
+ #include "eap_peer/eap.h"
+ #include "p2p/p2p.h"
+ #include "fst/fst.h"
++#include "ap/sta_info.h"
+ #include "config.h"
+
+
+@@ -2421,6 +2422,97 @@ static char * wpa_config_write_mac_value(const struct parse_data *data,
+ #endif /* NO_CONFIG_WRITE */
+
+
++static int wpa_config_parse_mcast_rate(const struct parse_data *data,
++ struct wpa_ssid *ssid, int line,
++ const char *value)
++{
++ ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
++
++ return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_mcast_rate(const struct parse_data *data,
++ struct wpa_ssid *ssid)
++{
++ char *value;
++ int res;
++
++ if (!ssid->mcast_rate == 0)
++ return NULL;
++
++ value = os_malloc(6); /* longest: 300.0 */
++ if (value == NULL)
++ return NULL;
++ res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
++ if (res < 0) {
++ os_free(value);
++ return NULL;
++ }
++ return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
++static int wpa_config_parse_rates(const struct parse_data *data,
++ struct wpa_ssid *ssid, int line,
++ const char *value)
++{
++ int i;
++ char *pos, *r, *sptr, *end;
++ double rate;
++
++ pos = (char *)value;
++ r = strtok_r(pos, ",", &sptr);
++ i = 0;
++ while (pos && i < WLAN_SUPP_RATES_MAX) {
++ rate = 0.0;
++ if (r)
++ rate = strtod(r, &end);
++ ssid->rates[i] = rate * 2;
++ if (*end != '\0' || rate * 2 != ssid->rates[i])
++ return 1;
++
++ i++;
++ r = strtok_r(NULL, ",", &sptr);
++ }
++
++ return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_rates(const struct parse_data *data,
++ struct wpa_ssid *ssid)
++{
++ char *value, *pos;
++ int res, i;
++
++ if (ssid->rates[0] <= 0)
++ return NULL;
++
++ value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
++ if (value == NULL)
++ return NULL;
++ pos = value;
++ for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
++ res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
++ if (res < 0) {
++ os_free(value);
++ return NULL;
++ }
++ pos += res;
++ }
++ res = os_snprintf(pos, 6, "%.1f",
++ (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
++ if (res < 0) {
++ os_free(value);
++ return NULL;
++ }
++
++ value[6 * WLAN_SUPP_RATES_MAX] = '\0';
++ return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
+ /* Helper macros for network block parser */
+
+ #ifdef OFFSET
+@@ -2639,6 +2731,7 @@ static const struct parse_data ssid_fields[] = {
+ #else /* CONFIG_MESH */
+ { INT_RANGE(mode, 0, 4) },
+ #endif /* CONFIG_MESH */
++ { INT_RANGE(noscan, 0, 1) },
+ { INT_RANGE(proactive_key_caching, 0, 1) },
+ { INT_RANGE(disabled, 0, 2) },
+ { STR(id_str) },
+@@ -2712,6 +2805,8 @@ static const struct parse_data ssid_fields[] = {
+ { INT(ap_max_inactivity) },
+ { INT(dtim_period) },
+ { INT(beacon_int) },
++ { FUNC(rates) },
++ { FUNC(mcast_rate) },
+ #ifdef CONFIG_MACSEC
+ { INT_RANGE(macsec_policy, 0, 1) },
+ { INT_RANGE(macsec_integ_only, 0, 1) },
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index 1a2c0c9be..7a3ed6373 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ while (cred_tail && cred_tail->next)
+ cred_tail = cred_tail->next;
+
++ if (!strncmp(name, "data:", 5)) {
++ f = fmemopen((void *)(name + 5), strlen(name + 5), "r");
++ name = "<inline>";
++ } else {
++ f = fopen(name, "r");
++ }
+ wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
+- f = fopen(name, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+ "error: %s", name, strerror(errno));
+@@ -775,6 +780,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
+ #endif /* IEEE8021X_EAPOL */
+ INT(mode);
+ INT(no_auto_peer);
++ INT(noscan);
+ INT(mesh_fwding);
+ INT(frequency);
+ INT(enable_edmg);
+diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
+index e40650c27..de79972b6 100644
+--- a/wpa_supplicant/config_ssid.h
++++ b/wpa_supplicant/config_ssid.h
+@@ -10,8 +10,10 @@
+ #define CONFIG_SSID_H
+
+ #include "common/defs.h"
++#include "ap/sta_info.h"
+ #include "utils/list.h"
+ #include "eap_peer/eap_config.h"
++#include "drivers/nl80211_copy.h"
+
+
+ #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
+@@ -879,6 +881,9 @@ struct wpa_ssid {
+ */
+ void *parent_cred;
+
++ unsigned char rates[WLAN_SUPP_RATES_MAX];
++ double mcast_rate;
++
+ #ifdef CONFIG_MACSEC
+ /**
+ * macsec_policy - Determines the policy for MACsec secure session
+@@ -1035,6 +1040,8 @@ struct wpa_ssid {
+ */
+ int no_auto_peer;
+
++ int noscan;
++
+ /**
+ * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
+ *
+diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
+index d0fda4cd9..ec45f29bb 100644
+--- a/wpa_supplicant/ctrl_iface.c
++++ b/wpa_supplicant/ctrl_iface.c
+@@ -2355,7 +2355,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+ pos += ret;
+ }
+
+-#ifdef CONFIG_AP
++#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
+ if (wpa_s->ap_iface) {
+ pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
+ end - pos,
+@@ -12542,6 +12542,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strcmp(buf, "MIB") == 0) {
+ reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+ if (reply_len >= 0) {
+@@ -12554,6 +12555,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ reply_size - reply_len);
+ #endif /* CONFIG_MACSEC */
+ }
++#endif
+ } else if (os_strncmp(buf, "STATUS", 6) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_status(
+ wpa_s, buf + 6, reply, reply_size);
+@@ -13042,6 +13044,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ reply_len = wpa_supplicant_ctrl_iface_bss(
+ wpa_s, buf + 4, reply, reply_size);
+ #ifdef CONFIG_AP
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strcmp(buf, "STA-FIRST") == 0) {
+ reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "STA ", 4) == 0) {
+@@ -13050,12 +13053,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
+ reply_size);
++#endif
++#ifdef CONFIG_CTRL_IFACE_MIB
+ } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+ if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+ if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
+ reply_len = -1;
++#endif
+ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+ if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+ reply_len = -1;
+@@ -13214,7 +13220,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
+ reply_len = -1;
+ #endif /* CONFIG_WNM */
+-#ifdef CONFIG_WNM_AP
++#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
+ } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+ if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
+ reply_len = -1;
+@@ -13224,7 +13230,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+ if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
+ reply_len = -1;
+-#endif /* CONFIG_WNM_AP */
++#endif /* CONFIG_AP && CONFIG_WNM_AP */
+ } else if (os_strcmp(buf, "FLUSH") == 0) {
+ wpa_supplicant_ctrl_iface_flush(wpa_s);
+ } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
+index 52befd8f1..ace6c5530 100644
+--- a/wpa_supplicant/defconfig
++++ b/wpa_supplicant/defconfig
+@@ -10,8 +10,8 @@
+ # to override previous values of the variables.
+
+
+-# Uncomment following two lines and fix the paths if you have installed OpenSSL
+-# or GnuTLS in non-default location
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
+ #CFLAGS += -I/usr/local/openssl/include
+ #LIBS += -L/usr/local/openssl/lib
+
+@@ -20,6 +20,7 @@
+ # used to fix build issues on such systems (krb5.h not found).
+ #CFLAGS += -I/usr/include/kerberos
+
++
+ # Driver interface for generic Linux wireless extensions
+ # Note: WEXT is deprecated in the current Linux kernel version and no new
+ # functionality is added to it. nl80211-based interface is the new
+@@ -329,6 +330,7 @@ CONFIG_BACKEND=file
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
+index 95953de92..673c3cc11 100644
+--- a/wpa_supplicant/eapol_test.c
++++ b/wpa_supplicant/eapol_test.c
+@@ -31,7 +31,12 @@
+ #include "ctrl_iface.h"
+ #include "pcsc_funcs.h"
+ #include "wpas_glue.h"
++#include "drivers/driver.h"
+
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+
+@@ -1325,6 +1330,10 @@ static void usage(void)
+ "option several times.\n");
+ }
+
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ int main(int argc, char *argv[])
+ {
+@@ -1348,6 +1357,8 @@ int main(int argc, char *argv[])
+ if (os_program_init())
+ return -1;
+
++ wpa_supplicant_event = supplicant_event;
++ wpa_supplicant_event_global = supplicant_event_global;
+ hostapd_logger_register_cb(hostapd_logger_cb);
+
+ os_memset(&eapol_test, 0, sizeof(eapol_test));
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index ca2794638..2a9342318 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -2935,8 +2935,6 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
+ }
+
+
+-#ifdef CONFIG_INTERWORKING
+-
+ static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
+ size_t len)
+ {
+@@ -2969,8 +2967,6 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
+ }
+ }
+
+-#endif /* CONFIG_INTERWORKING */
+-
+
+ static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
+ {
+@@ -3349,10 +3345,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
+ wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+ #endif /* CONFIG_WNM */
+-#ifdef CONFIG_INTERWORKING
+ interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+-#endif /* CONFIG_INTERWORKING */
+ if (wpa_s->hw_capab == CAPAB_VHT &&
+ get_ie(data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
+@@ -5928,8 +5922,8 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
+ }
+
+
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data)
++void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data)
+ {
+ struct wpa_supplicant *wpa_s = ctx;
+ int resched;
+@@ -5964,6 +5958,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ event_to_string(event), event);
+ #endif /* CONFIG_NO_STDOUT_DEBUG */
+
++ wpas_ucode_event(wpa_s, event, data);
+ switch (event) {
+ case EVENT_AUTH:
+ #ifdef CONFIG_FST
+@@ -6881,7 +6876,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+
+
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+ {
+ struct wpa_supplicant *wpa_s;
+diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
+index 9229eb51f..ee152c5b9 100644
+--- a/wpa_supplicant/main.c
++++ b/wpa_supplicant/main.c
+@@ -12,6 +12,7 @@
+ #endif /* __linux__ */
+
+ #include "common.h"
++#include "build_features.h"
+ #include "crypto/crypto.h"
+ #include "fst/fst.h"
+ #include "wpa_supplicant_i.h"
+@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
+
+ for (;;) {
+ c = getopt(argc, argv,
+- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
++ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
+ if (c < 0)
+ break;
+ switch (c) {
+@@ -267,6 +268,9 @@ int main(int argc, char *argv[])
+ params.conf_p2p_dev = optarg;
+ break;
+ #endif /* CONFIG_P2P */
++ case 'n':
++ iface_count = 0;
++ break;
+ case 'o':
+ params.override_driver = optarg;
+ break;
+@@ -302,8 +306,12 @@ int main(int argc, char *argv[])
+ break;
+ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+ case 'v':
+- printf("%s\n", wpa_supplicant_version);
+- exitcode = 0;
++ if (optarg) {
++ exitcode = !has_feature(optarg);
++ } else {
++ printf("%s\n", wpa_supplicant_version);
++ exitcode = 0;
++ }
+ goto out;
+ case 'W':
+ params.wait_for_monitor++;
+diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
+index 85c1ea8ba..dabbb0334 100644
+--- a/wpa_supplicant/mesh.c
++++ b/wpa_supplicant/mesh.c
+@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
+ frequency);
+ goto out_free;
+ }
++ if (conf->noscan)
++ ssid->noscan = 1;
+
+ if (ssid->mesh_basic_rates == NULL) {
+ /*
+@@ -630,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+
+ params->meshid = ssid->ssid;
+ params->meshid_len = ssid->ssid_len;
++ params->mcast_rate = ssid->mcast_rate;
+ ibss_mesh_setup_freq(wpa_s, ssid, ¶ms->freq);
+ wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
+ wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
+diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
+index 60f85624f..67352a121 100644
+--- a/wpa_supplicant/wpa_cli.c
++++ b/wpa_supplicant/wpa_cli.c
+@@ -26,6 +26,15 @@
+ #include <cutils/properties.h>
+ #endif /* ANDROID */
+
++#ifndef CONFIG_P2P
++#define CONFIG_P2P
++#endif
++#ifndef CONFIG_AP
++#define CONFIG_AP
++#endif
++#ifndef CONFIG_MESH
++#define CONFIG_MESH
++#endif
+
+ static const char *const wpa_cli_version =
+ "wpa_cli v" VERSION_STR "\n"
+diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
+index 88f3f2a52..92efe5629 100644
+--- a/wpa_supplicant/wpa_priv.c
++++ b/wpa_supplicant/wpa_priv.c
+@@ -1042,8 +1042,8 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
+ }
+
+
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+- union wpa_event_data *data)
++static void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data)
+ {
+ struct wpa_priv_interface *iface = ctx;
+
+@@ -1106,7 +1106,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+
+
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+ {
+ struct wpa_priv_global *global = ctx;
+@@ -1220,6 +1220,8 @@ int main(int argc, char *argv[])
+ if (os_program_init())
+ return -1;
+
++ wpa_supplicant_event = supplicant_event;
++ wpa_supplicant_event_global = supplicant_event_global;
+ wpa_priv_fd_workaround();
+
+ os_memset(&global, 0, sizeof(global));
+diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
+index ab71e2f27..fea84fe49 100644
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -1060,6 +1060,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+ sme_sched_obss_scan(wpa_s, 0);
+ }
+ wpa_s->wpa_state = state;
++ wpas_ucode_update_state(wpa_s);
+
+ #ifdef CONFIG_BGSCAN
+ if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
+@@ -2698,7 +2699,7 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+ }
+
+
+-static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
++static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
+ {
+ int i;
+
+@@ -2707,7 +2708,10 @@ static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan ||
+- chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++ chan->flag & HOSTAPD_CHAN_DISABLED)
++ return false;
++
++ if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
+ return false;
+ }
+
+@@ -2767,7 +2771,7 @@ static bool ibss_mesh_can_use_vht(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_hw_modes *mode)
+ {
+- if (mode->mode != HOSTAPD_MODE_IEEE80211A)
++ if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
+ return false;
+
+ if (!drv_supports_vht(wpa_s, ssid))
+@@ -2834,12 +2838,13 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_hw_modes *mode,
+ struct hostapd_freq_params *freq,
+- int obss_scan) {
++ int obss_scan, bool dfs_enabled) {
+ int chan_idx;
+ struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+ int i, res;
+ unsigned int j;
+ static const int ht40plus[] = {
++ 1, 2, 3, 4, 5, 6, 7,
+ 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+ 149, 157, 165, 173, 184, 192
+ };
+@@ -2858,8 +2863,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ return;
+
+ /* Check primary channel flags */
+- if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++ if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
++ if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++ if (!dfs_enabled)
++ return;
+
+ #ifdef CONFIG_HT_OVERRIDES
+ if (ssid->disable_ht40)
+@@ -2885,8 +2893,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ return;
+
+ /* Check secondary channel flags */
+- if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++ if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
++ if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++ if (!dfs_enabled)
++ return;
+
+ if (ht40 == -1) {
+ if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+@@ -2941,7 +2952,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_hw_modes *mode,
+ struct hostapd_freq_params *freq,
+- int ieee80211_mode, bool is_6ghz) {
++ int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
+ static const int bw80[] = {
+ 5180, 5260, 5500, 5580, 5660, 5745, 5825,
+ 5955, 6035, 6115, 6195, 6275, 6355, 6435,
+@@ -2986,7 +2997,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ goto skip_80mhz;
+
+ /* Use 40 MHz if channel not usable */
+- if (!ibss_mesh_is_80mhz_avail(channel, mode))
++ if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
+ goto skip_80mhz;
+
+ chwidth = CONF_OPER_CHWIDTH_80MHZ;
+@@ -3000,7 +3011,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ if ((mode->he_capab[ieee80211_mode].phy_cap[
+ HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
+- ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
++ ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
+ for (j = 0; j < ARRAY_SIZE(bw160); j++) {
+ if (freq->freq == bw160[j]) {
+ chwidth = CONF_OPER_CHWIDTH_160MHZ;
+@@ -3028,10 +3039,12 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ if (!chan)
+ continue;
+
+- if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+- HOSTAPD_CHAN_NO_IR |
+- HOSTAPD_CHAN_RADAR))
++ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
++ if (chan->flag & (HOSTAPD_CHAN_RADAR |
++ HOSTAPD_CHAN_NO_IR))
++ if (!dfs_enabled)
++ continue;
+
+ /* Found a suitable second segment for 80+80 */
+ chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+@@ -3083,12 +3096,17 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
+ enum hostapd_hw_mode hw_mode;
+ struct hostapd_hw_modes *mode = NULL;
+- int i, obss_scan = 1;
++ int i, obss_scan = !(ssid->noscan);
+ u8 channel;
+ bool is_6ghz, is_24ghz;
++ bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
+ freq->freq = ssid->frequency;
+
++ if (ssid->fixed_freq) {
++ obss_scan = 0;
++ }
++
+ if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
+ struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
+
+@@ -3132,11 +3150,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
+ ieee80211_mode);
+ freq->channel = channel;
++ if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
++ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ /* Setup higher BW only for 5 GHz */
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
+- ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
++ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
+- ieee80211_mode, is_6ghz))
++ ieee80211_mode, is_6ghz, dfs_enabled))
+ freq->he_enabled = freq->vht_enabled = false;
+ }
+
+@@ -4240,6 +4260,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+ params.beacon_int = ssid->beacon_int;
+ else
+ params.beacon_int = wpa_s->conf->beacon_int;
++ int i = 0;
++ while (i < WLAN_SUPP_RATES_MAX) {
++ params.rates[i] = ssid->rates[i];
++ i++;
++ }
++ params.mcast_rate = ssid->mcast_rate;
+ }
+
+ if (bss && ssid->enable_edmg)
+@@ -5861,7 +5887,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
+ if (wpa_s == NULL)
+ return NULL;
+ wpa_s->scan_req = INITIAL_SCAN_REQ;
+- wpa_s->scan_interval = 5;
++ wpa_s->scan_interval = 1;
+ wpa_s->new_connection = 1;
+ wpa_s->parent = parent ? parent : wpa_s;
+ wpa_s->p2pdev = wpa_s->parent;
+@@ -7576,7 +7602,6 @@ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+ return NULL;
+ }
+
+-
+ /**
+ * wpa_supplicant_match_existing - Match existing interfaces
+ * @global: Pointer to global data from wpa_supplicant_init()
+@@ -7611,6 +7636,11 @@ static int wpa_supplicant_match_existing(struct wpa_global *global)
+
+ #endif /* CONFIG_MATCH_IFACE */
+
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
++
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ /**
+ * wpa_supplicant_add_iface - Add a new network interface
+@@ -7693,6 +7723,9 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+ }
+ #endif /* CONFIG_P2P */
+
++ wpas_ubus_add_bss(wpa_s);
++ wpas_ucode_add_bss(wpa_s);
++
+ return wpa_s;
+ }
+
+@@ -7719,6 +7752,9 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
+ struct wpa_supplicant *parent = wpa_s->parent;
+ #endif /* CONFIG_MESH */
+
++ wpas_ucode_free_bss(wpa_s);
++ wpas_ubus_free_bss(wpa_s);
++
+ /* Remove interface from the global list of interfaces */
+ prev = global->ifaces;
+ if (prev == wpa_s) {
+@@ -7867,6 +7903,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+ #ifndef CONFIG_NO_WPA_MSG
+ wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+ #endif /* CONFIG_NO_WPA_MSG */
++ wpa_supplicant_event = supplicant_event;
++ wpa_supplicant_event_global = supplicant_event_global;
+
+ if (params->wpa_debug_file_path)
+ wpa_debug_open_file(params->wpa_debug_file_path);
+@@ -8025,6 +8063,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
++ wpas_ucode_init(global);
+
+ return global;
+ }
+@@ -8097,6 +8136,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
+
+ wpas_notify_supplicant_deinitialized(global);
+
++ wpas_ucode_free();
++
+ eap_peer_unregister_methods();
+ #ifdef CONFIG_AP
+ eap_server_unregister_methods();
+diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
+index 426d077d2..e0c0e5b0c 100644
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -21,6 +21,8 @@
+ #include "config_ssid.h"
+ #include "wmm_ac.h"
+ #include "pasn/pasn_common.h"
++#include "ubus.h"
++#include "ucode.h"
+
+ extern const char *const wpa_supplicant_version;
+ extern const char *const wpa_supplicant_license;
+@@ -319,6 +321,8 @@ struct wpa_global {
+ #endif /* CONFIG_WIFI_DISPLAY */
+
+ struct psk_list_entry *add_psk; /* From group formation */
++
++ struct ubus_object ubus_global;
+ };
+
+
+@@ -693,6 +697,8 @@ struct wpa_supplicant {
+ unsigned char own_addr[ETH_ALEN];
+ unsigned char perm_addr[ETH_ALEN];
+ char ifname[100];
++ struct wpas_ubus_bss ubus;
++ struct wpas_ucode_bss ucode;
+ #ifdef CONFIG_MATCH_IFACE
+ int matched;
+ #endif /* CONFIG_MATCH_IFACE */
+diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
+index 8cd355f6b..136b06583 100644
+--- a/wpa_supplicant/wps_supplicant.c
++++ b/wpa_supplicant/wps_supplicant.c
+@@ -33,6 +33,7 @@
+ #include "p2p/p2p.h"
+ #include "p2p_supplicant.h"
+ #include "wps_supplicant.h"
++#include "ubus.h"
+
+
+ #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
+@@ -401,6 +402,8 @@ static int wpa_supplicant_wps_cred(void *ctx,
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+ cred->cred_attr, cred->cred_attr_len);
+
++ wpas_ubus_notify(wpa_s, cred);
++
+ if (wpa_s->conf->wps_cred_processing == 1)
+ return 0;
+
+diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
+index aae3f7cb5..30b4e9105 100644
+--- a/wpa_supplicant/wps_supplicant.h
++++ b/wpa_supplicant/wps_supplicant.h
+@@ -9,6 +9,7 @@
+ #ifndef WPS_SUPPLICANT_H
+ #define WPS_SUPPLICANT_H
+
++struct wpa_bss;
+ struct wpa_scan_results;
+
+ #ifdef CONFIG_WPS
+@@ -16,8 +17,6 @@ struct wpa_scan_results;
+ #include "wps/wps.h"
+ #include "wps/wps_defs.h"
+
+-struct wpa_bss;
+-
+ struct wps_new_ap_settings {
+ const char *ssid_hex;
+ const char *auth;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
new file mode 100644
index 0000000..2d4b438
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
@@ -0,0 +1,441 @@
+From 63eb73fe7fe0cb9e088b95cffe4b123885bf9ede 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 029/104] mtk: hostapd: Add neighbor report and BSS Termination
+ for MBO certification
+
+1. 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.
+
+2. Support including neighbor report elements in ANQP response
+3. Support including neignbor report elements in BTM response
+4. Support configuring BSS Termination TSF by using hostapd_cli command
+5. Disable interface if BSS Termination TSF is set
+6. Support including neighbor report elements in BTM request
+7. Add hostapd_neighbor_set_own_report_pref()
+8. Add hostapd_neighbor_set_pref_by_non_pref_chan()
+
+Revert set_send_disassoc_frame_timer
+---
+ hostapd/ctrl_iface.c | 5 ++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ctrl_iface_ap.c | 18 ++++++-
+ src/ap/gas_serv.c | 29 ++++++++++
+ src/ap/gas_serv.h | 2 +
+ src/ap/neighbor_db.c | 118 +++++++++++++++++++++++++++++++++++++++++
+ src/ap/neighbor_db.h | 9 ++++
+ src/ap/wnm_ap.c | 43 +++++++++++++--
+ 9 files changed, 221 insertions(+), 5 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index f76226cf4..4240319b7 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1300,6 +1300,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 ca67aeb41..6c8b10291 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -175,6 +175,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+ bss->pasn_comeback_after = 10;
+ bss->pasn_noauth = 1;
+ #endif /* CONFIG_PASN */
++ bss->bss_termination_tsf = 0;
+ }
+
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d10b00be9..379dc22cf 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -560,6 +560,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;
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index cd7db4fc6..a2f89260c 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1377,6 +1377,10 @@ 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));
+ }
+@@ -1403,16 +1407,26 @@ 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"))
++ 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);
++ }
+ if (os_strstr(cmd, " link_removal_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
+
+ #ifdef CONFIG_MBO
++ hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
++
+ pos = os_strstr(cmd, "mbo=");
+ if (pos) {
+ unsigned int mbo_reason, cell_pref, reassoc_delay;
+diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
+index 4642e4927..cce6df41c 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 7646a98a4..ce492b53f 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.
+diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
+index f7a7d83d4..d9216a5ae 100644
+--- a/src/ap/neighbor_db.c
++++ b/src/ap/neighbor_db.c
+@@ -89,6 +89,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);
+@@ -364,3 +396,89 @@ int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
+
+ return 0;
+ }
++
++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;
++ }
++ }
++ }
++ }
++}
++
++#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 53f714203..cf1400256 100644
+--- a/src/ap/neighbor_db.h
++++ b/src/ap/neighbor_db.h
+@@ -25,4 +25,13 @@ 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);
++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 */
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index d259200c9..4ac96b1be 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
+
+@@ -390,13 +391,24 @@ 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;
++ }
+
+ sta = ap_get_sta(hapd, addr);
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+-
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
+@@ -406,10 +418,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 "
+@@ -915,6 +935,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)
+@@ -1006,6 +1042,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.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
new file mode 100644
index 0000000..56988b8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
@@ -0,0 +1,36 @@
+From d87f115b26430a4edd465d21be00ec61599c332e Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 20 Sep 2022 19:33:45 +0800
+Subject: [PATCH 030/104] mtk: hostapd: print sae groups by hostapd ctrl
+
+---
+ hostapd/ctrl_iface.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 4240319b7..1f950bc46 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1376,6 +1376,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
++ } else if (os_strcmp(cmd, "sae_group_capability") == 0) {
++#ifdef CONFIG_SAE
++ /* see sae_set_group() */
++ res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
++ dh_groups_get(15) ? "15 ": "",
++ dh_groups_get(16) ? "16 ": "",
++ dh_groups_get(17) ? "17 ": "",
++ dh_groups_get(18) ? "18 ": "");
++
++ if (os_snprintf_error(buflen, res))
++ return -1;
++ return res;
++#endif /* CONFIG_SAE */
+ }
+
+ return -1;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
new file mode 100644
index 0000000..55286b8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
@@ -0,0 +1,199 @@
+From 5453bd56aa134865ecaab20bde482b6c389831cb Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 31 May 2022 21:15:54 +0800
+Subject: [PATCH 031/104] mtk: hostapd: add support for runtime set in-band
+ discovery
+
+Usage:
+hostapd_cli unsolic_probe_resp [tx_type] [interval]
+
+0: disable all in-band discovery
+1: enable unsolicited probe response
+2: enable FILS discovery
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+
+The mac80211 layer already has a new variable "update",
+so the redundant variable "disable" has been removed.
+
+CR-ID: WCNCR00240597
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 66 ++++++++++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c | 20 +++++++++++
+ src/ap/beacon.c | 5 ++-
+ src/drivers/driver_nl80211.c | 6 ++--
+ 4 files changed, 94 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 1f950bc46..fb9f09bb1 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -772,6 +772,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
+
+ #endif /* CONFIG_INTERWORKING */
+
++static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
++ const char *cmd)
++{
++ struct hostapd_bss_config *conf = hapd->conf;
++ const char *pos = cmd;
++ int tx_type, interval, ret;
++
++ tx_type = atoi(pos);
++ if (tx_type < 0 || tx_type > 2) {
++ wpa_printf(MSG_ERROR, "Invalid tx type\n");
++ return -1;
++ }
++
++ pos = os_strchr(pos, ' ');
++ if(!pos)
++ return -1;
++ pos++;
++ interval = atoi(pos);
++ if (interval < 0 || interval > 20) {
++ wpa_printf(MSG_ERROR, "Invalid interval value\n");
++ return -1;
++ }
++
++ wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
++ tx_type, interval);
++
++#define DISABLE_INBAND_DISC 0
++#define UNSOL_PROBE_RESP 1
++#define FILS_DISCOVERY 2
++
++#ifdef CONFIG_FILS
++ conf->fils_discovery_max_int = 0;
++ conf->fils_discovery_min_int = 0;
++#endif /* CONFIG_FILS */
++ conf->unsol_bcast_probe_resp_interval = 0;
++
++ switch (tx_type) {
++ case DISABLE_INBAND_DISC:
++ default:
++ /* Disable both Unsolicited probe response and FILS discovery*/
++ break;
++ case UNSOL_PROBE_RESP:
++ /* Enable Unsolicited probe response */
++ conf->unsol_bcast_probe_resp_interval = interval;
++ break;
++#ifdef CONFIG_FILS
++ case FILS_DISCOVERY:
++ /* Enable FILS discovery */
++ conf->fils_discovery_min_int = interval;
++ conf->fils_discovery_max_int = interval;
++ break;
++#endif /* CONFIG_FILS */
++ }
++
++ ret = ieee802_11_update_beacons(hapd->iface);
++ if(ret) {
++ wpa_printf(MSG_DEBUG,
++ "Failed to update with inband discovery parameters\n");
++ return -1;
++ }
++
++ return 0;
++}
+
+ #ifdef CONFIG_WNM_AP
+
+@@ -4045,6 +4108,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
+ reply_len = -1;
+ #endif /* CONFIG_WNM_AP */
++ } else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
++ if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
++ reply_len = -1;
+ } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
+ reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
+ reply_size);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index a469b1f4d..1fb6d999e 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -655,6 +655,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ return wpa_ctrl_command(ctrl, buf);
+ }
+
++static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ char buf[300];
++ int res;
++
++ if (argc < 2) {
++ printf("Invalid 'inband_discovery' command - two arguments"
++ "tx_type interval\n");
++ return -1;
++ }
++
++ res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
++ argv[0], argv[1]);
++ if (os_snprintf_error(sizeof(buf), res))
++ return -1;
++ return wpa_ctrl_command(ctrl, buf);
++}
+
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+@@ -1851,6 +1869,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ { "driver", hostapd_cli_cmd_driver, NULL,
+ "<driver sub command> [<hex formatted data>] = send driver command data" },
+ #endif /* ANDROID */
++ { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
++ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 26453cb2c..a5c46b067 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2055,6 +2055,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+ {
+ params->fd_max_int = hapd->conf->fils_discovery_max_int;
++ params->unsol_bcast_probe_resp_interval =
++ hapd->conf->unsol_bcast_probe_resp_interval;
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
+ params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
+@@ -2063,7 +2065,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ if (params->fd_min_int > params->fd_max_int)
+ params->fd_min_int = params->fd_max_int;
+
+- if (params->fd_max_int)
++ if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
++ !params->unsol_bcast_probe_resp_interval))
+ return hostapd_gen_fils_discovery(hapd,
+ ¶ms->fd_frame_tmpl_len);
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 6778ad369..501d0e42e 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4780,7 +4780,6 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
+ nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
+ params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
+ return -1;
+-
+ nla_nest_end(msg, attr);
+ return 0;
+ }
+@@ -5412,7 +5411,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
+ #endif /* CONFIG_SAE */
+
+ #ifdef CONFIG_FILS
+- if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
++ if ((params->fd_max_int ||
++ ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
++ !(params->unsol_bcast_probe_resp_interval))) &&
++ nl80211_fils_discovery(bss, msg, params) < 0)
+ goto fail;
+ #endif /* CONFIG_FILS */
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
new file mode 100644
index 0000000..e93390d
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
@@ -0,0 +1,216 @@
+From 5be34a5e9682f4448e41322dc4f96d5d9a58240e Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 15:04:57 +0800
+Subject: [PATCH 032/104] mtk: hostapd: Add mtk_vendor.h
+
+---
+ src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 197 insertions(+)
+ create mode 100644 src/common/mtk_vendor.h
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+new file mode 100644
+index 000000000..4a19d2fc9
+--- /dev/null
++++ b/src/common/mtk_vendor.h
+@@ -0,0 +1,197 @@
++// SPDX-License-Identifier: ISC
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++#ifndef MTK_VENDOR_H
++#define MTK_VENDOR_H
++
++#define OUI_MTK 0x000ce7
++
++enum mtk_nl80211_vendor_subcmds {
++ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
++ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++ MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++ MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++ MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++ EDCCA_CTRL_SET_EN = 0,
++ EDCCA_CTRL_SET_THERS,
++ EDCCA_CTRL_GET_EN,
++ EDCCA_CTRL_GET_THERS,
++ EDCCA_CTRL_NUM,
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++enum mtk_vendor_attr_csi_ctrl {
++ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++ MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++ MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++ MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++ MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++ MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++ MTK_VENDOR_ATTR_CSI_DATA_VER,
++ MTK_VENDOR_ATTR_CSI_DATA_TS,
++ MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++ MTK_VENDOR_ATTR_CSI_DATA_SNR,
++ MTK_VENDOR_ATTR_CSI_DATA_BW,
++ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++ MTK_VENDOR_ATTR_CSI_DATA_TA,
++ MTK_VENDOR_ATTR_CSI_DATA_I,
++ MTK_VENDOR_ATTR_CSI_DATA_Q,
++ MTK_VENDOR_ATTR_CSI_DATA_INFO,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++ MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++ MTK_VENDOR_ATTR_CSI_DATA_MODE,
++ MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++ MTK_VENDOR_ATTR_CSI_DATA_MAX =
++ NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
++};
++
++enum mtk_vendor_attr_mnt_ctrl {
++ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_CTRL_SET,
++ MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
++ MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_set {
++ MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_SET_INDEX,
++ MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_SET,
++ MTK_VENDOR_ATTR_AMNT_SET_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
++};
++
++enum mtk_vendor_attr_mnt_dump {
++ MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
++ MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
++ MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++ MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++#define CSI_MAX_COUNT 256
++#define ETH_ALEN 6
++
++struct csi_data {
++ s16 data_i[CSI_MAX_COUNT];
++ s16 data_q[CSI_MAX_COUNT];
++ s8 rssi;
++ u8 snr;
++ u32 ts;
++ u8 data_bw;
++ u8 pri_ch_idx;
++ u8 ta[ETH_ALEN];
++ u32 info;
++ u8 rx_mode;
++ u32 h_idx;
++ u16 tx_idx;
++ u16 rx_idx;
++};
++
++struct amnt_data {
++ u8 idx;
++ u8 addr[ETH_ALEN];
++ s8 rssi[4];
++ u32 last_seen;
++};
++#endif /* MTK_VENDOR_H */
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
new file mode 100644
index 0000000..d7544bc
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
@@ -0,0 +1,630 @@
+From 7b735e95647fe46cc5dcf7d859c8f9abbed5ae0d Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 16:31:34 +0800
+Subject: [PATCH 033/104] mtk: hostapd: Support EDCCA hostapd configuration
+
+edcca_enable and edcca_compensation and implement edcca related handlers.
+---
+ hostapd/config_file.c | 34 ++++++
+ hostapd/ctrl_iface.c | 124 +++++++++++++++++++++
+ src/ap/ap_config.c | 4 +
+ src/ap/ap_config.h | 30 ++++++
+ src/ap/ap_drv_ops.c | 24 +++++
+ src/ap/ap_drv_ops.h | 4 +
+ src/ap/hostapd.c | 7 ++
+ src/common/mtk_vendor.h | 20 ++--
+ src/drivers/driver.h | 4 +
+ src/drivers/driver_nl80211.c | 174 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 7 ++
+ 12 files changed, 427 insertions(+), 6 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 0094db279..f8c1eec0a 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5348,6 +5348,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->mld_indicate_disabled = atoi(pos);
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++ } else if (os_strcmp(buf, "edcca_threshold") == 0) {
++ if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
++ conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
++ conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
++ conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
++ conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
++ conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
++ line);
++ return 1;
++ }
++ } else if (os_strcmp(buf, "edcca_enable") == 0) {
++ int mode = atoi(pos);
++ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
++ " allowed value 0 (Force Disable) or 1(Auto) ",
++ line, mode);
++ return 1;
++ }
++ conf->edcca_enable = (u8) mode;
++ } else if (os_strcmp(buf, "edcca_compensation") == 0) {
++ int val = atoi(pos);
++ if (val < EDCCA_MIN_COMPENSATION ||
++ val > EDCCA_MAX_COMPENSATION) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
++ " value %d; allowed value %d ~ %d.",
++ line, val, EDCCA_MIN_COMPENSATION,
++ EDCCA_MAX_COMPENSATION);
++ return 1;
++ }
++ conf->edcca_compensation = (s8) val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index fb9f09bb1..78a3380f2 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -544,6 +544,19 @@ static const char * pbc_status_str(enum pbc_status status)
+ }
+
+
++static const char *edcca_mode_str(enum edcca_mode status)
++{
++ switch (status) {
++ case EDCCA_MODE_FORCE_DISABLE:
++ return "Force Disable";
++ case EDCCA_MODE_AUTO:
++ return "Auto";
++ default:
++ return "Unknown";
++ }
++}
++
++
+ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+ {
+@@ -3644,6 +3657,111 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+
++static int
++hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *config, *value;
++ config = cmd;
++ pos = os_strchr(config, ' ');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if (pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strcmp(config, "enable") == 0) {
++ int mode = atoi(value);
++ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++ wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
++ return -1;
++ }
++ hapd->iconf->edcca_enable = (u8) mode;
++ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++ return -1;
++ } else if (os_strcmp(config, "compensation") == 0) {
++ int compensation = atoi(value);
++ if (compensation < EDCCA_MIN_COMPENSATION ||
++ compensation > EDCCA_MAX_COMPENSATION) {
++ wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
++ return -1;
++ }
++ hapd->iconf->edcca_compensation = (s8) compensation;
++ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++ return -1;
++ } else if (os_strcmp(config, "threshold") == 0) {
++ char *thres_value;
++ thres_value = os_strchr(value, ':');
++ if (thres_value == NULL)
++ return -1;
++ *thres_value++ = '\0';
++
++ if (thres_value == NULL)
++ return -1;
++ int bw_idx = atoi(value);
++ int threshold = atoi(thres_value);
++
++ if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
++ wpa_printf(MSG_ERROR,
++ "Unsupported Bandwidth idx %d for SET_EDCCA",
++ bw_idx);
++ return -1;
++ }
++ if (threshold < EDCCA_MIN_CONFIG_THRES ||
++ threshold > EDCCA_MAX_CONFIG_THRES) {
++ wpa_printf(MSG_ERROR,
++ "Unsupported threshold %d for SET_EDCCA",
++ threshold);
++ return -1;
++ }
++
++ int threshold_arr[EDCCA_MAX_BW_NUM];
++ /* 0x7f means keep the origival value in firmware */
++ os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
++ threshold_arr[bw_idx] = threshold;
++
++ if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
++ return -1;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for SET_EDCCA", config);
++ return -1;
++ }
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++static int
++hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
++ size_t buflen)
++{
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++ u8 value[EDCCA_MAX_BW_NUM] = {0};
++
++ if (os_strcmp(cmd, "enable") == 0) {
++ return os_snprintf(pos, end - pos, "Enable: %s\n",
++ edcca_mode_str(hapd->iconf->edcca_enable));
++ } else if (os_strcmp(cmd, "compensation") == 0) {
++ return os_snprintf(pos, end - pos, "Compensation: %d\n",
++ hapd->iconf->edcca_compensation);
++ } else if (os_strcmp(cmd, "threshold") == 0) {
++ if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
++ return -1;
++ return os_snprintf(pos, end - pos,
++ "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
++ value[0], value[1], value[2], value[3]);
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for GET_EDCCA", cmd);
++ return -1;
++ }
++}
++
+
+ #ifdef CONFIG_NAN_USD
+
+@@ -4531,6 +4649,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++ } else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
++ reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
++ reply_size);
++ } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
++ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
++ reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 6c8b10291..965600577 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -303,6 +303,9 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+ #endif /* CONFIG_AIRTIME_POLICY */
+
++ conf->edcca_enable = EDCCA_MODE_AUTO;
++ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+ return conf;
+@@ -1034,6 +1037,7 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #ifdef CONFIG_ACS
+ os_free(conf->acs_chan_bias);
+ #endif /* CONFIG_ACS */
++ os_free(conf->edcca_threshold);
+ wpabuf_free(conf->lci);
+ wpabuf_free(conf->civic);
+ #ifdef CONFIG_AFC
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 379dc22cf..09718fada 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1281,8 +1281,38 @@ struct hostapd_config {
+ int min_power;
+ } afc;
+ #endif /* CONFIG_AFC */
++
++ u8 edcca_enable;
++ s8 edcca_compensation;
++ int *edcca_threshold;
++};
++
++enum edcca_mode {
++ EDCCA_MODE_FORCE_DISABLE = 0,
++ EDCCA_MODE_AUTO = 1,
++};
++
++enum edcca_bw_id {
++ EDCCA_BW_20 = 0,
++ EDCCA_BW_40,
++ EDCCA_BW_80,
++ EDCCA_BW_160,
++ EDCCA_MAX_BW_NUM,
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++ EDCCA_CTRL_SET_EN = 0,
++ EDCCA_CTRL_SET_THRES,
++ EDCCA_CTRL_GET_EN,
++ EDCCA_CTRL_GET_THRES,
++ EDCCA_CTRL_NUM,
+ };
+
++#define EDCCA_DEFAULT_COMPENSATION -6
++#define EDCCA_MIN_COMPENSATION -126
++#define EDCCA_MAX_COMPENSATION 126
++#define EDCCA_MIN_CONFIG_THRES -126
++#define EDCCA_MAX_CONFIG_THRES 0
+
+ static inline enum oper_chan_width
+ hostapd_get_oper_chwidth(struct hostapd_config *conf)
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 527b2c984..a6caf6a73 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1245,3 +1245,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms);
+ }
+ #endif /* CONFIG_PASN */
++
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->configure_edcca_enable)
++ return 0;
++ return hapd->driver->configure_edcca_enable(hapd->drv_priv,
++ hapd->iconf->edcca_enable,
++ hapd->iconf->edcca_compensation);
++}
++
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++ const int *threshold)
++{
++ if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++ return 0;
++ return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
++}
++
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
++{
++ if (!hapd->driver || !hapd->driver->get_edcca)
++ return 0;
++ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f8a8725be..98836153f 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -149,6 +149,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ u8 ltf_keyseed_len,
+ const u8 *ltf_keyseed, u32 action);
+
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++ const int *threshold);
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7959859b0..6af31179e 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2693,6 +2693,13 @@ dfs_offload:
+ }
+ #endif /* CONFIG_MESH */
+
++ if (hostapd_drv_configure_edcca_enable(hapd) < 0)
++ goto fail;
++
++ if (hostapd_drv_configure_edcca_threshold(hapd,
++ hapd->iconf->edcca_threshold) < 0)
++ goto fail;
++
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+ if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 4a19d2fc9..6121857dd 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
+ };
+
+-enum mtk_vendor_attr_edcca_ctrl_mode {
+- EDCCA_CTRL_SET_EN = 0,
+- EDCCA_CTRL_SET_THERS,
+- EDCCA_CTRL_GET_EN,
+- EDCCA_CTRL_GET_THERS,
+- EDCCA_CTRL_NUM,
++enum mtk_vendor_attr_edcca_dump {
++ MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
++
++ MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
++ MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+
++
+ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 3e3e309f4..ed5f5c013 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5220,6 +5220,10 @@ struct wpa_driver_ops {
+ const u8 *match, size_t match_len,
+ bool multicast);
+ #endif /* CONFIG_TESTING_OPTIONS */
++ int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
++ const s8 edcca_compensation);
++ int (*configure_edcca_threshold)(void *priv, const int *threshold);
++ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 501d0e42e..d59efe8b6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -42,6 +42,8 @@
+ #include "radiotap_iter.h"
+ #include "rfkill.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
++#include "ap/ap_config.h"
+
+
+ #ifndef NETLINK_CAP_ACK
+@@ -14079,6 +14081,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+
+ #endif /* CONFIG_TESTING_OPTIONS */
+
++static int nl80211_configure_edcca_enable(void *priv,
++ const u8 edcca_enable,
++ const s8 edcca_compensation)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_edcca_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting EDCCA enable");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++ edcca_compensation)) {
++ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
++static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_edcca_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting EDCCA threshold");
++ return 0;
++ }
++
++ if (!threshold) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Input EDCCA threshold is empty!");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
++ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
++
++static int edcca_info_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *info = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
++ return NL_SKIP;
++ }
++
++ *info++ = nla_get_u8(attr);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
++ return NL_SKIP;
++ }
++
++ *info++ = nla_get_u8(attr);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
++ return NL_SKIP;
++ }
++
++ *info++ = nla_get_u8(attr);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
++ return NL_SKIP;
++ }
++
++ *info = nla_get_u8(attr);
++ return NL_SKIP;
++}
++
++
++static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_edcca_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting EDCCA threshold");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
++ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_resp(drv, msg, edcca_info_handler, value);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
+
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+@@ -14240,4 +14410,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .register_frame = testing_nl80211_register_frame,
+ .radio_disable = testing_nl80211_radio_disable,
+ #endif /* CONFIG_TESTING_OPTIONS */
++/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
++ .configure_edcca_enable = nl80211_configure_edcca_enable,
++ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
++ .get_edcca = nl80211_get_edcca,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 618746e67..62c47efbd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -200,6 +200,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
+ unsigned int puncturing:1;
+ unsigned int qca_ap_allowed_freqs:1;
++ unsigned int mtk_edcca_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index d6a887cef..cd4d799a1 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -18,6 +18,7 @@
+ #include "common/qca-vendor-attr.h"
+ #include "common/brcm_vendor.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+
+
+ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+@@ -1138,6 +1139,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ break;
+ }
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++ } else if (vinfo->vendor_id == OUI_MTK) {
++ switch (vinfo->subcmd) {
++ case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
++ drv->mtk_edcca_vendor_cmd_avail = 1;
++ break;
++ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
new file mode 100644
index 0000000..3849fa4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
@@ -0,0 +1,454 @@
+From fa905ce61f3cfecf94012fc2a11680e2615fc05d Mon Sep 17 00:00:00 2001
+From: TomLiu <tomml.liu@mediatek.com>
+Date: Tue, 9 Aug 2022 10:23:44 -0700
+Subject: [PATCH 034/104] mtk: hostapd: Add hostapd MU SET/GET control
+
+---
+ hostapd/config_file.c | 9 +++
+ hostapd/ctrl_iface.c | 66 ++++++++++++++++++
+ hostapd/hostapd_cli.c | 18 +++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 14 ++++
+ src/ap/ap_drv_ops.h | 2 +
+ src/ap/hostapd.c | 2 +
+ src/common/mtk_vendor.h | 15 ++++
+ src/drivers/driver.h | 13 ++++
+ src/drivers/driver_nl80211.c | 110 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 13 files changed, 255 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index f8c1eec0a..637c2df9f 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4159,6 +4159,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->mbssid = mbssid;
++ } else if (os_strcmp(buf, "mu_onoff") == 0) {
++ int val = atoi(pos);
++ if (val < 0 || val > 15) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid mu_onoff value",
++ line);
++ return 1;
++ }
++ conf->mu_onoff = val;
+ #endif /* CONFIG_IEEE80211AX */
+ } else if (os_strcmp(buf, "max_listen_interval") == 0) {
+ bss->max_listen_interval = atoi(pos);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 78a3380f2..3a79a1284 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4049,6 +4049,67 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+
+
++static int
++hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *config, *value;
++ config = cmd;
++ pos = os_strchr(config, ' ');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if(pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strcmp(config, "onoff") == 0) {
++ int mu = atoi(value);
++ if (mu < 0 || mu > 15) {
++ wpa_printf(MSG_ERROR, "Invalid value for mu");
++ return -1;
++ }
++ hapd->iconf->mu_onoff = (u8) mu;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for SET_MU", config);
++ return -1;
++ }
++
++ if(hostapd_drv_mu_ctrl(hapd) == 0) {
++ return os_snprintf(buf, buflen, "OK\n");
++ } else {
++ return -1;
++ }
++}
++
++
++static int
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ u8 mu_onoff;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hapd->iface->state != HAPD_IFACE_ENABLED)
++ return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
++ hostapd_state_text(hapd->iface->state));
++
++ if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
++ hapd->iconf->mu_onoff = mu_onoff;
++ return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++ !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++ } else {
++ wpa_printf(MSG_INFO, "ctrl iface failed to call");
++ return -1;
++ }
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4655,6 +4716,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
+ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ reply_size);
++ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
++ reply_size);
++ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
++ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 1fb6d999e..da9dabd6f 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1442,6 +1442,20 @@ static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
++static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++}
++
++
+ #ifdef CONFIG_DPP
+
+ static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+@@ -1801,6 +1815,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = show supported driver flags"},
+ { "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL,
+ " = show supported driver flags2"},
++ { "set_mu", hostapd_cli_cmd_set_mu, NULL,
++ "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
++ { "get_mu", hostapd_cli_cmd_get_mu, NULL,
++ " = show mu onoff value in 0-15 bitmap"},
+ #ifdef CONFIG_DPP
+ { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
+ "report a scanned DPP URI from a QR Code" },
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 965600577..9b3ef0b5b 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -289,6 +289,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->reg_def_cli_eirp_psd = -1;
+ conf->reg_sub_cli_eirp_psd = -1;
+ conf->reg_def_cli_eirp = -1;
++ conf->mu_onoff = 15;
+ #endif /* CONFIG_IEEE80211AX */
+
+ /* The third octet of the country string uses an ASCII space character
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 09718fada..f7dbbbec3 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1185,6 +1185,7 @@ struct hostapd_config {
+ int reg_def_cli_eirp;
+
+ bool require_he;
++ u8 mu_onoff;
+ #endif /* CONFIG_IEEE80211AX */
+
+ /* VHT enable/disable config from CHAN_SWITCH */
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index a6caf6a73..897ed6af8 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1269,3 +1269,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ return 0;
+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
++
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->mu_ctrl)
++ return 0;
++ return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++}
++
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
++{
++ if (!hapd->driver || !hapd->driver->mu_dump)
++ return 0;
++ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 98836153f..5ab20cc41 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -153,6 +153,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 6af31179e..d29b51fc5 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2699,6 +2699,8 @@ dfs_offload:
+ if (hostapd_drv_configure_edcca_threshold(hapd,
+ hapd->iconf->edcca_threshold) < 0)
+ goto fail;
++ if (hostapd_drv_mu_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6121857dd..60bc4cd4c 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
+ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
+ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ };
+
+@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
+ };
+
++enum mtk_vendor_attr_mu_ctrl {
++ MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
++ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
++ MTK_VENDOR_ATTR_MU_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
++};
++
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ed5f5c013..df7ce5ab9 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -176,6 +176,11 @@ struct hostapd_channel_data {
+ * punct_bitmap - RU puncturing bitmap
+ */
+ u16 punct_bitmap;
++
++ /**
++ * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
++ */
++ u8 mu_onoff;
+ };
+
+ #define HE_MAC_CAPAB_0 0
+@@ -5224,6 +5229,14 @@ struct wpa_driver_ops {
+ const s8 edcca_compensation);
+ int (*configure_edcca_threshold)(void *priv, const int *threshold);
+ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
++
++ /**
++ * mu_ctrl - ctrl on off for UL/DL MURU
++ * @priv: Private driver interface data
++ *
++ */
++ int (*mu_ctrl)(void *priv, u8 mu_onoff);
++ int (*mu_dump)(void *priv, u8 *mu_onoff);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index d59efe8b6..c234eb029 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13917,6 +13917,114 @@ fail:
+ }
+
+
++#ifdef CONFIG_IEEE80211AX
++static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_mu_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting mu control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if(ret){
++ wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++ }
++ return ret;
++}
++
++
++static int mu_dump_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *mu_onoff = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ static const struct nla_policy
++ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
++ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++ };
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
++ return NL_SKIP;
++ }
++
++ *mu_onoff = nla_get_u8(attr);
++ wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
++
++ return 0;
++}
++
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *attr;
++ int ret;
++
++ if (!drv->mtk_mu_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting mu control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++
++ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!attr) {
++ nlmsg_free(msg);
++ return -1;
++ }
++
++ nla_nest_end(msg, attr);
++
++ ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
++
++ if(ret){
++ wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++}
++#endif /* CONFIG_IEEE80211AX */
++
++
+ #ifdef CONFIG_DPP
+ static int nl80211_dpp_listen(void *priv, bool enable)
+ {
+@@ -14396,6 +14504,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .update_connect_params = nl80211_update_connection_params,
+ .send_external_auth_status = nl80211_send_external_auth_status,
+ .set_4addr_mode = nl80211_set_4addr_mode,
++ .mu_ctrl = nl80211_mu_onoff,
++ .mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ .dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 62c47efbd..f99bba9e1 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -201,6 +201,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int puncturing:1;
+ unsigned int qca_ap_allowed_freqs:1;
+ unsigned int mtk_edcca_vendor_cmd_avail:1;
++ unsigned int mtk_mu_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index cd4d799a1..9c0a47971 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1144,6 +1144,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
+ drv->mtk_edcca_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
++ drv->mtk_mu_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
new file mode 100644
index 0000000..596fdd3
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
@@ -0,0 +1,247 @@
+From 588292b2fac44452523a27ece07b85fbd0f41c5d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 2 Sep 2022 01:03:23 +0800
+Subject: [PATCH 035/104] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
+ command
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c | 4 ++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 13 ++++++++++++
+ src/ap/ap_drv_ops.c | 11 +++++++++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/ap/hostapd.c | 2 ++
+ src/common/mtk_vendor.h | 16 +++++++++++++++
+ src/drivers/driver.h | 8 ++++++++
+ src/drivers/driver_nl80211.c | 33 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +++
+ 11 files changed, 93 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 637c2df9f..3d9923692 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5391,6 +5391,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->edcca_compensation = (s8) val;
++ } else if (os_strcmp(buf, "three_wire_enable") == 0) {
++ u8 en = atoi(pos);
++
++ conf->three_wire_enable = en;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 9b3ef0b5b..79fd3a24b 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -306,6 +306,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+
+ conf->edcca_enable = EDCCA_MODE_AUTO;
+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index f7dbbbec3..d1bbd238c 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1286,6 +1286,19 @@ struct hostapd_config {
+ u8 edcca_enable;
+ s8 edcca_compensation;
+ int *edcca_threshold;
++ u8 three_wire_enable;
++};
++
++enum three_wire_mode {
++ THREE_WIRE_MODE_DISABLE,
++ THREE_WIRE_MODE_EXT0_ENABLE,
++ THREE_WIRE_MODE_EXT1_ENABLE,
++ THREE_WIRE_MODE_ALL_ENABLE,
++
++ /* keep last */
++ NUM_THREE_WIRE_MODE,
++ THREE_WIRE_MODE_MAX =
++ NUM_THREE_WIRE_MODE - 1
+ };
+
+ enum edcca_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 897ed6af8..587b8f37f 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1283,3 +1283,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ return 0;
+ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
+ }
++
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->three_wire_ctrl)
++ return 0;
++ if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
++ wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
++ return 0;
++ }
++ return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 5ab20cc41..7448e7954 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -155,6 +155,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index d29b51fc5..5ceb49962 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2701,6 +2701,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_mu_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 60bc4cd4c..99ecbaf71 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+ };
+
++enum mtk_vendor_attr_3wire_ctrl {
++ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
++ MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_csi_ctrl {
+ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index df7ce5ab9..dec4336a4 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5237,6 +5237,14 @@ struct wpa_driver_ops {
+ */
+ int (*mu_ctrl)(void *priv, u8 mu_onoff);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
++
++ /**
++ * three_wire_ctrl - set three_wire_ctrl mode
++ * @priv: Private driver interface data
++ * @three_wire_enable: three_wire_ctrl mode
++ *
++ */
++ int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c234eb029..c9899e492 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14357,6 +14357,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
+ return ret;
+ }
+
++static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ /* Prepare nl80211 cmd */
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_3wire_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting three wire control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
+
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+@@ -14524,4 +14556,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .configure_edcca_enable = nl80211_configure_edcca_enable,
+ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ .get_edcca = nl80211_get_edcca,
++ .three_wire_ctrl = nl80211_enable_three_wire,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index f99bba9e1..5de6ca6f0 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int qca_ap_allowed_freqs:1;
+ unsigned int mtk_edcca_vendor_cmd_avail:1;
+ unsigned int mtk_mu_vendor_cmd_avail:1;
++ unsigned int mtk_3wire_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 9c0a47971..fddcf8349 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1147,6 +1147,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
+ drv->mtk_mu_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
++ drv->mtk_3wire_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
new file mode 100644
index 0000000..56149a4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
@@ -0,0 +1,431 @@
+From 235a6041bde6ce30da6e631b901260dec5fadcda Mon Sep 17 00:00:00 2001
+From: mtk27835 <shurong.wen@mediatek.com>
+Date: Wed, 7 Sep 2022 14:41:51 -0700
+Subject: [PATCH 036/104] mtk: hostapd: Add hostapd iBF control
+
+Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
+---
+ hostapd/config_file.c | 3 +
+ hostapd/ctrl_iface.c | 26 +++++++
+ hostapd/hostapd_cli.c | 9 +++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 2 +
+ src/ap/ap_drv_ops.c | 14 ++++
+ src/ap/ap_drv_ops.h | 2 +
+ src/ap/hostapd.c | 2 +
+ src/common/mtk_vendor.h | 35 +++++++++-
+ src/drivers/driver.h | 19 ++++++
+ src/drivers/driver_nl80211.c | 108 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 13 files changed, 224 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 3d9923692..247d68811 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5395,6 +5395,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ u8 en = atoi(pos);
+
+ conf->three_wire_enable = en;
++ } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
++ int val = atoi(pos);
++ conf->ibf_enable = !!val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3a79a1284..10bbce341 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4110,6 +4110,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ }
+
+
++static int
++hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ u8 ibf_enable;
++ int ret;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
++ hapd->iconf->ibf_enable = ibf_enable;
++ ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
++ ibf_enable);
++ }
++
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++
++ return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4721,6 +4745,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_size);
+ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index da9dabd6f..276ca578c 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1666,6 +1666,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ #endif /* ANDROID */
+
+
++static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1889,6 +1896,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ #endif /* ANDROID */
+ { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
+ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
++ { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
++ " = show iBF state (enabled/disabled)"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 79fd3a24b..04e263167 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -307,6 +307,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->edcca_enable = EDCCA_MODE_AUTO;
+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
++ conf->ibf_enable = IBF_DEFAULT_ENABLE;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d1bbd238c..5f084796d 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1287,6 +1287,7 @@ struct hostapd_config {
+ s8 edcca_compensation;
+ int *edcca_threshold;
+ u8 three_wire_enable;
++ u8 ibf_enable;
+ };
+
+ enum three_wire_mode {
+@@ -1450,6 +1451,7 @@ hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
+ #endif /* CONFIG_IEEE80211BE */
+ }
+
++#define IBF_DEFAULT_ENABLE 0
+
+ int hostapd_mac_comp(const void *a, const void *b);
+ struct hostapd_config * hostapd_config_defaults(void);
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 587b8f37f..3cace58e5 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1294,3 +1294,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+ }
+ return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
+ }
++
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->ibf_ctrl)
++ return 0;
++ return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
++}
++
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
++{
++ if (!hapd->driver || !hapd->driver->ibf_dump)
++ return 0;
++ return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 7448e7954..0886acb2d 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,6 +156,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 5ceb49962..1d941683f 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2703,6 +2703,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_ibf_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 99ecbaf71..9811f266e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
++ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
++ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+
++enum mtk_vendor_attr_ibf_ctrl {
++ MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
++ MTK_VENDOR_ATTR_IBF_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_dump {
++ MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
++ MTK_VENDOR_ATTR_IBF_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
++};
++
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
+
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dec4336a4..f5cff646e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -181,6 +181,11 @@ struct hostapd_channel_data {
+ * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
+ */
+ u8 mu_onoff;
++
++ /**
++ * ibf_enable=<val>
++ */
++ u8 ibf_enable;
+ };
+
+ #define HE_MAC_CAPAB_0 0
+@@ -5245,6 +5250,20 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
++
++ /**
++ * ibf_ctrl - ctrl disable/enable for ibf
++ * @priv: Private driver interface data
++ *
++ */
++ int (*ibf_ctrl)(void *priv, u8 ibf_enable);
++
++ /**
++ * ibf_dump - dump ibf
++ * @priv: Private driver interface data
++ *
++ */
++ int (*ibf_dump)(void *priv, u8 *ibf_enable);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c9899e492..c17052f22 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14390,6 +14390,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
+ return ret;
+ }
+
++static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_ibf_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ibf control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int ibf_dump_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *ibf_enable = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
++ if (!attr) {
++ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
++ return NL_SKIP;
++ }
++
++ *ibf_enable = nla_get_u8(attr);
++
++ return NL_SKIP;
++}
++
++static int
++nl80211_ibf_dump(void *priv, u8 *ibf_enable)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, ibf_dump_handler, ibf_enable);
++
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -14557,4 +14663,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ .get_edcca = nl80211_get_edcca,
+ .three_wire_ctrl = nl80211_enable_three_wire,
++ .ibf_ctrl = nl80211_ibf_enable,
++ .ibf_dump = nl80211_ibf_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5de6ca6f0..1432eeda8 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_edcca_vendor_cmd_avail:1;
+ unsigned int mtk_mu_vendor_cmd_avail:1;
+ unsigned int mtk_3wire_vendor_cmd_avail:1;
++ unsigned int mtk_ibf_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index fddcf8349..615af2eb2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1150,6 +1150,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
+ drv->mtk_3wire_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
++ drv->mtk_ibf_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
new file mode 100644
index 0000000..ade8550
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
@@ -0,0 +1,34 @@
+From dce3106e1c43076ab410af89f817e15cee7959a3 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 22 Sep 2022 16:08:09 +0800
+Subject: [PATCH 037/104] mtk: hostapd: Do not include HE capab IE if
+ associated sta's HE capab IE is invalid
+
+The parameter 'sta' passed to send_assoc_resp() might be NULL, so an
+NULL check is necessary before access the 'sta'.
+Only one such check was missed in this function, and this patch fixs it.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I028b7779acfbce5292a60c9f800a83c57c999943
+---
+ src/ap/ieee802_11.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index bda61b998..d972a25f1 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4931,7 +4931,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ #endif /* CONFIG_IEEE80211AC */
+
+ #ifdef CONFIG_IEEE80211AX
+- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
++ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && sta &&
++ sta->flags & WLAN_STA_HE) {
+ p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_he_operation(hapd, p);
+ p = hostapd_eid_cca(hapd, p);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
new file mode 100644
index 0000000..c540b5e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
@@ -0,0 +1,136 @@
+From 9d180d46527a6a53824227c2b7c6e4e37bf3acfb Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:55:49 +0800
+Subject: [PATCH 038/104] mtk: hostapd: Add DFS detection mode
+
+Add DFS detection mode for testing radar detection rate.
+If DFS detection mode is on, AP will not switch channels when receiving
+a radar signal.
+This detection mode also supports background chain.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c | 4 ++++
+ hostapd/ctrl_iface.c | 23 +++++++++++++++++++++++
+ src/ap/ap_config.h | 13 +++++++++++++
+ src/ap/dfs.c | 10 ++++++++++
+ 4 files changed, 50 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 247d68811..40ade89c0 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5398,6 +5398,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
+ int val = atoi(pos);
+ conf->ibf_enable = !!val;
++ } else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
++ u8 en = strtol(pos, NULL, 10);
++
++ conf->dfs_detect_mode = en;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 10bbce341..71a87459c 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4134,6 +4134,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
+ }
+
+
++static int
++hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
++ char *buf, size_t buflen)
++{
++ u8 dfs_detect_mode;
++
++ if (!value)
++ return -1;
++
++ dfs_detect_mode = strtol(value, NULL, 10);
++ if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
++ wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
++ return -1;
++ }
++ hapd->iconf->dfs_detect_mode = dfs_detect_mode;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4747,6 +4767,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
++ reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 5f084796d..7607c63e1 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1288,6 +1288,7 @@ struct hostapd_config {
+ int *edcca_threshold;
+ u8 three_wire_enable;
+ u8 ibf_enable;
++ u8 dfs_detect_mode;
+ };
+
+ enum three_wire_mode {
+@@ -1302,6 +1303,18 @@ enum three_wire_mode {
+ NUM_THREE_WIRE_MODE - 1
+ };
+
++enum dfs_mode {
++ DFS_DETECT_MODE_DISABLE,
++ DFS_DETECT_MODE_AP_ENABLE,
++ DFS_DETECT_MODE_BACKGROUND_ENABLE,
++ DFS_DETECT_MODE_ALL_ENABLE,
++
++ /* keep last */
++ NUM_DFS_DETECT_MODE,
++ DFS_DETECT_MODE_MAX =
++ NUM_DFS_DETECT_MODE - 1
++};
++
+ enum edcca_mode {
+ EDCCA_MODE_FORCE_DISABLE = 0,
+ EDCCA_MODE_AUTO = 1,
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index d14fad136..1df4de6b8 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1336,6 +1336,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ __func__, iface->radar_background.cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
++ /* Skip channel switch when background dfs detect mode is on */
++ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
++ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++ return 0;
++
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+@@ -1384,6 +1389,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ __func__, iface->cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
++ /* Skip channel switch when dfs detect mode is on */
++ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
++ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++ return 0;
++
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
new file mode 100644
index 0000000..a37e541
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
@@ -0,0 +1,192 @@
+From 7f0f977e36718a7a827f7d78e6ab578d53aa630b Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:56:55 +0800
+Subject: [PATCH 039/104] mtk: hostapd: Add DFS offchan channel switch
+
+Add DFS background chain channel switch command for testing purpose.
+This feature is implemented via hostapd_cli command.
+Command format:
+hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
+ src/ap/dfs.c | 25 ++++++---------
+ src/ap/dfs.h | 15 +++++++++
+ 3 files changed, 96 insertions(+), 16 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 71a87459c..68dcc7982 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4154,6 +4154,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
+ }
+
+
++static int
++hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ struct hostapd_iface *iface = hapd->iface;
++ char *pos, *param;
++ enum hostapd_hw_mode hw_mode;
++ bool chan_found = false;
++ int i, num_available_chandefs, channel, chan_width, sec = 0;
++ int sec_chan_idx_80p80 = -1;
++ u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
++ struct hostapd_channel_data *chan;
++ enum dfs_channel_type type = DFS_NO_CAC_YET;
++
++ param = os_strchr(cmd, ' ');
++ if (!param)
++ return -1;
++ *param++ = '\0';
++
++ pos = os_strstr(param, "chan=");
++ if (pos)
++ channel = strtol(pos + 5, NULL, 10);
++ else
++ return -1;
++
++ num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
++ for (i = 0; i < num_available_chandefs; i++) {
++ dfs_find_channel(iface, &chan, i, type);
++ if (chan->chan == channel) {
++ chan_found = true;
++ break;
++ }
++ }
++
++ if (!chan_found)
++ return -1;
++
++ if (iface->conf->secondary_channel)
++ sec = 1;
++
++ dfs_adjust_center_freq(iface, chan,
++ sec,
++ sec_chan_idx_80p80,
++ &oper_centr_freq_seg0_idx,
++ &oper_centr_freq_seg1_idx);
++
++ if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++ chan->freq, chan->chan,
++ iface->conf->ieee80211n,
++ iface->conf->ieee80211ac,
++ iface->conf->ieee80211ax,
++ iface->conf->ieee80211be,
++ sec, hostapd_get_oper_chwidth(iface->conf),
++ oper_centr_freq_seg0_idx,
++ oper_centr_freq_seg1_idx, true)) {
++ wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
++ iface->radar_background.channel = -1;
++ return -1;
++ }
++
++ iface->radar_background.channel = chan->chan;
++ iface->radar_background.freq = chan->freq;
++ iface->radar_background.secondary_channel = sec;
++ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
++ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4770,6 +4840,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+ reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
++ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 1df4de6b8..ece27d070 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -19,13 +19,6 @@
+ #include "dfs.h"
+ #include "crypto/crypto.h"
+
+-
+-enum dfs_channel_type {
+- DFS_ANY_CHANNEL,
+- DFS_AVAILABLE, /* non-radar or radar-available */
+- DFS_NO_CAC_YET, /* radar-not-yet-available */
+-};
+-
+ static struct hostapd_channel_data *
+ dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+ u8 *oper_centr_freq_seg0_idx,
+@@ -238,9 +231,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+ * - hapd->vht/he_oper_centr_freq_seg0_idx
+ * - hapd->vht/he_oper_centr_freq_seg1_idx
+ */
+-static int dfs_find_channel(struct hostapd_iface *iface,
+- struct hostapd_channel_data **ret_chan,
+- int idx, enum dfs_channel_type type)
++int dfs_find_channel(struct hostapd_iface *iface,
++ struct hostapd_channel_data **ret_chan,
++ int idx, enum dfs_channel_type type)
+ {
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+@@ -300,12 +293,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
+ }
+
+
+-static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+- struct hostapd_channel_data *chan,
+- int secondary_channel,
+- int sec_chan_idx_80p80,
+- u8 *oper_centr_freq_seg0_idx,
+- u8 *oper_centr_freq_seg1_idx)
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++ struct hostapd_channel_data *chan,
++ int secondary_channel,
++ int sec_chan_idx_80p80,
++ u8 *oper_centr_freq_seg0_idx,
++ u8 *oper_centr_freq_seg1_idx)
+ {
+ if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+ return;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 606c1b393..c2556d2d9 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -9,6 +9,12 @@
+ #ifndef DFS_H
+ #define DFS_H
+
++enum dfs_channel_type {
++ DFS_ANY_CHANNEL,
++ DFS_AVAILABLE, /* non-radar or radar-available */
++ DFS_NO_CAC_YET, /* radar-not-yet-available */
++};
++
+ int hostapd_handle_dfs(struct hostapd_iface *iface);
+
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ int center_freq);
++int dfs_find_channel(struct hostapd_iface *iface,
++ struct hostapd_channel_data **ret_chan,
++ int idx, enum dfs_channel_type type);
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++ struct hostapd_channel_data *chan,
++ int secondary_channel,
++ int sec_chan_idx_80p80,
++ u8 *oper_centr_freq_seg0_idx,
++ u8 *oper_centr_freq_seg1_idx);
+
+ #endif /* DFS_H */
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
new file mode 100644
index 0000000..ead86d5
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
@@ -0,0 +1,400 @@
+From 8291551130127914cbe3b5346fbd2edc89437df8 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 16 Dec 2022 03:57:11 +0800
+Subject: [PATCH 040/104] mtk: hostapd: Add amsdu set get ctrl
+
+---
+ hostapd/config_file.c | 9 +++
+ hostapd/ctrl_iface.c | 26 +++++++
+ hostapd/hostapd_cli.c | 9 +++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 14 ++++
+ src/ap/ap_drv_ops.h | 2 +
+ src/ap/hostapd.c | 2 +
+ src/common/mtk_vendor.h | 17 ++++-
+ src/drivers/driver.h | 9 +++
+ src/drivers/driver_nl80211.c | 114 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 13 files changed, 207 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 40ade89c0..7695ab196 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5402,6 +5402,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ u8 en = strtol(pos, NULL, 10);
+
+ conf->dfs_detect_mode = en;
++ } else if (os_strcmp(buf, "amsdu") == 0) {
++ int val = atoi(pos);
++ if (val < 0 || val > 1) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid amsdu value",
++ line);
++ return 1;
++ }
++ conf->amsdu = val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 68dcc7982..5f6278ecd 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4224,6 +4224,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
+ }
+
+
++static int
++hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ u8 amsdu;
++ int ret;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
++ hapd->iconf->amsdu = amsdu;
++ ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
++ hapd->iconf->amsdu);
++ }
++
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++
++ return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -4842,6 +4866,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply, reply_size);
+ } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
+ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
++ reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 276ca578c..847f867ab 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1673,6 +1673,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
++static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1898,6 +1905,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
+ " = show iBF state (enabled/disabled)"},
++ { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
++ " = show AMSDU state"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 04e263167..2420a251e 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -308,6 +308,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ conf->ibf_enable = IBF_DEFAULT_ENABLE;
++ conf->amsdu = 1;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 7607c63e1..123e12c8f 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1289,6 +1289,7 @@ struct hostapd_config {
+ u8 three_wire_enable;
+ u8 ibf_enable;
+ u8 dfs_detect_mode;
++ u8 amsdu;
+ };
+
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 3cace58e5..23228a8d2 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1307,4 +1307,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
+ if (!hapd->driver || !hapd->driver->ibf_dump)
+ return 0;
+ return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
++
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->amsdu_ctrl)
++ return 0;
++ return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
++}
++
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
++{
++ if (!hapd->driver || !hapd->driver->amsdu_dump)
++ return 0;
++ return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+ }
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 0886acb2d..f3a044557 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -158,6 +158,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 1d941683f..a5b683676 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2705,6 +2705,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_ibf_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 9811f266e..7b4d7c11a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
+
+@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
+ };
+
++enum mtk_vendor_attr_wireless_dump {
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
++
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
++ MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
++ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_rfeature_ctrl {
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index f5cff646e..6eeb9c22e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5264,6 +5264,15 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*ibf_dump)(void *priv, u8 *ibf_enable);
++
++ /**
++ * amsdu_ctrl - enable/disable amsdu
++ * amsdu_dump - get current amsdu status
++ * @priv: Private driver interface data
++ *
++ */
++ int (*amsdu_ctrl)(void *priv, u8 amsdu);
++ int (*amsdu_dump)(void *priv, u8 *amsdu);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c17052f22..eca2ff077 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14496,6 +14496,118 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int nl80211_enable_amsdu(void *priv, u8 amsdu)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_wireless_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap wireless control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
++{
++ u8 *amsdu = (u8 *) arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr_amsdu;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
++ if (!attr_amsdu ){
++ wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
++ return NL_SKIP;
++ }
++
++ *amsdu = nla_get_u8(attr_amsdu);
++
++ return NL_SKIP;
++}
++
++static int
++nl80211_dump_amsdu(void *priv, u8 *amsdu)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_wireless_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support ap_wireless control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, dump_amsdu_handler, amsdu);
++
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -14665,4 +14777,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .three_wire_ctrl = nl80211_enable_three_wire,
+ .ibf_ctrl = nl80211_ibf_enable,
+ .ibf_dump = nl80211_ibf_dump,
++ .amsdu_ctrl = nl80211_enable_amsdu,
++ .amsdu_dump = nl80211_dump_amsdu,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 1432eeda8..5aa813e26 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_mu_vendor_cmd_avail:1;
+ unsigned int mtk_3wire_vendor_cmd_avail:1;
+ unsigned int mtk_ibf_vendor_cmd_avail:1;
++ unsigned int mtk_wireless_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 615af2eb2..474d4e273 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1153,6 +1153,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
+ drv->mtk_ibf_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
++ drv->mtk_wireless_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
new file mode 100644
index 0000000..5dac957
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
@@ -0,0 +1,102 @@
+From 0fa801d52f2e29c87aef757efc690aa5b2474f1b Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Thu, 12 Jan 2023 15:18:19 +0800
+Subject: [PATCH 041/104] mtk: hostapd: Add he_ldpc configuration
+
+---
+ hostapd/config_file.c | 2 ++
+ hostapd/hostapd.conf | 5 +++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 1 +
+ src/ap/ieee802_11_he.c | 7 +++++++
+ src/common/ieee802_11_defs.h | 3 +++
+ 6 files changed, 19 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 7695ab196..dadc8f108 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -3908,6 +3908,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ conf->he_phy_capab.he_su_beamformee = atoi(pos);
+ } else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
+ conf->he_phy_capab.he_mu_beamformer = atoi(pos);
++ } else if (os_strcmp(buf, "he_ldpc") == 0) {
++ conf->he_phy_capab.he_ldpc = atoi(pos);
+ } else if (os_strcmp(buf, "he_bss_color") == 0) {
+ conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ conf->he_op.he_bss_color_disabled = 0;
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 0d10998af..f988b17b2 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -833,6 +833,11 @@ wmm_ac_vo_acm=0
+ # 1 = supported
+ #he_mu_beamformer=1
+
++#he_ldpc: HE LDPC support
++# 0 = not supported
++# 1 = supported (default)
++#he_ldpc=1
++
+ # he_bss_color: BSS color (1-63)
+ #he_bss_color=1
+
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 2420a251e..ba1b2a7a3 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -273,6 +273,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ #endif /* CONFIG_ACS */
+
+ #ifdef CONFIG_IEEE80211AX
++ conf->he_phy_capab.he_ldpc = 1;
+ conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+ HE_OPERATION_RTS_THRESHOLD_OFFSET;
+ /* Set default basic MCS/NSS set to single stream MCS 0-7 */
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 123e12c8f..d995b8d9c 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -985,6 +985,7 @@ struct hostapd_bss_config {
+ * struct he_phy_capabilities_info - HE PHY capabilities
+ */
+ struct he_phy_capabilities_info {
++ bool he_ldpc;
+ bool he_su_beamformer;
+ bool he_su_beamformee;
+ bool he_mu_beamformer;
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index a2deda6c4..3c6ee72fe 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -139,6 +139,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ os_memcpy(&cap->optional[mcs_nss_size],
+ mode->he_capab[opmode].ppet, ppet_size);
+
++ if (hapd->iface->conf->he_phy_capab.he_ldpc)
++ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
++ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++ else
++ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
++ ~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index 7a1da3252..a289c2d87 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -2452,6 +2452,9 @@ struct ieee80211_spatial_reuse {
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G ((u8) BIT(3))
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G ((u8) BIT(4))
+
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX 1
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD ((u8) BIT(5))
++
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7))
+ #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
new file mode 100644
index 0000000..8676424
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
@@ -0,0 +1,26 @@
+From d342772023b344ce09a22eaeff4307e5bfe52d7c Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Tue, 24 Jan 2023 19:06:44 +0800
+Subject: [PATCH 042/104] mtk: hostapd: Add vendor command attribute for RTS BW
+ signaling.
+
+Signed-off-by: himanshu.goyal <himanshu.goyal@mediatek.com>
+---
+ src/common/mtk_vendor.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 7b4d7c11a..ace993bc8 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -172,6 +172,7 @@ enum mtk_vendor_attr_wireless_ctrl {
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
new file mode 100644
index 0000000..304e444
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
@@ -0,0 +1,24 @@
+From 931c93d5d19249a0b4e4efbc5957f537578dfd81 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 13 Feb 2023 11:03:53 +0800
+Subject: [PATCH 043/104] mtk: hostapd: 6G band does not require DFS
+
+---
+ src/ap/dfs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index ece27d070..44f3a2cb1 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1525,6 +1525,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ !iface->conf->ieee80211h) ||
+ !iface->current_mode ||
++ is_6ghz_freq(iface->freq) ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
new file mode 100644
index 0000000..9a8d8ae
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
@@ -0,0 +1,46 @@
+From 79d4323faf5c14f9a4a8e0cf3219582210463206 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 11:01:18 +0800
+Subject: [PATCH 044/104] mtk: hostapd: Fix sending wrong VHT operation IE in
+ CSA while using ZWDFS
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 44f3a2cb1..c703d2fb8 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1129,6 +1129,14 @@ static int
+ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ {
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++ int ret;
++
++ ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
++ iface->radar_background.freq,
++ iface->radar_background.secondary_channel,
++ current_vht_oper_chwidth,
++ iface->radar_background.centr_freq_seg0_idx,
++ iface->radar_background.centr_freq_seg1_idx);
+
+ iface->conf->channel = iface->radar_background.channel;
+ iface->freq = iface->radar_background.freq;
+@@ -1141,11 +1149,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+
+ hostapd_dfs_update_background_chain(iface);
+
+- return hostapd_dfs_request_channel_switch(
+- iface, iface->conf->channel, iface->freq,
+- iface->conf->secondary_channel, current_vht_oper_chwidth,
+- hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+- hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
++ return ret;
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
new file mode 100644
index 0000000..5ef55c0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
@@ -0,0 +1,189 @@
+From cc4ace547c0b0ebef084e9634f729267b6c89f08 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 10:51:47 +0800
+Subject: [PATCH 045/104] mtk: hostapd: Add sta-assisted DFS state update
+ mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 20 ++++++++++++++++++++
+ src/ap/dfs.h | 3 +++
+ src/ap/drv_callbacks.c | 28 ++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.h | 1 +
+ src/drivers/driver.h | 14 ++++++++++++++
+ src/drivers/driver_nl80211_event.c | 6 ++++++
+ src/drivers/nl80211_copy.h | 6 ++++++
+ 7 files changed, 78 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index c703d2fb8..3e036441b 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1522,6 +1522,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ }
+
+
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, u32 state)
++{
++ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
++ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
++ freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
++ (state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
++
++ /* Proceed only if DFS is not offloaded to the driver */
++ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
++ return 0;
++
++ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
++ cf1, cf2, state);
++
++ return 0;
++}
++
++
+ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ {
+ int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index c2556d2d9..25ba29ca1 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, u32 state);
+ int hostapd_is_dfs_required(struct hostapd_iface *iface);
+ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index e8796f709..caa171474 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2226,6 +2226,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ radar->cf1, radar->cf2);
+ }
+
++static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
++ struct dfs_event *radar)
++{
++ wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
++ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++ radar->chan_offset, radar->chan_width,
++ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
++}
++
++static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
++ struct dfs_event *radar)
++{
++ wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
++ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++ radar->chan_offset, radar->chan_width,
++ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
++}
++
+ #endif /* NEED_AP_MLME */
+
+
+@@ -2592,6 +2610,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ break;
++ case EVENT_DFS_STA_CAC_SKIPPED:
++ if (!data)
++ break;
++ hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
++ break;
++ case EVENT_DFS_STA_CAC_EXPIRED:
++ if (!data)
++ break;
++ hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
++ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ /* channel list changed (regulatory?), update channel list */
+ /* TODO: check this. hostapd_get_hw_features() initializes
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index c5bb9abd7..88ad54d6f 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -383,6 +383,7 @@ extern "C" {
+ #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+ #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+ #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
++#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
+
+ #define AP_CSA_FINISHED "AP-CSA-FINISHED "
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 6eeb9c22e..dbd0137ac 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5900,6 +5900,20 @@ enum wpa_event_type {
+ * EVENT_LINK_RECONFIG - Notification that AP links removed
+ */
+ EVENT_LINK_RECONFIG,
++
++ /**
++ * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
++ *
++ * The channel in the notification is now marked as available.
++ */
++ EVENT_DFS_STA_CAC_SKIPPED,
++
++ /**
++ * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
++ *
++ * The channel in the notification is now marked as usable.
++ */
++ EVENT_DFS_STA_CAC_EXPIRED,
+ };
+
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 4a12d749c..7889930a0 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ case NL80211_RADAR_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
++ case NL80211_RADAR_STA_CAC_SKIPPED:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
++ break;
++ case NL80211_RADAR_STA_CAC_EXPIRED:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
++ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+ "received", event_type);
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index dced2c49d..8917d565b 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ * when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ * when STA is disconnected or leaving the channel
+ */
+ enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+@@ -6707,6 +6711,8 @@ enum nl80211_radar_event {
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
++ NL80211_RADAR_STA_CAC_SKIPPED,
++ NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+
+ /**
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
new file mode 100644
index 0000000..7d905c5
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
@@ -0,0 +1,60 @@
+From 21476005ab6b517c7765fec7c3d6c3383c5f44f4 Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Fri, 3 Mar 2023 12:45:42 +0800
+Subject: [PATCH 046/104] mtk: hostapd: Mark DFS channel as available for CSA.
+
+---
+ hostapd/ctrl_iface.c | 10 ++++++++++
+ hostapd/hostapd_cli.c | 2 +-
+ src/ap/ctrl_iface_ap.c | 1 +
+ 3 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 5f6278ecd..052588da4 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2776,6 +2776,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ break;
+ }
+
++ if (settings.freq_params.radar_background) {
++ hostapd_dfs_sta_update_state(iface,
++ settings.freq_params.freq,
++ settings.freq_params.ht_enabled,
++ settings.freq_params.sec_channel_offset,
++ bandwidth, settings.freq_params.center_freq1,
++ settings.freq_params.center_freq2,
++ HOSTAPD_CHAN_DFS_AVAILABLE);
++ }
++
+ if (settings.freq_params.center_freq1)
+ dfs_range += hostapd_is_dfs_overlap(
+ iface, bandwidth, settings.freq_params.center_freq1);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 847f867ab..da9c0f931 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1775,7 +1775,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<addr> = send QoS Map Configure frame" },
+ { "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
+ "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
+- " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
++ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
+ " = initiate channel switch announcement" },
+ { "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
+ "<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index a2f89260c..b92311e32 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1117,6 +1117,7 @@ int hostapd_parse_csa_settings(const char *pos,
+ settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+ settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
++ settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
+ settings->block_tx = !!os_strstr(pos, " blocktx");
+ #undef SET_CSA_SETTING
+ #undef SET_CSA_SETTING_EXT
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
new file mode 100644
index 0000000..9f3c55b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
@@ -0,0 +1,476 @@
+From 50ceb0f2eb9b542ab115ed79fd2d68d46e9e03a0 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Thu, 26 Jan 2023 09:16:00 +0800
+Subject: [PATCH 047/104] mtk: hostapd: Add available color bitmap
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 74 +++++++++++
+ hostapd/hostapd_cli.c | 18 +++
+ src/ap/ap_drv_ops.c | 10 +-
+ src/ap/ap_drv_ops.h | 2 +
+ src/common/mtk_vendor.h | 11 ++
+ src/drivers/driver.h | 8 ++
+ src/drivers/driver_nl80211.c | 198 +++++++++++++++++++++++++++++-
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 9 files changed, 323 insertions(+), 2 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 052588da4..7b83bdd4f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4257,6 +4257,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
+ return ret;
+ }
+
++static int
++hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ int ret;
++ char *pos, *end;
++ int i;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (hapd->iface->conf->he_op.he_bss_color_disabled)
++ ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
++ else
++ ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
++ hapd->iface->conf->he_op.he_bss_color);
++
++ pos += ret;
++
++ return pos - buf;
++}
++
++
++static int
++hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
++ size_t buflen)
++{
++ int ret;
++ char *pos, *end;
++ int i;
++ u64 aval_color_bmp = 0;
++
++ hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
++ hapd->color_collision_bitmap = ~aval_color_bmp;
++
++ pos = buf;
++ end = buf + buflen;
++
++ ret = os_snprintf(buf, buflen,
++ "available color bitmap=0x%llx\n",
++ aval_color_bmp);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
++ int bit = !!((aval_color_bmp >> i) & 1LLU);
++
++ if (i % 8 == 0) {
++ ret = os_snprintf(pos, end - pos, "%2d: ", i);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "%d ", bit);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ if (i % 8 == 7) {
++ ret = os_snprintf(pos, end - pos, "\n");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++ }
++ return pos - buf;
++}
++
+
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+@@ -4878,6 +4948,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
++ reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
++ reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index da9c0f931..865c11432 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1658,6 +1658,20 @@ static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc,
+ #endif /* CONFIG_IEEE80211R_AP */
+
+
++static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
++}
++
++
++static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
++}
++
++
+ #ifdef ANDROID
+ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+@@ -1897,6 +1911,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ { "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL,
+ "= get R0KHs and R1KHs" },
+ #endif /* CONFIG_IEEE80211R_AP */
++ { "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
++ "= get current BSS color" },
++ { "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
++ "= get available BSS color bitmap" },
+ #ifdef ANDROID
+ { "driver", hostapd_cli_cmd_driver, NULL,
+ "<driver sub command> [<hex formatted data>] = send driver command data" },
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 23228a8d2..cabcd47af 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1321,4 +1321,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
+ if (!hapd->driver || !hapd->driver->amsdu_dump)
+ return 0;
+ return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+-}
+\ No newline at end of file
++}
++
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
++{
++ if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
++ hapd->iface->conf->he_op.he_bss_color_disabled)
++ return 0;
++ return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f3a044557..9da2b0049 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -160,6 +160,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
++ u64 *aval_color_bmp);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index ace993bc8..e27fe69b3 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
++ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -256,6 +257,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+ };
+
++enum mtk_vendor_attr_bss_color_ctrl {
++ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
++ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
++};
+
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dbd0137ac..6b6317bfa 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5273,6 +5273,14 @@ struct wpa_driver_ops {
+ */
+ int (*amsdu_ctrl)(void *priv, u8 amsdu);
+ int (*amsdu_dump)(void *priv, u8 *amsdu);
++
++ /**
++ * get_aval_color_bmp - get available BSS color bitmap
++ * @priv: Private driver interface data
++ * @aval_color_bmp: available bss color bitmap
++ *
++ */
++ int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index eca2ff077..4c98e8ab3 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13153,7 +13153,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+ num, MAC2STR(candidate->bssid), buf);
+ }
+
+-
+ static int
+ nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -14608,6 +14607,202 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
++{
++ u64 *aval_color_bmp = arg;
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *nl_vend, *attr;
++
++ static const struct nla_policy
++ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
++ [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
++ };
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!nl_vend)
++ return NL_SKIP;
++
++ nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
++ nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++ *aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
++
++ return 0;
++}
++
++static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *attr;
++ int ret;
++
++ if (!drv->mtk_bss_color_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support BSS COLOR vendor cmd");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
++ return -ENOBUFS;
++
++ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!attr) {
++ nlmsg_free(msg);
++ return -1;
++ }
++
++ nla_nest_end(msg, attr);
++
++ ret = send_and_recv_resp(drv, msg, nl80211_get_aval_color_bmp_handler, aval_color_bmp);
++
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
++static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_wireless_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap wireless control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
++ nla_put_u16(msg, sub_vendor_id, (u16) value);
++ else
++ nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap rfeatures control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
++static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data, *data2;
++ int ret;
++
++ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting ap rfeatures control");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
++ if (!data2)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
++ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
++
++ nla_nest_end(msg, data2);
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -14779,4 +14974,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .ibf_dump = nl80211_ibf_dump,
+ .amsdu_ctrl = nl80211_enable_amsdu,
+ .amsdu_dump = nl80211_dump_amsdu,
++ .get_aval_color_bmp = nl80211_get_aval_color_bmp,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5aa813e26..5b4d45567 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_3wire_vendor_cmd_avail:1;
+ unsigned int mtk_ibf_vendor_cmd_avail:1;
+ unsigned int mtk_wireless_vendor_cmd_avail:1;
++ unsigned int mtk_bss_color_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 474d4e273..a7df2d172 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1156,6 +1156,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
+ drv->mtk_wireless_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
++ drv->mtk_bss_color_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
new file mode 100644
index 0000000..e6f30ed
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
@@ -0,0 +1,210 @@
+From 1b8fc72bfd653ce3ef422e86617b1821948f4805 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Mar 2023 16:08:30 +0800
+Subject: [PATCH 048/104] mtk: hostapd: Fix ZWDFS issue in BW 160
+
+When background radar is enabled and bandwidth is set to 160, AP will
+fail to startup due to the lack of non-DFS channel.
+Under this circumstance, AP should perform CAC itself, and the background
+chain could also perform CAC simultaneously.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 79 insertions(+), 19 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 3e036441b..f5794753e 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -69,15 +69,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ enum dfs_channel_type type)
+ {
++ int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
++
++ if (chan->flag & HOSTAPD_CHAN_DISABLED)
++ return -1;
++
+ if (type == DFS_NO_CAC_YET) {
+ /* 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)
++ if (!(chan->flag & HOSTAPD_CHAN_RADAR))
++ return 0;
++
++ if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
+ return 1;
+- return 0;
++
++ return -1;
+ }
+
+ /*
+@@ -86,16 +93,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ * channel for CSA, unless they are available for immediate use.
+ */
+ if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+- HOSTAPD_CHAN_DFS_AVAILABLE))
+- return 0;
++ (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
++ return -1;
+
+- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+- return 0;
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+- HOSTAPD_CHAN_DFS_UNAVAILABLE))
+- return 0;
++ ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
++ (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
++ return -1;
++
+ return 1;
+ }
+
+@@ -167,7 +172,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ enum dfs_channel_type type)
+ {
+ struct hostapd_channel_data *first_chan, *chan;
+- int i;
++ int i, available = 0, ret = 0;
+ u32 bw = num_chan_to_bw(num_chans);
+
+ if (first_chan_idx + num_chans > mode->num_channels) {
+@@ -203,14 +208,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ return 0;
+ }
+
+- if (!dfs_channel_available(chan, type)) {
++ ret = dfs_channel_available(chan, type);
++ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
+ first_chan->freq + i * 20);
+ return 0;
+ }
++
++ available |= ret;
+ }
+
+- return 1;
++ return available;
+ }
+
+
+@@ -838,8 +846,12 @@ 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 sec = 0, skip_radar = 0;
++ u8 cf1 = 0, cf2 = 0;
++ bool use_radar_background = dfs_use_radar_background(iface);
++ enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+
+ if (is_6ghz_freq(iface->freq))
+ return 1;
+@@ -902,7 +914,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ /* Finally start CAC */
+ hostapd_set_state(iface, HAPD_IFACE_DFS);
+ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+- dfs_use_radar_background(iface) ? " (background)" : "");
++ use_radar_background ? " (background)" : "");
+ 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,
+@@ -912,6 +924,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+ iface->dfs_cac_ms / 1000);
+
++ if (use_radar_background) {
++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
++ /*
++ * AP cannot get any random available channel.
++ * Let AP and dedicated radar chain both perform CAC.
++ */
++ if (!channel)
++ use_radar_background = false;
++ }
++
+ res = hostapd_start_dfs_cac(
+ iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+ iface->conf->ieee80211n, iface->conf->ieee80211ac,
+@@ -920,14 +942,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ hostapd_get_oper_chwidth(iface->conf),
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+- dfs_use_radar_background(iface));
++ use_radar_background);
+
+ if (res) {
+ wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+ return -1;
+ }
+
+- if (dfs_use_radar_background(iface)) {
++ if (use_radar_background) {
+ /* Cache background radar parameters. */
+ iface->radar_background.channel = iface->conf->channel;
+ iface->radar_background.secondary_channel =
+@@ -948,6 +970,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+
+ iface->radar_background.temp_ch = 1;
+ return 1;
++ } else if (dfs_use_radar_background(iface)) {
++ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
++ channel_type = DFS_ANY_CHANNEL;
++
++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
++
++ if (!channel ||
++ (channel->chan == iface->conf->channel &&
++ cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
++ cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
++ wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
++ iface->radar_background.channel = -1;
++ return 0;
++ }
++
++ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++ channel->freq, channel->chan,
++ iface->conf->ieee80211n,
++ iface->conf->ieee80211ac,
++ iface->conf->ieee80211ax,
++ iface->conf->ieee80211be,
++ sec, hostapd_get_oper_chwidth(iface->conf),
++ cf1, cf2, true);
++
++ iface->radar_background.channel = channel->chan;
++ iface->radar_background.freq = channel->freq;
++ iface->radar_background.secondary_channel = sec;
++ iface->radar_background.centr_freq_seg0_idx = cf1;
++ iface->radar_background.centr_freq_seg1_idx = cf2;
+ }
+
+ return 0;
+@@ -1204,6 +1255,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ hostapd_setup_interface_complete(iface, 0);
+ iface->cac_started = 0;
+ }
++
++ /*
++ * When background radar is enabled but the CAC completion
++ * is not received from the background chain.
++ * Then, reset radar background chain.
++ */
++ if (dfs_use_radar_background(iface) &&
++ iface->radar_background.channel == -1)
++ hostapd_dfs_update_background_chain(iface);
+ }
+ } else if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
new file mode 100644
index 0000000..8b21535
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
@@ -0,0 +1,396 @@
+From b9b137827e9c0584682606bd5fe1cd9f50635819 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 17 Mar 2023 16:17:14 +0800
+Subject: [PATCH 049/104] mtk: hostapd: Add vendor for CAPI certification
+ commands
+
+---
+ hostapd/ctrl_iface.c | 99 +++++++++++++++++++++++++++++++
+ src/ap/ap_drv_ops.c | 21 +++++++
+ src/ap/ap_drv_ops.h | 3 +
+ src/common/mtk_vendor.h | 33 +----------
+ src/drivers/driver.h | 22 +++++++
+ src/drivers/driver_nl80211.c | 55 +++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 8 files changed, 206 insertions(+), 31 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 7b83bdd4f..1154a2394 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -71,6 +71,7 @@
+ #include "config_file.h"
+ #include "ctrl_iface.h"
+
++#include "common/mtk_vendor.h"
+
+ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+
+@@ -4327,6 +4328,100 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
+ return pos - buf;
+ }
+
++static int
++hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *value, *config = cmd;
++ enum mtk_vendor_attr_wireless_ctrl sub_cmd;
++
++ pos = os_strchr(config, '=');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if(pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strncmp(config, "fixed_mcs", 9) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
++ else if (os_strncmp(config, "ofdma", 5) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
++ else if (os_strncmp(config, "ppdu_type", 9) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
++ else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
++ else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
++ else if (os_strncmp(config, "mimo", 4) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
++ else if (os_strncmp(config, "cert", 4) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
++ else if (os_strncmp(config, "amsdu", 5) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
++ else if (os_strncmp(config, "rts_sigta", 9) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
++ else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for ap_wireless", config);
++ return -1;
++ }
++
++ if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
++ return -1;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *pos, *value, *type, *config = cmd;
++ enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
++
++ pos = os_strchr(config, '=');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if(pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strncmp(config, "he_gi", 5) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
++ else if (os_strncmp(config, "he_ltf", 6) == 0)
++ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
++ else if (os_strncmp(config, "trig_type", 9) == 0) {
++ pos = os_strchr(value, ',');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++ if(pos == NULL)
++ return -1;
++ type = pos;
++ goto trigtype;
++ } else if (os_strcmp(config, "ack_policy") == 0)
++ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
++ else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for ap_rfeatures", config);
++ return -1;
++ }
++
++ if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
++ return -1;
++ goto exit;
++
++trigtype:
++ if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
++ return -1;
++
++exit:
++ return os_snprintf(buf, buflen, "OK\n");
++}
+
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+@@ -4952,6 +5047,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
+ reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
++ reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
++ } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
++ reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index cabcd47af..06d71f309 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1330,3 +1330,24 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ return 0;
+ return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
+ }
++
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++ if (!hapd->driver || !hapd->driver->ap_wireless)
++ return 0;
++ return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++ if (!hapd->driver || !hapd->driver->ap_rfeatures)
++ return 0;
++ return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
++{
++ if (!hapd->driver || !hapd->driver->ap_trigtype)
++ return 0;
++ return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 9da2b0049..c58930217 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -162,6 +162,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
+ u64 *aval_color_bmp);
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index e27fe69b3..0b23c76ad 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+
+-
+-static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
+- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_3wire_ctrl {
+ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
+
+@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
+ };
+
+-static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
+- [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_csi_ctrl {
+ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+
+@@ -172,7 +157,7 @@ enum mtk_vendor_attr_wireless_ctrl {
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
+ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+
+ /* keep last */
+@@ -192,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
+ };
+
+-static const struct nla_policy
+-wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
+- [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_rfeature_ctrl {
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+
+@@ -206,6 +186,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
+ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+@@ -247,16 +228,6 @@ enum mtk_vendor_attr_ibf_dump {
+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
+ };
+
+-static struct nla_policy
+-ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+- [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
+-};
+-
+-static struct nla_policy
+-ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+- [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_bss_color_ctrl {
+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 6b6317bfa..a25601c91 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5281,6 +5281,28 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
++
++ /**
++ * ap_wireless - set wireless command
++ * @priv: Private driver interface data
++ * @value: value
++ */
++ int (*ap_wireless)(void *priv, u8 mode, int value);
++
++ /**
++ * ap_rfeatures - set ap rf features command
++ * @priv: Private driver interface data
++ * @value: value
++ */
++ int (*ap_rfeatures)(void *priv, u8 mode, int value);
++
++ /**
++ * ap_trigtype - set trigger type
++ * @priv: Private driver interface data
++ * @enable: enable or disable
++ * @type: trigger type
++ */
++ int (*ap_trigtype)(void *priv, u8 enable, u8 type);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 4c98e8ab3..86e5844cd 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -91,6 +91,58 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
+ wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
+ }
+
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -14975,4 +15027,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .amsdu_ctrl = nl80211_enable_amsdu,
+ .amsdu_dump = nl80211_dump_amsdu,
+ .get_aval_color_bmp = nl80211_get_aval_color_bmp,
++ .ap_wireless = nl80211_ap_wireless,
++ .ap_rfeatures = nl80211_ap_rfeatures,
++ .ap_trigtype = nl80211_ap_trigtype,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5b4d45567..046991a3d 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_ibf_vendor_cmd_avail:1;
+ unsigned int mtk_wireless_vendor_cmd_avail:1;
+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
++ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index a7df2d172..6498eba6d 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1159,6 +1159,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ drv->mtk_bss_color_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
++ drv->mtk_rfeatures_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
new file mode 100644
index 0000000..aecb14a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
@@ -0,0 +1,506 @@
+From 994774c363a07fa90a7a21974b7b4a371b235673 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:18:48 +0800
+Subject: [PATCH 050/104] mtk: hostapd: Air Monitor support in hostapd by
+ vendor
+
+Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 113 +++++++++++++++++++
+ hostapd/hostapd_cli.c | 15 +++
+ src/ap/ap_drv_ops.c | 14 +++
+ src/ap/ap_drv_ops.h | 3 +
+ src/common/mtk_vendor.h | 8 ++
+ src/drivers/driver.h | 16 +++
+ src/drivers/driver_nl80211.c | 179 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 2 +
+ 9 files changed, 351 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 1154a2394..56722384b 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4370,6 +4370,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
+
+ if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
+ return -1;
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp, sta_mac[ETH_ALEN] = {0};
++ int amnt_idx = 0;
++
++ tmp = strtok_r(cmd, " ", &cmd);
++
++ if (!tmp) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ amnt_idx = strtol(tmp, &tmp, 10);
++
++ if (amnt_idx < 0 || amnt_idx > 15) {
++ wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
++ return -1;
++ }
++
++ if (!cmd) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ if (hwaddr_aton(cmd, sta_mac) < 0) {
++ wpa_printf(MSG_ERROR, "station mac is not right.\n");
++ return -1;
++ }
++
++ if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
++ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++ return -1;
++ }
+
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+@@ -4423,6 +4461,75 @@ exit:
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
++static int
++hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp;
++ int amnt_idx = 0, ret = 0;
++ struct amnt_resp_data *resp_buf;
++ char *pos, *end;
++ struct amnt_data *res;
++
++ pos = buf;
++ end = buf + buflen;
++
++ tmp = strtok_r(cmd, " ", &cmd);
++
++ if (!tmp) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ amnt_idx = strtoul(tmp, &tmp, 0);
++
++ if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
++ wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
++ return -1;
++ }
++
++ if (amnt_idx == 0xff)
++ resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
++ * sizeof(struct amnt_data) + 1);
++ else
++ resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
++
++ if (resp_buf == NULL) {
++ wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++ return -1;
++ }
++
++ if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
++ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++ os_free(resp_buf);
++ return -1;
++ }
++
++ for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
++ res = &resp_buf->resp_data[i];
++ ret = os_snprintf(pos, end - pos,
++ "[hostapd_cli] amnt_idx: %d, addr="MACSTR
++ ", rssi=%d/%d/%d/%d, last_seen=%u\n",
++ res->idx,
++ MAC2STR(res->addr), res->rssi[0],
++ res->rssi[1], res->rssi[2],
++ res->rssi[3], res->last_seen);
++ if (os_snprintf_error(end - pos, ret)) {
++ os_free(resp_buf);
++ return 0;
++ }
++ pos = pos + ret;
++ }
++
++ os_free(resp_buf);
++
++ if (pos == buf)
++ return os_snprintf(buf, buflen, "Index %d is not monitored\n",
++ amnt_idx);
++ else
++ return pos - buf;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5051,6 +5158,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
+ } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
+ reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
++ } else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
++ reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
++ reply, reply_size);
++ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
++ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 865c11432..12c580455 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1693,6 +1693,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
+ return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
+ }
+
++static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
++}
+
+ struct hostapd_cli_cmd {
+ const char *cmd;
+@@ -1925,6 +1936,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = show iBF state (enabled/disabled)"},
+ { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
+ " = show AMSDU state"},
++ { "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
++ " = Set Station index and mac to monitor"},
++ { "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
++ " = Dump RSSI of monitoring Station"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 06d71f309..df652b12f 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1351,3 +1351,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
+ return 0;
+ return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
+ }
++
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++ if (!hapd->driver || !hapd->driver->amnt_set)
++ return 0;
++ return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
++}
++
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
++{
++ if (!hapd->driver || !hapd->driver->amnt_dump)
++ return 0;
++ return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index c58930217..4805a2e84 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -166,6 +166,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++
+ #include "drivers/driver.h"
+
+ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 0b23c76ad..dd1ca2164 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -258,10 +258,18 @@ struct csi_data {
+ u16 rx_idx;
+ };
+
++#define AIR_MONITOR_MAX_ENTRY 16
++
+ struct amnt_data {
+ u8 idx;
+ u8 addr[ETH_ALEN];
+ s8 rssi[4];
+ u32 last_seen;
+ };
++
++struct amnt_resp_data {
++ u8 sta_num;
++ struct amnt_data resp_data[0];
++};
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index a25601c91..dd9c33201 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5303,6 +5303,22 @@ struct wpa_driver_ops {
+ * @type: trigger type
+ */
+ int (*ap_trigtype)(void *priv, u8 enable, u8 type);
++
++ /**
++ * amnt_set - add/delete station from monitoring
++ * @priv: Private driver interface data
++ * @amnt_idx: Monitor Index
++ * @amnt_sta_mac: station mac address
++ */
++ int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
++
++ /**
++ * amnt_dump - Dump particular/ all station
++ * @priv: Private driver interface data
++ * @amnt_idx: Monitor Index
++ * @amnt_dump_buf: Buffer to print
++ */
++ int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 86e5844cd..a2a6807f4 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -143,6 +143,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
+ };
+
++static struct nla_policy
++amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
++ [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy
++amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
++ [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
++ [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -14855,6 +14868,170 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int
++nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ void *tb1;
++ int ret;
++
++ if (!drv->mtk_amnt_vendor_cmd_avail) {
++ wpa_printf(MSG_ERROR,
++ "nl80211: Driver does not support air monitor");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
++ if (!tb1)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
++
++ nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
++
++ nla_nest_end(msg, tb1);
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
++ ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++
++}
++
++static int
++mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
++{
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
++ struct nlattr *attr, *cur, *data;
++ struct amnt_data *res;
++ int len = 0, rem;
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ attr = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!attr)
++ return NL_SKIP;
++
++ nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++ attr, amnt_ctrl_policy);
++
++ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
++ return NL_SKIP;
++
++ nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
++
++ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
++ return NL_SKIP;
++
++ len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
++ if (!len)
++ return 0;
++
++ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
++ return NL_SKIP;
++
++ data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
++
++ nla_for_each_nested(cur, data, rem) {
++ if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
++ return NL_SKIP;
++ res = (struct amnt_data *) nla_data(cur);
++ wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
++ "addr="MACSTR", "
++ "rssi=%d/%d/%d/%d, last_seen=%u\n",
++ res->idx,
++ MAC2STR(res->addr),
++ res->rssi[0], res->rssi[1], res->rssi[2],
++ res->rssi[3], res->last_seen);
++ os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
++ sizeof(struct amnt_data));
++ amnt_dump->sta_num++;
++ }
++ return 0;
++}
++
++static int
++nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ void *tb1;
++ int ret;
++
++ if (!drv->mtk_amnt_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support air monitor");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
++ | NLA_F_NESTED);
++ if (!tb1)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
++
++ nla_nest_end(msg, tb1);
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, mt76_amnt_dump_cb, dump_buf);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
++ , ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15030,4 +15207,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .ap_wireless = nl80211_ap_wireless,
+ .ap_rfeatures = nl80211_ap_rfeatures,
+ .ap_trigtype = nl80211_ap_trigtype,
++ .amnt_set = nl80211_amnt_set,
++ .amnt_dump = nl80211_amnt_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 046991a3d..adc1b9bf7 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_wireless_vendor_cmd_avail:1;
+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
++ unsigned int mtk_amnt_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 6498eba6d..38e83e42b 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1158,6 +1158,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ break;
+ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ drv->mtk_bss_color_vendor_cmd_avail = 1;
++ case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
++ drv->mtk_amnt_vendor_cmd_avail = 1;
+ break;
+ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ drv->mtk_rfeatures_vendor_cmd_avail = 1;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
new file mode 100644
index 0000000..85e1f7a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
@@ -0,0 +1,228 @@
+From ccef6202191f2a17f84f021e6e2ade206b8c9cc1 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:24:19 +0800
+Subject: [PATCH 051/104] mtk: hostapd: Add muru user number debug command
+
+---
+ hostapd/ctrl_iface.c | 13 ++++++++++++-
+ src/ap/ap_drv_ops.c | 4 ++--
+ src/ap/ap_drv_ops.h | 2 +-
+ src/ap/hostapd.c | 3 ++-
+ src/common/mtk_vendor.h | 7 +++++++
+ src/drivers/driver.h | 4 ++--
+ src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
+ 7 files changed, 55 insertions(+), 15 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 56722384b..88475b321 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3673,6 +3673,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+ {
+ char *pos, *config, *value;
++ u8 mode;
++
+ config = cmd;
+ pos = os_strchr(config, ' ');
+ if (pos == NULL)
+@@ -4065,6 +4067,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+ {
+ char *pos, *config, *value;
++ u8 mode;
++
+ config = cmd;
+ pos = os_strchr(config, ' ');
+ if (pos == NULL)
+@@ -4082,13 +4086,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ return -1;
+ }
+ hapd->iconf->mu_onoff = (u8) mu;
++ mode = MU_CTRL_ONOFF;
++ } else if (os_strcmp(config, "ul_user_cnt") == 0) {
++ mode = MU_CTRL_UL_USER_CNT;
++ wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
++ } else if (os_strcmp(config, "dl_user_cnt") == 0) {
++ mode = MU_CTRL_DL_USER_CNT;
++ wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Unsupported parameter %s for SET_MU", config);
+ return -1;
+ }
+
+- if(hostapd_drv_mu_ctrl(hapd) == 0) {
++ if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+ return os_snprintf(buf, buflen, "OK\n");
+ } else {
+ return -1;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index df652b12f..8878db380 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
+ {
+ if (!hapd->driver || !hapd->driver->mu_ctrl)
+ return 0;
+- return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
+ }
+
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 4805a2e84..f77d07da0 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index a5b683676..5fd46d53d 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -58,6 +58,7 @@
+ #include "wpa_auth_kay.h"
+ #include "hw_features.h"
+
++#include "common/mtk_vendor.h"
+
+ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
+ #ifdef CONFIG_WEP
+@@ -2699,7 +2700,7 @@ dfs_offload:
+ if (hostapd_drv_configure_edcca_threshold(hapd,
+ hapd->iconf->edcca_threshold) < 0)
+ goto fail;
+- if (hostapd_drv_mu_ctrl(hapd) < 0)
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
+ goto fail;
+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index dd1ca2164..99371bf73 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -199,6 +199,8 @@ enum mtk_vendor_attr_mu_ctrl {
+
+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
++ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -272,4 +274,9 @@ struct amnt_resp_data {
+ struct amnt_data resp_data[0];
+ };
+
++enum {
++ MU_CTRL_ONOFF,
++ MU_CTRL_DL_USER_CNT,
++ MU_CTRL_UL_USER_CNT,
++};
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dd9c33201..3be4562e7 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5236,11 +5236,11 @@ struct wpa_driver_ops {
+ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+
+ /**
+- * mu_ctrl - ctrl on off for UL/DL MURU
++ * mu_ctrl - ctrl for UL/DL MURU
+ * @priv: Private driver interface data
+ *
+ */
+- int (*mu_ctrl)(void *priv, u8 mu_onoff);
++ int (*mu_ctrl)(void *priv, u8 mode, u8 val);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index a2a6807f4..035a477e2 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13982,13 +13982,13 @@ fail:
+
+
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
+- int ret;
++ int ret = -ENOBUFS;
+
+ if (!drv->mtk_mu_vendor_cmd_avail) {
+ wpa_printf(MSG_INFO,
+@@ -13999,17 +13999,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
+- !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
+- nlmsg_free(msg);
+- return -ENOBUFS;
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
++ goto fail;
++
++ switch (mode) {
++ case MU_CTRL_ONOFF:
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
++ goto fail;
++ break;
++ case MU_CTRL_UL_USER_CNT:
++ case MU_CTRL_DL_USER_CNT:
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
++ goto fail;
++ break;
++ default:
++ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++ ret = -EINVAL;
++ goto fail;
+ }
++
+ nla_nest_end(msg, data);
++
+ ret = send_and_recv_cmd(drv, msg);
+ if(ret){
+- wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++ wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+ }
+ return ret;
++
++fail:
++ nl80211_nlmsg_clear(msg);
++ nlmsg_free(msg);
++ return ret;
+ }
+
+
+@@ -15178,7 +15199,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .update_connect_params = nl80211_update_connection_params,
+ .send_external_auth_status = nl80211_send_external_auth_status,
+ .set_4addr_mode = nl80211_set_4addr_mode,
+- .mu_ctrl = nl80211_mu_onoff,
++ .mu_ctrl = nl80211_mu_ctrl,
+ .mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ .dpp_listen = nl80211_dpp_listen,
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
new file mode 100644
index 0000000..757c28b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
@@ -0,0 +1,647 @@
+From e64468ed944634f41f7f305e7460b6a696b00127 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Sat, 3 Jun 2023 17:12:15 +0800
+Subject: [PATCH 052/104] mtk: hostapd: add connac3 PHY MURU manual mode config
+ support
+
+This commit supports read the following two formats to set MU/RU manual
+mode:
+1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
+2. hostapd_cli -i <intf> set_mu <field> <value>
+
+For the <field>, we support the following field:
+1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
+2. ul_comm_bw/dl_comm_bw: set the bandwith
+3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
+allocate idx
+4. ul_user_mcs/dl_user_mcs: set the mcs for each user
+5. ul_user_ssAlloc_raru: set the number of ss for each user
+6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
+7. dl_comm_toneplan: fix ru toneplan allocation
+8. dl_comm_ack_policy: fix station ack policy
+9. update : trigger driver to send mcu command to set muru manual mode.
+
+For the value of each field, please check wiki to learn the details:
+https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
+
+For the fields that mt76 support to use, we will update in this wiki:
+https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
+
+Please noted that this commit is only for connac 3 gen chips. If this
+feature is to be used in other generations, the following actions must
+be taken:
+1. Different data structue needs to be defined for different
+generations, e.g. connac4_muru_comm, connac4_muru_dl.
+2. hostapd_ctrl_iface_set_mu() shall be modified.
+3. A new code level configuration shall be defined to differentiate the
+code flow that different generations will go through.
+---
+ hostapd/ctrl_iface.c | 237 +++++++++++++++++++++++++++++++----
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 4 +-
+ src/ap/ap_drv_ops.h | 2 +-
+ src/ap/hostapd.c | 2 +-
+ src/common/mtk_vendor.h | 166 +++++++++++++++++++++++-
+ src/drivers/driver.h | 2 +-
+ src/drivers/driver_nl80211.c | 21 ++--
+ 8 files changed, 391 insertions(+), 44 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 88475b321..ed383df7d 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3775,7 +3775,6 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
+ }
+ }
+
+-
+ #ifdef CONFIG_NAN_USD
+
+ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
+@@ -4062,21 +4061,61 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+
+
++static int
++hostapd_parse_argument_helper(char *value, u16 **ptr_input)
++{
++#define MAX_MU_CTRL_NUM 17
++ u16 *input;
++ char *endptr;
++ int cnt = 0;
++
++ input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
++ if (input == NULL) {
++ wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
++ return -1;
++ }
++ while (value) {
++ u8 val = strtol(value, &endptr, 10);
++
++ if (value != endptr) {
++ input[cnt++] = val;
++ value = os_strchr(endptr, ':');
++ if (value)
++ value++;
++ } else {
++ break;
++ }
++ }
++
++ *ptr_input = input;
++ return cnt;
++}
++
++#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do { \
++ if ((le_to_host32(_val) & (_mask)) != _mask) { \
++ wpa_printf(MSG_ERROR, "Set %s first\n", #_mask); \
++ goto fail; \
++ } \
++ } while(0)
++
+ static int
+ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+- char *buf, size_t buflen)
++ char *buf, size_t buflen)
+ {
+ char *pos, *config, *value;
+- u8 mode;
++ u8 i;
++ int cnt = 0, ret;
++ u16 *val;
++ struct connac3_muru *muru;
++ struct connac3_muru_dl *dl;
++ struct connac3_muru_ul *ul;
++ struct connac3_muru_comm *comm;
+
+ config = cmd;
+ pos = os_strchr(config, ' ');
+- if (pos == NULL)
+- return -1;
+- *pos++ = '\0';
++ if (pos != NULL)
++ *pos++ = '\0';
+
+- if(pos == NULL)
+- return -1;
+ value = pos;
+
+ if (os_strcmp(config, "onoff") == 0) {
+@@ -4086,24 +4125,167 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ return -1;
+ }
+ hapd->iconf->mu_onoff = (u8) mu;
+- mode = MU_CTRL_ONOFF;
+- } else if (os_strcmp(config, "ul_user_cnt") == 0) {
+- mode = MU_CTRL_UL_USER_CNT;
+- wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
+- } else if (os_strcmp(config, "dl_user_cnt") == 0) {
+- mode = MU_CTRL_DL_USER_CNT;
+- wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+- } else {
+- wpa_printf(MSG_ERROR,
+- "Unsupported parameter %s for SET_MU", config);
+- return -1;
++
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
++ return os_snprintf(buf, buflen, "OK\n");
++ else
++ goto fail;
+ }
+
+- if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+- return os_snprintf(buf, buflen, "OK\n");
++ if (hapd->iconf->muru_config == NULL)
++ hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
++
++ muru = hapd->iconf->muru_config;
++ dl = &muru->dl;
++ ul = &muru->ul;
++ comm = &muru->comm;
++
++ if (os_strncmp(config, "update", 6) == 0) {
++ ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
++
++ os_free(hapd->iconf->muru_config);
++ hapd->iconf->muru_config = NULL;
++
++ if (ret)
++ goto fail;
++ } else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
++ ul->user_num = (u8)atoi(value);
++ comm->ppdu_format |= MURU_PPDU_HE_TRIG;
++ comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
++ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
++ } else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
++ dl->user_num = (u8)atoi(value);
++ comm->ppdu_format |= MURU_PPDU_HE_MU;
++ comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
++ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
++ } else if (os_strcmp(config, "dl_comm_bw") == 0) {
++ dl->bw = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
++ } else if (os_strcmp(config, "ul_comm_bw") == 0) {
++ ul->bw = (u8)atoi(value);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
++ } else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != (dl->user_num * 2))
++ goto para_fail;
++ for (i = 0; i < dl->user_num; i++) {
++ dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++ dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++ dl->usr[i].ru_idx = val[(2 * i) + 1];
++ }
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
++ } else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != (ul->user_num * 2))
++ goto para_fail;
++ for (i = 0; i < ul->user_num; i++) {
++ ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++ ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++ ul->usr[i].ru_idx = val[(2 * i) + 1];
++ }
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
++ } else if (os_strcmp(config, "dl_user_mcs") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != dl->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ dl->usr[i].mcs = (u8) val[i];
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
++ } else if (os_strcmp(config, "ul_user_mcs") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != ul->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ ul->usr[i].mcs = (u8) val[i];
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
++ } else if (os_strcmp(config, "dl_user_cod") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != dl->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ dl->usr[i].ldpc = (u8) val[i];
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
++ } else if (os_strcmp(config, "ul_user_cod") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != ul->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ ul->usr[i].ldpc = (u8) val[i];
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
++ } else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt != ul->user_num)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ ul->usr[i].nss = (u8) val[i];
++ os_free(val);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
++ } else if (os_strcmp(config, "dl_comm_gi") == 0) {
++ dl->gi = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
++ } else if (os_strcmp(config, "dl_comm_ltf") == 0) {
++ dl->ltf = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
++ } else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
++ ul->gi_ltf = (u8)atoi(value);
++ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
++ } else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
++ dl->ack_policy = (u8)atoi(value);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
++ } else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
++ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ i = pow(2, dl->bw);
++ if (cnt != i)
++ goto para_fail;
++ for (i = 0; i < cnt; i++)
++ dl->ru[i] = host_to_le16(val[i]);
++ os_free(val);
++ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
+ } else {
+- return -1;
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for SET_MU", config);
++ goto fail;
+ }
++
++ return os_snprintf(buf, buflen, "OK\n");
++
++para_fail:
++ os_free(val);
++ wpa_printf(MSG_ERROR, "Incorrect input number\n");
++fail:
++ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+
+@@ -5148,8 +5330,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+- reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
+- reply_size);
++ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+@@ -5175,6 +5356,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
++ // Replace first ':' with a single space ' '
++ char *pos = buf + 23;
++
++ pos = os_strchr(pos, ':');
++ if (pos)
++ *pos = ' ';
++ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d995b8d9c..3827a8fc8 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1291,6 +1291,7 @@ struct hostapd_config {
+ u8 ibf_enable;
+ u8 dfs_detect_mode;
+ u8 amsdu;
++ void *muru_config;
+ };
+
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 8878db380..116bc4ceb 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
+ {
+ if (!hapd->driver || !hapd->driver->mu_ctrl)
+ return 0;
+- return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
++ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
+ }
+
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f77d07da0..84b41881a 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 5fd46d53d..d1ee0764b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2700,7 +2700,7 @@ dfs_offload:
+ if (hostapd_drv_configure_edcca_threshold(hapd,
+ hapd->iconf->edcca_threshold) < 0)
+ goto fail;
+- if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
+ goto fail;
+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 99371bf73..e140de60b 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -199,8 +199,11 @@ enum mtk_vendor_attr_mu_ctrl {
+
+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
+- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
+- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
++ /**
++ * The above attrs are also used by connac 2. It is best not to modify the
++ * above data structure.
++ */
++ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -275,8 +278,163 @@ struct amnt_resp_data {
+ };
+
+ enum {
++ MU_CTRL_UPDATE,
+ MU_CTRL_ONOFF,
+- MU_CTRL_DL_USER_CNT,
+- MU_CTRL_UL_USER_CNT,
+ };
++
++struct connac3_muru_comm {
++ u8 pda_pol;
++ u8 band;
++ u8 spe_idx;
++ u8 proc_type;
++
++ le16 mlo_ctrl;
++ u8 sch_type;
++ u8 ppdu_format;
++ u8 ac;
++ u8 _rsv[3];
++};
++
++struct connac3_muru_dl {
++ u8 user_num;
++ u8 tx_mode;
++ u8 bw;
++ u8 gi;
++
++ u8 ltf;
++ u8 mcs;
++ u8 dcm;
++ u8 cmprs;
++
++ le16 ru[16];
++
++ u8 c26[2];
++ u8 ack_policy;
++ u8 tx_power;
++
++ le16 mu_ppdu_duration;
++ u8 agc_disp_order;
++ u8 _rsv1;
++
++ u8 agc_disp_pol;
++ u8 agc_disp_ratio;
++ le16 agc_disp_linkMFG;
++
++ le16 prmbl_punc_bmp;
++ u8 _rsv2[2];
++
++ struct {
++ le16 wlan_idx;
++ u8 ru_alloc_seg;
++ u8 ru_idx;
++ u8 ldpc;
++ u8 nss;
++ u8 mcs;
++ u8 mu_group_idx;
++ u8 vht_groud_id;
++ u8 vht_up;
++ u8 he_start_stream;
++ u8 he_mu_spatial;
++ le16 tx_power_alpha;
++ u8 ack_policy;
++ u8 ru_allo_ps160;
++ } usr[16];
++};
++
++struct connac3_muru_ul {
++ u8 user_num;
++ u8 tx_mode;
++
++ u8 ba_type;
++ u8 _rsv;
++
++ u8 bw;
++ u8 gi_ltf;
++ le16 ul_len;
++
++ le16 trig_cnt;
++ u8 pad;
++ u8 trig_type;
++
++ le16 trig_intv;
++ u8 trig_ta[ETH_ALEN];
++ le16 ul_ru[16];
++
++ u8 c26[2];
++ le16 agc_disp_linkMFG;
++
++ u8 agc_disp_mu_len;
++ u8 agc_disp_pol;
++ u8 agc_disp_ratio;
++ u8 agc_disp_pu_idx;
++
++ struct {
++ le16 wlan_idx;
++ u8 ru_alloc_seg;
++ u8 ru_idx;
++ u8 ldpc;
++ u8 nss;
++ u8 mcs;
++ u8 target_rssi;
++ le32 trig_pkt_size;
++ u8 ru_allo_ps160;
++ u8 _rsv2[3];
++ } usr[16];
++};
++
++struct connac3_muru_dbg {
++ /* HE TB RX Debug */
++ le32 rx_hetb_nonsf_en_bitmap;
++ le32 rx_hetb_cfg[2];
++};
++
++struct connac3_muru {
++ le32 cfg_comm;
++ le32 cfg_dl;
++ le32 cfg_ul;
++ le32 cfg_dbg;
++
++ struct connac3_muru_comm comm;
++ struct connac3_muru_dl dl;
++ struct connac3_muru_ul ul;
++ struct connac3_muru_dbg dbg;
++};
++
++#define MURU_OFDMA_SCH_TYPE_DL BIT(0)
++#define MURU_OFDMA_SCH_TYPE_UL BIT(1)
++#define MURU_PPDU_HE_TRIG BIT(2)
++#define MURU_PPDU_HE_MU BIT(3)
++
++/* Common Config */
++#define MURU_COMM_PPDU_FMT BIT(0)
++#define MURU_COMM_BAND BIT(2)
++#define MURU_COMM_WMM BIT(3)
++#define MURU_COMM_SPE_IDX BIT(4)
++#define MURU_COMM_SET (MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
++ MURU_COMM_WMM | MURU_COMM_SPE_IDX)
++
++/* DL Common config */
++#define MURU_FIXED_DL_BW BIT(0)
++#define MURU_FIXED_DL_GI BIT(1)
++#define MURU_FIXED_DL_TONE_PLAN BIT(3)
++#define MURU_FIXED_DL_TOTAL_USER_CNT BIT(4)
++#define MURU_FIXED_DL_LTF BIT(5)
++#define MURU_FIXED_DL_ACK_PLY BIT(9)
++
++/* DL Per User Config */
++#define MURU_FIXED_USER_DL_COD BIT(17)
++#define MURU_FIXED_USER_DL_MCS BIT(18)
++#define MURU_FIXED_USER_DL_RU_ALLOC BIT(20)
++
++/* UL Common Config */
++#define MURU_FIXED_UL_TOTAL_USER_CNT BIT(4)
++#define MURU_FIXED_UL_BW BIT(5)
++#define MURU_FIXED_UL_GILTF BIT(6)
++
++/* UL Per User Config */
++#define MURU_FIXED_USER_UL_COD BIT(18)
++#define MURU_FIXED_USER_UL_MCS BIT(19)
++#define MURU_FIXED_USER_UL_NSS BIT(20)
++#define MURU_FIXED_USER_UL_RU_ALLOC BIT(21)
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 3be4562e7..1c0c38e24 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5240,7 +5240,7 @@ struct wpa_driver_ops {
+ * @priv: Private driver interface data
+ *
+ */
+- int (*mu_ctrl)(void *priv, u8 mode, u8 val);
++ int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 035a477e2..aeb755b11 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13982,12 +13982,13 @@ fail:
+
+
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
++static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
++ struct hostapd_config *cfg = config;
+ int ret = -ENOBUFS;
+
+ if (!drv->mtk_mu_vendor_cmd_avail) {
+@@ -14004,17 +14005,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+
+ switch (mode) {
+ case MU_CTRL_ONOFF:
+- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
+- goto fail;
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++ goto fail;
+ break;
+- case MU_CTRL_UL_USER_CNT:
+- case MU_CTRL_DL_USER_CNT:
+- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
+- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
+- goto fail;
++ case MU_CTRL_UPDATE:
++ if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++ sizeof(struct connac3_muru), cfg->muru_config))
++ goto fail;
+ break;
+ default:
+- wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
+ ret = -EINVAL;
+ goto fail;
+ }
+@@ -14022,9 +14022,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ nla_nest_end(msg, data);
+
+ ret = send_and_recv_cmd(drv, msg);
+- if(ret){
++ if (ret)
+ wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+- }
+ return ret;
+
+ fail:
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
new file mode 100644
index 0000000..c9fc8b7
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
@@ -0,0 +1,52 @@
+From 2dd00ec0a8231bd8c6893f9517875ad94022f9b2 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 9 Jun 2023 09:03:05 +0800
+Subject: [PATCH 053/104] mtk: hostapd: Add HE capabilities check
+
+Add HE capabilities check.
+Since "HE capabilities" check has been removed by driver,
+add the support for "HE capabilities" check in hostapd.
+---
+ src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 672e43a10..a35c5974a 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -709,6 +709,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+ #ifdef CONFIG_IEEE80211AX
+ static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+ {
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
++ struct hostapd_config *conf = iface->conf;
++
++#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap) \
++ do { \
++ if (cfg_cap && !(hw_cap[phy_idx] & field)) { \
++ wpa_printf(MSG_ERROR, "Driver does not support configured" \
++ " HE capability [%s]", #field); \
++ return 0; \
++ } \
++ } while (0)
++
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
++ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
++ conf->he_phy_capab.he_ldpc);
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
++ HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
++ conf->he_phy_capab.he_su_beamformer);
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
++ HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
++ conf->he_phy_capab.he_su_beamformee);
++ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
++ HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
++ conf->he_phy_capab.he_mu_beamformer);
++
+ return 1;
+ }
+ #endif /* CONFIG_IEEE80211AX */
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
new file mode 100644
index 0000000..e14b996
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
@@ -0,0 +1,64 @@
+From cc4db7ad22853f72f43128f96e5d4edcb7c245a1 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:44:15 +0800
+Subject: [PATCH 054/104] mtk: hostapd: Fix background channel overlapping
+ operating channel issue
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index f5794753e..8be953287 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -814,6 +814,20 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+ }
+
+
++static void dfs_check_background_overlapped(struct hostapd_iface *iface)
++{
++ int width = hostapd_get_oper_chwidth(iface->conf);
++
++ if (!dfs_use_radar_background(iface))
++ return;
++
++ if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
++ width, iface->radar_background.centr_freq_seg0_idx,
++ iface->radar_background.centr_freq_seg1_idx))
++ iface->radar_background.channel = -1;
++}
++
++
+ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+ {
+@@ -1141,6 +1155,8 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
+ &oper_centr_freq_seg1_idx,
+ &channel_type);
+ if (!channel ||
++ channel->chan == iface->conf->channel ||
++ channel->chan == iface->radar_background.channel ||
+ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ channel->freq, channel->chan,
+ iface->conf->ieee80211n,
+@@ -1375,6 +1391,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+ oper_centr_freq_seg1_idx);
+ err = 0;
++ dfs_check_background_overlapped(iface);
+
+ hostapd_setup_interface_complete(iface, err);
+ return err;
+@@ -1502,6 +1519,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ hostapd_set_oper_centr_freq_seg1_idx(
+ iface->conf, oper_centr_freq_seg1_idx);
+
++ dfs_check_background_overlapped(iface);
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
new file mode 100644
index 0000000..4f42bfa
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
@@ -0,0 +1,31 @@
+From 0232fabaf99094f319d03ab818cb0c847b6727c2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:47:20 +0800
+Subject: [PATCH 055/104] mtk: hostapd: Fix hostapd_dfs_start_cac log
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 8be953287..7adaf81ac 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1664,9 +1664,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ /* TODO: How to check CAC time for ETSI weather channels? */
+ iface->dfs_cac_ms = 60000;
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+- "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
++ "freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
+ "seg1=%d cac_time=%ds%s",
+- freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
++ freq, (freq - 5000) / 5, chan_offset,
++ channel_width_to_string(chan_width),
++ (cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
+ iface->dfs_cac_ms / 1000,
+ hostapd_dfs_is_background_event(iface, freq) ?
+ " (background)" : "");
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
new file mode 100644
index 0000000..6943e83
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
@@ -0,0 +1,62 @@
+From 942808028d207776f1a4dbe678166282fb272b37 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 13 Jul 2023 13:14:26 +0800
+Subject: [PATCH 056/104] mtk: hostapd: Check the bridge after ioctl
+ SIOCBRADDIF failed
+
+If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
+already be bridged by others, and linux_br_add_if should not indicate an
+error in the case.
+
+This patch checks whether the interface is correctly brigded when ioctl
+returns EBUSY.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/drivers/linux_ioctl.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
+index 29abc0c59..73d27825d 100644
+--- a/src/drivers/linux_ioctl.c
++++ b/src/drivers/linux_ioctl.c
+@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
+ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ {
+ struct ifreq ifr;
+- int ifindex;
++ int ifindex, ret;
++ char in_br[IFNAMSIZ];
+
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0)
+@@ -165,6 +166,17 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+
+ wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+ "%s: %s", ifname, brname, strerror(errno));
++
++ /* If ioctl returns -EBUSY when adding interface into bridge,
++ * the interface might already be added by netifd, so here we
++ * check whether the interface is currently on the right
++ * bridge. */
++ if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
++ os_strcmp(in_br, brname) == 0)
++ ret = 0;
++ else
++ ret = -1;
++
+ errno = saved_errno;
+
+ /* If ioctl() returns EBUSY when adding an interface into the
+@@ -175,6 +187,8 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
+ os_strcmp(in_br, brname) != 0)
+ return -1;
++
++ return ret;
+ }
+
+ return 0;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
new file mode 100644
index 0000000..9a8f33f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
@@ -0,0 +1,29 @@
+From cd4001cf3751979177cefa215f438888397f5bcb Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 14 Jul 2023 17:19:13 +0800
+Subject: [PATCH 057/104] mtk: hostapd: Update parameter_set_count in MU EDCA
+ IE
+
+without this patch, MU EDCA Parameter update count not equal to
+WMM Parameter set count.
+---
+ src/ap/ieee802_11_he.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index 3c6ee72fe..3b6b2041c 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -317,6 +317,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+ edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+ os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+
++ if (hapd->conf->wmm_enabled)
++ edca->he_qos_info = hapd->parameter_set_count % 0xf;
++
+ wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+ pos, sizeof(*edca));
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
new file mode 100644
index 0000000..07e0656
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
@@ -0,0 +1,36 @@
+From 43840874fe5c56d76612c953d0e31b771818c0d3 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Mon, 24 Jul 2023 11:30:27 +0800
+Subject: [PATCH 058/104] mtk: hostapd: add extension IE list for non-inherit
+ IE in mbssid
+
+Certain clients do not scan all non tx profiles due to absence of
+element ID extension list which is mandatory field in non inheritance
+IE. Non inheritance Element ID is followed by extension element ID.
+Length is expected to be mentioned. Currently we do not support any
+extension element and hence filling length as 0.
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100644 => 100755 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100644
+new mode 100755
+index d972a25f1..e42d4e1cc
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7999,7 +7999,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+ else if (hapd->conf->xrates_supported)
+ ie_count++;
+ if (ie_count)
+- nontx_profile_len += 4 + ie_count;
++ nontx_profile_len += 5 + ie_count;
+
+ if (len + nontx_profile_len > 255)
+ break;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..690dcc0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From ab91679c1eeee2c48b871335756601df995e1a19 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 8 Aug 2023 19:21:41 +0800
+Subject: [PATCH 059/104] mtk: hostapd: add back ht vht cap missing field
+ before dfs channel fallback
+
+hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
+on the bandwidth of switched channel.
+For example, vht bw 160 support field would be cleared if we switch to
+non bw 160 channel.
+This design works fine with NON-DFS channel switch.
+However, for those DFS channels who require CAC, channel switch command
+calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
+This is simply restarting the interface not CHANNEL SWITCHING, so
+hostapd will not receive any ch_switch event from kernel.
+Therefore, the cleared field in vht_capab will not be set back to 1,
+even if we channel switch to dfs channel bw 160.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index d1ee0764b..db451387b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4569,6 +4569,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ break;
+ }
+
++ if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
++ freq_params->bandwidth > 20)
++ iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
++ if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
++ freq_params->bandwidth == 160)
++ iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
++
+ iface->freq = freq_params->freq;
+ iface->conf->channel = freq_params->channel;
+ iface->conf->secondary_channel = freq_params->sec_channel_offset;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
new file mode 100644
index 0000000..ccbe766
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
@@ -0,0 +1,45 @@
+From 00c2dff4bf8d15c1c84321cef1892009aa32e9ed Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 26 May 2023 14:52:35 +0800
+Subject: [PATCH 060/104] mtk: hostapd: Add support for gtk rekeying in hostapd
+ cli
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ hostapd/hostapd_cli.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 12c580455..e0b175386 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1291,6 +1291,15 @@ static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
+ }
+
+
++#ifdef CONFIG_TESTING_OPTIONS
++static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "REKEY_GTK");
++}
++#endif
++
++
+ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ char cmd[256];
+@@ -1831,6 +1840,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "= update Beacon frame contents\n"},
+ { "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
+ "= stop AP\n"},
++#ifdef CONFIG_TESTING_OPTIONS
++ { "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
++ "= rekey gtk\n"},
++#endif
+ { "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+ "= drop all ERP keys"},
+ { "log_level", hostapd_cli_cmd_log_level, NULL,
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
new file mode 100644
index 0000000..65ba1f0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
@@ -0,0 +1,48 @@
+From 2dc6e435eef405ae0cfb69a89c1c8ec7d2852635 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 11 Jul 2023 14:17:43 +0800
+Subject: [PATCH 061/104] mtk: hostapd: Set WMM and TX queue parameters for
+ wpa_supplicant
+
+Since most of the time, wpa_supplicant will be used to setup an STA
+interface, it's default WMM and TX queue parameters should be set for
+STA.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/config.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index c3943355d..7bb57e2ab 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -4720,19 +4720,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+ const struct hostapd_wmm_ac_params ac_bk =
+ { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ const struct hostapd_wmm_ac_params ac_be =
+- { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
++ { aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
+ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+- { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
++ { aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
+ const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+- { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
++ { aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
+ const struct hostapd_tx_queue_params txq_bk =
+ { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_be =
+- { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
++ { 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_vi =
+- { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
++ { 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
+ const struct hostapd_tx_queue_params txq_vo =
+- { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
++ { 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
+
+ #undef ecw2cw
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
new file mode 100644
index 0000000..502da4c
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
@@ -0,0 +1,78 @@
+From eaf45cd7a14dac2d5d601653792d2bc101118585 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 7 Jul 2023 17:16:11 +0800
+Subject: [PATCH 062/104] mtk: hostapd: Set STA TX queue parameters
+ configuration after association
+
+This patch adds the way for wpa_supplicant to set driver's TX queue
+parameters.
+Since STA parses and apply TX queue parameters from AP beacon's WMM IE
+during association, wpa_supplicant set driver's TX queue parameters
+after the association.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/driver_i.h | 12 ++++++++++++
+ wpa_supplicant/events.c | 16 ++++++++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index d01b52bb1..663e16053 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+ return 0;
+ }
+
++static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
++ int q, int aifs, int cw_min,
++ int cw_max, int burst_time)
++{
++ int link_id = -1;
++ if (wpa_s->driver->set_tx_queue_params)
++ return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
++ aifs, cw_min, cw_max,
++ burst_time, link_id);
++ return 0;
++}
++
+ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
+ const u8 *data, size_t data_len, int noack,
+ unsigned int freq, unsigned int wait)
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 2a9342318..8fd2f2049 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -4070,6 +4070,20 @@ out:
+ return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
+ }
+
++static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
++ struct hostapd_tx_queue_params *p;
++
++ for (int i = 0; i < NUM_TX_QUEUES; i++){
++ p = &wpa_s->conf->tx_queue[i];
++ if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
++ p->cwmin, p->cwmax,
++ p->burst)) {
++ wpa_printf(MSG_DEBUG, "Failed to set TX queue "
++ "parameters for queue %d.", i);
++ /* Continue anyway */
++ }
++ }
++}
+
+ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+@@ -4399,6 +4413,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
+ wpa_supplicant_set_4addr_mode(wpa_s);
++
++ wpa_supplicant_tx_queue_params(wpa_s);
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
new file mode 100644
index 0000000..e1384d9
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
@@ -0,0 +1,27 @@
+From 7449e88f54fb5e16296399c43c9f758535123cde Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Sep 2023 15:31:24 +0800
+Subject: [PATCH 063/104] mtk: hostapd: avoid color switch when beacon is not
+ set
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index db451387b..0d4b79b48 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4707,7 +4707,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+ {
+ struct os_reltime now;
+
+- if (hapd->cca_in_progress)
++ if (hapd->cca_in_progress || !hapd->beacon_set_done)
+ return;
+
+ if (os_get_reltime(&now))
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
new file mode 100644
index 0000000..7d6be8a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
@@ -0,0 +1,30 @@
+From 8e01f276c2d7be41f3521026c92d8f1bd833865f Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Wed, 13 Sep 2023 19:29:51 +0800
+Subject: [PATCH 064/104] mtk: hostapd: 6g bss connect do not consider ht
+ operation
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100755 => 100644 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100755
+new mode 100644
+index e42d4e1cc..923cbebcc
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5591,7 +5591,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ set_beacon = true;
+ }
+
+- if (update_ht_state(hapd, sta) > 0)
++ if (!is_6ghz_op_class(hapd->iconf->op_class) && update_ht_state(hapd, sta) > 0)
+ set_beacon = true;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
new file mode 100644
index 0000000..d87fef0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
@@ -0,0 +1,96 @@
+From 9639b495a347cdd2aadbe2bc2d336b4398518d29 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Sun, 8 Oct 2023 11:50:06 +0800
+Subject: [PATCH 065/104] mtk: hostapd: Add ACS chanlist info in get_config
+
+This patch is used to add ACS chanlist info displaying
+for upper layer application obtaining.
+
+Command format:
+hostapd_cli -i phy0-ap0 get_config
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 59 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index ed383df7d..581acc260 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1058,6 +1058,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ {
+ int ret;
+ char *pos, *end;
++ int i;
+
+ pos = buf;
+ end = buf + buflen;
+@@ -1237,6 +1238,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ pos += ret;
+ }
+
++ /* dump chanlist */
++ if (hapd->iface->conf->acs_ch_list.num > 0) {
++ ret = os_snprintf(pos, end - pos, "chanlist=");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
++ if (i > 0) {
++ ret = os_snprintf(pos, end - pos, ", ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "%d-%d",
++ hapd->iface->conf->acs_ch_list.range[i].min,
++ hapd->iface->conf->acs_ch_list.range[i].max);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "\n");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ /* dump freqlist */
++ if (hapd->iface->conf->acs_freq_list.num > 0) {
++ ret = os_snprintf(pos, end - pos, "freqlist=");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
++ if (i > 0) {
++ ret = os_snprintf(pos, end - pos, ", ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "%d-%d",
++ hapd->iface->conf->acs_freq_list.range[i].min,
++ hapd->iface->conf->acs_freq_list.range[i].max);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
++ ret = os_snprintf(pos, end - pos, "\n");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++
+ return pos - buf;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
new file mode 100644
index 0000000..3f59e13
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
@@ -0,0 +1,43 @@
+From b463e82d0eec8674e430a7e837c569be4c9fe2c2 Mon Sep 17 00:00:00 2001
+From: mtk25255 <rohit.kamat@mediatek.com>
+Date: Thu, 12 Oct 2023 14:29:23 +0800
+Subject: [PATCH 066/104] mtk: hostapd: Fix RSNXE Interop issue with STA
+
+---
+ src/ap/ieee802_11.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 923cbebcc..ce3874901 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5294,6 +5294,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ int omit_rsnxe = 0;
+ bool set_beacon = false;
+ bool mld_addrs_not_translated = false;
++ bool sae_pk = false;
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ sizeof(mgmt->u.assoc_req))) {
+@@ -5539,7 +5540,17 @@ static void handle_assoc(struct hostapd_data *hapd,
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+ omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+-
++#ifdef CONFIG_SAE_PK
++ sae_pk = hostapd_sae_pk_in_use(hapd->conf);
++#endif /* CONFIG_SAE_PK */
++ if (omit_rsnxe) {
++ if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
++ (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
++ hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
++ wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
++ omit_rsnxe = 0;
++ }
++ }
+ if (hostapd_get_aid(hapd, sta) < 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
new file mode 100644
index 0000000..8764aa7
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
@@ -0,0 +1,29 @@
+From a6db9becf71712107500adf239b89f4f8523d3f3 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 10 May 2023 13:11:34 +0800
+Subject: [PATCH 067/104] mtk: hostapd: update eht operation element
+
+---
+ src/ap/ieee802_11_eht.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 353a4116e..e13662a59 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -237,9 +237,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+
+ /* TODO: Fill in appropriate EHT-MCS max Nss information */
+ oper->basic_eht_mcs_nss_set[0] = 0x11;
+- oper->basic_eht_mcs_nss_set[1] = 0x00;
+- oper->basic_eht_mcs_nss_set[2] = 0x00;
+- oper->basic_eht_mcs_nss_set[3] = 0x00;
++ oper->basic_eht_mcs_nss_set[1] = 0x11;
++ oper->basic_eht_mcs_nss_set[2] = 0x11;
++ oper->basic_eht_mcs_nss_set[3] = 0x11;
+
+ if (!eht_oper_info_present)
+ return pos + elen;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
new file mode 100644
index 0000000..f383d62
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
@@ -0,0 +1,28 @@
+From f5256a36cd00b54955decd53961ece85dd5f11f9 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 30 Aug 2023 04:23:37 +0800
+Subject: [PATCH 068/104] mtk: hostapd: ucode: add support for ucode to parse
+ BW320MHz info
+
+---
+ src/utils/ucode.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 29c753c32..4b6ed3a94 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ case 2:
+ chanwidth = CONF_OPER_CHWIDTH_160MHZ;
+ break;
++ case 9:
++ width = 3;
++ chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++ break;
+ default:
+ return NULL;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
new file mode 100644
index 0000000..4ca6761
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
@@ -0,0 +1,279 @@
+From d7a803942f27759fe0e27c4550d70e44fb83c897 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 11 Sep 2023 10:16:35 +0800
+Subject: [PATCH 069/104] mtk: hostapd: synchronize bandwidth in AP/STA support
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c | 41 +++++++++++++++++++--
+ src/utils/ucode.c | 12 +++++--
+ wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
+ 3 files changed, 117 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 16d1b5153..98b2a3bf2 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -489,6 +489,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ int i;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++ iface->phy, hostapd_state_text(iface->state));
++
+ if (!iface)
+ return NULL;
+
+@@ -515,6 +518,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ uint64_t intval;
+ int i;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
++ iface->phy, hostapd_state_text(iface->state));
++
+ if (!iface)
+ return NULL;
+
+@@ -537,7 +543,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ UPDATE_VAL(op_class, "op_class");
+ UPDATE_VAL(hw_mode, "hw_mode");
+ UPDATE_VAL(channel, "channel");
+- UPDATE_VAL(secondary_channel, "sec_channel");
++
++ intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
++ if (!errno) {
++ conf->secondary_channel = intval;
++ changed = true;
++ }
++
+ if (!changed &&
+ (iface->bss[0]->beacon_set_done ||
+ iface->state == HAPD_IFACE_DFS))
+@@ -583,6 +595,18 @@ out:
+ return ucv_boolean_new(true);
+ }
+
++ wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
++ wpa_printf(MSG_INFO, " * channel: %d\n", conf->channel);
++ wpa_printf(MSG_INFO, " * op_class: %d\n", conf->op_class);
++ wpa_printf(MSG_INFO, " * secondary channel: %d\n",
++ conf->secondary_channel);
++ wpa_printf(MSG_INFO, " * seg0: %d\n",
++ hostapd_get_oper_centr_freq_seg0_idx(conf));
++ wpa_printf(MSG_INFO, " * seg1: %d\n",
++ hostapd_get_oper_centr_freq_seg0_idx(conf));
++ wpa_printf(MSG_INFO, " * oper_chwidth: %d\n",
++ hostapd_get_oper_chwidth(conf));
++
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+ int ret;
+@@ -617,6 +641,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ uint64_t intval;
+ int i, ret = 0;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
+ if (!iface || ucv_type(info) != UC_OBJECT)
+ return NULL;
+
+@@ -636,7 +661,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ if (errno)
+ intval = hostapd_get_oper_chwidth(conf);
+ if (intval)
+- csa.freq_params.bandwidth = 40 << intval;
++ csa.freq_params.bandwidth = 40 <<
++ (intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
+ else
+ csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
+
+@@ -647,6 +673,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ csa.freq_params.center_freq2 = intval;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
++ wpa_printf(MSG_INFO, " * freq is %d\n", csa.freq_params.freq);
++ wpa_printf(MSG_INFO, " * bandwidth is %d\n",
++ csa.freq_params.bandwidth);
++ wpa_printf(MSG_INFO, " * sec_chan_offset is %d\n",
++ csa.freq_params.sec_channel_offset);
++ wpa_printf(MSG_INFO, " * center_freq1 is %d\n",
++ csa.freq_params.center_freq1);
++ wpa_printf(MSG_INFO, " * center_freq2 is %d\n",
++ csa.freq_params.center_freq2);
++
+ for (i = 0; i < iface->num_bss; i++)
+ ret = hostapd_switch_channel(iface->bss[i], &csa);
+
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 4b6ed3a94..6f82382f3 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ uc_value_t *freq = uc_fn_arg(0);
+ uc_value_t *sec = uc_fn_arg(1);
+ int width = ucv_uint64_get(uc_fn_arg(2));
++ int bw320_offset = 1;
+ int freq_val, center_idx, center_ofs;
+ enum oper_chan_width chanwidth;
+ enum hostapd_hw_mode hw_mode;
+@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ case 9:
+ width = 3;
+ chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++
++ /* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
++ bw320_offset = ucv_uint64_get(uc_fn_arg(3));
+ break;
+ default:
+ return NULL;
+@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
+ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++ ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+
+- if (!sec_channel)
++ if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
++ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
++ ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
+ return ret;
++ }
+
+ if (freq_val >= 5900)
+- center_ofs = 0;
++ center_ofs = 32 * (1 - bw320_offset);
+ else if (freq_val >= 5745)
+ center_ofs = 20;
+ else
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 397f85bde..542ca25c9 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -7,6 +7,8 @@
+ #include "wps_supplicant.h"
+ #include "bss.h"
+ #include "ucode.h"
++#include "driver_i.h"
++#include "common/ieee802_11_common.h"
+
+ static struct wpa_global *wpa_global;
+ static uc_resource_type_t *global_type, *iface_type;
+@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ {
+ const char *state;
+ uc_value_t *val;
++ enum oper_chan_width ch_width;
++ int center_freq1, bw320_offset = 1;
+
+ if (event != EVENT_CH_SWITCH_STARTED)
+ return;
+@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ uc_value_push(ucv_get(val));
+
+ if (event == EVENT_CH_SWITCH_STARTED) {
++ center_freq1 = data->ch_switch.cf1;
++
++ switch (data->ch_switch.ch_width) {
++ case CHAN_WIDTH_80:
++ ch_width = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case CHAN_WIDTH_80P80:
++ ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
++ break;
++ case CHAN_WIDTH_160:
++ ch_width = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ case CHAN_WIDTH_320:
++ ch_width = CONF_OPER_CHWIDTH_320MHZ;
++ break;
++ case CHAN_WIDTH_20_NOHT:
++ case CHAN_WIDTH_20:
++ case CHAN_WIDTH_40:
++ default:
++ ch_width = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ }
++
++ /* Check bandwidth 320 MHz-2 */
++ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++ (center_freq1 == 6265) || center_freq1 == 6585 ||
++ center_freq1 == 6905)
++ bw320_offset = 2;
++
+ ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
+ ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
+ ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+- ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++ ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
+ ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++ ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
++ ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
+ }
+
+ ucv_put(wpa_ucode_call(4));
+@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ struct wpa_bss *bss;
+ uc_value_t *ret, *val;
++ struct wpa_channel_info ci;
++ u8 op_class, channel;
++ enum oper_chan_width ch_width;
++ int center_freq1, bw320_offset = 1, is_24ghz;
++ enum hostapd_hw_mode hw_mode;
+
+ if (!wpa_s)
+ return NULL;
+@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ bss = wpa_s->current_bss;
+ if (bss) {
+ int sec_chan = 0;
+- const u8 *ie;
+-
+- ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+- if (ie && ie[1] >= 2) {
+- const struct ieee80211_ht_operation *ht_oper;
+- int sec;
+-
+- ht_oper = (const void *) (ie + 2);
+- sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+- if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+- sec_chan = 1;
+- else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+- sec_chan = -1;
++
++ hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
++ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++ hw_mode == HOSTAPD_MODE_IEEE80211B;
++
++ wpa_drv_channel_info(wpa_s, &ci);
++ center_freq1 = ci.center_frq1;
++
++ if (bss->freq != center_freq1) {
++ if (is_24ghz)
++ sec_chan = (bss->freq < center_freq1) ? 1 : -1;
++ else
++ sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
++ }
++
++ if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++ sec_chan, &op_class, &channel))
++ return NULL;
++
++ ch_width = op_class_to_ch_width(op_class);
++ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++ (center_freq1 == 6265) || center_freq1 == 6585 ||
++ center_freq1 == 6905) {
++ /* Bandwidth 320 MHz-2 */
++ bw320_offset = 2;
+ }
+
+ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++ ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
++ ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ }
+
+ #ifdef CONFIG_MESH
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
new file mode 100644
index 0000000..d1800fe
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
@@ -0,0 +1,339 @@
+From 0455150c89b046a3ebd81134527ff4cae5025f3d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:25:01 +0800
+Subject: [PATCH 070/104] mtk: hostapd: Add support for updating background
+ channel by driver
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 107 ++++++++++++++++++++++++++++-
+ src/ap/dfs.h | 3 +
+ src/ap/drv_callbacks.c | 22 ++++++
+ src/ap/hostapd.h | 5 ++
+ src/drivers/driver.h | 12 ++++
+ src/drivers/driver_nl80211_event.c | 6 ++
+ src/drivers/nl80211_copy.h | 6 ++
+ 7 files changed, 160 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 7adaf81ac..e39f3c180 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -816,11 +816,14 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+
+ static void dfs_check_background_overlapped(struct hostapd_iface *iface)
+ {
+- int width = hostapd_get_oper_chwidth(iface->conf);
++ int width = iface->radar_background.new_chwidth;
+
+ if (!dfs_use_radar_background(iface))
+ return;
+
++ if (!width)
++ width = hostapd_get_oper_chwidth(iface->conf);
++
+ if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
+ width, iface->radar_background.centr_freq_seg0_idx,
+ iface->radar_background.centr_freq_seg1_idx))
+@@ -985,6 +988,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ iface->radar_background.temp_ch = 1;
+ return 1;
+ } else if (dfs_use_radar_background(iface)) {
++ /*
++ * AP is going to perform CAC, so reset temp_ch to 0,
++ * when dedicated rx has already started CAC.
++ */
++ if (iface->radar_background.cac_started) {
++ iface->radar_background.temp_ch = 0;
++ return 0;
++ }
++
+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ channel_type = DFS_ANY_CHANNEL;
+
+@@ -1125,6 +1137,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ * ch_switch_notify event is received */
+ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+
++ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
++
+ return 0;
+ }
+
+@@ -1176,6 +1190,9 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
+ iface->radar_background.secondary_channel = sec;
+ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++ /* if main channel do not require dfs, then set temp_ch = 1 */
++ if (!hostapd_is_dfs_required(iface))
++ iface->radar_background.temp_ch = 1;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: setting background chain to chan %d (%d MHz)",
+@@ -1198,6 +1215,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ int ret;
+
++ if (iface->radar_background.new_chwidth) {
++ hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
++ iface->radar_background.new_chwidth = 0;
++ }
+ ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
+ iface->radar_background.freq,
+ iface->radar_background.secondary_channel,
+@@ -1220,6 +1241,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ }
+
+
++static void
++hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
++{
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ struct hostapd_channel_data *chan;
++ int i, channel, width = channel_width_to_int(chan_width);
++
++ if (iface->conf->channel - iface->radar_background.channel == width / 5)
++ channel = iface->radar_background.channel;
++ else if (iface->radar_background.channel - iface->conf->channel == width / 5)
++ channel = iface->conf->channel;
++ else
++ return;
++
++ for (i = 0; i < mode->num_channels; i++) {
++ chan = &mode->channels[i];
++ if (chan->chan == channel)
++ break;
++ }
++
++ if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
++ return;
++
++ switch (chan_width) {
++ case CHAN_WIDTH_20_NOHT:
++ case CHAN_WIDTH_20:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ case CHAN_WIDTH_40:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case CHAN_WIDTH_80:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ default:
++ return;
++ }
++
++ iface->radar_background.freq = channel * 5 + 5000;
++ iface->radar_background.channel = channel;
++ iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
++ iface->radar_background.secondary_channel = 1;
++ iface->radar_background.expand_ch = 0;
++}
++
++
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+@@ -1253,6 +1320,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ return 0;
+
+ iface->radar_background.temp_ch = 0;
++
++ if (iface->radar_background.expand_ch)
++ hostapd_dfs_background_expand(iface, chan_width);
++
+ return hostapd_dfs_start_channel_switch_background(iface);
+ }
+
+@@ -1283,6 +1354,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ }
+ } else if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
++ iface->radar_background.temp_ch = 0;
++ iface->radar_background.expand_ch = 0;
+ hostapd_dfs_update_background_chain(iface);
+ }
+
+@@ -1415,6 +1488,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
+ return 0;
+
++ iface->radar_background.temp_ch = 0;
++ iface->radar_background.expand_ch = 0;
++
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+@@ -1649,6 +1725,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ }
+
+
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, bool expand)
++{
++ switch (chan_width) {
++ case CHAN_WIDTH_80:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++ break;
++ case CHAN_WIDTH_160:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++ break;
++ default:
++ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++ break;
++ };
++
++ iface->radar_background.freq = freq;
++ iface->radar_background.channel = (freq - 5000) / 5;
++ iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
++ iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
++ if (expand) {
++ iface->radar_background.temp_ch = 1;
++ iface->radar_background.expand_ch = 1;
++ }
++
++ return 0;
++}
++
++
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 25ba29ca1..a1a2be5ec 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2, bool expand);
+ int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2, u32 state);
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index caa171474..2d946afd6 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2226,6 +2226,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ radar->cf1, radar->cf2);
+ }
+
++
++static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
++ struct dfs_event *radar, bool expand)
++{
++ wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
++ expand ? "expand" : "update", radar->freq);
++ hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
++ radar->chan_offset, radar->chan_width,
++ radar->cf1, radar->cf2, expand);
++}
++
++
+ static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+ {
+@@ -2610,6 +2622,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ break;
++ case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
++ if (!data)
++ break;
++ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
++ break;
++ case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
++ if (!data)
++ break;
++ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
++ break;
+ case EVENT_DFS_STA_CAC_SKIPPED:
+ if (!data)
+ break;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 1e4113459..5b37be87b 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -640,6 +640,11 @@ struct hostapd_iface {
+ unsigned int temp_ch:1;
+ /* CAC started on radar offchain */
+ unsigned int cac_started:1;
++ /* Main chain should expand its width according to the
++ * current offchain channel after CAC detection on radar offchain.
++ */
++ unsigned int expand_ch:1;
++ int new_chwidth;
+ } radar_background;
+
+ u16 hw_flags;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 1c0c38e24..4e3dc9bdb 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5960,6 +5960,18 @@ enum wpa_event_type {
+ * The channel in the notification is now marked as usable.
+ */
+ EVENT_DFS_STA_CAC_EXPIRED,
++
++ /**
++ * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
++ * channel has been updated.
++ */
++ EVENT_DFS_BACKGROUND_CHAN_UPDATE,
++
++ /**
++ * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
++ * channel has been updated and operating channel should expand its width.
++ */
++ EVENT_DFS_BACKGROUND_CHAN_EXPAND,
+ };
+
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 7889930a0..6631285bf 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ case NL80211_RADAR_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
++ case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
++ break;
++ case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
++ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
++ break;
+ case NL80211_RADAR_STA_CAC_SKIPPED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
+ break;
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index 8917d565b..c56954306 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
++ * driver.
++ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
++ * driver and required to expand main operating channel.
+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
+ * when receiving CSA/assoc resp
+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
+@@ -6711,6 +6715,8 @@ enum nl80211_radar_event {
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
++ NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
++ NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
+ NL80211_RADAR_STA_CAC_SKIPPED,
+ NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
new file mode 100644
index 0000000..c2760d9
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
@@ -0,0 +1,291 @@
+From 5b2e33617bfafa8c6776e80b13c8747f0021a804 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 2 Aug 2023 19:00:34 +0800
+Subject: [PATCH 071/104] mtk: hostapd: add zwdfs mode ctrl for eagle efem
+ hwits
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ hostapd/ctrl_iface.c | 30 +++++++++++++++++++++++++++
+ src/ap/ap_config.h | 6 ++++++
+ src/ap/ap_drv_ops.c | 14 +++++++++++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/ap/dfs.c | 6 ++++++
+ src/common/mtk_vendor.h | 12 +++++++++++
+ src/drivers/driver.h | 7 +++++++
+ src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +++
+ 11 files changed, 116 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index dadc8f108..9467a1128 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -3576,6 +3576,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ conf->acs_exclude_6ghz_non_psc = atoi(pos);
+ } else if (os_strcmp(buf, "enable_background_radar") == 0) {
+ conf->enable_background_radar = atoi(pos);
++ } else if (os_strcmp(buf, "background_radar_mode") == 0) {
++ conf->background_radar_mode = atoi(pos);
+ } else if (os_strcmp(buf, "min_tx_power") == 0) {
+ int val = atoi(pos);
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 581acc260..9b072d1b2 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4782,6 +4782,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
+ return pos - buf;
+ }
+
++static int
++hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ struct hostapd_iface *iface = hapd->iface;
++ char *pos, *param;
++
++ param = os_strchr(cmd, ' ');
++ if (!param)
++ return -1;
++ *param++ = '\0';
++
++ pos = os_strstr(param, "mode=");
++ if (!pos)
++ return -1;
++
++ if (os_strncmp(pos + 5, "cert", 4) == 0)
++ iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
++ else if (os_strncmp(pos + 5, "normal", 6) == 0)
++ iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
++
++ if (hostapd_drv_background_radar_mode(hapd) < 0)
++ return -1;
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5423,6 +5450,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ if (pos)
+ *pos = ' ';
+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
++ } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
++ reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3827a8fc8..0b07be516 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1051,6 +1051,7 @@ struct hostapd_config {
+ bool hw_mode_set;
+ int acs_exclude_6ghz_non_psc;
+ int enable_background_radar;
++ int background_radar_mode;
+ enum {
+ LONG_PREAMBLE = 0,
+ SHORT_PREAMBLE = 1
+@@ -1306,6 +1307,11 @@ enum three_wire_mode {
+ NUM_THREE_WIRE_MODE - 1
+ };
+
++enum background_radar_mode {
++ BACKGROUND_RADAR_NORMAL_MODE,
++ BACKGROUND_RADAR_CERT_MODE,
++};
++
+ enum dfs_mode {
+ DFS_DETECT_MODE_DISABLE,
+ DFS_DETECT_MODE_AP_ENABLE,
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 116bc4ceb..2028e70fb 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1365,3 +1365,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ return 0;
+ return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
+ }
++
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->background_radar_mode ||
++ !(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) ||
++ !hapd->iface->conf->enable_background_radar)
++ return 0;
++ if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
++ wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
++ return 0;
++ }
++ return hapd->driver->background_radar_mode(hapd->drv_priv,
++ hapd->iconf->background_radar_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 84b41881a..f0e618bcc 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -168,6 +168,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index e39f3c180..b12290556 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -985,6 +985,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ if (res < 0)
+ return res;
+
++ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++ return -1;
++
+ iface->radar_background.temp_ch = 1;
+ return 1;
+ } else if (dfs_use_radar_background(iface)) {
+@@ -1025,6 +1028,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ iface->radar_background.secondary_channel = sec;
+ iface->radar_background.centr_freq_seg0_idx = cf1;
+ iface->radar_background.centr_freq_seg1_idx = cf2;
++
++ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++ return -1;
+ }
+
+ return 0;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index e140de60b..5bc1e0444 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -244,6 +245,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
+ };
+
++enum mtk_vendor_attr_background_radar_ctrl {
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
++ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 4e3dc9bdb..863748d4f 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5319,6 +5319,13 @@ struct wpa_driver_ops {
+ * @amnt_dump_buf: Buffer to print
+ */
+ int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
++
++ /**
++ * background_radar_mode - set background radar mode
++ * @priv: Private driver interface data
++ * @background_radar_mode: background radar mode
++ */
++ int (*background_radar_mode)(void *priv, u8 background_radar_mode);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index aeb755b11..e3f00b6d6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15052,6 +15052,39 @@ fail:
+ return -ENOBUFS;
+ }
+
++static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ /* Prepare nl80211 cmd */
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_background_radar_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support setting background radar mode");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++ if (ret) {
++ wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
++ ret, strerror(-ret));
++ }
++ return ret;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15229,4 +15262,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .ap_trigtype = nl80211_ap_trigtype,
+ .amnt_set = nl80211_amnt_set,
+ .amnt_dump = nl80211_amnt_dump,
++ .background_radar_mode = nl80211_background_radar_mode,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index adc1b9bf7..e9aae8d14 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ unsigned int mtk_amnt_vendor_cmd_avail:1;
++ unsigned int mtk_background_radar_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 38e83e42b..9bc98aae7 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1164,6 +1164,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ drv->mtk_rfeatures_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
++ drv->mtk_background_radar_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
new file mode 100644
index 0000000..4959fb4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
@@ -0,0 +1,375 @@
+From 51377e7c81b4164be42de4a5c4c48ba53a638afe Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 21 Sep 2023 10:29:46 +0800
+Subject: [PATCH 072/104] mtk: hostapd: add support enable/disable preamble
+ puncture from mtk vendor command
+
+This commit supports two ways to enable/disable preamble puncture
+feature.
+
+1. Add new hostapd configuration "pp_mode". The possible value could be
+1 to 3. When the value is 0, it means that the firmware will turn off
+the pp algorithm. When the value is 1, it means that the firmware will
+enable the pp algorithm, allowing the algorithm to determine whether pp
+could be applied on each txcmd. When the value is 2, it means that pp
+feature is manually configured by the user. Please noted that for
+current implementation, the default configuration is 0.
+
+2. $ hostapd_cli -i <intf_name> raw set_pp mode val
+The argument "val" could be 0 for PP feature disabled or 1 to configure
+PP feature as auto mode.
+
+This commit also let user check whether pp feature is enabled by
+hostapd_cli command. The usage shows as below:
+$ hostapd_cli -i <intf_name> raw get_pp mode
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/config_file.c | 12 +++++++
+ hostapd/ctrl_iface.c | 59 +++++++++++++++++++++++++++++++
+ src/ap/ap_config.c | 1 +
+ src/ap/ap_config.h | 7 ++++
+ src/ap/ap_drv_ops.c | 9 +++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/ap/hostapd.c | 2 ++
+ src/common/mtk_vendor.h | 12 +++++++
+ src/drivers/driver.h | 6 ++++
+ src/drivers/driver_nl80211.c | 49 +++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 ++
+ 12 files changed, 162 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 9467a1128..050ef290e 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5333,6 +5333,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "punct_bitmap") == 0) {
+ if (get_u16(pos, line, &conf->punct_bitmap))
+ return 1;
++ conf->punct_bitmap = atoi(pos);
++ conf->pp_mode = PP_MANUAL_MODE;
+ } else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ int val = atoi(pos);
+
+@@ -5415,6 +5417,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->amsdu = val;
++ } else if (os_strcmp(buf, "pp_mode") == 0) {
++ int val = atoi(pos);
++
++ if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
++ val < PP_DISABLE || val > PP_MANUAL_MODE) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
++ line);
++ return 1;
++ }
++ conf->pp_mode = (u8) val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 9b072d1b2..c9b53c64e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4809,6 +4809,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
++static int
++hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++ size_t buflen)
++{
++ char *pos, *config, *value;
++
++ config = cmd;
++ pos = os_strchr(config, ' ');
++ if (pos == NULL)
++ return -1;
++ *pos++ = '\0';
++
++ if (pos == NULL)
++ return -1;
++ value = pos;
++
++ if (os_strcmp(config, "mode") == 0) {
++ int val = atoi(value);
++
++ if (val < PP_DISABLE || val > PP_AUTO_MODE) {
++ wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++ return -1;
++ }
++ hapd->iconf->pp_mode = (u8) val;
++ if (hostapd_drv_pp_mode_set(hapd) != 0)
++ return -1;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for set_pp", config);
++ return -1;
++ }
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++ size_t buflen)
++{
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ if (os_strcmp(cmd, "mode") == 0) {
++ return os_snprintf(pos, end - pos, "pp_mode: %d\n",
++ hapd->iconf->pp_mode);
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unsupported parameter %s for get_pp", cmd);
++ return -1;
++ }
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5442,6 +5495,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "set_pp", 6) == 0) {
++ reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
++ reply_size);
++ } else if (os_strncmp(buf, "get_pp", 6) == 0) {
++ reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
++ reply_size);
+ } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
+ // Replace first ':' with a single space ' '
+ char *pos = buf + 23;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index ba1b2a7a3..056c38f73 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -310,6 +310,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ conf->amsdu = 1;
++ conf->pp_mode = PP_DISABLE;
+
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 0b07be516..40edcdaa7 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1293,6 +1293,7 @@ struct hostapd_config {
+ u8 dfs_detect_mode;
+ u8 amsdu;
+ void *muru_config;
++ u8 pp_mode;
+ };
+
+ enum three_wire_mode {
+@@ -1345,6 +1346,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+ EDCCA_CTRL_NUM,
+ };
+
++enum pp_mode {
++ PP_DISABLE = 0,
++ PP_AUTO_MODE,
++ PP_MANUAL_MODE,
++};
++
+ #define EDCCA_DEFAULT_COMPENSATION -6
+ #define EDCCA_MIN_COMPENSATION -126
+ #define EDCCA_MAX_COMPENSATION 126
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2028e70fb..c71cfe1bd 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1379,3 +1379,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ return hapd->driver->background_radar_mode(hapd->drv_priv,
+ hapd->iconf->background_radar_mode);
+ }
++
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->pp_mode_set ||
++ hapd->iconf->pp_mode > PP_AUTO_MODE)
++ return 0;
++ return hapd->driver->pp_mode_set(hapd->drv_priv,
++ hapd->iconf->pp_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f0e618bcc..ef61001e5 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -169,6 +169,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 0d4b79b48..cdbf81e38 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2708,6 +2708,8 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ goto fail;
++ if (hostapd_drv_pp_mode_set(hapd) < 0)
++ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 5bc1e0444..6275c141d 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
++ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -256,6 +257,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
+ };
+
++enum mtk_vendor_attr_pp_ctrl {
++ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_PP_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
++ MTK_VENDOR_ATTR_PP_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 863748d4f..be0e89ba3 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5326,6 +5326,12 @@ struct wpa_driver_ops {
+ * @background_radar_mode: background radar mode
+ */
+ int (*background_radar_mode)(void *priv, u8 background_radar_mode);
++ /**
++ * pp_mode_set - Set preamble puncture operation mode
++ * @priv: Private driver interface data
++ * @pp_mode: Value is defined in enum pp_mode
++ */
++ int (*pp_mode_set)(void *priv, const u8 pp_mode);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index e3f00b6d6..b47ab07ea 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -156,6 +156,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
+ };
+
++static struct nla_policy
++pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
++ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -15085,6 +15090,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ return ret;
+ }
+
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_pp_vendor_cmd_avail) {
++ wpa_printf(MSG_DEBUG,
++ "nl80211: Driver does not support setting preamble puncture");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++ if (!data)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
++
++ nla_nest_end(msg, data);
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
++ ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15263,4 +15311,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .amnt_set = nl80211_amnt_set,
+ .amnt_dump = nl80211_amnt_dump,
+ .background_radar_mode = nl80211_background_radar_mode,
++ .pp_mode_set = nl80211_pp_mode_set,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index e9aae8d14..707bb7fe4 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ unsigned int mtk_amnt_vendor_cmd_avail:1;
+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
++ unsigned int mtk_pp_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 9bc98aae7..ba3c0817b 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1167,6 +1167,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
+ drv->mtk_background_radar_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
++ drv->mtk_pp_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
new file mode 100644
index 0000000..31d1951
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
@@ -0,0 +1,247 @@
+From a7adff7d782e329e9f8b1063f78616757f944d51 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 22 Nov 2023 21:41:34 +0800
+Subject: [PATCH 073/104] mtk: hostapd: add no_beacon vendor command for cert
+
+Add the vendor command to disable/enable beacon
+
+[Usage]
+hostapd_cli -i <interface> no_beacon <value>
+ <value>
+ 0: enable beacon
+ 1: disable beacon
+
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 21 +++++++++++++++++++
+ hostapd/hostapd_cli.c | 7 +++++++
+ src/ap/ap_drv_ops.c | 8 ++++++++
+ src/ap/ap_drv_ops.h | 1 +
+ src/common/mtk_vendor.h | 12 +++++++++++
+ src/drivers/driver.h | 7 +++++++
+ src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +++
+ 9 files changed, 94 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index c9b53c64e..0fded7ed4 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4862,6 +4862,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ }
+ }
+
++static int
++hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
++ char *buf, size_t buflen)
++{
++ int disable_beacon = atoi(value);
++
++ if (disable_beacon < 0) {
++ wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
++ return -1;
++ }
++
++ if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
++ return os_snprintf(buf, buflen, "OK\n");
++ else
++ return -1;
++
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5512,6 +5530,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
+ reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
+ reply, reply_size);
++ } else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
++ reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
++ reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index e0b175386..7e4485cb8 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1464,6 +1464,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
+ }
+
++static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
++}
+
+ #ifdef CONFIG_DPP
+
+@@ -1871,6 +1876,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
+ { "get_mu", hostapd_cli_cmd_get_mu, NULL,
+ " = show mu onoff value in 0-15 bitmap"},
++ { "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
++ "<value> 0: Enable beacon, 1: Disable beacon"},
+ #ifdef CONFIG_DPP
+ { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
+ "report a scanned DPP URI from a QR Code" },
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c71cfe1bd..d6bd157d8 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1388,3 +1388,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ return hapd->driver->pp_mode_set(hapd->drv_priv,
+ hapd->iconf->pp_mode);
+ }
++
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
++{
++ if (!hapd->driver || !hapd->driver->beacon_ctrl)
++ return 0;
++ return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
++}
++
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index ef61001e5..78e5c8d5a 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -170,6 +170,7 @@ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_ma
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6275c141d..5531802b8 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++ MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ };
+
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -268,6 +269,17 @@ enum mtk_vendor_attr_pp_ctrl {
+ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
+ };
+
++enum mtk_vendor_attr_beacon_ctrl {
++ MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
++
++ MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
++
++ /* keep last */
++ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
++ MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
++ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index be0e89ba3..332a51c55 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5243,6 +5243,13 @@ struct wpa_driver_ops {
+ int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ int (*mu_dump)(void *priv, u8 *mu_onoff);
+
++ /**
++ * beacon_ctrl - ctrl on off for beacon
++ * @priv: Private driver interface data
++ *
++ */
++ int (*beacon_ctrl)(void *priv, u8 beacon_mode);
++
+ /**
+ * three_wire_ctrl - set three_wire_ctrl mode
+ * @priv: Private driver interface data
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index b47ab07ea..e588e7538 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14111,6 +14111,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+
+ return ret;
+ }
++static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++
++ if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
++ wpa_printf(MSG_ERROR,
++ "nl80211: Driver does not support setting beacon control");
++ return 0;
++ }
++
++ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
++ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
++
++ return ret;
++}
++
+ #endif /* CONFIG_IEEE80211AX */
+
+
+@@ -15281,6 +15314,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .set_4addr_mode = nl80211_set_4addr_mode,
+ .mu_ctrl = nl80211_mu_ctrl,
+ .mu_dump = nl80211_mu_dump,
++ .beacon_ctrl = nl80211_beacon_ctrl,
+ #ifdef CONFIG_DPP
+ .dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 707bb7fe4..9866c221c 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_amnt_vendor_cmd_avail:1;
+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ unsigned int mtk_pp_vendor_cmd_avail:1;
++ unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index ba3c0817b..f3e3d52e2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1170,6 +1170,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
+ drv->mtk_pp_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
++ drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
new file mode 100644
index 0000000..06ee95c
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
@@ -0,0 +1,64 @@
+From 817f2a256b166e07d7b0abcee789976b47224429 Mon Sep 17 00:00:00 2001
+From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
+Date: Wed, 13 Dec 2023 18:13:01 +0530
+Subject: [PATCH 074/104] mtk: hostapd: WPS added change to configure AP PIN
+ lock timout
+
+added config paramter ap_pin_lockout_time to configure
+AP PIN timeout from hosatpd.conf
+
+Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h | 1 +
+ src/ap/wps_hostapd.c | 9 ++++++---
+ 3 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 050ef290e..7bc19479d 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4191,6 +4191,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->wps_independent = atoi(pos);
+ } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+ bss->ap_setup_locked = atoi(pos);
++ } else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
++ bss->ap_pin_lockout_time = atoi(pos);
+ } else if (os_strcmp(buf, "uuid") == 0) {
+ if (uuid_str2bin(pos, bss->uuid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 40edcdaa7..7f48c71f5 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -493,6 +493,7 @@ struct hostapd_bss_config {
+ #ifdef CONFIG_WPS
+ int wps_independent;
+ int ap_setup_locked;
++ unsigned int ap_pin_lockout_time;
+ u8 uuid[16];
+ char *wps_pin_requests;
+ char *device_name;
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index dfc5c3ecb..8a6fc42b2 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+ } else if (!hapd->conf->ap_setup_locked) {
+- if (hapd->ap_pin_lockout_time == 0)
+- hapd->ap_pin_lockout_time = 60;
+- else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
++ if (hapd->ap_pin_lockout_time == 0) {
++ if (hapd->conf->ap_pin_lockout_time)
++ hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
++ else
++ hapd->ap_pin_lockout_time = 60;
++ } else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+ (hapd->ap_pin_failures % 3) == 0)
+ hapd->ap_pin_lockout_time *= 2;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
new file mode 100644
index 0000000..cfb3f3d
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
@@ -0,0 +1,45 @@
+From 76d8a7f29b56075df3ad756f65374b2b238c2120 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 23 Jan 2024 10:52:57 +0800
+Subject: [PATCH 075/104] hostapd: mtk: ACS: remove chan/freq list check when
+ scan request and factor calculation
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/acs.c | 12 ------------
+ 1 file changed, 12 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 4c4c750ab..cb4db7147 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -595,12 +595,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;
+
+@@ -1358,12 +1352,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;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
new file mode 100644
index 0000000..d4e52f8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
@@ -0,0 +1,44 @@
+From 29b7ebf83cad69c48012eb5a03eb01a2a9fbfcab Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Wed, 1 Nov 2023 19:58:05 +0800
+Subject: [PATCH 076/104] mtk: hostapd: 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.
+
+Solution:
+In the hostapd_switch_channel_fallback, need to disable acs if channel
+is valid.
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ src/ap/hostapd.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index cdbf81e38..636655ea1 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4580,6 +4580,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.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
new file mode 100644
index 0000000..16f32cf
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
@@ -0,0 +1,398 @@
+From 5a471a9025d9bf2a871339f5306e5c9050357703 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 29 Jan 2024 10:26:53 +0800
+Subject: [PATCH 077/104] Revert ACS upstream changes
+
+- 348c047af ACS: More consistent checking of the best channel pointer
+- 98f3bd26d ACS: Extend the 320 MHz support
+- 733de8568 ACS: Fix not selecting the best channel in the segment
+- dc57ede01 tests: Full validation of ACS selecting HT40- channel
+- 4881accbb ACS: Add HT40- support in the 2.4 GHz band
+- 29f38ebcf ACS: Check whether iface->current_mode is NULL before use
+- 6f014c0d0 ACS: Add 320 MHz support for EHT
+
+Note that "e6f2494c3 hostapd: Add eht_bw320_offset configuration option"
+is not reverted due to conflict.
+---
+ src/ap/acs.c | 160 +++++++++----------------------------
+ tests/hwsim/test_ap_acs.py | 19 ++---
+ 2 files changed, 41 insertions(+), 138 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index cb4db7147..cfa4a7d27 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -245,8 +245,6 @@ enum bw_type {
+ ACS_BW40,
+ ACS_BW80,
+ ACS_BW160,
+- ACS_BW320_1,
+- ACS_BW320_2,
+ };
+
+ struct bw_item {
+@@ -288,20 +286,10 @@ static const struct bw_item bw_160[] = {
+ { 6435, 6575, 111 }, { 6595, 6735, 143 },
+ { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+ };
+-static const struct bw_item bw_320_1[] = {
+- { 5955, 6255, 31 }, { 6275, 6575, 95 }, { 6595, 6895, 159 },
+- { -1, -1, -1 }
+-};
+-static const struct bw_item bw_320_2[] = {
+- { 6115, 6415, 63 }, { 6435, 6735, 127 }, { 6755, 7055, 191 },
+- { -1, -1, -1 }
+-};
+ static const struct bw_item *bw_desc[] = {
+ [ACS_BW40] = bw_40,
+ [ACS_BW80] = bw_80,
+ [ACS_BW160] = bw_160,
+- [ACS_BW320_1] = bw_320_1,
+- [ACS_BW320_2] = bw_320_2,
+ };
+
+
+@@ -773,42 +761,6 @@ static void acs_update_puncturing_bitmap(struct hostapd_iface *iface,
+ #endif /* CONFIG_IEEE80211BE */
+
+
+-static bool
+-acs_usable_bw320_chan(struct hostapd_iface *iface,
+- struct hostapd_channel_data *chan, int *bw320_offset)
+-{
+- const char *bw320_str[] = { "320 MHz", "320 MHz-1", "320 MHz-2" };
+- int conf_bw320_offset = hostapd_get_bw320_offset(iface->conf);
+-
+- *bw320_offset = 0;
+- switch (conf_bw320_offset) {
+- case 1:
+- if (acs_usable_bw_chan(chan, ACS_BW320_1))
+- *bw320_offset = 1;
+- break;
+- case 2:
+- if (acs_usable_bw_chan(chan, ACS_BW320_2))
+- *bw320_offset = 2;
+- break;
+- case 0:
+- default:
+- conf_bw320_offset = 0;
+- if (acs_usable_bw_chan(chan, ACS_BW320_1))
+- *bw320_offset = 1;
+- else if (acs_usable_bw_chan(chan, ACS_BW320_2))
+- *bw320_offset = 2;
+- break;
+- }
+-
+- if (!*bw320_offset)
+- wpa_printf(MSG_DEBUG,
+- "ACS: Channel %d: not allowed as primary channel for %s bandwidth",
+- chan->chan, bw320_str[conf_bw320_offset]);
+-
+- return *bw320_offset != 0;
+-}
+-
+-
+ static void
+ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode,
+@@ -820,18 +772,14 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
+ long double factor;
+ int i, j;
+- int bw320_offset = 0, ideal_bw320_offset = 0;
+ unsigned int k;
+- int secondary_channel = 1, freq_offset;
+-
+- if (is_24ghz_mode(mode->mode))
+- secondary_channel = iface->conf->secondary_channel;
+
+ for (i = 0; i < mode->num_channels; i++) {
+- double total_weight = 0;
++ 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
+@@ -863,7 +811,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ iface->conf->country[2] == 0x4f)
+ continue;
+
+- if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
++ if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: BW %u is not supported",
+ chan->chan, bw);
+@@ -884,8 +832,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+- iface->conf->ieee80211be)) {
++ (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_80MHZ &&
+ !acs_usable_bw_chan(chan, ACS_BW80)) {
+@@ -905,25 +852,13 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+ }
+
+- if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- iface->conf->ieee80211be) {
+- if (hostapd_get_oper_chwidth(iface->conf) ==
+- CONF_OPER_CHWIDTH_320MHZ &&
+- !acs_usable_bw320_chan(iface, chan, &bw320_offset))
+- continue;
+- }
+-
+ factor = 0;
+- best = NULL;
+- if (acs_usable_chan(chan)) {
++ if (acs_usable_chan(chan))
+ factor = chan->interference_factor;
+- total_weight = 1;
+- best = chan;
+- }
++ total_weight = 1;
+
+ for (j = 1; j < n_chans; j++) {
+- adj_chan = acs_find_chan(iface, chan->freq +
+- j * secondary_channel * 20);
++ adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ if (!adj_chan)
+ break;
+
+@@ -934,14 +869,16 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ break;
+ }
+
+- if (!acs_usable_chan(adj_chan))
+- continue;
+-
+- factor += adj_chan->interference_factor;
+- total_weight += 1;
++ 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 (!best || adj_chan->interference_factor <
++ if (update_best &&
++ adj_chan->interference_factor <
+ best->interference_factor)
+ best = adj_chan;
+ }
+@@ -954,9 +891,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+
+ /* 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 &&
+- iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- best && chan != best) {
++ 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,
+@@ -969,9 +905,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ * channel interference factor. */
+ if (is_24ghz_mode(mode->mode)) {
+ for (j = 0; j < n_chans; j++) {
+- freq_offset = j * 20 * secondary_channel;
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset - 5);
++ (j * 20) - 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -979,7 +914,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset - 10);
++ (j * 20) - 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -987,7 +922,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset + 5);
++ (j * 20) + 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -995,7 +930,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- freq_offset + 10);
++ (j * 20) + 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -1004,9 +939,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+ }
+
+- if (total_weight == 0)
+- continue;
+-
+ factor /= total_weight;
+
+ bias = NULL;
+@@ -1044,7 +976,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+
+ *ideal_factor = factor;
+ *ideal_chan = chan;
+- ideal_bw320_offset = bw320_offset;
+
+ #ifdef CONFIG_IEEE80211BE
+ if (iface->conf->ieee80211be)
+@@ -1055,13 +986,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ /* This channel would at least be usable */
+- if (!(*rand_chan)) {
++ if (!(*rand_chan))
+ *rand_chan = chan;
+- ideal_bw320_offset = bw320_offset;
+- }
+ }
+-
+- hostapd_set_and_check_bw320_offset(iface->conf, ideal_bw320_offset);
+ }
+
+
+@@ -1088,12 +1015,19 @@ 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;
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+- iface->conf->ieee80211be) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_80MHZ:
+ n_chans = 4;
+@@ -1101,9 +1035,6 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
+ case CONF_OPER_CHWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+- case CONF_OPER_CHWIDTH_320MHZ:
+- n_chans = 16;
+- break;
+ default:
+ break;
+ }
+@@ -1153,8 +1084,7 @@ static void acs_adjust_secondary(struct hostapd_iface *iface)
+ acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+ return;
+
+- wpa_printf(MSG_DEBUG,
+- "ACS: Adjusting HT/VHT/HE/EHT secondary frequency");
++ 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)
+@@ -1169,7 +1099,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ {
+ int center;
+
+- wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
++ wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+@@ -1187,29 +1117,12 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
+- break;
+- case CONF_OPER_CHWIDTH_320MHZ:
+- switch (hostapd_get_bw320_offset(iface->conf)) {
+- case 1:
+- center = acs_get_bw_center_chan(iface->freq,
+- ACS_BW320_1);
+- break;
+- case 2:
+- center = acs_get_bw_center_chan(iface->freq,
+- ACS_BW320_2);
+- break;
+- default:
+- wpa_printf(MSG_INFO,
+- "ACS: BW320 offset is not selected");
+- return;
+- }
+-
+ break;
+ default:
+ /* TODO: How can this be calculated? Adjust
+ * acs_find_ideal_chan() */
+ wpa_printf(MSG_INFO,
+- "ACS: Only VHT20/40/80/160/320 is supported now");
++ "ACS: Only VHT20/40/80/160 is supported now");
+ return;
+ }
+
+@@ -1272,8 +1185,7 @@ static void acs_study(struct hostapd_iface *iface)
+ iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
+ #endif /* CONFIG_IEEE80211BE */
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+- iface->conf->ieee80211be) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ acs_adjust_secondary(iface);
+ acs_adjust_center_freq(iface);
+ }
+diff --git a/tests/hwsim/test_ap_acs.py b/tests/hwsim/test_ap_acs.py
+index 001a5d4fd..e1359b6eb 100644
+--- a/tests/hwsim/test_ap_acs.py
++++ b/tests/hwsim/test_ap_acs.py
+@@ -205,20 +205,11 @@ def test_ap_acs_40mhz_minus(dev, apdev):
+ params['acs_num_scans'] = '1'
+ params['chanlist'] = '1 11'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+- wait_acs(hapd)
+-
+- freq = hapd.get_status_field("freq")
+- if int(freq) < 2400:
+- raise Exception("Unexpected frequency")
+- sec = hapd.get_status_field("secondary_channel")
+- if int(sec) != -1:
+- raise Exception("Unexpected secondary_channel: " + sec)
+-
+- dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+- sig = dev[0].request("SIGNAL_POLL").splitlines()
+- logger.info("SIGNAL_POLL: " + str(sig))
+- if "WIDTH=40 MHz" not in sig:
+- raise Exception("Station did not report 40 MHz bandwidth")
++ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
++ if not ev:
++ raise Exception("ACS start timed out")
++ # HT40- is not currently supported in hostapd ACS, so do not try to connect
++ # or verify that this operation succeeded.
+
+ def test_ap_acs_5ghz(dev, apdev):
+ """Automatic channel selection on 5 GHz"""
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
new file mode 100644
index 0000000..3f267b0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
@@ -0,0 +1,379 @@
+From 12fe53a7919abb791658bcc7a74b8131f0362300 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Thu, 28 Sep 2023 18:03:08 +0800
+Subject: [PATCH 078/104] mtk: hostapd: ACS: Add EHT320 and HT40- support, fix
+ issue
+
+1. Add 6G EHT320 support;
+2. Add 2.4G HT40- support;
+3. Fix issue: selected best channel is out of channels;
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ src/ap/acs.c | 191 +++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 124 insertions(+), 67 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index cfa4a7d27..1fa8b8e64 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -245,6 +245,7 @@ enum bw_type {
+ ACS_BW40,
+ ACS_BW80,
+ ACS_BW160,
++ ACS_BW320,
+ };
+
+ struct bw_item {
+@@ -286,10 +287,16 @@ static const struct bw_item bw_160[] = {
+ { 6435, 6575, 111 }, { 6595, 6735, 143 },
+ { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+ };
++static const struct bw_item bw_320[] = {
++ { 5955, 6255, 31 }, { 6115, 6415, 63 }, { 6275, 6575, 95 },
++ { 6435, 6735, 127 }, { 6595, 6895, 159 }, { 6755, 7055, 191 },
++ { -1, -1, -1 }
++};
+ static const struct bw_item *bw_desc[] = {
+ [ACS_BW40] = bw_40,
+ [ACS_BW80] = bw_80,
+ [ACS_BW160] = bw_160,
++ [ACS_BW320] = bw_320,
+ };
+
+
+@@ -769,10 +776,19 @@ 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;
+@@ -780,6 +796,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ bool update_best = 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
+@@ -811,7 +830,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 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);
+@@ -832,7 +851,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
++ (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
++ iface->conf->ieee80211be)) {
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_80MHZ &&
+ !acs_usable_bw_chan(chan, ACS_BW80)) {
+@@ -850,63 +870,86 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ chan->chan);
+ continue;
+ }
+- }
+
+- factor = 0;
+- if (acs_usable_chan(chan))
+- factor = chan->interference_factor;
+- total_weight = 1;
+-
+- 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)) {
++ if (iface->conf->ieee80211be &&
++ hostapd_get_oper_chwidth(iface->conf) ==
++ CONF_OPER_CHWIDTH_320MHZ &&
++ !acs_usable_bw_chan(chan, ACS_BW320)) {
+ wpa_printf(MSG_DEBUG,
+- "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
+- chan->chan, adj_chan->chan, bw);
+- break;
++ "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth",
++ chan->chan);
++ continue;
+ }
++ }
+
+- if (acs_usable_chan(adj_chan)) {
+- factor += adj_chan->interference_factor;
++ factor = 0;
++ total_weight = 0;
++
++ 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 (!is_in_chanlist(iface, adj_chan) || !is_in_freqlist(iface, adj_chan))
++ 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;
+- }
++ 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;
++ }
++
++ update_best = true;
++ if (acs_usable_chan(adj_chan)) {
++ factor += adj_chan->interference_factor;
++ total_weight += 1;
++ } else {
++ update_best = false;
++ }
+
+- /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+- * channel interference factor. */
+- if (is_24ghz_mode(mode->mode)) {
++ /* find the best channel in this segment */
++ if (update_best &&
++ adj_chan->interference_factor < best->interference_factor)
++ best = adj_chan;
++ }
++
++ if (j != n_chans) {
++ 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 * sec_ch_factor * 20));
++ 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 * sec_ch_factor * 20) - 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -914,7 +957,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- (j * 20) - 10);
++ (j * sec_ch_factor * 20) - 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -922,7 +965,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- (j * 20) + 5);
++ (j * sec_ch_factor * 20) + 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -930,7 +973,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+- (j * 20) + 10);
++ (j * sec_ch_factor * 20) + 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+@@ -939,7 +982,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) {
+@@ -958,11 +1002,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);
+ }
+@@ -1015,19 +1059,12 @@ 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;
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
++ iface->conf->ieee80211be) {
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_80MHZ:
+ n_chans = 4;
+@@ -1037,6 +1074,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
+ break;
+ default:
+ break;
++ /* 320 is supported only in 6GHz 11be mode */
+ }
+ }
+
+@@ -1057,7 +1095,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);
+
+ #ifdef CONFIG_IEEE80211BE
+@@ -1072,6 +1110,21 @@ bw_selected:
+ return rand_chan;
+ }
+
++static int acs_get_center_freq_320mhz(int channel)
++{
++ if (channel >= 1 && channel <= 45)
++ return 31;
++ else if (channel >= 49 && channel <= 77)
++ return 63;
++ else if (channel >= 81 && channel <= 109)
++ return 95;
++ else if (channel >= 113 && channel <= 141)
++ return 127;
++ else if (channel >= 145 && channel <= 173)
++ return 159;
++ else
++ return 191;
++}
+
+ static void acs_adjust_secondary(struct hostapd_iface *iface)
+ {
+@@ -1099,7 +1152,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ {
+ int center;
+
+- wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
++ wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
+
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+@@ -1115,6 +1168,9 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ case CONF_OPER_CHWIDTH_80MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
+ break;
++ case CONF_OPER_CHWIDTH_320MHZ:
++ center = acs_get_center_freq_320mhz(iface->conf->channel);
++ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
+ break;
+@@ -1122,7 +1178,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ /* TODO: How can this be calculated? Adjust
+ * acs_find_ideal_chan() */
+ wpa_printf(MSG_INFO,
+- "ACS: Only VHT20/40/80/160 is supported now");
++ "ACS: Only VHT20/40/80/160 EHT320 is supported now");
+ return;
+ }
+
+@@ -1185,7 +1241,8 @@ static void acs_study(struct hostapd_iface *iface)
+ iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
+ #endif /* CONFIG_IEEE80211BE */
+
+- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
++ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
++ iface->conf->ieee80211be) {
+ acs_adjust_secondary(iface);
+ acs_adjust_center_freq(iface);
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
new file mode 100644
index 0000000..a28db10
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
@@ -0,0 +1,122 @@
+From 26c23f7dc1fe47e22ceab581b7abed089148c68f Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 13:38:11 +0800
+Subject: [PATCH 079/104] mtk: hostapd: initialize i802_bss's flink->freq with
+ iface freq.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 6 +++---
+ src/ap/ap_drv_ops.h | 2 +-
+ src/ap/hostapd.c | 2 +-
+ src/drivers/driver.h | 2 +-
+ src/drivers/driver_nl80211.c | 4 ++--
+ wpa_supplicant/driver_i.h | 2 +-
+ 6 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index d6bd157d8..b7896c110 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -368,7 +368,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+ return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+- NULL, NULL, force_ifname, if_addr, NULL, 0);
++ NULL, NULL, force_ifname, if_addr, NULL, 0, hapd->iface->freq);
+ }
+
+
+@@ -560,13 +560,13 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+- const char *bridge, int use_existing)
++ const char *bridge, int use_existing, int freq)
+ {
+ if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+ return -1;
+ return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+ bss_ctx, drv_priv, force_ifname, if_addr,
+- bridge, use_existing, 1);
++ bridge, use_existing, 1, freq);
+ }
+
+
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 78e5c8d5a..5830705a3 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -58,7 +58,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+- const char *bridge, int use_existing);
++ const char *bridge, int use_existing, int freq);
+ int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname);
+ int hostapd_if_link_remove(struct hostapd_data *hapd,
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 636655ea1..e4fc1f85a 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1433,7 +1433,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ conf->iface, addr, hapd,
+ &hapd->drv_priv, force_ifname, if_addr,
+ conf->bridge[0] ? conf->bridge : NULL,
+- first == -1)) {
++ first == -1, hapd->iface->freq)) {
+ wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+ MACSTR ")", MAC2STR(hapd->own_addr));
+ hapd->interface_added = 0;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 332a51c55..2940650df 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3866,7 +3866,7 @@ struct wpa_driver_ops {
+ int (*if_add)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+- const char *bridge, int use_existing, int setup_ap);
++ const char *bridge, int use_existing, int setup_ap, int freq);
+
+ /**
+ * if_remove - Remove a virtual interface
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index e588e7538..3d69c9c49 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8872,7 +8872,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ void *bss_ctx, void **drv_priv,
+ char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing,
+- int setup_ap)
++ int setup_ap, int freq)
+ {
+ enum nl80211_iftype nlmode;
+ struct i802_bss *bss = priv;
+@@ -8992,7 +8992,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ new_bss->valid_links = 0;
+ os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
+
+- new_bss->flink->freq = drv->first_bss->flink->freq;
++ new_bss->flink->freq = (freq == -1) ? drv->first_bss->flink->freq : freq;
+ new_bss->ctx = bss_ctx;
+ new_bss->added_if = added;
+ drv->first_bss->next = new_bss;
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index 663e16053..624192ebd 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -446,7 +446,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
+ if (wpa_s->driver->if_add)
+ return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
+ addr, bss_ctx, NULL, force_ifname,
+- if_addr, bridge, 0, 0);
++ if_addr, bridge, 0, 0, -1);
+ return -1;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
new file mode 100644
index 0000000..42c06e4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
@@ -0,0 +1,35 @@
+From d06dd0d45977ce098df718a29ffbc765896a2758 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Jan 2024 11:24:28 +0800
+Subject: [PATCH 080/104] mtk: hostapd: fix mld_assoc_link_id
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/hostapd.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index e4fc1f85a..f8b05de45 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4034,11 +4034,13 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+
+ #ifdef CONFIG_IEEE80211BE
+- if (ap_sta_is_mld(hapd, sta) &&
+- sta->mld_assoc_link_id != hapd->mld_link_id)
+- return;
++ if (ap_sta_is_mld(hapd, sta)) {
++ if (sta->mld_assoc_link_id != hapd->mld_link_id)
++ return;
++ mld_assoc_link_id = sta->mld_assoc_link_id;
++ }
+ #endif /* CONFIG_IEEE80211BE */
+- if (mld_assoc_link_id != -2)
++ if (mld_assoc_link_id != -2)
+ hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
+
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
new file mode 100644
index 0000000..7bdb4fe
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
@@ -0,0 +1,25 @@
+From 3dbd0105364c15225a18098eeaae58119490918d Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:48:11 +0800
+Subject: [PATCH 081/104] mtk: wpa_s: correctly get assoc frequency
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 6631285bf..90084356d 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -328,6 +328,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+ wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ }
+
++ drv->assoc_freq = nl80211_get_assoc_freq(drv);
+ event.assoc_info.freq = drv->assoc_freq;
+ drv->first_bss->flink->freq = drv->assoc_freq;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
new file mode 100644
index 0000000..8ddecc9
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
@@ -0,0 +1,29 @@
+From 50e36560d14dbb6bf38b46dcfc58f9414e56b283 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:51:55 +0800
+Subject: [PATCH 082/104] mtk: wpa_s: force MLD STA to use SAE H2E during
+ authentication
+
+Otherwise the MLD STA setup will fail with hostapd MLD AP.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index f08184f98..e1183722f 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -199,7 +199,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
+ if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ use_pt = 1;
+- if (bss && is_6ghz_freq(bss->freq) &&
++ if (bss && (is_6ghz_freq(bss->freq) || !is_zero_ether_addr(bss->mld_addr)) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ use_pt = 1;
+ #ifdef CONFIG_SAE_PK
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
new file mode 100644
index 0000000..b3144cb
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
@@ -0,0 +1,71 @@
+From fb3820ff9fff1b15c13d4a799fbef8932fda7a1b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 11 Dec 2023 17:02:05 +0800
+Subject: [PATCH 083/104] mtk: hostapd: extend ap_get_sta() to find the correct
+ sta
+
+There're still some mld address tranlation issues that need to be dealt
+with on driver side (e.g. RX eapol frames). So add the code that find
+station also with link address and across hapds at the moment.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 1 +
+ src/ap/sta_info.c | 16 ++++++++++++++++
+ src/ap/sta_info.h | 1 +
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index ce3874901..0f357d786 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3116,6 +3116,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ mgmt->sa, ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].local_addr,
+ hapd->own_addr, ETH_ALEN);
++ os_memcpy(sta->setup_link_addr, mgmt->sa, ETH_ALEN);
+ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index ee6e20538..e9fa0ed6e 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -73,6 +73,22 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ s = hapd->sta_hash[STA_HASH(sta)];
+ while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ s = s->hnext;
++
++ if (hapd->conf->mld_ap && !s) {
++ u8 link_id;
++
++ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++ struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
++
++ if (!h)
++ continue;
++
++ for (s = h->sta_list; s; s = s->next)
++ if (!os_memcmp(s->setup_link_addr, sta, 6))
++ return s;
++ }
++ }
++
+ return s;
+ }
+
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 38b80903d..cd89db6c8 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -102,6 +102,7 @@ struct sta_info {
+ struct sta_info *next; /* next entry in sta list */
+ struct sta_info *hnext; /* next entry in hash table list */
+ u8 addr[6];
++ u8 setup_link_addr[6];
+ be32 ipaddr;
+ struct dl_list ip6addr; /* list head for struct ip6addr */
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
new file mode 100644
index 0000000..650da0e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
@@ -0,0 +1,39 @@
+From 665bc7cb59b4383aab615fff82fa601c468a4634 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 18 Dec 2023 18:53:35 +0800
+Subject: [PATCH 084/104] mtk: hostapd: update cookie only when noack is unset
+
+This can prevent cookie unmatched problems during setup.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 3d69c9c49..6d300c0c8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4472,7 +4472,7 @@ send_frame_cmd:
+ res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
+ use_cookie, no_cck, noack, offchanok,
+ csa_offs, csa_offs_len, link_id);
+- if (!res)
++ if (!res && !noack)
+ drv->send_frame_link_id = link_id;
+
+ return res;
+@@ -9205,8 +9205,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
+ "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+ (long long unsigned int) cookie);
+
+- if (save_cookie)
+- drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
++ if (save_cookie && !no_ack)
++ drv->send_frame_cookie = cookie;
+
+ if (!wait) {
+ /* There is no need to store this cookie since there
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
new file mode 100644
index 0000000..820e085
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
@@ -0,0 +1,34 @@
+From e2e07813d1e05a72aa649614ad942036b387aaf1 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 29 Dec 2023 15:04:27 +0800
+Subject: [PATCH 085/104] mtk: wpa_s: fix bss selection when setting
+ mld_connect_band_pref
+
+Without this patch, when setting mld_connect_band_pref as 5g, wrong bss
+will be selected.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index e1183722f..5b69812b5 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -437,8 +437,11 @@ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
+ }
+
+ for_each_link(wpa_s->valid_links, i) {
+- if (wpa_s->mlo_assoc_link_id == i)
++ if (wpa_s->mlo_assoc_link_id == i) {
++ if (bss->freq >= low && bss->freq <= high)
++ return bss;
+ continue;
++ }
+
+ if (wpa_s->links[i].freq >= low && wpa_s->links[i].freq <= high)
+ goto found;
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
new file mode 100644
index 0000000..3cf864d
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
@@ -0,0 +1,42 @@
+From bcb603194f7df4fd3060ed6a13a2e4da2715d959 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 26 Dec 2023 08:05:41 +0800
+Subject: [PATCH 086/104] mtk: hostapd: add mld_primary option
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h | 3 +++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 7bc19479d..e9caa45f3 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5349,6 +5349,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ conf->punct_acs_threshold = val;
+ } else if (os_strcmp(buf, "mld_ap") == 0) {
+ bss->mld_ap = !!atoi(pos);
++ } else if (os_strcmp(buf, "mld_primary") == 0) {
++ bss->mld_primary = !!atoi(pos);
+ } else if (os_strcmp(buf, "mld_addr") == 0) {
+ if (hwaddr_aton(pos, bss->mld_addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 7f48c71f5..1f686550e 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -966,6 +966,9 @@ struct hostapd_bss_config {
+ /* The AP is part of an AP MLD */
+ u8 mld_ap;
+
++ /* The AP is the primary AP of an AP MLD */
++ u8 mld_primary;
++
+ /* The MLD ID to which the AP MLD is affiliated with */
+ u8 mld_id;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
new file mode 100644
index 0000000..9634a0c
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
@@ -0,0 +1,104 @@
+From d6e854c62cd825756cc1b46c8b006855cf9e057e Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 6 Mar 2024 15:01:33 +0800
+Subject: [PATCH 087/104] mtk: wpa_supplicant: add 'mld_allowed_phy'
+ configuration option for MLD STA
+
+A new configuration option named 'mld_allowed_phy' is added for MLD STA.
+This option indicates the bitmap of allowed phy for MLO connection.
+Note that setting 'mld_allowed_phy' to 0 makes no phy allowed for MLO.
+In other word, the STA becomes a legacy STA.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I5ad15d3748c6fef476aa067cc4901157a96f8804
+---
+ wpa_supplicant/config.c | 1 +
+ wpa_supplicant/config.h | 1 +
+ wpa_supplicant/config_file.c | 2 ++
+ wpa_supplicant/sme.c | 18 ++++++++++++++++++
+ 4 files changed, 22 insertions(+)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index 7bb57e2ab..d3c75ee94 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -5680,6 +5680,7 @@ static const struct global_parse_data global_fields[] = {
+ #endif /* CONFIG_PASN */
+ #ifdef CONFIG_TESTING_OPTIONS
+ { INT_RANGE(mld_force_single_link, 0, 1), 0 },
++ { INT_RANGE(mld_allowed_phy, 0, 7), 0 },
+ { INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 },
+ { FUNC(mld_connect_bssid_pref), 0 },
+ #endif /* CONFIG_TESTING_OPTIONS */
+diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
+index 8981305c2..c0164fa76 100644
+--- a/wpa_supplicant/config.h
++++ b/wpa_supplicant/config.h
+@@ -1800,6 +1800,7 @@ struct wpa_config {
+ u8 mld_connect_bssid_pref[ETH_ALEN];
+
+ int mld_force_single_link;
++ u8 mld_allowed_phy; /* bitmap of allowed phy for MLO connection */
+ #endif /* CONFIG_TESTING_OPTIONS */
+ };
+
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index 7a3ed6373..875d00bb4 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -1622,6 +1622,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
+ #ifdef CONFIG_TESTING_OPTIONS
+ if (config->mld_force_single_link)
+ fprintf(f, "mld_force_single_link=1\n");
++ if (config->mld_allowed_phy)
++ fprintf(f, "mld_allowed_phy=%u\n", config->mld_allowed_phy);
+ if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO)
+ fprintf(f, "mld_connect_band_pref=%d\n",
+ config->mld_connect_band_pref);
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index 5b69812b5..ef258fadc 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -517,6 +517,16 @@ out:
+ }
+
+
++#ifdef CONFIG_TESTING_OPTIONS
++static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
++{
++ return ((wpa_s->conf->mld_allowed_phy & BIT(0)) && IS_2P4GHZ(freq)) ||
++ ((wpa_s->conf->mld_allowed_phy & BIT(1)) && IS_5GHZ(freq)) ||
++ ((wpa_s->conf->mld_allowed_phy & BIT(2)) && is_6ghz_freq(freq));
++}
++#endif /* CONFIG_TESTING_OPTIONS */
++
++
+ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+ {
+@@ -528,6 +538,11 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ for_each_link(bss->valid_links, i) {
+ const u8 *bssid = bss->mld_links[i].bssid;
+
++#ifdef CONFIG_TESTING_OPTIONS
++ if (!check_mld_allowed_phy(wpa_s, bss->mld_links[i].freq))
++ continue;
++#endif /* CONFIG_TESTING_OPTIONS */
++
+ wpa_s->valid_links |= BIT(i);
+ os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
+ wpa_s->links[i].freq = bss->mld_links[i].freq;
+@@ -577,6 +592,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
+ !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
+ NULL, ssid, NULL) &&
++#ifdef CONFIG_TESTING_OPTIONS
++ wpa_s->conf->mld_allowed_phy &&
++#endif /* CONFIG_TESTING_OPTIONS */
+ bss->valid_links) {
+ wpa_printf(MSG_DEBUG, "MLD: In authentication");
+ wpas_sme_set_mlo_links(wpa_s, bss);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
new file mode 100644
index 0000000..a04abae
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
@@ -0,0 +1,286 @@
+From a5b5d12eac0a1a4c53de67adc5793dbee87cb769 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 29 Feb 2024 19:55:34 +0800
+Subject: [PATCH 088/104] mtk: hostapd: support band_idx option for
+ set_mu/get_mu vendor command
+
+Support band_idx for set_mu and get_mu vendor command. The usage shows
+as below:
+1. get_mu: $ hostapd_cli -i <intf> get_mu <band_idx>
+2. set_mu: $ hostapd_cli -i <intf> set_mu <mu_onff>:<band_idx>
+
+CR-Id: WCNCR00240772
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Change-Id: I58635e588225a8876a77346c417ec281f9b76e4c
+---
+ hostapd/config_file.c | 9 +++++
+ hostapd/ctrl_iface.c | 78 ++++++++++++++++++++++++++++--------
+ hostapd/hostapd_cli.c | 2 +-
+ src/ap/ap_config.h | 1 +
+ src/ap/ap_drv_ops.c | 2 +-
+ src/common/mtk_vendor.h | 1 +
+ src/drivers/driver.h | 2 +-
+ src/drivers/driver_nl80211.c | 15 +++----
+ 8 files changed, 82 insertions(+), 28 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index e9caa45f3..ef9bafb28 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5431,6 +5431,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ return 1;
+ }
+ conf->pp_mode = (u8) val;
++ } else if (os_strcmp(buf, "band_idx") == 0) {
++ int val = atoi(pos);
++
++ if (val < 0) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid band_idx value",
++ line);
++ return 1;
++ }
++ conf->band_idx = (u8) val;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 0fded7ed4..c5540f5fd 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4178,17 +4178,42 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ value = pos;
+
+ if (os_strcmp(config, "onoff") == 0) {
+- int mu = atoi(value);
+- if (mu < 0 || mu > 15) {
+- wpa_printf(MSG_ERROR, "Invalid value for mu");
+- return -1;
++ cnt = hostapd_parse_argument_helper(value, &val);
++ if (cnt == -1)
++ goto fail;
++ if (cnt < 1 || val[0] > 15)
++ goto para_fail;
++
++ if (hostapd_is_mld_ap(hapd)) {
++ u8 band_idx;
++
++ if (cnt != 2)
++ goto para_fail;
++
++ band_idx = val[1];
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ struct hostapd_iface *iface;
++
++ iface = hapd->iface->interfaces->iface[i];
++ if (!iface || !iface->conf)
++ continue;
++
++ if (iface->conf->band_idx == band_idx) {
++ hapd = iface->bss[0];
++ break;
++ }
++ }
++ if (hapd->iface->conf->band_idx != band_idx)
++ goto para_fail;
+ }
+- hapd->iconf->mu_onoff = (u8) mu;
+
+- if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
+- return os_snprintf(buf, buflen, "OK\n");
+- else
++ hapd->iconf->mu_onoff = val[0];
++ os_free(val);
++ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) != 0)
+ goto fail;
++
++ return os_snprintf(buf, buflen, "OK\n");
+ }
+
+ if (hapd->iconf->muru_config == NULL)
+@@ -4200,6 +4225,7 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ comm = &muru->comm;
+
+ if (os_strncmp(config, "update", 6) == 0) {
++ // [ToDo] "update" needs to support band_idx argument
+ ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
+
+ os_free(hapd->iconf->muru_config);
+@@ -4342,15 +4368,14 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+
+ para_fail:
+ os_free(val);
+- wpa_printf(MSG_ERROR, "Incorrect input number\n");
++ wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
+ fail:
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+-
+ static int
+-hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+- size_t buflen)
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *input, char *buf,
++ size_t buflen)
+ {
+ u8 mu_onoff;
+ char *pos, *end;
+@@ -4358,14 +4383,35 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ pos = buf;
+ end = buf + buflen;
+
++ if (hostapd_is_mld_ap(hapd)) {
++ u8 band_idx, i;
++
++ band_idx = (u8)atoi(input);
++
++ for (i = 0; i < hapd->iface->interfaces->count; i++) {
++ struct hostapd_iface *iface;
++
++ iface = hapd->iface->interfaces->iface[i];
++ if (!iface || !iface->conf)
++ continue;
++
++ if (iface->conf->band_idx == band_idx) {
++ hapd = iface->bss[0];
++ break;
++ }
++ }
++ if (hapd->iface->conf->band_idx != band_idx)
++ return os_snprintf(pos, end - pos, "Invalid band idx to get_mu\n");
++ }
++
+ if (hapd->iface->state != HAPD_IFACE_ENABLED)
+ return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
+ hostapd_state_text(hapd->iface->state));
+
+ if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
+ hapd->iconf->mu_onoff = mu_onoff;
+- return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
+- !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++ return os_snprintf(pos, end - pos, "Band idx %u: UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++ hapd->iconf->band_idx, !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
+ } else {
+ wpa_printf(MSG_INFO, "ctrl iface failed to call");
+ return -1;
+@@ -5488,8 +5534,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ reply_size);
+ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+- } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "GET_MU ", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_get_mu(hapd, buf + 7, reply, reply_size);
+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 7e4485cb8..100896c34 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1461,7 +1461,7 @@ static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
+ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+- return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++ return hostapd_cli_cmd(ctrl, "GET_MU", 0, argc, argv);
+ }
+
+ static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 1f686550e..3bd8df9ce 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1298,6 +1298,7 @@ struct hostapd_config {
+ u8 amsdu;
+ void *muru_config;
+ u8 pp_mode;
++ u8 band_idx;
+ };
+
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index b7896c110..ac7ef00cd 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1281,7 +1281,7 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ {
+ if (!hapd->driver || !hapd->driver->mu_dump)
+ return 0;
+- return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff, hapd->iconf->band_idx);
+ }
+
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 5531802b8..1e4c3670e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -207,6 +207,7 @@ enum mtk_vendor_attr_mu_ctrl {
+ * above data structure.
+ */
+ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++ MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 2940650df..10ae48729 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5241,7 +5241,7 @@ struct wpa_driver_ops {
+ *
+ */
+ int (*mu_ctrl)(void *priv, u8 mode, void *config);
+- int (*mu_dump)(void *priv, u8 *mu_onoff);
++ int (*mu_dump)(void *priv, u8 *mu_onoff, u8 band_idx);
+
+ /**
+ * beacon_ctrl - ctrl on off for beacon
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 6d300c0c8..17aaa16a8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14010,7 +14010,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+
+ switch (mode) {
+ case MU_CTRL_ONOFF:
+- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, cfg->band_idx))
+ goto fail;
+ break;
+ case MU_CTRL_UPDATE:
+@@ -14074,7 +14075,7 @@ static int mu_dump_handler(struct nl_msg *msg, void *arg)
+ return 0;
+ }
+
+-static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff, u8 band_idx)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14090,17 +14091,13 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+
+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++ !(attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, band_idx)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+- attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+- if (!attr) {
+- nlmsg_free(msg);
+- return -1;
+- }
+-
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
new file mode 100644
index 0000000..fab4ef8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
@@ -0,0 +1,134 @@
+From a0cbcd04400458bf7f4f4086beecbf8db6800c36 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:11:05 +0800
+Subject: [PATCH 089/104] mtk: hostapd: Handle DFS radar detection in MLO
+
+To handle DFS CAC in MLO, we add the following changes:
+1. Add link id info to radar detect cmd for the kernel to use the correct link.
+2. Block RNR IE for disabled iface. (the EID len would be wrong without it)
+3. Only flush the old stations for the first BSS; otherwise, after DFS CAC
+stations would be flushed again.
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Idbeba92aeadd8b34f61202e4c3df52bae19d9b73
+
+Add background radar handling
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 9 +++++++++
+ src/ap/hostapd.c | 3 +++
+ src/ap/ieee802_11.c | 3 +++
+ src/drivers/driver_nl80211.c | 18 ++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_event.c | 3 ++-
+ 6 files changed, 36 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index ac7ef00cd..9357ce7b6 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1012,6 +1012,15 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ return -1;
+ }
+ data.radar_background = radar_background;
++ data.link_id = -1;
++
++#ifdef CONFIG_IEEE80211BE
++ if (hapd->conf->mld_ap) {
++ data.link_id = hapd->mld_link_id;
++ wpa_printf(MSG_DEBUG,
++ "hostapd_start_dfs_cac: link_id=%d", data.link_id);
++ }
++#endif /* CONFIG_IEEE80211BE */
+
+ res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ if (!res) {
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f8b05de45..8e3f0b281 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1371,6 +1371,9 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ if (!hostapd_mld_is_first_bss(hapd))
+ wpa_printf(MSG_DEBUG,
+ "MLD: %s: Setting non-first BSS", __func__);
++ else
++ /* Only flush old stations when setting up first BSS for MLD. */
++ flush_old_stations = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+ __func__, hapd, conf->iface, first);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 0f357d786..fe0a5bce4 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7852,6 +7852,9 @@ u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
++ if (!iface->bss[0]->started)
++ continue;
++
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ current_len, NULL, false);
+ }
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 17aaa16a8..ad73b4ac1 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -10589,6 +10589,24 @@ static int nl80211_start_radar_detection(void *priv,
+ return -1;
+ }
+
++ if (freq->link_id != NL80211_DRV_LINK_ID_NA) {
++ wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for radar detect",
++ freq->link_id);
++
++ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
++ nlmsg_free(msg);
++ return -ENOBUFS;
++ }
++
++ if (freq->radar_background) {
++ struct i802_link *link = nl80211_get_link(bss, freq->link_id);
++
++ link->background_freq = freq->freq;
++ } else {
++ nl80211_link_set_freq(bss, freq->link_id, freq->freq);
++ }
++ }
++
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret == 0)
+ return 0;
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 9866c221c..a0a62e536 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -56,6 +56,7 @@ struct i802_link {
+ unsigned int beacon_set:1;
+
+ int freq;
++ int background_freq;
+ int bandwidth;
+ u8 addr[ETH_ALEN];
+ void *ctx;
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 90084356d..03bad4bb3 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1631,7 +1631,8 @@ nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
+ unsigned int i;
+
+ for_each_link(bss->valid_links, i) {
+- if ((unsigned int) bss->links[i].freq == freq)
++ if ((unsigned int) bss->links[i].freq == freq ||
++ (unsigned int) bss->links[i].background_freq == freq)
+ return i;
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
new file mode 100644
index 0000000..8f3096b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
@@ -0,0 +1,60 @@
+From d71c29484010bcb0bda82eb529689d0748bd653e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 31 Jan 2024 10:39:08 +0800
+Subject: [PATCH 090/104] mtk: hostapd: add link id to hostapd cli chan switch
+
+temporary workaround for mlo channel switch
+
+CR-Id: WCNCR00274293
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 3 +--
+ hostapd/hostapd_cli.c | 2 +-
+ src/ap/ctrl_iface_ap.c | 2 ++
+ 3 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index c5540f5fd..f0c990314 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2801,10 +2801,9 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ if (ret)
+ return ret;
+
+- settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ if (iface->num_bss && iface->bss[0]->conf->mld_ap)
+- settings.link_id = iface->bss[0]->mld_link_id;
++ iface = iface->interfaces->iface[settings.link_id];
+ #endif /* CONFIG_IEEE80211BE */
+
+ ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 100896c34..acfa3b1d1 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1195,7 +1195,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
+ printf("Invalid chan_switch command: needs at least two "
+ "arguments (count and freq)\n"
+ "usage: <cs_count> <freq> [sec_channel_offset=] "
+- "[center_freq1=] [center_freq2=] [bandwidth=] "
++ "[center_freq1=] [center_freq2=] [bandwidth=] [link_id=] "
+ "[blocktx] [ht|vht|he|eht]\n");
+ return -1;
+ }
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index b92311e32..ca4e3e7a4 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1108,6 +1108,8 @@ int hostapd_parse_csa_settings(const char *pos,
+ } \
+ } while (0)
+
++ SET_CSA_SETTING(link_id);
++ settings->link_id = settings->freq_params.link_id;
+ SET_CSA_SETTING(center_freq1);
+ SET_CSA_SETTING(center_freq2);
+ SET_CSA_SETTING(bandwidth);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
new file mode 100644
index 0000000..1ce57ff
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
@@ -0,0 +1,160 @@
+From 11e9653a0df41e119ff5acad3ffcbd2825690413 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 16 Jan 2024 16:22:17 +0800
+Subject: [PATCH 091/104] mtk: wifi: hostapd: add wds mlo support
+
+1. Add mld_assoc_sta to get the primary sta_info.
+2. Find hapd according to mld address.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: Ic2d67c0623299dfc00021925296c54e25fe86959
+---
+ src/ap/drv_callbacks.c | 3 ++-
+ src/ap/ieee802_11.c | 8 ++++++++
+ src/ap/sta_info.c | 6 +++++-
+ src/ap/sta_info.h | 1 +
+ src/drivers/driver.h | 3 +++
+ src/drivers/driver_nl80211.c | 14 ++++++++++++++
+ 6 files changed, 33 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 2d946afd6..27c7555a1 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1762,6 +1762,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
+ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ const u8 *bssid, int link_id)
+ {
++ struct hostapd_data *ret = NULL;
+ size_t i;
+
+ if (bssid == NULL)
+@@ -1797,7 +1798,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ #endif /*CONFIG_IEEE80211BE */
+ }
+
+- return NULL;
++ return ret;
+ }
+
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fe0a5bce4..688393422 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3105,6 +3105,7 @@ static void handle_auth(struct hostapd_data *hapd,
+
+ ap_sta_set_mld(sta, true);
+ sta->mld_assoc_link_id = link_id;
++ sta->mld_assoc_sta = sta;
+
+ /*
+ * Set the MLD address as the station address and the
+@@ -4479,6 +4480,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+
+ sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
++ sta->mld_assoc_sta = origin_sta;
+
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ if (status != WLAN_STATUS_SUCCESS) {
+@@ -6957,6 +6959,12 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, src);
++
++#ifdef CONFIG_IEEE80211BE
++ if (sta && sta->mld_info.mld_sta)
++ sta = sta->mld_assoc_sta;
++#endif
++
+ if (sta &&
+ ((sta->flags & WLAN_STA_ASSOC) ||
+ ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index e9fa0ed6e..2b8307a27 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -74,6 +74,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ s = s->hnext;
+
++#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && !s) {
+ u8 link_id;
+
+@@ -84,10 +85,13 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ continue;
+
+ for (s = h->sta_list; s; s = s->next)
+- if (!os_memcmp(s->setup_link_addr, sta, 6))
++ if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
++ !os_memcmp(s->addr, sta, 6)) &&
++ s->flags & WLAN_STA_ASSOC)
+ return s;
+ }
+ }
++#endif
+
+ return s;
+ }
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index cd89db6c8..8e500ec9a 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -334,6 +334,7 @@ struct sta_info {
+ #ifdef CONFIG_IEEE80211BE
+ struct mld_info mld_info;
+ u8 mld_assoc_link_id;
++ struct sta_info *mld_assoc_sta;
+ #endif /* CONFIG_IEEE80211BE */
+ };
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 10ae48729..ba61f5842 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5339,6 +5339,9 @@ struct wpa_driver_ops {
+ * @pp_mode: Value is defined in enum pp_mode
+ */
+ int (*pp_mode_set)(void *priv, const u8 pp_mode);
++#ifdef CONFIG_IEEE80211BE
++ int (*get_mld_addr)(void *priv, u8 *addr);
++#endif
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index ad73b4ac1..df4a7ec41 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15181,6 +15181,17 @@ fail:
+ return -ENOBUFS;
+ }
+
++#ifdef CONFIG_IEEE80211BE
++static int nl80211_get_mld_addr(void *priv, u8 *addr)
++{
++ struct i802_bss *bss = priv;
++
++ os_memcpy(addr, bss->addr, ETH_ALEN);
++
++ return 0;
++}
++#endif
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15361,4 +15372,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .amnt_dump = nl80211_amnt_dump,
+ .background_radar_mode = nl80211_background_radar_mode,
+ .pp_mode_set = nl80211_pp_mode_set,
++#ifdef CONFIG_IEEE80211BE
++ .get_mld_addr = nl80211_get_mld_addr,
++#endif
+ };
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
new file mode 100644
index 0000000..607e260
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
@@ -0,0 +1,29 @@
+From ce0ccc758fc8a5076ce3476f627b00019cf90ab1 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Mar 2024 16:59:53 +0800
+Subject: [PATCH 093/104] mtk: hostapd: prevent getting non-MLD STA for other
+ links
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/sta_info.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 2b8307a27..9a8510980 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -87,7 +87,8 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ for (s = h->sta_list; s; s = s->next)
+ if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
+ !os_memcmp(s->addr, sta, 6)) &&
+- s->flags & WLAN_STA_ASSOC)
++ s->flags & WLAN_STA_ASSOC &&
++ s->mld_info.mld_sta)
+ return s;
+ }
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
new file mode 100644
index 0000000..a5e6812
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
@@ -0,0 +1,40 @@
+From 8c4eb9b740a9f2ae57e048edbbc4aad1d62734f2 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 27 Feb 2024 15:04:35 +0800
+Subject: [PATCH 094/104] mtk: hostapd: AP MLD: specify link id for unicast
+ DEAUTH
+
+When deauthenticating the STA, hostapd should specifies the setup link
+of the target STA so that the TX status of the DEAUTH can be forwarded
+to the correct link (BSS).
+
+(The original gerrit somehow disappears, so I commit it again)
+(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/hostapd/+/8715613)
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I938f0fb80862074b95fc33b3c4f566f92ae21de1
+---
+ src/ap/ap_drv_ops.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 9357ce7b6..2c535f24a 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -867,7 +867,11 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+- link_id = hapd->mld_link_id;
++ if (sta)
++ link_id = sta->mld_assoc_link_id;
++ else
++ link_id = hapd->mld_link_id;
++
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld->mld_addr;
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
new file mode 100644
index 0000000..90da0fd
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
@@ -0,0 +1,38 @@
+From 161c066295847f1c1828f641e789565fe4bacd11 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 14 Mar 2024 14:31:28 +0800
+Subject: [PATCH 095/104] mtk: hostapd: using MLD addr as SA/BSSID for
+ broadcast DEAUTH
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-Id: I4ba7763a0f5c5a16174cf80393b5f1588a9ce42c
+---
+ src/ap/ap_drv_ops.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2c535f24a..3c1609656 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -872,7 +872,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ else
+ link_id = hapd->mld_link_id;
+
+- if (ap_sta_is_mld(hapd, sta))
++ if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ own_addr = hapd->mld->mld_addr;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+@@ -893,7 +893,7 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+- if (ap_sta_is_mld(hapd, sta))
++ if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ own_addr = hapd->mld->mld_addr;
+ }
+ #endif /* CONFIG_IEEE80211BE */
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
new file mode 100644
index 0000000..6fd7d62
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
@@ -0,0 +1,32 @@
+From eb9916b7e0226793f14616dc98fda76c4d8337bf Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 27 Feb 2024 15:32:06 +0800
+Subject: [PATCH 096/104] mtk: hostapd: support vht bfee sts can be up to 0x4
+
+Without this commit, the maximum vht bfee sts can only be 0x3. This commit
+support to read BF-ANTENNA-5 to set vht bfee sts capability as 4.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Change-Id: I3ec1dc28149961bbb01c31a12cd4acd0fd77c2f4
+---
+ hostapd/config_file.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index ef9bafb28..b9a062193 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1190,6 +1190,9 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ if (os_strstr(capab, "[BF-ANTENNA-4]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
++ if (os_strstr(capab, "[BF-ANTENNA-5]") &&
++ (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
++ conf->vht_capab |= (4 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
new file mode 100644
index 0000000..1afcf3e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
@@ -0,0 +1,54 @@
+From c1e7c5e556cd372594a789091d2ab65a9d8dd56f Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 14:08:50 +0800
+Subject: [PATCH 097/104] mtk: hostapd: fix issue that tx status handle with
+ unmatch hostapd_data.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ieee802_11.c | 11 ++++++++++-
+ src/drivers/driver_nl80211_event.c | 2 +-
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 688393422..2dd763e63 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6536,11 +6536,20 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta) &&
+ hapd->mld_link_id != sta->mld_assoc_link_id) {
++ struct hostapd_data *temp_hapd = hapd;
++
+ /* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
+ wpa_printf(MSG_DEBUG,
+ "%s: MLD: ignore on link station (%d != %d)",
+ __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
+- return;
++
++ if (temp_hapd->conf->mld_ap && sta->mld_assoc_link_id >= 0) {
++ struct hostapd_data *link_bss;
++
++ link_bss = hostapd_mld_get_link_bss(temp_hapd, sta->mld_assoc_link_id);
++ if (link_bss)
++ hapd = link_bss;
++ }
+ }
+ #endif /* CONFIG_IEEE80211BE */
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 03bad4bb3..885fc4c9e 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1374,7 +1374,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ event.rx_mgmt.ctx = bss->ctx;
+ event.rx_mgmt.link_id = link_id;
+
+- wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
++ wpa_supplicant_event(bss->ctx, EVENT_RX_MGMT, &event);
+ }
+
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
new file mode 100644
index 0000000..061f038
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
@@ -0,0 +1,692 @@
+From c125d2615df25bd9a5a4801abfbcc91f21482e02 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Tue, 6 Feb 2024 15:46:05 +0800
+Subject: [PATCH 098/104] mtk: hostapd: add connac3 csi control interface
+
+1. add hostapd_cli interface
+2. add csi set/dump flow
+3. add csi raw data to json
+
+CR-Id: WCNCR00364748
+Change-Id: If16d4b39ebb7942e47ed4bf4459589d6191983ac
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 193 ++++++++++++++++++++++++
+ hostapd/hostapd_cli.c | 16 ++
+ src/ap/ap_drv_ops.c | 13 ++
+ src/ap/ap_drv_ops.h | 2 +
+ src/common/mtk_vendor.h | 31 +++-
+ src/drivers/driver.h | 16 ++
+ src/drivers/driver_nl80211.c | 235 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h | 1 +
+ src/drivers/driver_nl80211_capa.c | 3 +
+ 9 files changed, 503 insertions(+), 7 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index f0c990314..b5f6431bf 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4925,6 +4925,193 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
+
+ }
+
++static int
++hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp;
++ u8 sta_mac[ETH_ALEN] = {0};
++ u32 csi_para[4] = {0};
++ char mac_str[18] = {0};
++ u8 csi_para_cnt = 0;
++
++ tmp = strtok_r(cmd, ",", &cmd);
++
++ while (tmp) {
++ csi_para_cnt++;
++
++ if (csi_para_cnt <= 4)
++ csi_para[csi_para_cnt - 1] = strtol(tmp, &tmp, 10);
++ else if (csi_para_cnt == 5) {
++ memcpy(mac_str, tmp, sizeof(mac_str) - 1);
++ break;
++ }
++
++ tmp = strtok_r(NULL, ",", &cmd);
++ }
++
++ if (strlen(mac_str)) { /* user input mac string */
++ if (hwaddr_aton(mac_str, sta_mac) < 0) {
++ wpa_printf(MSG_ERROR, "station mac is not right.\n");
++ return -1;
++ }
++
++ if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], sta_mac)) {
++ wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d,%s\n",
++ csi_para[0], csi_para[1], csi_para[2], csi_para[3], mac_str);
++ return -1;
++ }
++ } else {
++ if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], NULL)) {
++ wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d\n",
++ csi_para[0], csi_para[1], csi_para[2], csi_para[3]);
++ return -1;
++ }
++ }
++
++ return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int mt76_csi_to_json(char *fname, struct csi_resp_data *resp_buf)
++{
++#define MAX_BUF_SIZE 10000
++ FILE *f;
++ int i;
++
++ if (!fname) {
++ wpa_printf(MSG_ERROR, "csi dump file name is null!\n");
++ return -1;
++ }
++
++ f = fopen(fname, "a+");
++ if (!f) {
++ wpa_printf(MSG_ERROR, "open csi dump file %s failed\n", fname);
++ return -1;
++ }
++
++ if (fwrite("[", 1, 1, f) != 1) {
++ fclose(f);
++ return -1;
++ }
++
++ for (i = 0; i < resp_buf->buf_cnt; i++) {
++ struct csi_data *c = &resp_buf->csi_buf[i];
++ char *pos, *buf;
++ int j;
++
++ buf = malloc(MAX_BUF_SIZE);
++ if (!buf) {
++ fclose(f);
++ return -1;
++ }
++
++ pos = buf;
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ts);
++ pos += snprintf(pos, MAX_BUF_SIZE, "\"%02x%02x%02x%02x%02x%02x\",", c->ta[0], c->ta[1], c->ta[2], c->ta[3], c->ta[4], c->ta[5]);
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->chain_info);
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ext_info);
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++ for (j = 0; j < c->data_num; j++) {
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]);
++ if (j != (c->data_num - 1))
++ pos += snprintf(pos, MAX_BUF_SIZE, ",");
++ }
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']');
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++ for (j = 0; j < c->data_num; j++) {
++ pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]);
++ if (j != (c->data_num - 1))
++ pos += snprintf(pos, MAX_BUF_SIZE, ",");
++ }
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++
++ pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++ if (i != resp_buf->buf_cnt - 1)
++ pos += snprintf(pos, MAX_BUF_SIZE, ",");
++
++ if (fwrite(buf, 1, pos - buf, f) != (pos - buf)) {
++ perror("fwrite");
++ free(buf);
++ fclose(f);
++ return -1;
++ }
++
++ free(buf);
++ }
++
++ if (fwrite("]", 1, 1, f) != 1) {
++ fclose(f);
++ return -1;
++ }
++
++ fclose(f);
++
++ return 0;
++}
++
++static int
++hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
++ char *buf, size_t buflen)
++{
++ char *tmp, *fname;
++ int data_cnt = 0, ret = 0;
++ struct csi_resp_data resp_buf;
++
++ tmp = strtok_r(cmd, ",", &cmd);
++
++ if (!tmp) {
++ wpa_printf(MSG_ERROR, "Error in command format\n");
++ return -1;
++ }
++
++ data_cnt = strtoul(tmp, &tmp, 0);
++
++ if (data_cnt > 3000) {
++ wpa_printf(MSG_ERROR, "Wrong input csi data cnt\n");
++ return -1;
++ }
++
++ fname = strtok_r(NULL, ",", &cmd);
++
++ if (!fname) {
++ wpa_printf(MSG_ERROR, "Error in command format, csi_filename.\n");
++ return -1;
++ }
++
++ resp_buf.csi_buf = (struct csi_data *)os_zalloc(sizeof(struct csi_data) * data_cnt);
++
++ if (resp_buf.csi_buf == NULL) {
++ wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++ return -1;
++ }
++
++ resp_buf.usr_need_cnt = data_cnt;
++ resp_buf.buf_cnt = 0;
++
++ if (hostapd_drv_csi_dump(hapd, (void *)&resp_buf)) {
++ wpa_printf(MSG_ERROR, "Not able to set csi dump\n");
++ os_free(resp_buf.csi_buf);
++ return -1;
++ }
++
++ mt76_csi_to_json(fname, &resp_buf);
++
++ os_free(resp_buf.csi_buf);
++ return 0;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+@@ -5578,6 +5765,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ } else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
+ reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
+ reply_size);
++ } else if (os_strncmp(buf, "SET_CSI ", 7) == 0) {
++ reply_len = hostapd_ctrl_iface_set_csi(hapd, buf + 8,
++ reply, reply_size);
++ } else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
++ reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
++ reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index acfa3b1d1..81b74d6de 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1719,6 +1719,18 @@ static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
+ return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
+ }
+
++static int hostapd_cli_cmd_set_csi(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "SET_CSI", 1, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1960,6 +1972,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = Set Station index and mac to monitor"},
+ { "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
+ " = Dump RSSI of monitoring Station"},
++ { "set_csi", hostapd_cli_cmd_set_csi, NULL,
++ " = Set csi configuaration"},
++ { "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
++ " = Dump csi data to a json file"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 3c1609656..cb782fa31 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1409,3 +1409,16 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+ return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
+ }
+
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++ if (!hapd->driver || !hapd->driver->csi_set)
++ return 0;
++ return hapd->driver->csi_set(hapd->drv_priv, mode, cfg, v1, v2, mac);
++}
++
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
++{
++ if (!hapd->driver || !hapd->driver->csi_dump)
++ return 0;
++ return hapd->driver->csi_dump(hapd->drv_priv, dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 5830705a3..7abc58377 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -171,6 +171,8 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
+
+ #include "drivers/driver.h"
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 1e4c3670e..c290e72a7 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -73,7 +73,6 @@ enum mtk_vendor_attr_csi_ctrl {
+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
+ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
+- MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
+
+ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
+
+@@ -96,6 +95,7 @@ enum mtk_vendor_attr_csi_data {
+ MTK_VENDOR_ATTR_CSI_DATA_BW,
+ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
+ MTK_VENDOR_ATTR_CSI_DATA_TA,
++ MTK_VENDOR_ATTR_CSI_DATA_NUM,
+ MTK_VENDOR_ATTR_CSI_DATA_I,
+ MTK_VENDOR_ATTR_CSI_DATA_Q,
+ MTK_VENDOR_ATTR_CSI_DATA_INFO,
+@@ -106,7 +106,7 @@ enum mtk_vendor_attr_csi_data {
+ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
+ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
+ MTK_VENDOR_ATTR_CSI_DATA_MODE,
+- MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++ MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
+@@ -281,23 +281,40 @@ enum mtk_vendor_attr_beacon_ctrl {
+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+
+-#define CSI_MAX_COUNT 256
++#define CSI_BW20_DATA_COUNT 64
++#define CSI_BW40_DATA_COUNT 128
++#define CSI_BW80_DATA_COUNT 256
++#define CSI_BW160_DATA_COUNT 512
++#define CSI_BW320_DATA_COUNT 1024
+ #define ETH_ALEN 6
+
+ struct csi_data {
+- s16 data_i[CSI_MAX_COUNT];
+- s16 data_q[CSI_MAX_COUNT];
++ u8 ch_bw;
++ u16 data_num;
++ s16 data_i[CSI_BW320_DATA_COUNT];
++ s16 data_q[CSI_BW320_DATA_COUNT];
++ u8 band;
+ s8 rssi;
+ u8 snr;
+ u32 ts;
+ u8 data_bw;
+ u8 pri_ch_idx;
+ u8 ta[ETH_ALEN];
+- u32 info;
++ u32 ext_info;
+ u8 rx_mode;
+- u32 h_idx;
++ u32 chain_info;
+ u16 tx_idx;
+ u16 rx_idx;
++ u32 segment_num;
++ u8 remain_last;
++ u16 pkt_sn;
++ u8 tr_stream;
++};
++
++struct csi_resp_data {
++ u16 usr_need_cnt;
++ u16 buf_cnt;
++ struct csi_data *csi_buf;
+ };
+
+ #define AIR_MONITOR_MAX_ENTRY 16
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ba61f5842..7efb5e342 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5342,6 +5342,22 @@ struct wpa_driver_ops {
+ #ifdef CONFIG_IEEE80211BE
+ int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
++ /**
++ * csi_set - Set csi related mode and parameter
++ * @priv: Private driver interface data
++ * @mode: Csi mode parameter
++ * @cfg: Csi config parameter
++ * @v1: Value1
++ * @v2: Value2
++ * @mac: Station mac for station filter
++ */
++ int (*csi_set)(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++ /**
++ * csi_dump - Dump csi data to json file
++ * @priv: Private driver interface data
++ * @dump_buf: Dump_struct that store csi data and related info
++ */
++ int (*csi_dump)(void *priv, void *dump_buf);
+ };
+
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index df4a7ec41..39b45ef4b 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -161,6 +161,35 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+ };
+
++static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
++ [MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
++ [MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_DATA_NUM] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED },
++ [MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 },
++ [MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ struct nl_sock *handle;
+@@ -15192,6 +15221,210 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
+ }
+ #endif
+
++static int
++nl80211_csi_set(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ void *tb1, *tb2;
++ int ret, i;
++
++ if (!drv->mtk_csi_vendor_cmd_avail) {
++ wpa_printf(MSG_ERROR,
++ "nl80211: Driver does not support csi");
++ return 0;
++ }
++
++ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED);
++ if (!tb1)
++ goto fail;
++
++ nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE, mode);
++ nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE, cfg);
++ nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, v1);
++ nla_put_u32(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, v2);
++
++ nla_nest_end(msg, tb1);
++
++ if (mac) {
++ tb2 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED);
++ if (!tb2)
++ goto fail;
++
++ for (i = 0; i < ETH_ALEN; i++)
++ nla_put_u8(msg, i, mac[i]);
++
++ nla_nest_end(msg, tb2);
++ }
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_cmd(drv, msg);
++
++ if (ret)
++ wpa_printf(MSG_ERROR, "Failed to set csi. ret=%d (%s)",
++ ret, strerror(-ret));
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++
++}
++
++static int
++mt76_csi_dump_cb(struct nl_msg *msg, void *arg)
++{
++ struct nlattr *tb[NL80211_ATTR_MAX + 1];
++ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
++ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_CSI_DATA];
++ struct nlattr *attr, *cur, *data;
++ int len = 0, rem, idx;
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct csi_resp_data *csi_resp = (struct csi_resp_data *)arg;
++ struct csi_data *c = csi_resp->csi_buf;
++
++ c += csi_resp->buf_cnt;
++
++ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), NULL);
++
++ attr = tb[NL80211_ATTR_VENDOR_DATA];
++ if (!attr)
++ return NL_SKIP;
++
++ nla_parse_nested(tb1, MTK_VENDOR_ATTR_CSI_CTRL_MAX,
++ attr, csi_ctrl_policy);
++
++ if (!tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA])
++ return NL_SKIP;
++
++ nla_parse_nested(tb2, MTK_VENDOR_ATTR_CSI_DATA_MAX,
++ tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy);
++
++ if (!(tb2[MTK_VENDOR_ATTR_CSI_DATA_VER] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_TS] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_BW] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_TA] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_I] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_Q] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE] &&
++ tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO])) {
++ fprintf(stderr, "Attributes error for CSI data\n");
++ return NL_SKIP;
++ }
++
++ c->rssi = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI]);
++ c->snr = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR]);
++ c->data_bw = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_BW]);
++ c->pri_ch_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]);
++ c->rx_mode = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE]);
++
++ c->tx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]);
++ c->rx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]);
++
++ c->ext_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO]);
++ c->chain_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO]);
++
++ c->ts = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_TS]);
++
++ c->data_num = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_NUM]);
++
++ idx = 0;
++ nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) {
++ if (idx < ETH_ALEN)
++ c->ta[idx++] = nla_get_u8(cur);
++ }
++
++ idx = 0;
++ nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_I], rem) {
++ if (idx < c->data_num)
++ c->data_i[idx++] = nla_get_u16(cur);
++ }
++
++ idx = 0;
++ nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) {
++ if (idx < c->data_num)
++ c->data_q[idx++] = nla_get_u16(cur);
++ }
++
++ csi_resp->buf_cnt++;
++
++ return NL_SKIP;
++}
++
++static int
++nl80211_csi_dump(void *priv, void *dump_buf)
++{
++ struct i802_bss *bss = priv;
++ struct wpa_driver_nl80211_data *drv = bss->drv;
++ struct nl_msg *msg;
++ struct nlattr *data;
++ int ret;
++ struct csi_resp_data *csi_resp;
++ u16 pkt_num, i;
++
++ if (!drv->mtk_csi_vendor_cmd_avail) {
++ wpa_printf(MSG_INFO,
++ "nl80211: Driver does not support csi");
++ return 0;
++ }
++
++ csi_resp = (struct csi_resp_data *)dump_buf;
++ pkt_num = csi_resp->usr_need_cnt;
++
++ if (pkt_num > 3000)
++ return -EINVAL;
++
++#define CSI_DUMP_PER_NUM 3
++ for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) {
++ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++ if (!msg)
++ goto fail;
++
++ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++ goto fail;
++
++ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++ if (!data)
++ goto fail;
++
++ nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM);
++
++ nla_nest_end(msg, data);
++
++ ret = send_and_recv_resp(drv, msg, mt76_csi_dump_cb, dump_buf);
++ }
++
++ return ret;
++
++fail:
++ nlmsg_free(msg);
++ return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+@@ -15375,4 +15608,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ #ifdef CONFIG_IEEE80211BE
+ .get_mld_addr = nl80211_get_mld_addr,
+ #endif
++ .csi_set = nl80211_csi_set,
++ .csi_dump = nl80211_csi_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index a0a62e536..b710b50cd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ unsigned int mtk_pp_vendor_cmd_avail:1;
+ unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
++ unsigned int mtk_csi_vendor_cmd_avail:1;
+
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index f3e3d52e2..8688b849f 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1173,6 +1173,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
+ drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
+ break;
++ case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
++ drv->mtk_csi_vendor_cmd_avail = 1;
++ break;
+ }
+ }
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
new file mode 100644
index 0000000..a63f81a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
@@ -0,0 +1,46 @@
+From a5c0e5c09398a247236d73078a4f86a960a97e34 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 8 Apr 2024 16:27:51 +0800
+Subject: [PATCH 099/104] fixup! mtk: wifi: hostapd: add wds mlo support
+
+The latest get_hapd_bssid return hapd only if link id is matched.
+However,the hostapd_rx_from_unknown_sta does not have link
+information so it cannot get hapd.
+
+Modify get_hapd_bssid to ignore link id when link id is -1.
+
+Without this patch, wds mode cannot work and the AP would not be
+aware that station is using 4 address.
+
+CR-Id: WCNCR00240772
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Change-Id: I9661226ed0847abb1de0d8c808daf3ad8cd5dc99
+---
+ src/ap/drv_callbacks.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 27c7555a1..82973c5e4 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1779,7 +1779,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ if (ether_addr_equal(bssid, hapd->own_addr) ||
+ (hapd->conf->mld_ap &&
+ ether_addr_equal(bssid, hapd->mld->mld_addr) &&
+- link_id == hapd->mld_link_id)) {
++ (link_id == hapd->mld_link_id || link_id == -1))) {
+ return hapd;
+ } else if (hapd->conf->mld_ap) {
+ for_each_mld_link(p_hapd, hapd) {
+@@ -1788,7 +1788,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+
+ if (ether_addr_equal(bssid, p_hapd->own_addr) ||
+ (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
+- link_id == p_hapd->mld_link_id))
++ (link_id == p_hapd->mld_link_id || link_id == -1)))
+ return p_hapd;
+ }
+ }
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch
deleted file mode 100644
index 0a51c84..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch
+++ /dev/null
@@ -1,135 +0,0 @@
-From 8de8cd8380af0c43d4fde67a668d79ef73b26b26 Mon Sep 17 00:00:00 2001
-From: Peter Oh <peter.oh@bowerswilkins.com>
-Date: Tue, 30 Jun 2020 14:18:58 +0200
-Subject: [PATCH 10/19] mesh: Allow DFS channels to be selected if dfs is
- enabled
-
-Note: DFS is assumed to be usable if a country code has been set
-
-Signed-off-by: Benjamin Berg <benjamin@sipsolutions.net>
-Signed-off-by: Peter Oh <peter.oh@bowerswilkins.com>
----
- wpa_supplicant/wpa_supplicant.c | 25 +++++++++++++++++++------
- 1 file changed, 19 insertions(+), 6 deletions(-)
-
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -2638,7 +2638,7 @@ static int drv_supports_vht(struct wpa_s
- }
-
-
--static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
-+static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
- {
- int i;
-
-@@ -2647,7 +2647,10 @@ static bool ibss_mesh_is_80mhz_avail(int
-
- chan = hw_get_channel_chan(mode, i, NULL);
- if (!chan ||
-- chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+ chan->flag & HOSTAPD_CHAN_DISABLED)
-+ return false;
-+
-+ if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
- return false;
- }
-
-@@ -2774,7 +2777,7 @@ static void ibss_mesh_select_40mhz(struc
- const struct wpa_ssid *ssid,
- struct hostapd_hw_modes *mode,
- struct hostapd_freq_params *freq,
-- int obss_scan) {
-+ int obss_scan, bool dfs_enabled) {
- int chan_idx;
- struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
- int i, res;
-@@ -2798,8 +2801,11 @@ static void ibss_mesh_select_40mhz(struc
- return;
-
- /* Check primary channel flags */
-- if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+ if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
- return;
-+ if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+ if (!dfs_enabled)
-+ return;
-
- #ifdef CONFIG_HT_OVERRIDES
- if (ssid->disable_ht40)
-@@ -2825,8 +2831,11 @@ static void ibss_mesh_select_40mhz(struc
- return;
-
- /* Check secondary channel flags */
-- if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+ if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
- return;
-+ if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+ if (!dfs_enabled)
-+ return;
-
- if (ht40 == -1) {
- if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
-@@ -2880,7 +2889,7 @@ static bool ibss_mesh_select_80_160mhz(s
- const struct wpa_ssid *ssid,
- struct hostapd_hw_modes *mode,
- struct hostapd_freq_params *freq,
-- int ieee80211_mode, bool is_6ghz) {
-+ int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
- static const int bw80[] = {
- 5180, 5260, 5500, 5580, 5660, 5745, 5825,
- 5955, 6035, 6115, 6195, 6275, 6355, 6435,
-@@ -2925,7 +2934,7 @@ static bool ibss_mesh_select_80_160mhz(s
- goto skip_80mhz;
-
- /* Use 40 MHz if channel not usable */
-- if (!ibss_mesh_is_80mhz_avail(channel, mode))
-+ if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
- goto skip_80mhz;
-
- chwidth = CONF_OPER_CHWIDTH_80MHZ;
-@@ -2939,7 +2948,7 @@ static bool ibss_mesh_select_80_160mhz(s
- if ((mode->he_capab[ieee80211_mode].phy_cap[
- HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
- HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
-- ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
-+ ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
- for (j = 0; j < ARRAY_SIZE(bw160); j++) {
- if (freq->freq == bw160[j]) {
- chwidth = CONF_OPER_CHWIDTH_160MHZ;
-@@ -2967,10 +2976,12 @@ static bool ibss_mesh_select_80_160mhz(s
- if (!chan)
- continue;
-
-- if (chan->flag & (HOSTAPD_CHAN_DISABLED |
-- HOSTAPD_CHAN_NO_IR |
-- HOSTAPD_CHAN_RADAR))
-+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
- continue;
-+ if (chan->flag & (HOSTAPD_CHAN_RADAR |
-+ HOSTAPD_CHAN_NO_IR))
-+ if (!dfs_enabled)
-+ continue;
-
- /* Found a suitable second segment for 80+80 */
- chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
-@@ -3025,6 +3036,7 @@ void ibss_mesh_setup_freq(struct wpa_sup
- int i, obss_scan = 1;
- u8 channel;
- bool is_6ghz;
-+ bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
-
- freq->freq = ssid->frequency;
-
-@@ -3070,9 +3082,9 @@ void ibss_mesh_setup_freq(struct wpa_sup
- freq->channel = channel;
- /* Setup higher BW only for 5 GHz */
- if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
-- ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
-+ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
-- ieee80211_mode, is_6ghz))
-+ ieee80211_mode, is_6ghz, dfs_enabled))
- freq->he_enabled = freq->vht_enabled = false;
- }
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
new file mode 100644
index 0000000..e688a56
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
@@ -0,0 +1,90 @@
+From 30cfdaf10a5b3bb2a173a096b2c708c4a18d55bc Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 8 Apr 2024 14:34:36 +0800
+Subject: [PATCH 100/104] mtk: hostapd: MLD: find partner links by BSSID and
+ SSID
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/bss.c | 8 ++++++--
+ wpa_supplicant/sme.c | 14 ++++++++------
+ 2 files changed, 14 insertions(+), 8 deletions(-)
+
+diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
+index 289035310..ae0e61bc3 100644
+--- a/wpa_supplicant/bss.c
++++ b/wpa_supplicant/bss.c
+@@ -1529,8 +1529,12 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
+ wpa_printf(MSG_DEBUG,
+ "MLD: Reported link not part of MLD");
+ } else if (!(BIT(link_id) & *seen)) {
+- struct wpa_bss *neigh_bss =
+- wpa_bss_get_bssid(wpa_s, pos + 1);
++ struct wpa_bss *neigh_bss;
++
++ if (ssid)
++ neigh_bss = wpa_bss_get(wpa_s, pos + 1, ssid->ssid, ssid->ssid_len);
++ else
++ neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1);
+
+ *seen |= BIT(link_id);
+ wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index ef258fadc..0b4b8e3ce 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -390,7 +390,8 @@ static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s,
+
+ #ifdef CONFIG_TESTING_OPTIONS
+ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
+- struct wpa_bss *bss)
++ struct wpa_bss *bss,
++ struct wpa_ssid *ssid)
+ {
+ unsigned int low, high, i;
+
+@@ -459,7 +460,7 @@ found:
+ MAC2STR(wpa_s->links[i].bssid));
+
+ /* Get the BSS entry and do the switch */
+- bss = wpa_bss_get_bssid(wpa_s, wpa_s->links[i].bssid);
++ bss = wpa_bss_get(wpa_s, wpa_s->links[i].bssid, ssid->ssid, ssid->ssid_len);
+ wpa_s->mlo_assoc_link_id = i;
+
+ return bss;
+@@ -528,7 +529,7 @@ static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
+
+
+ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+- struct wpa_bss *bss)
++ struct wpa_bss *bss, struct wpa_ssid *ssid)
+ {
+ u8 i;
+
+@@ -551,7 +552,8 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ if (bss->mld_link_id == i)
+ wpa_s->links[i].bss = bss;
+ else
+- wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
++ wpa_s->links[i].bss = wpa_bss_get(wpa_s, bssid, ssid->ssid,
++ ssid->ssid_len);
+ }
+ }
+
+@@ -597,10 +599,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ #endif /* CONFIG_TESTING_OPTIONS */
+ bss->valid_links) {
+ wpa_printf(MSG_DEBUG, "MLD: In authentication");
+- wpas_sme_set_mlo_links(wpa_s, bss);
++ wpas_sme_set_mlo_links(wpa_s, bss, ssid);
+
+ #ifdef CONFIG_TESTING_OPTIONS
+- bss = wpas_ml_connect_pref(wpa_s, bss);
++ bss = wpas_ml_connect_pref(wpa_s, bss, ssid);
+
+ if (wpa_s->conf->mld_force_single_link) {
+ wpa_printf(MSG_DEBUG, "MLD: Force single link");
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
new file mode 100644
index 0000000..27f986a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
@@ -0,0 +1,248 @@
+From 0cc6f925c0a97da331f41462a07c4ae4b6698409 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 2 Apr 2024 15:36:29 +0800
+Subject: [PATCH 101/104] mtk: hostapd: MLD: hostapd: add support for basic MLD
+ Extender
+
+Add basic MLD Extender support, including
+1. Extender STA stops all Extender AP's links before scaning.
+2. After finishing the connection with root AP, Extender STA
+ synchronizes control channel with each link on the Extender
+ AP.
+
+Advanced support includes BW cynchronization and channel switch.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c | 52 ++++++++++++++++++++++++++++++++++++++----
+ src/utils/ucode.c | 4 +++-
+ wpa_supplicant/ucode.c | 45 +++++++++++++++++++++++++++++-------
+ 3 files changed, 88 insertions(+), 13 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 98b2a3bf2..2642e87c7 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -487,14 +487,16 @@ static uc_value_t *
+ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ {
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ struct hostapd_data *first_hapd;
++ struct hostapd_bss_config *conf;
+ int i;
+
+- wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
+- iface->phy, hostapd_state_text(iface->state));
+-
+ if (!iface)
+ return NULL;
+
++ wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++ iface->phy, hostapd_state_text(iface->state));
++
+ if (iface->state != HAPD_IFACE_ENABLED)
+ uc_hostapd_disable_iface(iface);
+
+@@ -505,6 +507,32 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ hapd->beacon_set_done = 0;
+ }
+
++#ifdef CONFIG_IEEE80211BE
++ first_hapd = iface->bss[0];
++ conf = first_hapd->conf;
++ for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
++ struct hostapd_iface *h = iface->interfaces->iface[i];
++ struct hostapd_data *h_hapd = h->bss[0];
++ struct hostapd_bss_config *hconf = h_hapd->conf;
++
++ if (h == iface) {
++ wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
++ continue;
++ }
++
++ if (!hconf->mld_ap) {
++ wpa_printf(MSG_DEBUG,
++ "MLD: Skip non MLD");
++ continue;
++ }
++
++ if (hostapd_is_ml_partner(first_hapd, h_hapd)) {
++ hostapd_drv_stop_ap(h_hapd);
++ h_hapd->beacon_set_done = 0;
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ return NULL;
+ }
+
+@@ -512,11 +540,12 @@ static uc_value_t *
+ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ {
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ struct hostapd_data *tmp_hapd;
+ uc_value_t *info = uc_fn_arg(0);
+ struct hostapd_config *conf;
+ bool changed = false;
+ uint64_t intval;
+- int i;
++ int i, band_idx;
+
+ wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
+ iface->phy, hostapd_state_text(iface->state));
+@@ -532,6 +561,21 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ if (ucv_type(info) != UC_OBJECT)
+ return NULL;
+
++ intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
++ if (!errno)
++ band_idx = intval;
++
++#ifdef CONFIG_IEEE80211BE
++ if (hostapd_is_mld_ap(iface->bss[0])) {
++ for_each_mld_link(tmp_hapd, iface->bss[0]) {
++ if (band_idx == tmp_hapd->iconf->band_idx) {
++ iface = tmp_hapd->iface;
++ break;
++ }
++ }
++ }
++#endif /* CONFIG_IEEE80211BE */
++
+ #define UPDATE_VAL(field, name) \
+ if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) && \
+ !errno && intval != conf->field) do { \
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 6f82382f3..81d472f6b 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,7 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ uc_value_t *freq = uc_fn_arg(0);
+ uc_value_t *sec = uc_fn_arg(1);
+ int width = ucv_uint64_get(uc_fn_arg(2));
+- int bw320_offset = 1;
++ int bw320_offset = 1, band_idx;
+ int freq_val, center_idx, center_ofs;
+ enum oper_chan_width chanwidth;
+ enum hostapd_hw_mode hw_mode;
+@@ -123,6 +123,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ return NULL;
+
+ freq_val = ucv_int64_get(freq);
++ band_idx = ucv_int64_get(uc_fn_arg(4));
+ if (ucv_type(sec) == UC_INTEGER)
+ sec_channel = ucv_int64_get(sec);
+ else if (sec)
+@@ -183,6 +184,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
+ ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
++ ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+
+ if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
+ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 542ca25c9..ac0639a90 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -246,12 +246,13 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ {
+ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ struct wpa_bss *bss;
+- uc_value_t *ret, *val;
++ uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ struct wpa_channel_info ci;
+ u8 op_class, channel;
+- enum oper_chan_width ch_width;
+- int center_freq1, bw320_offset = 1, is_24ghz;
++ enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
++ int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ enum hostapd_hw_mode hw_mode;
++ int link_id = ucv_int64_get(link_obj);
+
+ if (!wpa_s)
+ return NULL;
+@@ -261,13 +262,25 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
+ ucv_object_add(ret, "state", ucv_get(val));
+
+- bss = wpa_s->current_bss;
++ bss = link_id == -1 ? wpa_s->current_bss : wpa_s->links[link_id].bss;
+ if (bss) {
+ int sec_chan = 0;
+
+ hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
+ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ hw_mode == HOSTAPD_MODE_IEEE80211B;
++ /*
++ * Assume that the mapping between band and band_idx is
++ * 2 GHz band: band_idx 0
++ * 5 GHz band: band_idx 1
++ * 6 GHz band: band_idx 2
++ * */
++ if (is_24ghz)
++ band_idx = 0;
++ else if (IS_5GHZ(bss->freq))
++ band_idx = 1;
++ else if (is_6ghz_freq(bss->freq))
++ band_idx = 2;
+
+ wpa_drv_channel_info(wpa_s, &ci);
+ center_freq1 = ci.center_frq1;
+@@ -279,11 +292,10 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
+ }
+
+- if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+- sec_chan, &op_class, &channel))
+- return NULL;
++ if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++ sec_chan, &op_class, &channel))
++ ch_width = op_class_to_ch_width(op_class);
+
+- ch_width = op_class_to_ch_width(op_class);
+ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
+ (center_freq1 == 6265) || center_freq1 == 6585 ||
+ center_freq1 == 6905) {
+@@ -295,6 +307,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+ ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
+ ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
++ ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ }
+
+ #ifdef CONFIG_MESH
+@@ -309,6 +322,21 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ return ret;
+ }
+
++static uc_value_t *
++uc_wpas_iface_get_valid_links(uc_vm_t *vm, size_t nargs)
++{
++ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++ uc_value_t *ret;
++
++ if (!wpa_s)
++ return NULL;
++
++ ret = ucv_object_new(vm);
++ ucv_object_add(ret, "valid_links", ucv_uint64_new(wpa_s->valid_links));
++
++ return ret;
++}
++
+ int wpas_ucode_init(struct wpa_global *gl)
+ {
+ static const uc_function_list_t global_fns[] = {
+@@ -320,6 +348,7 @@ int wpas_ucode_init(struct wpa_global *gl)
+ };
+ static const uc_function_list_t iface_fns[] = {
+ { "status", uc_wpas_iface_status },
++ { "get_valid_links", uc_wpas_iface_get_valid_links },
+ };
+ uc_value_t *data, *proto;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
new file mode 100644
index 0000000..8881b72
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
@@ -0,0 +1,338 @@
+From 80b2cbbd80700d1fa04d97aae5b8a083a4b4f2ba Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 2 Apr 2024 16:51:07 +0800
+Subject: [PATCH 102/104] mtk: hostapd: Refactor static PP and mld support
+
+Add band_idx attribute in pp cmd for vendor cmd under mld setting.
+
+CR-Id: WCNCR00259302
+Change-Id: I3c4c7e9dff5eaa2ff48537dc0c5398006a305713
+---
+ hostapd/config_file.c | 6 ++--
+ hostapd/ctrl_iface.c | 69 +++++++++++++++++++++++++-----------
+ hostapd/ctrl_iface.h | 3 +-
+ hostapd/hostapd_cli.c | 15 ++++++++
+ src/ap/ap_config.h | 4 +--
+ src/ap/ap_drv_ops.c | 7 ++--
+ src/ap/dfs.c | 3 ++
+ src/ap/hostapd.c | 4 +--
+ src/common/mtk_vendor.h | 1 +
+ src/drivers/driver.h | 3 +-
+ src/drivers/driver_nl80211.c | 4 ++-
+ 11 files changed, 87 insertions(+), 32 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index b9a062193..2add62ca9 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5339,7 +5339,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ if (get_u16(pos, line, &conf->punct_bitmap))
+ return 1;
+ conf->punct_bitmap = atoi(pos);
+- conf->pp_mode = PP_MANUAL_MODE;
++ conf->pp_mode = PP_USR_MODE;
+ } else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ int val = atoi(pos);
+
+@@ -5427,8 +5427,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ } else if (os_strcmp(buf, "pp_mode") == 0) {
+ int val = atoi(pos);
+
+- if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
+- val < PP_DISABLE || val > PP_MANUAL_MODE) {
++ if ((val != PP_USR_MODE && conf->punct_bitmap) ||
++ val < PP_DISABLE || val > PP_USR_MODE) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
+ line);
+ return 1;
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b5f6431bf..6d4ce8acb 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4854,27 +4854,57 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx)
++{
++ struct hostapd_data *link;
++
++ if (!hostapd_is_mld_ap(hapd))
++ return hapd;
++
++ for_each_mld_link(link, hapd) {
++ if (link->iconf->band_idx == band_idx)
++ break;
++ }
++
++ if (!link || link->iconf->band_idx != band_idx) {
++ wpa_printf(MSG_ERROR, "Invalid band idx %d\n", band_idx);
++ return NULL;
++ }
++
++ return link;
++}
++
+ static int
+ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ size_t buflen)
+ {
+- char *pos, *config, *value;
++ char *band, *config, *value;
++ u8 band_idx;
+
+ config = cmd;
+- pos = os_strchr(config, ' ');
+- if (pos == NULL)
++
++ value = os_strchr(config, ' ');
++ if (value == NULL)
+ return -1;
+- *pos++ = '\0';
++ *value++ = '\0';
+
+- if (pos == NULL)
++ band = os_strchr(value, ' ');
++ if (band == NULL)
++ return -1;
++ *band++ = '\0';
++ band_idx = strtol(band, NULL, 10);
++
++ hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++ if (!hapd)
+ return -1;
+- value = pos;
+
+ if (os_strcmp(config, "mode") == 0) {
+- int val = atoi(value);
++ int val = strtol(value, NULL, 10);
+
+- if (val < PP_DISABLE || val > PP_AUTO_MODE) {
+- wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++ if (val < PP_DISABLE || val > PP_FW_MODE) {
++ wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
+ return -1;
+ }
+ hapd->iconf->pp_mode = (u8) val;
+@@ -4882,7 +4912,8 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ return -1;
+ } else {
+ wpa_printf(MSG_ERROR,
+- "Unsupported parameter %s for set_pp", config);
++ "Unsupported parameter %s for SET_PP"
++ "Usage: set_pp mode <value> <band_idx>", config);
+ return -1;
+ }
+ return os_snprintf(buf, buflen, "OK\n");
+@@ -4892,19 +4923,17 @@ static int
+ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ size_t buflen)
+ {
+- char *pos, *end;
++ u8 band_idx;
+
+- pos = buf;
+- end = buf + buflen;
++ band_idx = strtol(cmd, NULL, 10);
+
+- if (os_strcmp(cmd, "mode") == 0) {
+- return os_snprintf(pos, end - pos, "pp_mode: %d\n",
+- hapd->iconf->pp_mode);
+- } else {
+- wpa_printf(MSG_ERROR,
+- "Unsupported parameter %s for get_pp", cmd);
++ hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++ if (!hapd)
+ return -1;
+- }
++
++ return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
++ hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
+ }
+
+ static int
+diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
+index 3341a66bd..82a64b880 100644
+--- a/hostapd/ctrl_iface.h
++++ b/hostapd/ctrl_iface.h
+@@ -35,5 +35,6 @@ hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
+ {
+ }
+ #endif /* CONFIG_NO_CTRL_IFACE */
+-
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx);
+ #endif /* CTRL_IFACE_H */
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 81b74d6de..4bf70d183 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1730,6 +1730,17 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
+ {
+ return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
+ }
++static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
++}
++
++static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
++}
+
+ struct hostapd_cli_cmd {
+ const char *cmd;
+@@ -1976,6 +1987,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ " = Set csi configuaration"},
+ { "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
+ " = Dump csi data to a json file"},
++ { "set_pp", hostapd_cli_cmd_set_pp, NULL,
++ " = Set preamble puncture mode"},
++ { "get_pp", hostapd_cli_cmd_get_pp, NULL,
++ " = Get preamble puncture status"},
+ { NULL, NULL, NULL, NULL }
+ };
+
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3bd8df9ce..5192c1f07 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1353,8 +1353,8 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+
+ enum pp_mode {
+ PP_DISABLE = 0,
+- PP_AUTO_MODE,
+- PP_MANUAL_MODE,
++ PP_FW_MODE,
++ PP_USR_MODE,
+ };
+
+ #define EDCCA_DEFAULT_COMPENSATION -6
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index cb782fa31..a76148eba 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1396,10 +1396,13 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ {
+ if (!hapd->driver || !hapd->driver->pp_mode_set ||
+- hapd->iconf->pp_mode > PP_AUTO_MODE)
++ hapd->iconf->pp_mode >= PP_USR_MODE ||
++ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
++
+ return hapd->driver->pp_mode_set(hapd->drv_priv,
+- hapd->iconf->pp_mode);
++ hapd->iconf->pp_mode,
++ hapd->iconf->band_idx);
+ }
+
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index b12290556..9bbeab1d6 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1080,6 +1080,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ os_memset(&csa_settings, 0, sizeof(csa_settings));
+ csa_settings.cs_count = 5;
+ csa_settings.block_tx = 1;
++ csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
+ csa_settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ if (iface->bss[0]->conf->mld_ap)
+@@ -1644,6 +1645,8 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ return 0;
+ }
+
++ iface->bss[0]->iconf->punct_bitmap = 0;
++
+ if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+ /* Radar detected while operating, switch the channel. */
+ return hostapd_dfs_start_channel_switch(iface);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 8e3f0b281..3d3359291 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2697,6 +2697,8 @@ dfs_offload:
+ }
+ #endif /* CONFIG_MESH */
+
++ if (hostapd_drv_pp_mode_set(hapd) < 0)
++ goto fail;
+ if (hostapd_drv_configure_edcca_enable(hapd) < 0)
+ goto fail;
+
+@@ -2711,8 +2713,6 @@ dfs_offload:
+ goto fail;
+ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ goto fail;
+- if (hostapd_drv_pp_mode_set(hapd) < 0)
+- goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index c290e72a7..09805b6fb 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -263,6 +263,7 @@ enum mtk_vendor_attr_pp_ctrl {
+ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_PP_MODE,
++ MTK_VENDOR_ATTR_PP_BAND_IDX,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 7efb5e342..539771729 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5337,8 +5337,9 @@ struct wpa_driver_ops {
+ * pp_mode_set - Set preamble puncture operation mode
+ * @priv: Private driver interface data
+ * @pp_mode: Value is defined in enum pp_mode
++ * @band_idx: chip band index
+ */
+- int (*pp_mode_set)(void *priv, const u8 pp_mode);
++ int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
+ #ifdef CONFIG_IEEE80211BE
+ int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 39b45ef4b..6ec2c32df 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -159,6 +159,7 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ static struct nla_policy
+ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++ [MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
+ };
+
+ static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+@@ -15167,7 +15168,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ return ret;
+ }
+
+-static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
+ {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15194,6 +15195,7 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
+ if (!data)
+ goto fail;
+
++ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
+ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
+
+ nla_nest_end(msg, data);
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
new file mode 100644
index 0000000..9f5346e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
@@ -0,0 +1,126 @@
+From ec7d47b2566da59c52d995f5e404a1c00e746fe5 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 11 Apr 2024 18:16:38 +0800
+Subject: [PATCH 103/104] mtk: hostapd: make sure all links are set before
+ enabling beacon
+
+NL80211_CMD_NEW_BEACON will first be set, but we've modified mac80211 to
+disable this beacon. After that, hostapd will block
+NL80211_CMD_SET_BEACON until all links are setting up.
+(use NL80211_CMD_START_AP event to check if all expected links are enabled)
+
+Update: in wpa_driver_nl80211_set_ap(), send_and_recv() is used, implies
+that hostapd should already sync with driver, so don't need to use
+NL80211_CMD_START_AP event.
+
+This can make sure that the first beacon of each link includes the
+correct RNR and per-STA profile.
+
+Note that in NL80211_CMD_NEW_BEACON, we also set beacon interval to 0,
+which helps to bypass some mac80211 beacon active checks, e.g., during ACS.
+
+CR-Id: WCNCR00238098
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I99320f7802041dc7d8e780041bb686ce3700c0b6
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h | 2 ++
+ src/ap/beacon.c | 10 ++++++++++
+ src/ap/hostapd.c | 14 ++++++++++++++
+ src/ap/hostapd.h | 1 +
+ 5 files changed, 29 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 2add62ca9..8abe1bc46 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5354,6 +5354,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ bss->mld_ap = !!atoi(pos);
+ } else if (os_strcmp(buf, "mld_primary") == 0) {
+ bss->mld_primary = !!atoi(pos);
++ } else if (os_strcmp(buf, "mld_allowed_links") == 0) {
++ bss->mld_allowed_links = atoi(pos);
+ } else if (os_strcmp(buf, "mld_addr") == 0) {
+ if (hwaddr_aton(pos, bss->mld_addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 5192c1f07..0ea5a04e2 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -968,6 +968,8 @@ struct hostapd_bss_config {
+
+ /* The AP is the primary AP of an AP MLD */
+ u8 mld_primary;
++ /* Allowed link bitmap of the AP MLD to which the AP is affiliated */
++ u16 mld_allowed_links;
+
+ /* The MLD ID to which the AP MLD is affiliated with */
+ u8 mld_id;
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index a5c46b067..a5c9dd87e 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2164,6 +2164,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+ head->u.beacon.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
++ /* if MLD AP hasn't finished setting up all links, also set beacon interval
++ * to 0. This allows mac80211 to bypass some beacon active checks, for
++ * example, when doing ACS
++ */
++ if (hapd->conf->mld_ap && !hapd->mld->started)
++ head->u.beacon.beacon_int = host_to_le16(0);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ capab_info = hostapd_own_capab_info(hapd);
+@@ -2553,6 +2559,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ int res, ret = -1, i;
+ struct hostapd_hw_modes *mode;
+
++ /* skip setting beacon if other links are not started yet */
++ if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
++ return 0;
++
+ if (!hapd->drv_priv) {
+ wpa_printf(MSG_ERROR, "Interface is disabled");
+ return -1;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 3d3359291..c31e0badd 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1314,6 +1314,20 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ return -1;
+
++ if (hapd->conf->mld_ap && !hapd->mld->started) {
++ struct hostapd_data *p_hapd;
++ u16 valid_links = 0;
++
++ for_each_mld_link(p_hapd, hapd)
++ valid_links |= BIT(p_hapd->mld_link_id);
++
++ if (valid_links == hapd->conf->mld_allowed_links ||
++ !hapd->conf->mld_allowed_links) {
++ hapd->mld->started = 1;
++ ieee802_11_set_beacon(hapd);
++ }
++ }
++
+ if (flush_old_stations && !conf->start_disabled &&
+ conf->broadcast_deauth) {
+ u8 addr[ETH_ALEN];
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 5b37be87b..83636649f 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -537,6 +537,7 @@ struct hostapd_mld {
+ * freed when num_links is 0.
+ */
+ u8 refcount;
++ bool started;
+
+ struct hostapd_data *fbss;
+ struct dl_list links; /* List head of all affiliated links */
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
new file mode 100644
index 0000000..95be18f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
@@ -0,0 +1,54 @@
+From 7b4363892397c667e65fae9e036c83ac039e308d Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 17 Apr 2024 13:17:59 +0800
+Subject: [PATCH 104/104] mtk: hostapd: ucode: add is_mld_finished check
+
+Add is_mld_finished check for ucode need.
+This function returns ture only if all links fromt all MLD APs are
+ready.
+
+CR-Id: WCNCR00289305
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 2642e87c7..9f9cc2022 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -734,6 +734,23 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ return ucv_boolean_new(!ret);
+ }
+
++static uc_value_t *
++uc_hostapd_iface_is_mld_finished(uc_vm_t *vm, size_t nargs)
++{
++ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++ bool finished = true;
++ int i;
++
++ for (i = 0; i < iface->num_bss; i++) {
++ if (iface->bss[i]->conf->mld_ap && !iface->bss[i]->mld->started) {
++ finished = false;
++ break;
++ }
++ }
++
++ return ucv_boolean_new(finished);
++}
++
+ static uc_value_t *
+ uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
+ {
+@@ -806,6 +823,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+ { "stop", uc_hostapd_iface_stop },
+ { "start", uc_hostapd_iface_start },
+ { "switch_channel", uc_hostapd_iface_switch_channel },
++ { "is_mld_finished", uc_hostapd_iface_is_mld_finished },
+ };
+ uc_value_t *data, *proto;
+
+--
+2.39.2
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/011-mesh-use-deterministic-channel-on-channel-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/011-mesh-use-deterministic-channel-on-channel-switch.patch
deleted file mode 100644
index 07b7a59..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/011-mesh-use-deterministic-channel-on-channel-switch.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From fc8ea40f6130ac18d9c66797de2cf1d5af55d496 Mon Sep 17 00:00:00 2001
-From: Markus Theil <markus.theil@tu-ilmenau.de>
-Date: Tue, 30 Jun 2020 14:19:07 +0200
-Subject: [PATCH 19/19] mesh: use deterministic channel on channel switch
-
-This patch uses a deterministic channel on DFS channel switch
-in mesh networks. Otherwise, when switching to a usable but not
-available channel, no CSA can be sent and a random channel is choosen
-without notification of other nodes. It is then quite likely, that
-the mesh network gets disconnected.
-
-Fix this by using a deterministic number, based on the sha256 hash
-of the mesh ID, in order to use at least a different number in each
-mesh network.
-
-Signed-off-by: Markus Theil <markus.theil@tu-ilmenau.de>
----
- src/ap/dfs.c | 20 +++++++++++++++++++-
- src/drivers/driver_nl80211.c | 4 ++++
- 2 files changed, 23 insertions(+), 1 deletion(-)
-
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -17,6 +17,7 @@
- #include "ap_drv_ops.h"
- #include "drivers/driver.h"
- #include "dfs.h"
-+#include "crypto/crypto.h"
-
-
- enum dfs_channel_type {
-@@ -526,9 +527,14 @@ dfs_get_valid_channel(struct hostapd_ifa
- int num_available_chandefs;
- int chan_idx, chan_idx2;
- int sec_chan_idx_80p80 = -1;
-+ bool is_mesh = false;
- int i;
- u32 _rand;
-
-+#ifdef CONFIG_MESH
-+ is_mesh = iface->mconf;
-+#endif
-+
- wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
- *secondary_channel = 0;
- *oper_centr_freq_seg0_idx = 0;
-@@ -548,8 +554,20 @@ dfs_get_valid_channel(struct hostapd_ifa
- if (num_available_chandefs == 0)
- return NULL;
-
-- if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
-+ /* try to use deterministic channel in mesh, so that both sides
-+ * have a chance to switch to the same channel */
-+ if (is_mesh) {
-+#ifdef CONFIG_MESH
-+ u64 hash[4];
-+ const u8 *meshid[1] = { &iface->mconf->meshid[0] };
-+ const size_t meshid_len = iface->mconf->meshid_len;
-+
-+ sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
-+ _rand = hash[0] + hash[1] + hash[2] + hash[3];
-+#endif
-+ } else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
- return NULL;
-+
- chan_idx = _rand % num_available_chandefs;
- dfs_find_channel(iface, &chan, chan_idx, type);
- if (!chan) {
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -11017,6 +11017,10 @@ static int nl80211_switch_channel(void *
- if (ret)
- goto error;
-
-+ if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
-+ nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
-+ }
-+
- /* beacon_csa params */
- beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
- if (!beacon_csa)
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/021-fix-sta-add-after-previous-connection.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/021-fix-sta-add-after-previous-connection.patch
deleted file mode 100644
index edf599e..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/021-fix-sta-add-after-previous-connection.patch
+++ /dev/null
@@ -1,26 +0,0 @@
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4621,6 +4621,13 @@ static int add_associated_sta(struct hos
- * drivers to accept the STA parameter configuration. Since this is
- * after a new FT-over-DS exchange, a new TK has been derived, so key
- * reinstallation is not a concern for this case.
-+ *
-+ * If the STA was associated and authorized earlier, but came for a new
-+ * connection (!added_unassoc + !reassoc), remove the existing STA entry
-+ * so that it can be re-added. This case is rarely seen when the AP could
-+ * not receive the deauth/disassoc frame from the STA. And the STA comes
-+ * back with new connection within a short period or before the inactive
-+ * STA entry is removed from the list.
- */
- wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
- " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
-@@ -4634,7 +4641,8 @@ static int add_associated_sta(struct hos
- (!(sta->flags & WLAN_STA_AUTHORIZED) ||
- (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
- (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
-- !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
-+ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
-+ (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
- hostapd_drv_sta_remove(hapd, sta->addr);
- wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
- set = 0;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/022-hostapd-fix-use-of-uninitialized-stack-variables.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/022-hostapd-fix-use-of-uninitialized-stack-variables.patch
deleted file mode 100644
index 8dec325..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/022-hostapd-fix-use-of-uninitialized-stack-variables.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 8 Jul 2021 16:33:03 +0200
-Subject: [PATCH] hostapd: fix use of uninitialized stack variables
-
-When a CSA is performed on an 80 MHz channel, hostapd_change_config_freq
-unconditionally calls hostapd_set_oper_centr_freq_seg0/1_idx with seg0/1
-filled by ieee80211_freq_to_chan.
-However, if ieee80211_freq_to_chan fails (because the freq is 0 or invalid),
-seg0/1 remains uninitialized and filled with stack garbage, causing errors
-such as "hostapd: 80 MHz: center segment 1 configured"
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -3764,7 +3764,7 @@ static int hostapd_change_config_freq(st
- struct hostapd_freq_params *old_params)
- {
- int channel;
-- u8 seg0, seg1;
-+ u8 seg0 = 0, seg1 = 0;
- struct hostapd_hw_modes *mode;
-
- if (!params->channel) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch
deleted file mode 100644
index 9ff9b23..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 28 Jul 2021 05:43:29 +0200
-Subject: [PATCH] ndisc_snoop: call dl_list_del before freeing ipv6 addresses
-
-Fixes a segmentation fault on sta disconnect
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/ndisc_snoop.c
-+++ b/src/ap/ndisc_snoop.c
-@@ -61,6 +61,7 @@ void sta_ip6addr_del(struct hostapd_data
- dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
- list) {
- hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
-+ dl_list_del(&ip6addr->list);
- os_free(ip6addr);
- }
- }
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch
deleted file mode 100644
index ef2bb40..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch
+++ /dev/null
@@ -1,275 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 28 Jul 2021 05:49:46 +0200
-Subject: [PATCH] driver_nl80211: rewrite neigh code to not depend on
- libnl3-route
-
-Removes an unnecessary dependency and also makes the code smaller
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -16,9 +16,6 @@
- #include <net/if.h>
- #include <netlink/genl/genl.h>
- #include <netlink/genl/ctrl.h>
--#ifdef CONFIG_LIBNL3_ROUTE
--#include <netlink/route/neighbour.h>
--#endif /* CONFIG_LIBNL3_ROUTE */
- #include <linux/rtnetlink.h>
- #include <netpacket/packet.h>
- #include <linux/errqueue.h>
-@@ -5783,26 +5780,29 @@ fail:
-
- static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- struct wpa_driver_nl80211_data *drv = bss->drv;
-- struct rtnl_neigh *rn;
-- struct nl_addr *nl_addr;
-+ struct ndmsg nhdr = {
-+ .ndm_state = NUD_PERMANENT,
-+ .ndm_ifindex = bss->ifindex,
-+ .ndm_family = AF_BRIDGE,
-+ };
-+ struct nl_msg *msg;
- int err;
-
-- rn = rtnl_neigh_alloc();
-- if (!rn)
-+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+ if (!msg)
- return;
-
-- rtnl_neigh_set_family(rn, AF_BRIDGE);
-- rtnl_neigh_set_ifindex(rn, bss->ifindex);
-- nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
-- if (!nl_addr) {
-- rtnl_neigh_put(rn);
-- return;
-- }
-- rtnl_neigh_set_lladdr(rn, nl_addr);
-+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
-+ goto errout;
-+
-+ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
-+ goto errout;
-+
-+ if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
-+ goto errout;
-
-- err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+ err = nl_wait_for_ack(drv->rtnl_sk);
- if (err < 0) {
- wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
- MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
-@@ -5812,9 +5812,8 @@ static void rtnl_neigh_delete_fdb_entry(
- MACSTR, MAC2STR(addr));
- }
-
-- nl_addr_put(nl_addr);
-- rtnl_neigh_put(rn);
--#endif /* CONFIG_LIBNL3_ROUTE */
-+errout:
-+ nlmsg_free(msg);
- }
-
-
-@@ -8492,7 +8491,6 @@ static void *i802_init(struct hostapd_da
- (params->num_bridge == 0 || !params->bridge[0]))
- add_ifidx(drv, br_ifindex, drv->ifindex);
-
--#ifdef CONFIG_LIBNL3_ROUTE
- if (bss->added_if_into_bridge || bss->already_in_bridge) {
- int err;
-
-@@ -8509,7 +8507,6 @@ static void *i802_init(struct hostapd_da
- goto failed;
- }
- }
--#endif /* CONFIG_LIBNL3_ROUTE */
-
- if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
- wpa_printf(MSG_DEBUG,
-@@ -11883,13 +11880,14 @@ static int wpa_driver_br_add_ip_neigh(vo
- const u8 *ipaddr, int prefixlen,
- const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
-- struct rtnl_neigh *rn;
-- struct nl_addr *nl_ipaddr = NULL;
-- struct nl_addr *nl_lladdr = NULL;
-- int family, addrsize;
-+ struct ndmsg nhdr = {
-+ .ndm_state = NUD_PERMANENT,
-+ .ndm_ifindex = bss->br_ifindex,
-+ };
-+ struct nl_msg *msg;
-+ int addrsize;
- int res;
-
- if (!ipaddr || prefixlen == 0 || !addr)
-@@ -11908,85 +11906,66 @@ static int wpa_driver_br_add_ip_neigh(vo
- }
-
- if (version == 4) {
-- family = AF_INET;
-+ nhdr.ndm_family = AF_INET;
- addrsize = 4;
- } else if (version == 6) {
-- family = AF_INET6;
-+ nhdr.ndm_family = AF_INET6;
- addrsize = 16;
- } else {
- return -EINVAL;
- }
-
-- rn = rtnl_neigh_alloc();
-- if (rn == NULL)
-+ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
-+ if (!msg)
- return -ENOMEM;
-
-- /* set the destination ip address for neigh */
-- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
-- if (nl_ipaddr == NULL) {
-- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
-- res = -ENOMEM;
-+ res = -ENOMEM;
-+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- goto errout;
-- }
-- nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
-- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
-- if (res) {
-- wpa_printf(MSG_DEBUG,
-- "nl80211: neigh set destination addr failed");
-+
-+ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- goto errout;
-- }
-
-- /* set the corresponding lladdr for neigh */
-- nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
-- if (nl_lladdr == NULL) {
-- wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
-- res = -ENOMEM;
-+ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
- goto errout;
-- }
-- rtnl_neigh_set_lladdr(rn, nl_lladdr);
-
-- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-- rtnl_neigh_set_state(rn, NUD_PERMANENT);
-+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+ if (res < 0)
-+ goto errout;
-
-- res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
-+ res = nl_wait_for_ack(drv->rtnl_sk);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Adding bridge ip neigh failed: %s",
- nl_geterror(res));
- }
- errout:
-- if (nl_lladdr)
-- nl_addr_put(nl_lladdr);
-- if (nl_ipaddr)
-- nl_addr_put(nl_ipaddr);
-- if (rn)
-- rtnl_neigh_put(rn);
-+ nlmsg_free(msg);
- return res;
--#else /* CONFIG_LIBNL3_ROUTE */
-- return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
-
-
- static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
- const u8 *ipaddr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
-- struct rtnl_neigh *rn;
-- struct nl_addr *nl_ipaddr;
-- int family, addrsize;
-+ struct ndmsg nhdr = {
-+ .ndm_state = NUD_PERMANENT,
-+ .ndm_ifindex = bss->br_ifindex,
-+ };
-+ struct nl_msg *msg;
-+ int addrsize;
- int res;
-
- if (!ipaddr)
- return -EINVAL;
-
- if (version == 4) {
-- family = AF_INET;
-+ nhdr.ndm_family = AF_INET;
- addrsize = 4;
- } else if (version == 6) {
-- family = AF_INET6;
-+ nhdr.ndm_family = AF_INET6;
- addrsize = 16;
- } else {
- return -EINVAL;
-@@ -12004,41 +11983,30 @@ static int wpa_driver_br_delete_ip_neigh
- return -1;
- }
-
-- rn = rtnl_neigh_alloc();
-- if (rn == NULL)
-+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+ if (!msg)
- return -ENOMEM;
-
-- /* set the destination ip address for neigh */
-- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
-- if (nl_ipaddr == NULL) {
-- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
-- res = -ENOMEM;
-+ res = -ENOMEM;
-+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- goto errout;
-- }
-- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
-- if (res) {
-- wpa_printf(MSG_DEBUG,
-- "nl80211: neigh set destination addr failed");
-+
-+ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- goto errout;
-- }
-
-- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+ if (res < 0)
-+ goto errout;
-
-- res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+ res = nl_wait_for_ack(drv->rtnl_sk);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Deleting bridge ip neigh failed: %s",
- nl_geterror(res));
- }
- errout:
-- if (nl_ipaddr)
-- nl_addr_put(nl_ipaddr);
-- if (rn)
-- rtnl_neigh_put(rn);
-+ nlmsg_free(msg);
- return res;
--#else /* CONFIG_LIBNL3_ROUTE */
-- return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
-
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/040-mesh-allow-processing-authentication-frames-in-block.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/040-mesh-allow-processing-authentication-frames-in-block.patch
deleted file mode 100644
index b7bf9e3..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/040-mesh-allow-processing-authentication-frames-in-block.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 18 Feb 2019 12:57:11 +0100
-Subject: [PATCH] mesh: allow processing authentication frames in blocked state
-
-If authentication fails repeatedly e.g. because of a weak signal, the link
-can end up in blocked state. If one of the nodes tries to establish a link
-again before it is unblocked on the other side, it will block the link to
-that other side. The same happens on the other side when it unblocks the
-link. In that scenario, the link never recovers on its own.
-
-To fix this, allow restarting authentication even if the link is in blocked
-state, but don't initiate the attempt until the blocked period is over.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -3020,15 +3020,6 @@ static void handle_auth(struct hostapd_d
- seq_ctrl);
- return;
- }
--#ifdef CONFIG_MESH
-- if ((hapd->conf->mesh & MESH_ENABLED) &&
-- sta->plink_state == PLINK_BLOCKED) {
-- wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
-- " is blocked - drop Authentication frame",
-- MAC2STR(sa));
-- return;
-- }
--#endif /* CONFIG_MESH */
- #ifdef CONFIG_PASN
- if (auth_alg == WLAN_AUTH_PASN &&
- (sta->flags & WLAN_STA_ASSOC)) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/050-build_fix.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/050-build_fix.patch
deleted file mode 100644
index 8680b07..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/050-build_fix.patch
+++ /dev/null
@@ -1,20 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -324,6 +324,7 @@ ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -331,6 +331,7 @@ endif
- ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/110-mbedtls-TLS-crypto-option-initial-port.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/110-mbedtls-TLS-crypto-option-initial-port.patch
deleted file mode 100644
index 2210794..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/110-mbedtls-TLS-crypto-option-initial-port.patch
+++ /dev/null
@@ -1,8051 +0,0 @@
-From e16f200dc1d2f69efc78c7c55af0d7b410a981f9 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 5 Jul 2022 02:49:50 -0400
-Subject: [PATCH 1/7] mbedtls: TLS/crypto option (initial port)
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- hostapd/Makefile | 91 +
- hostapd/defconfig | 15 +-
- src/crypto/crypto_mbedtls.c | 4043 +++++++++++++++++
- src/crypto/tls_mbedtls.c | 3313 ++++++++++++++
- .../build/build-wpa_supplicant-mbedtls.config | 24 +
- tests/hwsim/example-hostapd.config | 4 +
- tests/hwsim/example-wpa_supplicant.config | 4 +
- wpa_supplicant/Makefile | 74 +
- wpa_supplicant/defconfig | 6 +-
- 9 files changed, 7571 insertions(+), 3 deletions(-)
- create mode 100644 src/crypto/crypto_mbedtls.c
- create mode 100644 src/crypto/tls_mbedtls.c
- create mode 100644 tests/build/build-wpa_supplicant-mbedtls.config
-
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -745,6 +745,40 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
-
-+ifeq ($(CONFIG_TLS), mbedtls)
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls
-+ifndef CONFIG_DPP
-+LIBS += -lmbedx509
-+endif
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifdef NEED_FIPS186_2_PRF
-+OBJS += ../src/crypto/fips_prf_internal.o
-+SHA1OBJS += ../src/crypto/sha1-internal.o
-+endif
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+ifdef CONFIG_DPP
-+LIBS += -lmbedx509
-+LIBS_h += -lmbedx509
-+LIBS_n += -lmbedx509
-+LIBS_s += -lmbedx509
-+endif
-+LIBS += -lmbedcrypto
-+LIBS_h += -lmbedcrypto
-+LIBS_n += -lmbedcrypto
-+LIBS_s += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
-@@ -924,9 +958,11 @@ endif
-
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -936,38 +972,48 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_UNWRAP
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_AES_DEC=y
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_DEC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_DEC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-dec.o
-@@ -982,12 +1028,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -996,16 +1046,22 @@ endif
- endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
-
- ifdef NEED_SHA1
- OBJS += $(SHA1OBJS)
-@@ -1015,11 +1071,13 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
-
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
-@@ -1058,56 +1116,81 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- OBJS += ../src/crypto/sha256-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- ifdef NEED_SHA384
- CFLAGS += -DCONFIG_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- CFLAGS += -DCONFIG_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
-
- ifdef CONFIG_INTERNAL_SHA384
- CFLAGS += -DCONFIG_INTERNAL_SHA384
-@@ -1152,11 +1235,13 @@ HOBJS += $(SHA1OBJS)
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
-
- ifdef CONFIG_RADIUS_SERVER
- CFLAGS += -DRADIUS_SERVER
-@@ -1329,7 +1414,9 @@ NOBJS += ../src/utils/trace.o
- endif
-
- HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/aes-encblock.o
-+endif
- ifdef CONFIG_INTERNAL_AES
- HOBJS += ../src/crypto/aes-internal.o
- HOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1352,13 +1439,17 @@ SOBJS += ../src/common/sae.o
- SOBJS += ../src/common/sae_pk.o
- SOBJS += ../src/common/dragonfly.o
- SOBJS += $(AESOBJS)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-prf.o
- SOBJS += ../src/crypto/sha384-prf.o
- SOBJS += ../src/crypto/sha512-prf.o
-+endif
- SOBJS += ../src/crypto/dh_groups.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-kdf.o
- SOBJS += ../src/crypto/sha384-kdf.o
- SOBJS += ../src/crypto/sha512-kdf.o
-+endif
-
- _OBJS_VAR := NOBJS
- include ../src/objs.mk
---- a/hostapd/defconfig
-+++ b/hostapd/defconfig
-@@ -6,9 +6,21 @@
- # just setting VARIABLE=n is not disabling that variable.
- #
- # This file is included in Makefile, so variables like CFLAGS and LIBS can also
--# be modified from here. In most cass, these lines should use += in order not
-+# be modified from here. In most cases, these lines should use += in order not
- # to override previous values of the variables.
-
-+
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
-+#CFLAGS += -I/usr/local/openssl/include
-+#LIBS += -L/usr/local/openssl/lib
-+
-+# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
-+# the kerberos files are not in the default include path. Following line can be
-+# used to fix build issues on such systems (krb5.h not found).
-+#CFLAGS += -I/usr/include/kerberos
-+
-+
- # Driver interface for Host AP driver
- CONFIG_DRIVER_HOSTAP=y
-
-@@ -278,6 +290,7 @@ CONFIG_IPV6=y
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
---- /dev/null
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -0,0 +1,4043 @@
-+/*
-+ * crypto wrapper functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/entropy.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/asn1.h>
-+#include <mbedtls/asn1write.h>
-+#include <mbedtls/aes.h>
-+#include <mbedtls/md.h>
-+#include <mbedtls/md5.h>
-+#include <mbedtls/sha1.h>
-+#include <mbedtls/sha256.h>
-+#include <mbedtls/sha512.h>
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__ __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__ __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+#include "crypto.h"
-+#include "aes_wrap.h"
-+#include "aes.h"
-+#include "md5.h"
-+#include "sha1.h"
-+#include "sha256.h"
-+#include "sha384.h"
-+#include "sha512.h"
-+
-+
-+/*
-+ * selective code inclusion based on preprocessor defines
-+ *
-+ * future: additional code could be wrapped with preprocessor checks if
-+ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
-+ * setting preprocessor defines for named groups of functionality
-+ */
-+
-+#if defined(CONFIG_FIPS)
-+#undef MBEDTLS_MD4_C /* omit md4_vector() */
-+#undef MBEDTLS_MD5_C /* omit md5_vector() hmac_md5_vector() hmac_md5() */
-+#undef MBEDTLS_DES_C /* omit des_encrypt() */
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#define CRYPTO_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if !defined(CONFIG_FIPS)
-+#if defined(EAP_PWD) \
-+ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
-+ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
-+ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
-+ || defined(EAP_SERVER_MSCHAPV2)
-+#ifndef MBEDTLS_MD4_C /* (MD4 not in mbedtls 3.x) */
-+#include "md4-internal.c"/* pull in hostap local implementation */
-+#endif /* md4_vector() */
-+#else
-+#undef MBEDTLS_MD4_C /* omit md4_vector() */
-+#endif
-+#endif
-+
-+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
-+#ifndef MBEDTLS_ARC4_C /* (RC4 not in mbedtls 3.x) */
-+#include "rc4.c" /* pull in hostap local implementation */
-+#endif /* rc4_skip() */
-+#else
-+#undef MBEDTLS_ARC4_C /* omit rc4_skip() */
-+#endif
-+
-+#if defined(CONFIG_MACSEC) \
-+ || defined(CONFIG_NO_RADIUS) \
-+ || defined(CONFIG_IEEE80211R) \
-+ || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_SERVER_TEAP) \
-+ || !defined(CONFIG_NO_WPA)
-+ /* aes_wrap() aes_unwrap() */
-+#else
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#endif
-+
-+#if !defined(CONFIG_SHA256)
-+#undef MBEDTLS_SHA256_C
-+#endif
-+
-+#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
-+#undef MBEDTLS_SHA512_C
-+#endif
-+
-+#if defined(CONFIG_HMAC_SHA256_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+#endif
-+#if defined(CONFIG_HMAC_SHA384_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+#endif
-+#if defined(CONFIG_HMAC_SHA512_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+#endif
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define CRYPTO_MBEDTLS_SHA1_T_PRF
-+#endif
-+
-+#if defined(CONFIG_DES)
-+#define CRYPTO_MBEDTLS_DES_ENCRYPT
-+#endif /* des_encrypt() */
-+
-+#if !defined(CONFIG_NO_PBKDF2)
-+#define CRYPTO_MBEDTLS_PBKDF2_SHA1
-+#endif /* pbkdf2_sha1() */
-+
-+#if defined(EAP_IKEV2) \
-+ || defined(EAP_IKEV2_DYNAMIC) \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+#endif /* crypto_cipher_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HASH
-+#endif /* crypto_hash_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
-+ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#endif /* crypto_bignum_*() */
-+
-+#if defined(EAP_PWD) /* CONFIG_EAP_PWD=y */ \
-+ || defined(EAP_EKE) /* CONFIG_EAP_EKE=y */ \
-+ || defined(EAP_EKE_DYNAMIC) /* CONFIG_EAP_EKE=y */ \
-+ || defined(EAP_SERVER_EKE) /* CONFIG_EAP_EKE=y */ \
-+ || defined(EAP_IKEV2) /* CONFIG_EAP_IKEV2y */ \
-+ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */ \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */ \
-+ || defined(CONFIG_SAE) /* CONFIG_SAE=y */ \
-+ || defined(CONFIG_WPS) /* CONFIG_WPS=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_DH
-+#if defined(CONFIG_WPS_NFC)
-+#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+#endif /* dh5_init_fixed() */
-+#endif /* crypto_dh_*() */
-+
-+#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
-+#define CRYPTO_MBEDTLS_CRYPTO_ECDH
-+#endif /* crypto_ecdh_*() */
-+
-+#if defined(CONFIG_ECC)
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#define CRYPTO_MBEDTLS_CRYPTO_EC
-+#endif /* crypto_ec_*() crypto_ec_key_*() */
-+
-+#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
-+#define CRYPTO_MBEDTLS_CRYPTO_CSR
-+#endif /* crypto_csr_*() */
-+
-+#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#endif
-+
-+#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+#endif /* crypto_pkcs7_*() */
-+
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
-+ || defined(CONFIG_AP) || defined(HOSTAPD)
-+/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
-+#if defined(CRYPTO_RSA_OAEP_SHA256)
-+#define CRYPTO_MBEDTLS_CRYPTO_RSA
-+#endif
-+#endif /* crypto_rsa_*() */
-+
-+
-+static int ctr_drbg_init_state;
-+static mbedtls_ctr_drbg_context ctr_drbg;
-+static mbedtls_entropy_context entropy;
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#include <mbedtls/bignum.h>
-+static mbedtls_mpi mpi_sw_A;
-+#endif
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
-+{
-+ mbedtls_ctr_drbg_init(&ctr_drbg);
-+ mbedtls_entropy_init(&entropy);
-+ if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
-+ NULL, 0)) {
-+ wpa_printf(MSG_ERROR, "Init of random number generator failed");
-+ /* XXX: abort? */
-+ }
-+ else
-+ ctr_drbg_init_state = 1;
-+
-+ return &ctr_drbg;
-+}
-+
-+__attribute_cold__
-+void crypto_unload(void)
-+{
-+ if (ctr_drbg_init_state) {
-+ mbedtls_ctr_drbg_free(&ctr_drbg);
-+ mbedtls_entropy_free(&entropy);
-+ #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+ mbedtls_mpi_free(&mpi_sw_A);
-+ #endif
-+ ctr_drbg_init_state = 0;
-+ }
-+}
-+
-+/* init ctr_drbg on first use
-+ * crypto_global_init() and crypto_global_deinit() are not available here
-+ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+inline
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
-+{
-+ return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
-+int crypto_get_random(void *buf, size_t len)
-+{
-+ return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
-+}
-+#endif
-+
-+
-+#if 1
-+
-+/* tradeoff: slightly smaller code size here at cost of slight increase
-+ * in instructions and function calls at runtime versus the expanded
-+ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
-+
-+__attribute_noinline__
-+static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-+ u8 *mac, mbedtls_md_type_t md_type)
-+{
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_starts(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_finish(&ctx, mac);
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
-+}
-+#endif
-+
-+#else /* expanded per-message-digest functions */
-+
-+#ifdef MBEDTLS_SHA512_C
-+#include <mbedtls/sha512.h>
-+__attribute_noinline__
-+static int sha384_512_vector(size_t num_elem, const u8 *addr[],
-+ const size_t *len, u8 *mac, int is384)
-+{
-+ struct mbedtls_sha512_context ctx;
-+ mbedtls_sha512_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_sha512_starts(&ctx, is384);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha512_update(&ctx, addr[i], len[i]);
-+ mbedtls_sha512_finish(&ctx, mac);
-+ #else
-+ mbedtls_sha512_starts_ret(&ctx, is384);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_sha512_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_sha512_free(&ctx);
-+ return 0;
-+}
-+
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return sha384_512_vector(num_elem, addr, len, mac, 0);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return sha384_512_vector(num_elem, addr, len, mac, 1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#include <mbedtls/sha256.h>
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_sha256_context ctx;
-+ mbedtls_sha256_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_sha256_starts(&ctx, 0);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha256_update(&ctx, addr[i], len[i]);
-+ mbedtls_sha256_finish(&ctx, mac);
-+ #else
-+ mbedtls_sha256_starts_ret(&ctx, 0);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_sha256_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_sha256_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+#include <mbedtls/sha1.h>
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_sha1_context ctx;
-+ mbedtls_sha1_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_sha1_starts(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha1_update(&ctx, addr[i], len[i]);
-+ mbedtls_sha1_finish(&ctx, mac);
-+ #else
-+ mbedtls_sha1_starts_ret(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_sha1_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_sha1_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+#include <mbedtls/md5.h>
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_md5_context ctx;
-+ mbedtls_md5_init(&ctx);
-+ #if MBEDTLS_VERSION_MAJOR >= 3
-+ mbedtls_md5_starts(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md5_update(&ctx, addr[i], len[i]);
-+ mbedtls_md5_finish(&ctx, mac);
-+ #else
-+ mbedtls_md5_starts_ret(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_md5_finish_ret(&ctx, mac);
-+ #endif
-+ mbedtls_md5_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ struct mbedtls_md4_context ctx;
-+ mbedtls_md4_init(&ctx);
-+ mbedtls_md4_starts_ret(&ctx);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
-+ mbedtls_md4_finish_ret(&ctx, mac);
-+ mbedtls_md4_free(&ctx);
-+ return 0;
-+}
-+#endif
-+
-+#endif /* expanded per-message-digest functions */
-+
-+
-+__attribute_noinline__
-+static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac,
-+ mbedtls_md_type_t md_type)
-+{
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_hmac_starts(&ctx, key, key_len);
-+ for (size_t i = 0; i < num_elem; ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, mac);
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA384);
-+}
-+
-+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA256);
-+}
-+
-+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_SHA1);
-+}
-+
-+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
-+ const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+ return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+ MBEDTLS_MD_MD5);
-+}
-+
-+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+ u8 *mac)
-+{
-+ return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+ MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+
-+#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
-+
-+#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
-+
-+#include <mbedtls/hkdf.h>
-+
-+/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
-+
-+/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
-+/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
-+/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
-+__attribute_noinline__
-+static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
-+ const char *label, const u8 *info, size_t info_len,
-+ u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
-+{
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+ #ifdef MBEDTLS_HKDF_C
-+ if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */
-+ return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
-+ info_len, okm, okm_len) ? -1 : 0;
-+ #endif
-+
-+ const size_t mac_len = mbedtls_md_get_size(md_info);
-+ /* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
-+ if (okm_len > ((mac_len << 8) - mac_len))
-+ return -1;
-+
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_hmac_starts(&ctx, prk, prk_len);
-+
-+ u8 iter = 1;
-+ const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
-+ size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
-+
-+ for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, okm);
-+ mbedtls_md_hmac_reset(&ctx);
-+ addr[0] = okm;
-+ okm += mac_len;
-+ len[0] = mac_len; /*(include digest in subsequent rounds)*/
-+ }
-+
-+ if (okm_len) {
-+ u8 hash[MBEDTLS_MD_MAX_SIZE];
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, hash);
-+ os_memcpy(okm, hash, okm_len);
-+ forced_memzero(hash, mac_len);
-+ }
-+
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
-+ const char *label, const u8 *seed, size_t seed_len,
-+ u8 *out, size_t outlen)
-+{
-+ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+ out, outlen, MBEDTLS_MD_SHA512);
-+}
-+#endif
-+
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
-+ const char *label, const u8 *seed, size_t seed_len,
-+ u8 *out, size_t outlen)
-+{
-+ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+ out, outlen, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
-+ const char *label, const u8 *seed, size_t seed_len,
-+ u8 *out, size_t outlen)
-+{
-+ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+ out, outlen, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
-+
-+
-+/* sha256-prf.c sha384-prf.c sha512-prf.c */
-+
-+/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
-+__attribute_noinline__
-+static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf,
-+ size_t buf_len_bits, mbedtls_md_type_t md_type)
-+{
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+ mbedtls_md_free(&ctx);
-+ return -1;
-+ }
-+ mbedtls_md_hmac_starts(&ctx, key, key_len);
-+
-+ u16 ctr, n_le = host_to_le16(buf_len_bits);
-+ const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
-+ const size_t len[] = { 2, os_strlen(label), data_len, 2 };
-+ const size_t mac_len = mbedtls_md_get_size(md_info);
-+ size_t buf_len = (buf_len_bits + 7) / 8;
-+ for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
-+ #if __BYTE_ORDER == __BIG_ENDIAN
-+ ctr = host_to_le16(ctr);
-+ #endif
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, buf);
-+ mbedtls_md_hmac_reset(&ctx);
-+ buf += mac_len;
-+ #if __BYTE_ORDER == __BIG_ENDIAN
-+ ctr = le_to_host16(ctr);
-+ #endif
-+ }
-+
-+ if (buf_len) {
-+ u8 hash[MBEDTLS_MD_MAX_SIZE];
-+ #if __BYTE_ORDER == __BIG_ENDIAN
-+ ctr = host_to_le16(ctr);
-+ #endif
-+ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+ mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+ mbedtls_md_hmac_finish(&ctx, hash);
-+ os_memcpy(buf, hash, buf_len);
-+ buf += buf_len;
-+ forced_memzero(hash, mac_len);
-+ }
-+
-+ /* Mask out unused bits in last octet if it does not use all the bits */
-+ if ((buf_len_bits &= 0x7))
-+ buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
-+
-+ mbedtls_md_free(&ctx);
-+ return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len * 8, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len * 8, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len * 8, MBEDTLS_MD_SHA256);
-+}
-+
-+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf,
-+ size_t buf_len_bits)
-+{
-+ return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+ buf_len_bits, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
-+
-+
-+#ifdef MBEDTLS_SHA1_C
-+
-+/* sha1-prf.c */
-+
-+/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
-+
-+int sha1_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+ /*(note: algorithm differs from hmac_prf_bits() */
-+ /*(note: smaller code size instead of expanding hmac_sha1_vector()
-+ * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+ u8 counter = 0;
-+ const u8 *addr[] = { (u8 *)label, data, &counter };
-+ const size_t len[] = { os_strlen(label)+1, data_len, 1 };
-+
-+ for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
-+ if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
-+ return -1;
-+ buf += SHA1_MAC_LEN;
-+ }
-+
-+ if (buf_len) {
-+ u8 hash[SHA1_MAC_LEN];
-+ if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
-+ return -1;
-+ os_memcpy(buf, hash, buf_len);
-+ forced_memzero(hash, sizeof(hash));
-+ }
-+
-+ return 0;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
-+
-+/* sha1-tprf.c */
-+
-+/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
-+
-+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
-+{
-+ /*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
-+ /*(note: smaller code size instead of expanding hmac_sha1_vector()
-+ * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+ u8 ctr;
-+ u16 olen = host_to_be16(buf_len);
-+ const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
-+ size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
-+
-+ for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
-+ if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
-+ return -1;
-+ addr[0] = buf;
-+ buf += SHA1_MAC_LEN;
-+ len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
-+ }
-+
-+ if (buf_len) {
-+ u8 hash[SHA1_MAC_LEN];
-+ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
-+ return -1;
-+ os_memcpy(buf, hash, buf_len);
-+ forced_memzero(hash, sizeof(hash));
-+ }
-+
-+ return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
-+
-+#endif /* MBEDTLS_SHA1_C */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
-+#ifdef MBEDTLS_DES_C
-+#include <mbedtls/des.h>
-+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-+{
-+ u8 pkey[8], next, tmp;
-+ int i;
-+
-+ /* Add parity bits to the key */
-+ next = 0;
-+ for (i = 0; i < 7; i++) {
-+ tmp = key[i];
-+ pkey[i] = (tmp >> i) | next | 1;
-+ next = tmp << (7 - i);
-+ }
-+ pkey[i] = next | 1;
-+
-+ mbedtls_des_context des;
-+ mbedtls_des_init(&des);
-+ int ret = mbedtls_des_setkey_enc(&des, pkey)
-+ || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
-+ mbedtls_des_free(&des);
-+ return ret;
-+}
-+#else
-+#include "des-internal.c"/* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
-+/* sha1-pbkdf2.c */
-+#include <mbedtls/pkcs5.h>
-+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
-+ int iterations, u8 *buf, size_t buflen)
-+{
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
-+ return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
-+ (const u8 *)passphrase, os_strlen(passphrase),
-+ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+ #else
-+ const mbedtls_md_info_t *md_info;
-+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
-+ if (md_info == NULL)
-+ return -1;
-+ mbedtls_md_context_t ctx;
-+ mbedtls_md_init(&ctx);
-+ int ret = mbedtls_md_setup(&ctx, md_info, 1)
-+ || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
-+ (const u8 *)passphrase, os_strlen(passphrase),
-+ ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+ mbedtls_md_free(&ctx);
-+ return ret;
-+ #endif
-+}
-+#endif
-+
-+
-+/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
-+
-+static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
-+{
-+ mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
-+ if (!aes)
-+ return NULL;
-+
-+ mbedtls_aes_init(aes);
-+ if ((mode == MBEDTLS_AES_ENCRYPT
-+ ? mbedtls_aes_setkey_enc(aes, key, len * 8)
-+ : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
-+ return aes;
-+
-+ mbedtls_aes_free(aes);
-+ os_free(aes);
-+ return NULL;
-+}
-+
-+void *aes_encrypt_init(const u8 *key, size_t len)
-+{
-+ return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-+{
-+ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
-+}
-+
-+void aes_encrypt_deinit(void *ctx)
-+{
-+ mbedtls_aes_free(ctx);
-+ os_free(ctx);
-+}
-+
-+void *aes_decrypt_init(const u8 *key, size_t len)
-+{
-+ return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-+{
-+ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
-+}
-+
-+void aes_decrypt_deinit(void *ctx)
-+{
-+ mbedtls_aes_free(ctx);
-+ os_free(ctx);
-+}
-+
-+
-+#include "aes_wrap.h"
-+
-+
-+#ifdef MBEDTLS_NIST_KW_C
-+
-+#include <mbedtls/nist_kw.h>
-+
-+/* aes-wrap.c */
-+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
-+{
-+ mbedtls_nist_kw_context ctx;
-+ mbedtls_nist_kw_init(&ctx);
-+ size_t olen;
-+ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+ kek, kek_len*8, 1)
-+ || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
-+ cipher, &olen, (n+1)*8) ? -1 : 0;
-+ mbedtls_nist_kw_free(&ctx);
-+ return ret;
-+}
-+
-+/* aes-unwrap.c */
-+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
-+{
-+ mbedtls_nist_kw_context ctx;
-+ mbedtls_nist_kw_init(&ctx);
-+ size_t olen;
-+ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+ kek, kek_len*8, 0)
-+ || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
-+ (n+1)*8, plain, &olen, n*8) ? -1 : 0;
-+ mbedtls_nist_kw_free(&ctx);
-+ return ret;
-+}
-+
-+#else
-+
-+#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
-+#include "aes-wrap.c" /* pull in hostap local implementation */
-+#include "aes-unwrap.c" /* pull in hostap local implementation */
-+#endif
-+
-+#endif /* MBEDTLS_NIST_KW_C */
-+
-+
-+#ifdef MBEDTLS_CMAC_C
-+
-+/* aes-omac1.c */
-+
-+#include <mbedtls/cmac.h>
-+
-+int omac1_aes_vector(
-+ const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
-+ const size_t *len, u8 *mac)
-+{
-+ mbedtls_cipher_type_t cipher_type;
-+ switch (key_len) {
-+ case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
-+ case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
-+ case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
-+ default: return -1;
-+ }
-+ const mbedtls_cipher_info_t *cipher_info;
-+ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+ if (cipher_info == NULL)
-+ return -1;
-+
-+ mbedtls_cipher_context_t ctx;
-+ mbedtls_cipher_init(&ctx);
-+ int ret = -1;
-+ if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
-+ && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
-+ ret = 0;
-+ for (size_t i = 0; i < num_elem && ret == 0; ++i)
-+ ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
-+ }
-+ if (ret == 0)
-+ ret = mbedtls_cipher_cmac_finish(&ctx, mac);
-+ mbedtls_cipher_free(&ctx);
-+ return ret ? -1 : 0;
-+}
-+
-+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-+ const u8 *addr[], const size_t *len,
-+ u8 *mac)
-+{
-+ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
-+}
-+
-+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+ return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
-+}
-+
-+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
-+}
-+
-+#else
-+
-+#include "aes-omac1.c" /* pull in hostap local implementation */
-+
-+#ifndef MBEDTLS_AES_BLOCK_SIZE
-+#define MBEDTLS_AES_BLOCK_SIZE 16
-+#endif
-+
-+#endif /* MBEDTLS_CMAC_C */
-+
-+
-+/* These interfaces can be inefficient when used in loops, as the overhead of
-+ * initialization each call is large for each block input (e.g. 16 bytes) */
-+
-+
-+/* aes-encblock.c */
-+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
-+{
-+ mbedtls_aes_context aes;
-+ mbedtls_aes_init(&aes);
-+ int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
-+ || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
-+ ? -1
-+ : 0;
-+ mbedtls_aes_free(&aes);
-+ return ret;
-+}
-+
-+
-+/* aes-ctr.c */
-+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
-+ u8 *data, size_t data_len)
-+{
-+ unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
-+ unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
-+ os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
-+
-+ mbedtls_aes_context ctx;
-+ mbedtls_aes_init(&ctx);
-+ size_t nc_off = 0;
-+ int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
-+ || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
-+ counter, stream_block,
-+ data, data) ? -1 : 0;
-+ forced_memzero(stream_block, sizeof(stream_block));
-+ mbedtls_aes_free(&ctx);
-+ return ret;
-+}
-+
-+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
-+ u8 *data, size_t data_len)
-+{
-+ return aes_ctr_encrypt(key, 16, nonce, data, data_len);
-+}
-+
-+
-+/* aes-cbc.c */
-+static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
-+ u8 *data, size_t data_len, int mode)
-+{
-+ unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
-+ os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
-+
-+ mbedtls_aes_context ctx;
-+ mbedtls_aes_init(&ctx);
-+ int ret = (mode == MBEDTLS_AES_ENCRYPT
-+ ? mbedtls_aes_setkey_enc(&ctx, key, 128)
-+ : mbedtls_aes_setkey_dec(&ctx, key, 128))
-+ || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
-+ mbedtls_aes_free(&ctx);
-+ return ret ? -1 : 0;
-+}
-+
-+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+
-+/*
-+ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
-+ * but such comments are not accurate:
-+ *
-+ * "This function is only used with internal TLSv1 implementation
-+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
-+ * to implement this."
-+ */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+
-+#include <mbedtls/cipher.h>
-+
-+struct crypto_cipher
-+{
-+ mbedtls_cipher_context_t ctx_enc;
-+ mbedtls_cipher_context_t ctx_dec;
-+};
-+
-+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-+ const u8 *iv, const u8 *key,
-+ size_t key_len)
-+{
-+ /* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
-+ * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
-+
-+ mbedtls_cipher_type_t cipher_type;
-+ size_t iv_len;
-+ switch (alg) {
-+ #ifdef MBEDTLS_ARC4_C
-+ #if 0
-+ case CRYPTO_CIPHER_ALG_RC4:
-+ cipher_type = MBEDTLS_CIPHER_ARC4_128;
-+ iv_len = 0;
-+ break;
-+ #endif
-+ #endif
-+ #ifdef MBEDTLS_AES_C
-+ case CRYPTO_CIPHER_ALG_AES:
-+ if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
-+ if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
-+ if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
-+ iv_len = 16;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_DES_C
-+ case CRYPTO_CIPHER_ALG_3DES:
-+ cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
-+ iv_len = 8;
-+ break;
-+ #if 0
-+ case CRYPTO_CIPHER_ALG_DES:
-+ cipher_type = MBEDTLS_CIPHER_DES_CBC;
-+ iv_len = 8;
-+ break;
-+ #endif
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+
-+ const mbedtls_cipher_info_t *cipher_info;
-+ cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+ if (cipher_info == NULL)
-+ return NULL;
-+
-+ key_len *= 8; /* key_bitlen */
-+ #if 0 /*(were key_bitlen not already available)*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+ key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
-+ #else
-+ key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
-+ #endif
-+ #endif
-+
-+ #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
-+ iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
-+ #endif
-+
-+ struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
-+ if (!ctx)
-+ return NULL;
-+
-+ mbedtls_cipher_init(&ctx->ctx_enc);
-+ mbedtls_cipher_init(&ctx->ctx_dec);
-+ if ( mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
-+ && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
-+ && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
-+ && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
-+ && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
-+ && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
-+ && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
-+ && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
-+ return ctx;
-+ }
-+
-+ mbedtls_cipher_free(&ctx->ctx_enc);
-+ mbedtls_cipher_free(&ctx->ctx_dec);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+int crypto_cipher_encrypt(struct crypto_cipher *ctx,
-+ const u8 *plain, u8 *crypt, size_t len)
-+{
-+ size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
-+ return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
-+ || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
-+}
-+
-+int crypto_cipher_decrypt(struct crypto_cipher *ctx,
-+ const u8 *crypt, u8 *plain, size_t len)
-+{
-+ size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
-+ return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
-+ || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
-+}
-+
-+void crypto_cipher_deinit(struct crypto_cipher *ctx)
-+{
-+ mbedtls_cipher_free(&ctx->ctx_enc);
-+ mbedtls_cipher_free(&ctx->ctx_dec);
-+ os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
-+
-+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
-+ size_t key_len)
-+{
-+ mbedtls_md_type_t md_type;
-+ int is_hmac = 0;
-+
-+ switch (alg) {
-+ #ifdef MBEDTLS_MD5_C
-+ case CRYPTO_HASH_ALG_MD5:
-+ md_type = MBEDTLS_MD_MD5;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA1_C
-+ case CRYPTO_HASH_ALG_SHA1:
-+ md_type = MBEDTLS_MD_SHA1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_MD5_C
-+ case CRYPTO_HASH_ALG_HMAC_MD5:
-+ md_type = MBEDTLS_MD_MD5;
-+ is_hmac = 1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA1_C
-+ case CRYPTO_HASH_ALG_HMAC_SHA1:
-+ md_type = MBEDTLS_MD_SHA1;
-+ is_hmac = 1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA256_C
-+ case CRYPTO_HASH_ALG_SHA256:
-+ md_type = MBEDTLS_MD_SHA256;
-+ break;
-+ case CRYPTO_HASH_ALG_HMAC_SHA256:
-+ md_type = MBEDTLS_MD_SHA256;
-+ is_hmac = 1;
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_SHA512_C
-+ case CRYPTO_HASH_ALG_SHA384:
-+ md_type = MBEDTLS_MD_SHA384;
-+ break;
-+ case CRYPTO_HASH_ALG_SHA512:
-+ md_type = MBEDTLS_MD_SHA512;
-+ break;
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+ if (!md_info)
-+ return NULL;
-+
-+ mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
-+ if (mctx == NULL)
-+ return NULL;
-+
-+ mbedtls_md_init(mctx);
-+ if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
-+ os_free(mctx);
-+ return NULL;
-+ }
-+
-+ if (is_hmac)
-+ mbedtls_md_hmac_starts(mctx, key, key_len);
-+ else
-+ mbedtls_md_starts(mctx);
-+ return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
-+}
-+
-+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
-+{
-+ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+ #if 0
-+ /*(mbedtls_md_hmac_update() and mbedtls_md_update()
-+ * make same modifications under the hood in mbedtls)*/
-+ if ((uintptr_t)ctx & 1uL)
-+ mbedtls_md_hmac_update(mctx, data, len);
-+ else
-+ #endif
-+ mbedtls_md_update(mctx, data, len);
-+}
-+
-+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
-+{
-+ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+ if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
-+ #else
-+ const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
-+ #endif
-+ size_t maclen = mbedtls_md_get_size(md_info);
-+ if (*len < maclen) {
-+ *len = maclen;
-+ /*(note: ctx not freed; can call again with larger *len)*/
-+ return -1;
-+ }
-+ *len = maclen;
-+ if ((uintptr_t)ctx & 1uL)
-+ mbedtls_md_hmac_finish(mctx, mac);
-+ else
-+ mbedtls_md_finish(mctx, mac);
-+ }
-+ mbedtls_md_free(mctx);
-+ os_free(mctx);
-+ return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+
-+#include <mbedtls/bignum.h>
-+
-+/* crypto.h bignum interfaces */
-+
-+struct crypto_bignum *crypto_bignum_init(void)
-+{
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn)
-+ mbedtls_mpi_init(bn);
-+ return (struct crypto_bignum *)bn;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
-+{
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn) {
-+ mbedtls_mpi_init(bn);
-+ if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
-+ return (struct crypto_bignum *)bn;
-+ }
-+
-+ os_free(bn);
-+ return NULL;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
-+{
-+ #if 0 /*(hostap use of this interface passes int, not uint)*/
-+ val = host_to_be32(val);
-+ return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
-+ #else
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn) {
-+ mbedtls_mpi_init(bn);
-+ if (mbedtls_mpi_lset(bn, (int)val) == 0)
-+ return (struct crypto_bignum *)bn;
-+ }
-+
-+ os_free(bn);
-+ return NULL;
-+ #endif
-+}
-+
-+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
-+{
-+ mbedtls_mpi_free((mbedtls_mpi *)n);
-+ os_free(n);
-+}
-+
-+int crypto_bignum_to_bin(const struct crypto_bignum *a,
-+ u8 *buf, size_t buflen, size_t padlen)
-+{
-+ size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
-+ if (n < padlen)
-+ n = padlen;
-+ return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
-+ ? -1
-+ : (int)(n);
-+}
-+
-+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
-+{
-+ /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
-+ return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+ #else
-+ /* (needed by EAP_PWD, SAE, DPP) */
-+ wpa_printf(MSG_ERROR,
-+ "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
-+ return -1;
-+ #endif
-+}
-+
-+int crypto_bignum_add(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_exptmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ const struct crypto_bignum *c,
-+ struct crypto_bignum *d)
-+{
-+ /* (check if input params match d; d is the result) */
-+ /* (a == d) is ok in current mbedtls implementation */
-+ if (b == d || c == d) { /*(not ok; store result in intermediate)*/
-+ mbedtls_mpi R;
-+ mbedtls_mpi_init(&R);
-+ int rc = mbedtls_mpi_exp_mod(&R,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b,
-+ (const mbedtls_mpi *)c,
-+ NULL)
-+ || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
-+ mbedtls_mpi_free(&R);
-+ return rc;
-+ }
-+ else {
-+ return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b,
-+ (const mbedtls_mpi *)c,
-+ NULL) ? -1 : 0;
-+ }
-+}
-+
-+int crypto_bignum_inverse(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sub(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_div(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ /*(most current use of this crypto.h interface has a == c (result),
-+ * so store result in an intermediate to avoid overwritten input)*/
-+ mbedtls_mpi R;
-+ mbedtls_mpi_init(&R);
-+ int rc = mbedtls_mpi_div_mpi(&R, NULL,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b)
-+ || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
-+ mbedtls_mpi_free(&R);
-+ return rc;
-+}
-+
-+int crypto_bignum_addmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ const struct crypto_bignum *c,
-+ struct crypto_bignum *d)
-+{
-+ return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b)
-+ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+ (mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mulmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ const struct crypto_bignum *c,
-+ struct crypto_bignum *d)
-+{
-+ return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)a,
-+ (const mbedtls_mpi *)b)
-+ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+ (mbedtls_mpi *)d,
-+ (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b,
-+ struct crypto_bignum *c)
-+{
-+ #if 1
-+ return crypto_bignum_mulmod(a, a, b, c);
-+ #else
-+ mbedtls_mpi bn;
-+ mbedtls_mpi_init(&bn);
-+ if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
-+ return -1;
-+ int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
-+ (const mbedtls_mpi *)a, &bn,
-+ (const mbedtls_mpi *)b, NULL) ? -1 : 0;
-+ mbedtls_mpi_free(&bn);
-+ return ret;
-+ #endif
-+}
-+
-+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-+ struct crypto_bignum *r)
-+{
-+ return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
-+ || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
-+}
-+
-+int crypto_bignum_cmp(const struct crypto_bignum *a,
-+ const struct crypto_bignum *b)
-+{
-+ return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
-+}
-+
-+int crypto_bignum_is_zero(const struct crypto_bignum *a)
-+{
-+ /* XXX: src/common/sae.c:sswu() contains comment:
-+ * "TODO: Make sure crypto_bignum_is_zero() is constant time"
-+ * Note: mbedtls_mpi_cmp_int() *is not* constant time */
-+ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
-+}
-+
-+int crypto_bignum_is_one(const struct crypto_bignum *a)
-+{
-+ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
-+}
-+
-+int crypto_bignum_is_odd(const struct crypto_bignum *a)
-+{
-+ return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
-+}
-+
-+#include "utils/const_time.h"
-+int crypto_bignum_legendre(const struct crypto_bignum *a,
-+ const struct crypto_bignum *p)
-+{
-+ /* Security Note:
-+ * mbedtls_mpi_exp_mod() is not documented to run in constant time,
-+ * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
-+ * Compare to crypto_openssl.c:crypto_bignum_legendre()
-+ * which uses openssl BN_mod_exp_mont_consttime()
-+ * mbedtls/library/ecp.c has further countermeasures to timing attacks,
-+ * (but ecp.c funcs are not used here) */
-+
-+ mbedtls_mpi exp, tmp;
-+ mbedtls_mpi_init(&exp);
-+ mbedtls_mpi_init(&tmp);
-+
-+ /* exp = (p-1) / 2 */
-+ int res;
-+ if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
-+ && mbedtls_mpi_shift_r(&exp, 1) == 0
-+ && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
-+ (const mbedtls_mpi *)p, NULL) == 0) {
-+ /*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
-+ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
-+ * to use constant time selection to avoid branches here. */
-+ unsigned int mask;
-+ res = -1;
-+ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
-+ res = const_time_select_int(mask, 1, res);
-+ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
-+ res = const_time_select_int(mask, 0, res);
-+ } else {
-+ res = -2;
-+ }
-+
-+ mbedtls_mpi_free(&tmp);
-+ mbedtls_mpi_free(&exp);
-+ return res;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
-+
-+/* crypto_internal-modexp.c */
-+
-+#include <mbedtls/bignum.h>
-+#include <mbedtls/dhm.h>
-+
-+#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
-+int crypto_mod_exp(const u8 *base, size_t base_len,
-+ const u8 *power, size_t power_len,
-+ const u8 *modulus, size_t modulus_len,
-+ u8 *result, size_t *result_len)
-+{
-+ mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
-+ mbedtls_mpi_init(&bn_base);
-+ mbedtls_mpi_init(&bn_exp);
-+ mbedtls_mpi_init(&bn_modulus);
-+ mbedtls_mpi_init(&bn_result);
-+
-+ size_t len;
-+ int ret = mbedtls_mpi_read_binary(&bn_base, base, base_len)
-+ || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
-+ || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
-+ || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
-+ || (len = mbedtls_mpi_size(&bn_result)) > *result_len
-+ || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
-+ ? -1
-+ : 0;
-+
-+ mbedtls_mpi_free(&bn_base);
-+ mbedtls_mpi_free(&bn_exp);
-+ mbedtls_mpi_free(&bn_modulus);
-+ mbedtls_mpi_free(&bn_result);
-+ return ret;
-+}
-+#endif
-+
-+static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
-+ const u8 *prime, size_t prime_len)
-+{
-+ /*(could set these directly in MBEDTLS_PRIVATE members)*/
-+ mbedtls_mpi P, G;
-+ mbedtls_mpi_init(&P);
-+ mbedtls_mpi_init(&G);
-+ int ret = mbedtls_mpi_lset(&G, generator)
-+ || mbedtls_mpi_read_binary(&P, prime, prime_len)
-+ || mbedtls_dhm_set_group(ctx, &P, &G);
-+ mbedtls_mpi_free(&P);
-+ mbedtls_mpi_free(&G);
-+ return ret;
-+}
-+
-+__attribute_noinline__
-+static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
-+ const u8 *prime, size_t prime_len,
-+ u8 *privkey, u8 *pubkey)
-+{
-+ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
-+ || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()))
-+ return -1;
-+
-+ /*(enable later when upstream mbedtls interface changes require)*/
-+ #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ mbedtls_mpi X;
-+ mbedtls_mpi_init(&X);
-+ int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
-+ || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
-+ mbedtls_mpi_free(&X);
-+ return ret;
-+ #else
-+ return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
-+ privkey, prime_len) ? -1 : 0;
-+ #endif
-+}
-+
-+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
-+ u8 *pubkey)
-+{
-+ #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
-+ size_t pubkey_len, pad;
-+
-+ if (os_get_random(privkey, prime_len) < 0)
-+ return -1;
-+ if (os_memcmp(privkey, prime, prime_len) > 0) {
-+ /* Make sure private value is smaller than prime */
-+ privkey[0] = 0;
-+ }
-+
-+ pubkey_len = prime_len;
-+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
-+ pubkey, &pubkey_len) < 0)
-+ return -1;
-+ if (pubkey_len < prime_len) {
-+ pad = prime_len - pubkey_len;
-+ os_memmove(pubkey + pad, pubkey, pubkey_len);
-+ os_memset(pubkey, 0, pad);
-+ }
-+
-+ return 0;
-+ #else
-+ /* Prefer to use mbedtls to derive our public/private key, as doing so
-+ * leverages mbedtls to properly format output and to perform blinding*/
-+ mbedtls_dhm_context ctx;
-+ mbedtls_dhm_init(&ctx);
-+ int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
-+ prime_len, privkey, pubkey);
-+ mbedtls_dhm_free(&ctx);
-+ return ret;
-+ #endif
-+}
-+
-+/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
-+ * instead of being reimplemented in each crypto_*.c)*/
-+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
-+ const u8 *order, size_t order_len,
-+ const u8 *privkey, size_t privkey_len,
-+ const u8 *pubkey, size_t pubkey_len,
-+ u8 *secret, size_t *len)
-+{
-+ #if 0
-+ if (pubkey_len > prime_len ||
-+ (pubkey_len == prime_len &&
-+ os_memcmp(pubkey, prime, prime_len) >= 0))
-+ return -1;
-+
-+ int res = 0;
-+ mbedtls_mpi pub;
-+ mbedtls_mpi_init(&pub);
-+ if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
-+ || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
-+ res = -1;
-+ } else if (order) {
-+ mbedtls_mpi p, q, tmp;
-+ mbedtls_mpi_init(&p);
-+ mbedtls_mpi_init(&q);
-+ mbedtls_mpi_init(&tmp);
-+
-+ /* verify: pubkey^q == 1 mod p */
-+ res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
-+ || mbedtls_mpi_read_binary(&q, order, order_len)
-+ || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
-+ || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
-+
-+ mbedtls_mpi_free(&p);
-+ mbedtls_mpi_free(&q);
-+ mbedtls_mpi_free(&tmp);
-+ }
-+ mbedtls_mpi_free(&pub);
-+
-+ return (res == 0)
-+ ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-+ prime, prime_len, secret, len)
-+ : -1;
-+ #else
-+ /* Prefer to use mbedtls to derive DH shared secret, as doing so
-+ * leverages mbedtls to validate params and to perform blinding.
-+ *
-+ * Attempt to reconstitute DH context to derive shared secret
-+ * (due to limitations of the interface, which ought to pass context).
-+ * Force provided G (our private key) into context without validation.
-+ * Regenerating GX (our public key) not needed to derive shared secret.
-+ */
-+ /*(older compilers might not support VLAs)*/
-+ /*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
-+ unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
-+ unsigned char *p = buf + 2 + prime_len;
-+ if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
-+ return -1;
-+ WPA_PUT_BE16(buf, prime_len); /*(2-byte big-endian size of prime)*/
-+ p[0] = 0; /*(2-byte big-endian size of generator)*/
-+ p[1] = 1;
-+ p[2] = generator;
-+ WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
-+ os_memcpy(p+5, pubkey, pubkey_len);
-+ os_memcpy(buf+2, prime, prime_len);
-+
-+ mbedtls_dhm_context ctx;
-+ mbedtls_dhm_init(&ctx);
-+ p = buf;
-+ int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
-+ || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
-+ privkey, privkey_len)
-+ || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+ mbedtls_dhm_free(&ctx);
-+ return ret;
-+ #endif
-+}
-+
-+/* dh_group5.c */
-+
-+#include "dh_group5.h"
-+
-+/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
-+
-+static const unsigned char RFC3526_PRIME_1536[] = {
-+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
-+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
-+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
-+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
-+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
-+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
-+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
-+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
-+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
-+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
-+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
-+ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
-+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
-+ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
-+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
-+ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-+};
-+
-+static const unsigned char RFC3526_GENERATOR_1536[] = {
-+ 0x02
-+};
-+
-+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
-+{
-+ const unsigned char * const prime = RFC3526_PRIME_1536;
-+ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+ const u8 generator = *RFC3526_GENERATOR_1536;
-+ struct wpabuf *wpubl = NULL, *wpriv = NULL;
-+
-+ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_dhm_init(ctx);
-+
-+ if ( (wpubl = wpabuf_alloc(prime_len))
-+ && (wpriv = wpabuf_alloc(prime_len))
-+ && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
-+ wpabuf_put(wpriv, prime_len),
-+ wpabuf_put(wpubl, prime_len))==0) {
-+ wpabuf_free(*publ);
-+ wpabuf_clear_free(*priv);
-+ *publ = wpubl;
-+ *priv = wpriv;
-+ return ctx;
-+ }
-+
-+ wpabuf_clear_free(wpriv);
-+ wpabuf_free(wpubl);
-+ mbedtls_dhm_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
-+{
-+ const unsigned char * const prime = RFC3526_PRIME_1536;
-+ const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+ const u8 generator = *RFC3526_GENERATOR_1536;
-+
-+ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_dhm_init(ctx);
-+
-+ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
-+ #if 0 /*(ignore; not required to derive shared secret)*/
-+ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
-+ wpabuf_head(publ),wpabuf_len(publ))==0
-+ #endif
-+ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
-+ wpabuf_head(priv),wpabuf_len(priv))==0) {
-+ return ctx;
-+ }
-+
-+ mbedtls_dhm_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+#endif
-+
-+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
-+ const struct wpabuf *own_private)
-+{
-+ /*((mbedtls_dhm_context *)ctx must already contain own_private)*/
-+ /* mbedtls 2.x: prime_len = ctx->len; */
-+ /* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
-+ size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
-+ struct wpabuf *buf = wpabuf_alloc(olen);
-+ if (buf == NULL)
-+ return NULL;
-+ if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
-+ wpabuf_head(peer_public),
-+ wpabuf_len(peer_public)) == 0
-+ && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0) {
-+ wpabuf_put(buf, olen);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+void dh5_free(void *ctx)
-+{
-+ mbedtls_dhm_free(ctx);
-+ os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
-+
-+#include <mbedtls/ecp.h>
-+
-+#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
-+#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
-+#define CRYPTO_EC_P(e) (&((mbedtls_ecp_group *)(e))->P)
-+#define CRYPTO_EC_N(e) (&((mbedtls_ecp_group *)(e))->N)
-+#define CRYPTO_EC_A(e) (&((mbedtls_ecp_group *)(e))->A)
-+#define CRYPTO_EC_B(e) (&((mbedtls_ecp_group *)(e))->B)
-+#define CRYPTO_EC_G(e) (&((mbedtls_ecp_group *)(e))->G)
-+
-+static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
-+{
-+ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+ switch (group) {
-+ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+ case 19: return MBEDTLS_ECP_DP_SECP256R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+ case 20: return MBEDTLS_ECP_DP_SECP384R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+ case 21: return MBEDTLS_ECP_DP_SECP521R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+ case 25: return MBEDTLS_ECP_DP_SECP192R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+ case 26: return MBEDTLS_ECP_DP_SECP224R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+ case 28: return MBEDTLS_ECP_DP_BP256R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+ case 29: return MBEDTLS_ECP_DP_BP384R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+ case 30: return MBEDTLS_ECP_DP_BP512R1;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+ case 31: return MBEDTLS_ECP_DP_CURVE25519;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+ case 32: return MBEDTLS_ECP_DP_CURVE448;
-+ #endif
-+ default: return MBEDTLS_ECP_DP_NONE;
-+ }
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
-+{
-+ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+ /*(for crypto_ec_key_group())*/
-+ switch (grp_id) {
-+ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP256R1: return 19;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP384R1: return 20;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP521R1: return 21;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP192R1: return 25;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP224R1: return 26;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP256R1: return 28;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP384R1: return 29;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP512R1: return 30;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE25519: return 31;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE448: return 32;
-+ #endif
-+ default: return -1;
-+ }
-+}
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
-+
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return -1;
-+ const mbedtls_pk_info_t *pk_info =
-+ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+ if (pk_info == NULL)
-+ return -1;
-+ return mbedtls_pk_setup(pk, pk_info)
-+ || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
-+
-+#include <mbedtls/ecdh.h>
-+#include <mbedtls/ecdsa.h>
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+/* wrap mbedtls_ecdh_context for more future-proof direct access to components
-+ * (mbedtls_ecdh_context internal implementation may change between releases)
-+ *
-+ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
-+ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
-+ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
-+ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
-+ * wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
-+struct crypto_ecdh {
-+ mbedtls_ecdh_context ctx;
-+ mbedtls_ecp_group grp;
-+ mbedtls_ecp_point Q;
-+};
-+
-+struct crypto_ecdh * crypto_ecdh_init(int group)
-+{
-+ mbedtls_pk_context pk;
-+ mbedtls_pk_init(&pk);
-+ struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
-+ ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
-+ : NULL;
-+ mbedtls_pk_free(&pk);
-+ return ecdh;
-+}
-+
-+struct crypto_ecdh * crypto_ecdh_init2(int group,
-+ struct crypto_ec_key *own_key)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
-+ struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
-+ if (ecdh == NULL)
-+ return NULL;
-+ mbedtls_ecdh_init(&ecdh->ctx);
-+ mbedtls_ecp_group_init(&ecdh->grp);
-+ mbedtls_ecp_point_init(&ecdh->Q);
-+ if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
-+ && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
-+ /* copy grp and Q for later use
-+ * (retrieving this info later is more convoluted
-+ * even if mbedtls_ecdh_make_public() is considered)*/
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ mbedtls_mpi d;
-+ mbedtls_mpi_init(&d);
-+ if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
-+ mbedtls_mpi_free(&d);
-+ return ecdh;
-+ }
-+ mbedtls_mpi_free(&d);
-+ #else
-+ if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
-+ && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
-+ return ecdh;
-+ #endif
-+ }
-+
-+ mbedtls_ecp_point_free(&ecdh->Q);
-+ mbedtls_ecp_group_free(&ecdh->grp);
-+ mbedtls_ecdh_free(&ecdh->ctx);
-+ os_free(ecdh);
-+ return NULL;
-+}
-+
-+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
-+{
-+ mbedtls_ecp_group *grp = &ecdh->grp;
-+ size_t len = CRYPTO_EC_plen(grp);
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ /* len */
-+ #endif
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-+ len = inc_y ? len*2+1 : len+1;
-+ #endif
-+ struct wpabuf *buf = wpabuf_alloc(len);
-+ if (buf == NULL)
-+ return NULL;
-+ inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
-+ if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &len,
-+ wpabuf_mhead_u8(buf), len) == 0) {
-+ wpabuf_put(buf, len);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
-+ mbedtls_mpi *bn,
-+ int parity_bit)
-+{
-+ /* y^2 = x^3 + ax + b
-+ * sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) */
-+ mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+ crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
-+ (const struct crypto_bignum *)bn); /*x*/
-+ if (cy2 == NULL)
-+ return -1;
-+
-+ /*mbedtls_mpi_free(bn);*/
-+ /*(reuse bn to store result (y))*/
-+
-+ mbedtls_mpi exp;
-+ mbedtls_mpi_init(&exp);
-+ int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
-+ || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
-+ || mbedtls_mpi_add_int(&exp, &grp->P, 1)
-+ || mbedtls_mpi_shift_r(&exp, 2)
-+ || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
-+ || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
-+ && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
-+ mbedtls_mpi_free(&exp);
-+ mbedtls_mpi_free(cy2);
-+ os_free(cy2);
-+ return ret;
-+}
-+#endif
-+
-+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
-+ const u8 *key, size_t len)
-+{
-+ if (len == 0) /*(invalid peer key)*/
-+ return NULL;
-+
-+ mbedtls_ecp_group *grp = &ecdh->grp;
-+
-+ #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ /* add header for mbedtls_ecdh_read_public() */
-+ u8 buf[256];
-+ if (sizeof(buf)-1 < len)
-+ return NULL;
-+ buf[0] = (u8)(len);
-+ os_memcpy(buf+1, key, len);
-+
-+ if (inc_y) {
-+ if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
-+ if (sizeof(buf)-2 < len)
-+ return NULL;
-+ buf[0] = (u8)(1+len);
-+ buf[1] = 0x04;
-+ os_memcpy(buf+2, key, len);
-+ }
-+ len >>= 1; /*(repurpose len to prime_len)*/
-+ }
-+ else if (key[0] == 0x02 || key[0] == 0x03) { /* (inc_y == 0) */
-+ --len; /*(repurpose len to prime_len)*/
-+
-+ /* mbedtls_ecp_point_read_binary() does not currently support
-+ * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
-+ * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
-+
-+ /* derive y, amend buf[] with y for UNCOMPRESSED format */
-+ if (sizeof(buf)-2 < len*2 || len == 0)
-+ return NULL;
-+ buf[0] = (u8)(1+len*2);
-+ buf[1] = 0x04;
-+ mbedtls_mpi bn;
-+ mbedtls_mpi_init(&bn);
-+ int ret = mbedtls_mpi_read_binary(&bn, key+1, len)
-+ || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn,
-+ key[0] & 1)
-+ || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
-+ mbedtls_mpi_free(&bn);
-+ if (ret != 0)
-+ return NULL;
-+ }
-+
-+ if (key[0] == 0) /*(repurpose len to prime_len)*/
-+ len = CRYPTO_EC_plen(grp);
-+
-+ if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
-+ return NULL;
-+ }
-+ #endif
-+ #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
-+ return NULL;
-+ }
-+ #endif
-+
-+ struct wpabuf *buf = wpabuf_alloc(len);
-+ if (buf == NULL)
-+ return NULL;
-+
-+ if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
-+ wpabuf_mhead(buf), len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0) {
-+ wpabuf_put(buf, len);
-+ return buf;
-+ }
-+
-+ wpabuf_clear_free(buf);
-+ return NULL;
-+}
-+
-+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
-+{
-+ if (ecdh == NULL)
-+ return;
-+ mbedtls_ecp_point_free(&ecdh->Q);
-+ mbedtls_ecp_group_free(&ecdh->grp);
-+ mbedtls_ecdh_free(&ecdh->ctx);
-+ os_free(ecdh);
-+}
-+
-+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
-+{
-+ return CRYPTO_EC_plen(&ecdh->grp);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+
-+#include <mbedtls/ecp.h>
-+
-+struct crypto_ec *crypto_ec_init(int group)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ mbedtls_ecp_group *e = os_malloc(sizeof(*e));
-+ if (e == NULL)
-+ return NULL;
-+ mbedtls_ecp_group_init(e);
-+ if (mbedtls_ecp_group_load(e, grp_id) == 0)
-+ return (struct crypto_ec *)e;
-+
-+ mbedtls_ecp_group_free(e);
-+ os_free(e);
-+ return NULL;
-+}
-+
-+void crypto_ec_deinit(struct crypto_ec *e)
-+{
-+ mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
-+ os_free(e);
-+}
-+
-+size_t crypto_ec_prime_len(struct crypto_ec *e)
-+{
-+ return CRYPTO_EC_plen(e);
-+}
-+
-+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
-+{
-+ return CRYPTO_EC_pbits(e);
-+}
-+
-+size_t crypto_ec_order_len(struct crypto_ec *e)
-+{
-+ return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
-+{
-+ return (const struct crypto_bignum *)CRYPTO_EC_P(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
-+{
-+ return (const struct crypto_bignum *)CRYPTO_EC_N(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
-+{
-+ static const uint8_t secp256r1_a[] =
-+ {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
-+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+ static const uint8_t secp384r1_a[] =
-+ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
-+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
-+ static const uint8_t secp521r1_a[] =
-+ {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xfc};
-+ static const uint8_t secp192r1_a[] =
-+ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+ static const uint8_t secp224r1_a[] =
-+ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+ 0xff,0xff,0xff,0xfe};
-+
-+ const uint8_t *bin = NULL;
-+ size_t len = 0;
-+
-+ /* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
-+ switch (((mbedtls_ecp_group *)e)->id) {
-+ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP256R1:
-+ bin = secp256r1_a;
-+ len = sizeof(secp256r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP384R1:
-+ bin = secp384r1_a;
-+ len = sizeof(secp384r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP521R1:
-+ bin = secp521r1_a;
-+ len = sizeof(secp521r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP192R1:
-+ bin = secp192r1_a;
-+ len = sizeof(secp192r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+ case MBEDTLS_ECP_DP_SECP224R1:
-+ bin = secp224r1_a;
-+ len = sizeof(secp224r1_a);
-+ break;
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP256R1:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP384R1:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+ case MBEDTLS_ECP_DP_BP512R1:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE25519:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+ case MBEDTLS_ECP_DP_CURVE448:
-+ return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+
-+ /*(note: not thread-safe; returns file-scoped static storage)*/
-+ if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
-+ return (const struct crypto_bignum *)&mpi_sw_A;
-+ return NULL;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
-+{
-+ return (const struct crypto_bignum *)CRYPTO_EC_B(e);
-+}
-+
-+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
-+{
-+ return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
-+}
-+
-+struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
-+{
-+ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+ if (p != NULL)
-+ mbedtls_ecp_point_init(p);
-+ return (struct crypto_ec_point *)p;
-+}
-+
-+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
-+{
-+ mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
-+ os_free(p);
-+}
-+
-+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
-+ struct crypto_bignum *x)
-+{
-+ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+ return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
-+ ? -1
-+ : 0;
-+}
-+
-+int crypto_ec_point_to_bin(struct crypto_ec *e,
-+ const struct crypto_ec_point *point, u8 *x, u8 *y)
-+{
-+ /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
-+ size_t len = CRYPTO_EC_plen(e);
-+ if (x) {
-+ mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
-+ if (mbedtls_mpi_write_binary(px, x, len))
-+ return -1;
-+ }
-+ if (y) {
-+ #if 0 /*(should not be necessary; py mpi should be in initial state)*/
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ os_memset(y, 0, len);
-+ return 0;
-+ }
-+ #endif
-+ #endif
-+ mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
-+ if (mbedtls_mpi_write_binary(py, y, len))
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
-+ const u8 *val)
-+{
-+ size_t len = CRYPTO_EC_plen(e);
-+ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+ if (p == NULL)
-+ return NULL;
-+ mbedtls_ecp_point_init(p);
-+
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
-+ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+ mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
-+
-+ if (mbedtls_mpi_read_binary(px, val, len) == 0
-+ && mbedtls_mpi_read_binary(py, val + len, len) == 0
-+ && mbedtls_mpi_lset(pz, 1) == 0)
-+ return (struct crypto_ec_point *)p;
-+ #else
-+ buf[0] = 0x04;
-+ os_memcpy(buf+1, val, len*2);
-+ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+ buf, 1+len*2) == 0)
-+ return (struct crypto_ec_point *)p;
-+ #endif
-+ }
-+ #endif
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ /* crypto.h interface documents crypto_ec_point_from_bin()
-+ * val is length: prime_len * 2 and is big-endian
-+ * (Short Weierstrass is assumed by hostap)
-+ * Reverse to little-endian format for Montgomery */
-+ for (unsigned int i = 0; i < len; ++i)
-+ buf[i] = val[len-1-i];
-+ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+ buf, len) == 0)
-+ return (struct crypto_ec_point *)p;
-+ }
-+ #endif
-+
-+ mbedtls_ecp_point_free(p);
-+ os_free(p);
-+ return NULL;
-+}
-+
-+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
-+ const struct crypto_ec_point *b,
-+ struct crypto_ec_point *c)
-+{
-+ /* mbedtls does not provide an mbedtls_ecp_point add function */
-+ mbedtls_mpi one;
-+ mbedtls_mpi_init(&one);
-+ int ret = mbedtls_mpi_lset(&one, 1)
-+ || mbedtls_ecp_muladd(
-+ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
-+ &one, (const mbedtls_ecp_point *)a,
-+ &one, (const mbedtls_ecp_point *)b) ? -1 : 0;
-+ mbedtls_mpi_free(&one);
-+ return ret;
-+}
-+
-+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
-+ const struct crypto_bignum *b,
-+ struct crypto_ec_point *res)
-+{
-+ return mbedtls_ecp_mul(
-+ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
-+ (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
-+ mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
-+{
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+ /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
-+ wpa_printf(MSG_ERROR,
-+ "%s not implemented for Montgomery curves",__func__);
-+ return -1;
-+ }
-+
-+ /* mbedtls does not provide an mbedtls_ecp_point invert function */
-+ /* below works for Short Weierstrass; incorrect for Montgomery curves */
-+ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
-+ || mbedtls_mpi_cmp_int(py, 0) == 0 /*point is its own inverse*/
-+ || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
-+}
-+
-+#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+static int
-+crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+ mbedtls_mpi *y2)
-+{
-+ /* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS y^2 = x^3 + a x + b */
-+
-+ /* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
-+ /* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
-+ * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
-+ * treated as if A = -3. */
-+
-+ #if 0
-+ /* y^2 = x^3 + ax + b */
-+ mbedtls_mpi *A = &e->A;
-+ mbedtls_mpi t, A_neg3;
-+ if (&e->A.p == NULL) {
-+ mbedtls_mpi_init(&A_neg3);
-+ if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
-+ mbedtls_mpi_free(&A_neg3);
-+ return -1;
-+ }
-+ A = &A_neg3;
-+ }
-+ mbedtls_mpi_init(&t);
-+ int ret = /* x^3 */
-+ mbedtls_mpi_lset(&t, 3)
-+ || mbedtls_mpi_exp_mod(y2, x, &t, &e->P, NULL)
-+ /* ax */
-+ || mbedtls_mpi_mul_mpi(y2, y2, A)
-+ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+ /* ax + b */
-+ || mbedtls_mpi_add_mpi(&t, &t, &e->B)
-+ || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+ /* x^3 + ax + b */
-+ || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
-+ || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
-+ mbedtls_mpi_free(&t);
-+ if (A == &A_neg3)
-+ mbedtls_mpi_free(&A_neg3);
-+ return ret; /* 0: success, non-zero: failure */
-+ #else
-+ /* y^2 = x^3 + ax + b = (x^2 + a)x + b */
-+ return /* x^2 */
-+ mbedtls_mpi_mul_mpi(y2, x, x)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* x^2 + a */
-+ || (e->A.MBEDTLS_PRIVATE(p)
-+ ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
-+ : mbedtls_mpi_sub_int(y2, y2, 3))
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x^2 + a)x */
-+ || mbedtls_mpi_mul_mpi(y2, y2, x)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x^2 + a)x + b */
-+ || mbedtls_mpi_add_mpi(y2, y2, &e->B)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+ #endif
-+}
-+#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
-+
-+#if 0 /* not used by hostap */
-+#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+static int
-+crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+ mbedtls_mpi *y2)
-+{
-+ /* XXX: !!! must be reviewed and audited for correctness !!! */
-+
-+ /* MBEDTLS_ECP_TYPE_MONTGOMERY y^2 = x^3 + a x^2 + x */
-+
-+ /* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
-+ mbedtls_mpi x2;
-+ mbedtls_mpi_init(&x2);
-+ int ret = /* x^2 */
-+ mbedtls_mpi_mul_mpi(&x2, x, x)
-+ || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
-+ /* x + a */
-+ || mbedtls_mpi_add_mpi(y2, x, &e->A)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x + a)x^2 */
-+ || mbedtls_mpi_mul_mpi(y2, y2, &x2)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+ /* (x + a)x^2 + x */
-+ || mbedtls_mpi_add_mpi(y2, y2, x)
-+ || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+ mbedtls_mpi_free(&x2);
-+ return ret; /* 0: success, non-zero: failure */
-+}
-+#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
-+#endif
-+
-+struct crypto_bignum *
-+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
-+ const struct crypto_bignum *x)
-+{
-+ mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
-+ if (y2 == NULL)
-+ return NULL;
-+ mbedtls_mpi_init(y2);
-+
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+ && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
-+ (const mbedtls_mpi *)x,
-+ y2) == 0)
-+ return (struct crypto_bignum *)y2;
-+ #endif
-+ #if 0 /* not used by hostap */
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+ == MBEDTLS_ECP_TYPE_MONTGOMERY
-+ && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
-+ (const mbedtls_mpi *)x,
-+ y2) == 0)
-+ return (struct crypto_bignum *)y2;
-+ #endif
-+ #endif
-+
-+ mbedtls_mpi_free(y2);
-+ os_free(y2);
-+ return NULL;
-+}
-+
-+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
-+ const struct crypto_ec_point *p)
-+{
-+ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
-+}
-+
-+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
-+ const struct crypto_ec_point *p)
-+{
-+ #if 1
-+ return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
-+ (const mbedtls_ecp_point *)p) == 0;
-+ #else
-+ /* compute y^2 mod P and compare to y^2 mod P */
-+ /*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
-+ const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+ mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+ crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
-+ if (cy2 == NULL)
-+ return 0;
-+
-+ mbedtls_mpi y2;
-+ mbedtls_mpi_init(&y2);
-+ const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+ int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
-+ || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
-+ || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
-+
-+ mbedtls_mpi_free(&y2);
-+ mbedtls_mpi_free(cy2);
-+ os_free(cy2);
-+ return is_on_curve;
-+ #endif
-+}
-+
-+int crypto_ec_point_cmp(const struct crypto_ec *e,
-+ const struct crypto_ec_point *a,
-+ const struct crypto_ec_point *b)
-+{
-+ return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
-+ (const mbedtls_ecp_point *)b);
-+}
-+
-+#if !defined(CONFIG_NO_STDOUT_DEBUG)
-+void crypto_ec_point_debug_print(const struct crypto_ec *e,
-+ const struct crypto_ec_point *p,
-+ const char *title)
-+{
-+ u8 x[MBEDTLS_MPI_MAX_SIZE];
-+ u8 y[MBEDTLS_MPI_MAX_SIZE];
-+ size_t len = CRYPTO_EC_plen(e);
-+ /* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
-+ struct crypto_ec *ee;
-+ *(const struct crypto_ec **)&ee = e; /*(cast away const)*/
-+ if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
-+ if (title)
-+ wpa_printf(MSG_DEBUG, "%s", title);
-+ wpa_hexdump(MSG_DEBUG, "x:", x, len);
-+ wpa_hexdump(MSG_DEBUG, "y:", y, len);
-+ }
-+}
-+#endif
-+
-+
-+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
-+{
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
-+ #else
-+ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0)
-+ #endif
-+ return (struct crypto_ec_key *)ctx;
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#ifdef CONFIG_MODULE_TESTS
-+/*(for crypto_module_tests.c)*/
-+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
-+ const u8 *raw, size_t raw_len)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ const mbedtls_pk_info_t *pk_info =
-+ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+ if (pk_info == NULL)
-+ return NULL;
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ if (mbedtls_pk_setup(ctx, pk_info) == 0
-+ && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
-+ return (struct crypto_ec_key *)ctx;
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+#endif
-+#endif
-+
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
-+{
-+ /* The following is modified from:
-+ * mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
-+ * mbedtls/library/pkparse.c:pk_get_pk_alg()
-+ * mbedtls/library/pkparse.c:pk_use_ecparams()
-+ */
-+ mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
-+ const mbedtls_pk_info_t *pk_info;
-+ int ret;
-+ size_t len;
-+ const unsigned char *end = der+der_len;
-+ unsigned char *p;
-+ *(const unsigned char **)&p = der;
-+
-+ if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
-+ {
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
-+ }
-+
-+ end = p + len;
-+
-+ /*
-+ if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
-+ return( ret );
-+ */
-+ mbedtls_asn1_buf alg_oid, params;
-+ memset( ¶ms, 0, sizeof(mbedtls_asn1_buf) );
-+ if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, ¶ms ) ) != 0 )
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
-+ if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
-+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+ if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
-+
-+ if( p + len != end )
-+ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
-+ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
-+
-+ if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
-+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
-+ return( ret );
-+
-+ /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
-+ * has already run with ctx initialized up to pk_get_ecpubkey(),
-+ * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
-+ *
-+ * mbedtls mbedtls_ecp_point_read_binary()
-+ * does not handle point in COMPRESSED format
-+ *
-+ * (validate assumption that algorithm is EC) */
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+ if (ecp_kp == NULL)
-+ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ mbedtls_ecp_group_id grp_id;
-+
-+
-+ /* mbedtls/library/pkparse.c:pk_use_ecparams() */
-+
-+ if( params.tag == MBEDTLS_ASN1_OID )
-+ {
-+ if( mbedtls_oid_get_ec_grp( ¶ms, &grp_id ) != 0 )
-+ return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
-+ }
-+ else
-+ {
-+#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
-+ /*(large code block not copied from mbedtls; unsupported)*/
-+ #if 0
-+ if( ( ret = pk_group_id_from_specified( ¶ms, &grp_id ) ) != 0 )
-+ return( ret );
-+ #endif
-+#endif
-+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+ }
-+
-+ /*
-+ * grp may already be initialized; if so, make sure IDs match
-+ */
-+ if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
-+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+
-+ if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
-+ return( ret );
-+
-+
-+ /* (validate assumption that EC point is in COMPRESSED format) */
-+ len = CRYPTO_EC_plen(ecp_kp_grp);
-+ if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+ || (end - p) != 1+len
-+ || (*p != 0x02 && *p != 0x03) )
-+ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+
-+ /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
-+ * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
-+ mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
-+ mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
-+ mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
-+ ret = mbedtls_mpi_lset(Z, 1);
-+ if (ret != 0)
-+ return( ret );
-+ ret = mbedtls_mpi_read_binary(X, p+1, len);
-+ if (ret != 0)
-+ return( ret );
-+ /* derive Y
-+ * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
-+ ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
-+ || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
-+ if (ret != 0)
-+ return( ret );
-+
-+ return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
-+{
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ /*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
-+ int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
-+ if (rc == 0)
-+ return (struct crypto_ec_key *)ctx;
-+ else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
-+ /* mbedtls mbedtls_ecp_point_read_binary()
-+ * does not handle point in COMPRESSED format; parse internally */
-+ rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
-+ if (rc == 0)
-+ return (struct crypto_ec_key *)ctx;
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+static struct crypto_ec_key *
-+crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
-+ const mbedtls_ecp_point *pub,
-+ const u8 *buf, size_t len)
-+{
-+ const mbedtls_pk_info_t *pk_info =
-+ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+ if (pk_info == NULL)
-+ return NULL;
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ if (mbedtls_pk_setup(ctx, pk_info) == 0) {
-+ /* (Is private key generation necessary for callers?)
-+ * alt: gen key then overwrite Q
-+ * mbedtls_ecp_gen_key(grp_id, ecp_kp,
-+ * mbedtls_ctr_drbg_random,
-+ * crypto_mbedtls_ctr_drbg()) == 0
-+ */
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+ if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
-+ && (pub
-+ ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
-+ : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
-+ buf, len) == 0)
-+ && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0){
-+ return (struct crypto_ec_key *)ctx;
-+ }
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
-+ const u8 *y, size_t len)
-+{
-+ mbedtls_ecp_group_id grp_id =
-+ crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+ if (grp_id == MBEDTLS_ECP_DP_NONE)
-+ return NULL;
-+ if (len > MBEDTLS_MPI_MAX_SIZE)
-+ return NULL;
-+ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+ buf[0] = 0x04; /* assume x,y for Short Weierstrass */
-+ os_memcpy(buf+1, x, len);
-+ os_memcpy(buf+1+len, y, len);
-+
-+ return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
-+}
-+
-+struct crypto_ec_key *
-+crypto_ec_key_set_pub_point(struct crypto_ec *e,
-+ const struct crypto_ec_point *pub)
-+{
-+ mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
-+ mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
-+ return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
-+}
-+
-+
-+struct crypto_ec_key * crypto_ec_key_gen(int group)
-+{
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL)
-+ return NULL;
-+ mbedtls_pk_init(ctx);
-+ if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
-+ return (struct crypto_ec_key *)ctx;
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+void crypto_ec_key_deinit(struct crypto_ec_key *key)
-+{
-+ mbedtls_pk_free((mbedtls_pk_context *)key);
-+ os_free(key);
-+}
-+
-+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
-+{
-+ /* (similar to crypto_ec_key_get_pubkey_point(),
-+ * but compressed point format and ASN.1 DER wrapping)*/
-+#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+ unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
-+ int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
-+ buf, sizeof(buf));
-+ if (len < 0)
-+ return NULL;
-+ /* Note: data is written at the end of the buffer! Use the
-+ * return value to determine where you should start
-+ * using the buffer */
-+ unsigned char *p = buf+sizeof(buf)-len;
-+
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ /* Note: sae_pk.c expects pubkey point in compressed format,
-+ * but mbedtls_pk_write_pubkey_der() writes uncompressed format.
-+ * Manually translate format and update lengths in DER format */
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ unsigned char *end = buf+sizeof(buf);
-+ size_t n;
-+ /* SubjectPublicKeyInfo SEQUENCE */
-+ mbedtls_asn1_get_tag(&p, end, &n,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ /* algorithm AlgorithmIdentifier */
-+ unsigned char *a = p;
-+ size_t alen;
-+ mbedtls_asn1_get_tag(&p, end, &alen,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ p += alen;
-+ alen = (size_t)(p - a);
-+ /* subjectPublicKey BIT STRING */
-+ mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
-+ /* rewrite into compressed point format and rebuild ASN.1 */
-+ p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
-+ n = 1 + 1 + (n-2)/2;
-+ len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
-+ len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
-+ os_memmove(p-alen, a, alen);
-+ len += alen;
-+ p -= alen;
-+ len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
-+ len += mbedtls_asn1_write_tag(&p, buf,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ }
-+ #endif
-+ return wpabuf_alloc_copy(p, (size_t)len);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
-+ bool include_pub)
-+{
-+#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+ unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
-+ int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
-+ priv, sizeof(priv));
-+ if (privlen < 0)
-+ return NULL;
-+
-+ struct wpabuf *wbuf;
-+
-+ /* Note: data is written at the end of the buffer! Use the
-+ * return value to determine where you should start
-+ * using the buffer */
-+ /* mbedtls_pk_write_key_der() includes publicKey in DER */
-+ if (include_pub)
-+ wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
-+ else {
-+ /* calculate publicKey offset and skip from end of buffer */
-+ unsigned char *p = priv+sizeof(priv)-privlen;
-+ unsigned char *end = priv+sizeof(priv);
-+ size_t len;
-+ /* ECPrivateKey SEQUENCE */
-+ mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ /* version INTEGER */
-+ unsigned char *v = p;
-+ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
-+ p += len;
-+ /* privateKey OCTET STRING */
-+ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
-+ p += len;
-+ /* parameters ECParameters */
-+ mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
-+ p += len;
-+
-+ /* write new SEQUENCE header (we know that it fits in priv[]) */
-+ len = (size_t)(p - v);
-+ p = v;
-+ len += mbedtls_asn1_write_len(&p, priv, len);
-+ len += mbedtls_asn1_write_tag(&p, priv,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ wbuf = wpabuf_alloc_copy(p, len);
-+ }
-+
-+ forced_memzero(priv, sizeof(priv));
-+ return wbuf;
-+}
-+
-+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
-+ int prefix)
-+{
-+ /*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ size_t len = CRYPTO_EC_plen(grp);
-+ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+ /* len */
-+ #endif
-+ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-+ len = len*2+1;
-+ #endif
-+ struct wpabuf *buf = wpabuf_alloc(len);
-+ if (buf == NULL)
-+ return NULL;
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
-+ MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
-+ wpabuf_mhead_u8(buf), len) == 0) {
-+ if (!prefix) /* Remove 0x04 prefix if requested */
-+ os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
-+ wpabuf_put(buf, len);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+struct crypto_ec_point *
-+crypto_ec_key_get_public_key(struct crypto_ec_key *key)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+ if (p != NULL) {
-+ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+ mbedtls_ecp_point_init(p);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+ if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
-+ mbedtls_ecp_point_free(p);
-+ os_free(p);
-+ p = NULL;
-+ }
-+ }
-+ return (struct crypto_ec_point *)p;
-+}
-+
-+struct crypto_bignum *
-+crypto_ec_key_get_private_key(struct crypto_ec_key *key)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+ mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+ if (bn) {
-+ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+ mbedtls_mpi_init(bn);
-+ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+ if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
-+ mbedtls_mpi_free(bn);
-+ os_free(bn);
-+ bn = NULL;
-+ }
-+ }
-+ return (struct crypto_bignum *)bn;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
-+{
-+ /* get mbedtls_md_type_t from length of hash data to be signed */
-+ switch (len) {
-+ case 64: return MBEDTLS_MD_SHA512;
-+ case 48: return MBEDTLS_MD_SHA384;
-+ case 32: return MBEDTLS_MD_SHA256;
-+ case 20: return MBEDTLS_MD_SHA1;
-+ case 16: return MBEDTLS_MD_MD5;
-+ default: return MBEDTLS_MD_NONE;
-+ }
-+}
-+
-+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
-+ size_t len)
-+{
-+ #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
-+ #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
-+ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
-+ #else
-+ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
-+ #endif
-+ #endif
-+ size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
-+ struct wpabuf *buf = wpabuf_alloc(sig_len);
-+ if (buf == NULL)
-+ return NULL;
-+ if (mbedtls_pk_sign((mbedtls_pk_context *)key,
-+ crypto_ec_key_sign_md(len), data, len,
-+ wpabuf_mhead_u8(buf),
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ sig_len,
-+ #endif
-+ &sig_len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) == 0) {
-+ wpabuf_put(buf, sig_len);
-+ return buf;
-+ }
-+
-+ wpabuf_free(buf);
-+ return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
-+ const u8 *data, size_t len)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return NULL;
-+
-+ size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
-+ u8 buf[MBEDTLS_ECDSA_MAX_LEN];
-+ if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
-+ data, len, buf,
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ sig_len,
-+ #endif
-+ &sig_len,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg())) {
-+ return NULL;
-+ }
-+
-+ /*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
-+ /* parse ASN.1 to get r and s and lengths */
-+ u8 *p = buf, *r, *s;
-+ u8 *end = p + sig_len;
-+ size_t rlen, slen;
-+ mbedtls_asn1_get_tag(&p, end, &rlen,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
-+ r = p;
-+ p += rlen;
-+ mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
-+ s = p;
-+
-+ /* write raw r and s into out
-+ * (including removal of leading 0 if added for ASN.1 integer)
-+ * note: DPP caller expects raw r, s each padded to prime len */
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
-+ if (rlen > plen) {
-+ r += (rlen - plen);
-+ rlen = plen;
-+ }
-+ if (slen > plen) {
-+ s += (slen - plen);
-+ slen = plen;
-+ }
-+ struct wpabuf *out = wpabuf_alloc(plen*2);
-+ if (out) {
-+ wpabuf_put(out, plen*2);
-+ p = wpabuf_mhead_u8(out);
-+ os_memset(p, 0, plen*2);
-+ os_memcpy(p+plen*1-rlen, r, rlen);
-+ os_memcpy(p+plen*2-slen, s, slen);
-+ }
-+ return out;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
-+ size_t len, const u8 *sig, size_t sig_len)
-+{
-+ switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
-+ crypto_ec_key_sign_md(len), data, len,
-+ sig, sig_len)) {
-+ case 0:
-+ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+ return 1;
-+ case MBEDTLS_ERR_ECP_VERIFY_FAILED:
-+ return 0;
-+ default:
-+ return -1;
-+ }
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
-+ const u8 *data, size_t len,
-+ const u8 *r, size_t r_len,
-+ const u8 *s, size_t s_len)
-+{
-+ /* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
-+ * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return -1;
-+ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+
-+ mbedtls_mpi mpi_r;
-+ mbedtls_mpi mpi_s;
-+ mbedtls_mpi_init(&mpi_r);
-+ mbedtls_mpi_init(&mpi_s);
-+ int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
-+ || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
-+ if (ret == 0) {
-+ ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
-+ ecp_kp_Q, &mpi_r, &mpi_s);
-+ ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
-+ }
-+ mbedtls_mpi_free(&mpi_r);
-+ mbedtls_mpi_free(&mpi_s);
-+ return ret;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_group(struct crypto_ec_key *key)
-+{
-+ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+ if (ecp_kp == NULL)
-+ return -1;
-+ mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+ return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
-+{
-+#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+ (const mbedtls_pk_context *)key2) ? -1 : 0;
-+ #else
-+ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+ (const mbedtls_pk_context *)key2,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+ #endif
-+#else
-+ mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
-+ mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
-+ if (ecp_kp1 == NULL || ecp_kp2 == NULL)
-+ return -1;
-+ mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
-+ mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
-+ mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
-+ return ecp_kp1_grp->id != ecp_kp2_grp->id
-+ || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
-+#endif
-+}
-+
-+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
-+ const char *title)
-+{
-+ /* TBD: what info is desirable here and in what human readable format?*/
-+ /*(crypto_openssl.c prints a human-readably public key and attributes)*/
-+ #if 0
-+ struct mbedtls_pk_debug_item debug_item;
-+ if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
-+ return;
-+ /* ... */
-+ #endif
-+ wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
-+
-+#include <mbedtls/x509_csr.h>
-+#include <mbedtls/oid.h>
-+
-+struct crypto_csr * crypto_csr_init(void)
-+{
-+ mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
-+ if (csr != NULL)
-+ mbedtls_x509write_csr_init(csr);
-+ return (struct crypto_csr *)csr;
-+}
-+
-+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
-+{
-+ /* future: look for alternatives to MBEDTLS_PRIVATE() access */
-+
-+ /* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
-+ * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
-+ * so allocate different object (mbedtls_x509_csr *) and special-case
-+ * object when used in crypto_csr_get_attribute() and when free()d in
-+ * crypto_csr_deinit(). */
-+
-+ mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
-+ if (csr == NULL)
-+ return NULL;
-+ mbedtls_x509_csr_init(csr);
-+ const mbedtls_md_info_t *md_info;
-+ unsigned char digest[MBEDTLS_MD_MAX_SIZE];
-+ if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
-+ && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
-+ != NULL
-+ && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
-+ switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
-+ digest, mbedtls_md_get_size(md_info),
-+ csr->MBEDTLS_PRIVATE(sig).p,
-+ csr->MBEDTLS_PRIVATE(sig).len)) {
-+ case 0:
-+ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+ return (struct crypto_csr *)((uintptr_t)csr | 1uL);
-+ default:
-+ break;
-+ }
-+ }
-+
-+ mbedtls_x509_csr_free(csr);
-+ os_free(csr);
-+ return NULL;
-+}
-+
-+void crypto_csr_deinit(struct crypto_csr *csr)
-+{
-+ if ((uintptr_t)csr & 1uL) {
-+ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+ mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
-+ }
-+ else
-+ mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
-+ os_free(csr);
-+}
-+
-+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
-+ struct crypto_ec_key *key)
-+{
-+ mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
-+ (mbedtls_pk_context *)key);
-+ return 0;
-+}
-+
-+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
-+ const char *name)
-+{
-+ /* specialized for src/common/dpp_crypto.c */
-+
-+ /* sole caller src/common/dpp_crypto.c:dpp_build_csr()
-+ * calls this function only once, using type == CSR_NAME_CN
-+ * (If called more than once, this code would need to append
-+ * components to the subject name, which we could do by
-+ * appending to (mbedtls_x509write_csr *) private member
-+ * mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
-+
-+ const char *label;
-+ switch (type) {
-+ case CSR_NAME_CN: label = "CN="; break;
-+ case CSR_NAME_SN: label = "SN="; break;
-+ case CSR_NAME_C: label = "C="; break;
-+ case CSR_NAME_O: label = "O="; break;
-+ case CSR_NAME_OU: label = "OU="; break;
-+ default: return -1;
-+ }
-+
-+ size_t len = strlen(name);
-+ struct wpabuf *buf = wpabuf_alloc(3+len+1);
-+ if (buf == NULL)
-+ return -1;
-+ wpabuf_put_data(buf, label, strlen(label));
-+ wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
-+ /* Note: 'name' provided is set as given and should be backslash-escaped
-+ * by caller when necessary, e.g. literal ',' which are not separating
-+ * components should be backslash-escaped */
-+
-+ int ret =
-+ mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
-+ wpabuf_head(buf)) ? -1 : 0;
-+ wpabuf_free(buf);
-+ return ret;
-+}
-+
-+/* OBJ_pkcs9_challengePassword 1 2 840 113549 1 9 7 */
-+static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
-+
-+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
-+ int attr_type, const u8 *value, size_t len)
-+{
-+ /* specialized for src/common/dpp_crypto.c */
-+ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+ * attr == CSR_ATTR_CHALLENGE_PASSWORD
-+ * attr_type == ASN1_TAG_UTF8STRING */
-+
-+ const char *oid;
-+ size_t oid_len;
-+ switch (attr) {
-+ case CSR_ATTR_CHALLENGE_PASSWORD:
-+ oid = OBJ_pkcs9_challengePassword;
-+ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+ break;
-+ default:
-+ return -1;
-+ }
-+
-+ #if 0 /*(incorrect; sets an extension, not an attribute)*/
-+ return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
-+ oid, oid_len,
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ 0, /*(critical flag)*/
-+ #endif
-+ value, len) ? -1 : 0;
-+ #else
-+ (void)oid;
-+ (void)oid_len;
-+ #endif
-+
-+ /* mbedtls does not currently provide way to set an attribute in a CSR:
-+ * https://github.com/Mbed-TLS/mbedtls/issues/4886 */
-+ wpa_printf(MSG_ERROR,
-+ "mbedtls does not currently support setting challengePassword "
-+ "attribute in CSR");
-+ return -1;
-+}
-+
-+const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
-+ const char *oid, size_t oid_len,
-+ size_t *vlen, int *vtype)
-+{
-+ /* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
-+ * so validation checks are not repeated here
-+ *
-+ * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
-+ * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
-+ * already parsed the rest of CertificationRequestInfo, some of which is
-+ * repeated here to step to Attributes. Since csr->subject_raw.p points
-+ * into csr->cri.p, which points into csr->raw.p, step over version and
-+ * subject of CertificationRequestInfo (SEQUENCE) */
-+ unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
-+ unsigned char *end = csr->cri.p + csr->cri.len, *ext;
-+ size_t len;
-+
-+ /* step over SubjectPublicKeyInfo */
-+ mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+ p += len;
-+
-+ /* Attributes
-+ * { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
-+ */
-+ if (mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
-+ return NULL;
-+ }
-+ while (p < end) {
-+ if (mbedtls_asn1_get_tag(&p, end, &len,
-+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
-+ return NULL;
-+ }
-+ ext = p;
-+ p += len;
-+
-+ if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
-+ return NULL;
-+ if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
-+ continue;
-+
-+ /* found oid; return value */
-+ *vtype = *ext++; /* tag */
-+ return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
-+ }
-+
-+ return NULL;
-+}
-+
-+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
-+ enum crypto_csr_attr attr,
-+ size_t *len, int *type)
-+{
-+ /* specialized for src/common/dpp_crypto.c */
-+ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+ * attr == CSR_ATTR_CHALLENGE_PASSWORD */
-+
-+ const char *oid;
-+ size_t oid_len;
-+ switch (attr) {
-+ case CSR_ATTR_CHALLENGE_PASSWORD:
-+ oid = OBJ_pkcs9_challengePassword;
-+ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+ break;
-+ default:
-+ return NULL;
-+ }
-+
-+ /* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
-+ if (!((uintptr_t)csr & 1uL))
-+ return NULL;
-+ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+
-+ return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
-+ oid, oid_len, len, type);
-+}
-+
-+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
-+ struct crypto_ec_key *key,
-+ enum crypto_hash_alg algo)
-+{
-+ mbedtls_md_type_t sig_md;
-+ switch (algo) {
-+ #ifdef MBEDTLS_SHA256_C
-+ case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
-+ #endif
-+ #ifdef MBEDTLS_SHA512_C
-+ case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
-+ case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
-+ #endif
-+ default:
-+ return NULL;
-+ }
-+ mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
-+
-+ #if 0
-+ unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
-+ | MBEDTLS_X509_KU_KEY_CERT_SIGN;
-+ if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
-+ key_usage))
-+ return NULL;
-+ #endif
-+
-+ #if 0
-+ unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
-+ | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
-+ if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
-+ ns_cert_type))
-+ return NULL;
-+ #endif
-+
-+ #if 0
-+ /* mbedtls does not currently provide way to set an attribute in a CSR:
-+ * https://github.com/Mbed-TLS/mbedtls/issues/4886
-+ * XXX: hwsim dpp_enterprise test fails due to this limitation.
-+ *
-+ * Current usage of this function is solely by dpp_build_csr(),
-+ * so as a kludge, might consider custom (struct crypto_csr *)
-+ * containing (mbedtls_x509write_csr *) and a list of attributes
-+ * (i.e. challengePassword). Might have to totally reimplement
-+ * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
-+ * handles signing the CSR. (This is more work that appending an
-+ * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
-+ */
-+ #endif
-+
-+ unsigned char buf[4096]; /* XXX: large enough? too large? */
-+ int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
-+ buf, sizeof(buf),
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg());
-+ if (len < 0)
-+ return NULL;
-+ /* Note: data is written at the end of the buffer! Use the
-+ * return value to determine where you should start
-+ * using the buffer */
-+ return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+
-+#if 0
-+#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
-+#include <mbedtls/pem.h>
-+#endif
-+
-+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
-+{
-+ /* PKCS7 is not currently supported in mbedtls */
-+ return NULL;
-+
-+#if 0
-+ /* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
-+ * (??? potential future contribution to mbedtls ???) */
-+
-+ /* Note: PKCS7 signature *is not* verified by this function.
-+ * The function interface does not provide for passing a certificate */
-+
-+ mbedtls_pkcs7 mpkcs7;
-+ mbedtls_pkcs7_init(&mpkcs7);
-+ int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
-+ wpabuf_len(pkcs7),
-+ &mpkcs7);
-+ wpabuf *buf = NULL;
-+ do {
-+ if (pkcs7_type < 0)
-+ break;
-+
-+ /* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
-+ * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
-+ * (? are adding certificate headers and footers desired ?) */
-+
-+ /* development-pkcs7 branch does not currently provide
-+ * additional interfaces to retrieve the parsed data */
-+
-+ mbedtls_x509_crt *certs =
-+ &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
-+ int ncerts =
-+ mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
-+
-+ /* allocate buffer for PEM (base64-encoded DER)
-+ * plus header, footer, newlines, and some extra */
-+ buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
-+ if (buf == NULL)
-+ break;
-+
-+ #define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
-+ #define PEM_END_CRT "-----END CERTIFICATE-----\n"
-+ size_t olen;
-+ for (int i = 0; i < ncerts; ++i) {
-+ int ret = mbedtls_pem_write_buffer(
-+ PEM_BEGIN_CRT, PEM_END_CRT,
-+ certs[i].raw.p, certs[i].raw.len,
-+ wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
-+ &olen));
-+ if (ret == 0)
-+ wpabuf_put(buf, olen);
-+ } else {
-+ if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
-+ ret = wpabuf_resize(
-+ &buf,olen-wpabuf_tailroom(buf));
-+ if (ret == 0) {
-+ --i;/*(adjust loop iterator for retry)*/
-+ continue;
-+ }
-+ wpabuf_free(buf);
-+ buf = NULL;
-+ break;
-+ }
-+ }
-+ } while (0);
-+
-+ mbedtls_pkcs7_free(&mpkcs7);
-+ return buf;
-+#endif
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
-+
-+
-+#ifdef MBEDTLS_ARC4_C
-+#include <mbedtls/arc4.h>
-+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
-+ u8 *data, size_t data_len)
-+{
-+ mbedtls_arc4_context ctx;
-+ mbedtls_arc4_init(&ctx);
-+ mbedtls_arc4_setup(&ctx, key, keylen);
-+
-+ if (skip) {
-+ /*(prefer [16] on ancient hardware with smaller cache lines)*/
-+ unsigned char skip_buf[64]; /*('skip' is generally small)*/
-+ /*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
-+ size_t len;
-+ do {
-+ len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
-+ mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
-+ } while ((skip -= len));
-+ }
-+
-+ int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
-+ mbedtls_arc4_free(&ctx);
-+ return ret;
-+}
-+#endif
-+
-+
-+/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+ #if 0 /* #ifdef MBEDTLS_FS_IO */
-+ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+ return -1;
-+ }
-+ #else
-+ /*(use os_readfile() so that we can use os_free()
-+ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+ * on buf aborts in tests if buf not allocated via os_malloc())*/
-+ *buf = (u8 *)os_readfile(path, n);
-+ if (!*buf) {
-+ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+ return -1;
-+ }
-+ u8 *buf0 = os_realloc(*buf, *n+1);
-+ if (!buf0) {
-+ bin_clear_free(*buf, *n);
-+ *buf = NULL;
-+ return -1;
-+ }
-+ buf0[(*n)++] = '\0';
-+ *buf = buf0;
-+ #endif
-+ return 0;
-+}
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
-+#ifdef MBEDTLS_RSA_C
-+
-+#include <mbedtls/pk.h>
-+#include <mbedtls/rsa.h>
-+
-+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
-+{
-+ /* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
-+ * require #ifdef MBEDTLS_FS_IO in mbedtls library. Prefer to use
-+ * crypto_mbedtls_readfile(), which wraps os_readfile() */
-+ u8 *data;
-+ size_t len;
-+ if (crypto_mbedtls_readfile(file, &data, &len) != 0)
-+ return NULL;
-+
-+ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+ if (ctx == NULL) {
-+ bin_clear_free(data, len);
-+ return NULL;
-+ }
-+ mbedtls_pk_init(ctx);
-+
-+ int rc;
-+ rc = (private_key
-+ ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ ,mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg()
-+ #endif
-+ )
-+ : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
-+ && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
-+
-+ bin_clear_free(data, len);
-+
-+ if (rc) {
-+ /* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
-+ /* use MBEDTLS_MD_SHA256 for these hostap interfaces */
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ /*(no return value in mbedtls 2.x)*/
-+ mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+ MBEDTLS_RSA_PKCS_V21,
-+ MBEDTLS_MD_SHA256);
-+ #else
-+ if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+ MBEDTLS_RSA_PKCS_V21,
-+ MBEDTLS_MD_SHA256) == 0)
-+ #endif
-+ return (struct crypto_rsa_key *)ctx;
-+ }
-+
-+ mbedtls_pk_free(ctx);
-+ os_free(ctx);
-+ return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
-+ const struct wpabuf *in)
-+{
-+ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+ size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+ struct wpabuf *buf = wpabuf_alloc(olen);
-+ if (buf == NULL)
-+ return NULL;
-+
-+ /* mbedtls_pk_encrypt() takes a few more hops to get to same func */
-+ if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg(),
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ MBEDTLS_RSA_PRIVATE,
-+ #endif
-+ NULL, 0,
-+ wpabuf_len(in), wpabuf_head(in),
-+ wpabuf_put(buf, olen)) == 0) {
-+ return buf;
-+ }
-+
-+ wpabuf_clear_free(buf);
-+ return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
-+ const struct wpabuf *in)
-+{
-+ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+ size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+ struct wpabuf *buf = wpabuf_alloc(olen);
-+ if (buf == NULL)
-+ return NULL;
-+
-+ /* mbedtls_pk_decrypt() takes a few more hops to get to same func */
-+ if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
-+ mbedtls_ctr_drbg_random,
-+ crypto_mbedtls_ctr_drbg(),
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ MBEDTLS_RSA_PUBLIC,
-+ #endif
-+ NULL, 0, &olen, wpabuf_head(in),
-+ wpabuf_mhead(buf), olen) == 0) {
-+ wpabuf_put(buf, olen);
-+ return buf;
-+ }
-+
-+ wpabuf_clear_free(buf);
-+ return NULL;
-+}
-+
-+void crypto_rsa_key_free(struct crypto_rsa_key *key)
-+{
-+ mbedtls_pk_free((mbedtls_pk_context *)key);
-+ os_free(key);
-+}
-+
-+#endif /* MBEDTLS_RSA_C */
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+
-+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
-+ enum hpke_kdf_id kdf_id,
-+ enum hpke_aead_id aead_id,
-+ struct crypto_ec_key *peer_pub,
-+ const u8 *info, size_t info_len,
-+ const u8 *aad, size_t aad_len,
-+ const u8 *pt, size_t pt_len)
-+{
-+ /* not yet implemented */
-+ return NULL;
-+}
-+
-+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
-+ enum hpke_kdf_id kdf_id,
-+ enum hpke_aead_id aead_id,
-+ struct crypto_ec_key *own_priv,
-+ const u8 *info, size_t info_len,
-+ const u8 *aad, size_t aad_len,
-+ const u8 *enc_ct, size_t enc_ct_len)
-+{
-+ /* not yet implemented */
-+ return NULL;
-+}
-+
-+#endif
---- /dev/null
-+++ b/src/crypto/tls_mbedtls.c
-@@ -0,0 +1,3313 @@
-+/*
-+ * SSL/TLS interface functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ *
-+ * template: src/crypto/tls_none.c
-+ * reference: src/crypto/tls_*.c
-+ *
-+ * Known Limitations:
-+ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
-+ * - no OCSP (not yet available in mbedtls)
-+ * - mbedtls does not support all certificate encodings used by hwsim tests
-+ * PCKS#5 v1.5
-+ * PCKS#12
-+ * DH DSA
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - mbedtls does not currently provide way to set an attribute in a CSR
-+ * https://github.com/Mbed-TLS/mbedtls/issues/4886
-+ * so tests/hwsim dpp_enterprise tests fail
-+ * - DPP2 not supported
-+ * PKCS#7 parsing is not supported in mbedtls
-+ * See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
-+ * - DPP3 not supported
-+ * hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
-+ *
-+ * Status:
-+ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
-+ * (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
-+ * (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
-+ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
-+ * - passes all tests/ crypto module tests (incomplete coverage)
-+ * ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
-+ * - passes almost all tests/hwsim tests
-+ * (hwsim tests skipped for missing features)
-+ *
-+ * RFE:
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - client/server session resumption, and/or save client session ticket
-+ */
-+
-+#include "includes.h"
-+#include "common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+#include <mbedtls/pem.h>
-+#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/ssl.h>
-+#include <mbedtls/ssl_ticket.h>
-+#include <mbedtls/x509.h>
-+#include <mbedtls/x509_crt.h>
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
-+#include <mbedtls/net_sockets.h>
-+#else
-+#include <mbedtls/net.h>
-+#endif
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
-+ ((ssl)->MBEDTLS_PRIVATE(session) \
-+ ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
-+ : 0)
-+#define mbedtls_ssl_ciphersuite_get_name(info) \
-+ (info)->MBEDTLS_PRIVATE(name)
-+#endif
-+
-+#include "crypto.h" /* sha256_vector() */
-+#include "tls.h"
-+
-+#ifndef SHA256_DIGEST_LENGTH
-+#define SHA256_DIGEST_LENGTH 32
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
-+#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_RAND_LEN
-+#define MBEDTLS_EXPKEY_RAND_LEN 32
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
-+#else /*(not implemented; return error)*/
-+#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
-+typedef mbedtls_tls_prf_types int;
-+#endif
-+
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#ifdef MBEDTLS_SSL_SESSION_TICKETS
-+#ifdef MBEDTLS_SSL_TICKET_C
-+#define TLS_MBEDTLS_SESSION_TICKETS
-+#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#define TLS_MBEDTLS_EAP_TEAP
-+#endif
-+#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define TLS_MBEDTLS_EAP_FAST
-+#endif
-+#endif
-+#endif
-+#endif
-+#endif
-+
-+
-+struct tls_conf {
-+ mbedtls_ssl_config conf;
-+
-+ unsigned int verify_peer:1;
-+ unsigned int verify_depth0_only:1;
-+ unsigned int check_crl:2; /*(needs :2 bits for 0, 1, 2)*/
-+ unsigned int check_crl_strict:1; /*(needs :1 bit for 0, 1)*/
-+ unsigned int ca_cert_probe:1;
-+ unsigned int has_ca_cert:1;
-+ unsigned int has_client_cert:1;
-+ unsigned int has_private_key:1;
-+ unsigned int suiteb128:1;
-+ unsigned int suiteb192:1;
-+ mbedtls_x509_crl *crl;
-+ mbedtls_x509_crt ca_cert;
-+ mbedtls_x509_crt client_cert;
-+ mbedtls_pk_context private_key;
-+
-+ uint32_t refcnt;
-+
-+ unsigned int flags;
-+ char *subject_match;
-+ char *altsubject_match;
-+ char *suffix_match;
-+ char *domain_match;
-+ char *check_cert_subject;
-+ u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
-+
-+ int *ciphersuites; /* list of ciphersuite ids for mbedtls_ssl_config */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+ mbedtls_ecp_group_id *curves;
-+#else
-+ uint16_t *curves; /* list of curve ids for mbedtls_ssl_config */
-+#endif
-+};
-+
-+
-+struct tls_global {
-+ struct tls_conf *tls_conf;
-+ char *ocsp_stapling_response;
-+ mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ mbedtls_ssl_ticket_context ticket_ctx;
-+ #endif
-+ char *ca_cert_file;
-+ struct os_reltime crl_reload_previous;
-+ unsigned int crl_reload_interval;
-+ uint32_t refcnt;
-+ struct tls_config init_conf;
-+};
-+
-+static struct tls_global tls_ctx_global;
-+
-+
-+struct tls_connection {
-+ struct tls_conf *tls_conf;
-+ struct wpabuf *push_buf;
-+ struct wpabuf *pull_buf;
-+ size_t pull_buf_offset;
-+
-+ unsigned int established:1;
-+ unsigned int resumed:1;
-+ unsigned int verify_peer:1;
-+ unsigned int is_server:1;
-+
-+ mbedtls_ssl_context ssl;
-+
-+ mbedtls_tls_prf_types tls_prf_type;
-+ size_t expkey_keyblock_size;
-+ size_t expkey_secret_len;
-+ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+ unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
-+ #else
-+ unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
-+ #endif
-+ unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
-+
-+ int read_alerts, write_alerts, failed;
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ tls_session_ticket_cb session_ticket_cb;
-+ void *session_ticket_cb_ctx;
-+ unsigned char *clienthello_session_ticket;
-+ size_t clienthello_session_ticket_len;
-+ #endif
-+ char *peer_subject; /* peer subject info for authenticated peer */
-+ struct wpabuf *success_data;
-+};
-+
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__ __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__ __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsg(int level, const char * const msg)
-+{
-+ wpa_printf(level, "MTLS: %s", msg);
-+}
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsgrc(int level, const char * const msg, int rc)
-+{
-+ #ifdef MBEDTLS_ERROR_C
-+ /* error logging convenience function that decodes mbedtls result codes */
-+ char buf[256];
-+ mbedtls_strerror(rc, buf, sizeof(buf));
-+ wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
-+ #else
-+ wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
-+ #endif
-+}
-+
-+
-+#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
-+#define ilog(rc, msg) emsgrc(MSG_INFO, (msg), (rc))
-+
-+
-+struct tls_conf * tls_conf_init(void *tls_ctx)
-+{
-+ struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
-+ if (tls_conf == NULL)
-+ return NULL;
-+ tls_conf->refcnt = 1;
-+
-+ mbedtls_ssl_config_init(&tls_conf->conf);
-+ mbedtls_ssl_conf_rng(&tls_conf->conf,
-+ mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
-+ mbedtls_x509_crt_init(&tls_conf->ca_cert);
-+ mbedtls_x509_crt_init(&tls_conf->client_cert);
-+ mbedtls_pk_init(&tls_conf->private_key);
-+
-+ return tls_conf;
-+}
-+
-+
-+void tls_conf_deinit(struct tls_conf *tls_conf)
-+{
-+ if (tls_conf == NULL || --tls_conf->refcnt != 0)
-+ return;
-+
-+ mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+ mbedtls_x509_crt_free(&tls_conf->client_cert);
-+ if (tls_conf->crl) {
-+ mbedtls_x509_crl_free(tls_conf->crl);
-+ os_free(tls_conf->crl);
-+ }
-+ mbedtls_pk_free(&tls_conf->private_key);
-+ mbedtls_ssl_config_free(&tls_conf->conf);
-+ os_free(tls_conf->curves);
-+ os_free(tls_conf->ciphersuites);
-+ os_free(tls_conf->subject_match);
-+ os_free(tls_conf->altsubject_match);
-+ os_free(tls_conf->suffix_match);
-+ os_free(tls_conf->domain_match);
-+ os_free(tls_conf->check_cert_subject);
-+ os_free(tls_conf);
-+}
-+
-+
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+
-+__attribute_cold__
-+void * tls_init(const struct tls_config *conf)
-+{
-+ /* RFE: review struct tls_config *conf (different from tls_conf) */
-+
-+ if (++tls_ctx_global.refcnt > 1)
-+ return &tls_ctx_global;
-+
-+ tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
-+ mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
-+ mbedtls_ctr_drbg_random,
-+ tls_ctx_global.ctr_drbg,
-+ MBEDTLS_CIPHER_AES_256_GCM,
-+ 43200); /* ticket timeout: 12 hours */
-+ #endif
-+ /* copy struct for future use */
-+ tls_ctx_global.init_conf = *conf;
-+ if (conf->openssl_ciphers)
-+ tls_ctx_global.init_conf.openssl_ciphers =
-+ os_strdup(conf->openssl_ciphers);
-+
-+ tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
-+ os_get_reltime(&tls_ctx_global.crl_reload_previous);
-+
-+ return &tls_ctx_global;
-+}
-+
-+
-+__attribute_cold__
-+void tls_deinit(void *tls_ctx)
-+{
-+ if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
-+ return;
-+
-+ tls_conf_deinit(tls_ctx_global.tls_conf);
-+ os_free(tls_ctx_global.ca_cert_file);
-+ os_free(tls_ctx_global.ocsp_stapling_response);
-+ char *openssl_ciphers; /*(allocated in tls_init())*/
-+ *(const char **)&openssl_ciphers =
-+ tls_ctx_global.init_conf.openssl_ciphers;
-+ os_free(openssl_ciphers);
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
-+ #endif
-+ os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
-+}
-+
-+
-+int tls_get_errors(void *tls_ctx)
-+{
-+ return 0;
-+}
-+
-+
-+static void tls_connection_deinit_expkey(struct tls_connection *conn)
-+{
-+ conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
-+ conn->expkey_keyblock_size = 0;
-+ conn->expkey_secret_len = 0;
-+ forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
-+ forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
-+{
-+ if (conn->clienthello_session_ticket) {
-+ mbedtls_platform_zeroize(conn->clienthello_session_ticket,
-+ conn->clienthello_session_ticket_len);
-+ mbedtls_free(conn->clienthello_session_ticket);
-+ conn->clienthello_session_ticket = NULL;
-+ conn->clienthello_session_ticket_len = 0;
-+ }
-+}
-+#endif
-+
-+
-+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
-+{
-+ if (conn == NULL)
-+ return;
-+
-+ #if 0 /*(good intention, but never sent since we destroy self below)*/
-+ if (conn->established)
-+ mbedtls_ssl_close_notify(&conn->ssl);
-+ #endif
-+
-+ if (conn->tls_prf_type)
-+ tls_connection_deinit_expkey(conn);
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ if (conn->clienthello_session_ticket)
-+ tls_connection_deinit_clienthello_session_ticket(conn);
-+ #endif
-+
-+ os_free(conn->peer_subject);
-+ wpabuf_free(conn->success_data);
-+ wpabuf_free(conn->push_buf);
-+ wpabuf_free(conn->pull_buf);
-+ mbedtls_ssl_free(&conn->ssl);
-+ tls_conf_deinit(conn->tls_conf);
-+ os_free(conn);
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void);
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
-+
-+struct tls_connection * tls_connection_init(void *tls_ctx)
-+{
-+ struct tls_connection *conn = os_zalloc(sizeof(*conn));
-+ if (conn == NULL)
-+ return NULL;
-+
-+ mbedtls_ssl_init(&conn->ssl);
-+
-+ conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
-+ if (conn->tls_conf) {
-+ ++conn->tls_conf->refcnt;
-+ /* check for CRL refresh if inheriting from global config */
-+ tls_mbedtls_refresh_crl();
-+
-+ conn->verify_peer = conn->tls_conf->verify_peer;
-+ if (tls_mbedtls_ssl_setup(conn) != 0) {
-+ tls_connection_deinit(&tls_ctx_global, conn);
-+ return NULL;
-+ }
-+ }
-+
-+ return conn;
-+}
-+
-+
-+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
-+{
-+ return conn ? conn->established : 0;
-+}
-+
-+
-+__attribute_noinline__
-+char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
-+{
-+ /* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
-+ * colons, so generate the hex serial number here. The func
-+ * wpa_snprintf_hex_uppercase() is similarly inefficient. */
-+ size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
-+ while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
-+ if (i == crt->serial.len) --i;
-+
-+ const unsigned char *s = crt->serial.p + i;
-+ const size_t e = (crt->serial.len - i) * 2;
-+ if (e >= len)
-+ return NULL;
-+ #if 0
-+ wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
-+ #else
-+ for (i = 0; i < e; i+=2, ++s) {
-+ serial_num[i+0] = "0123456789ABCDEF"[(*s >> 4)];
-+ serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
-+ }
-+ serial_num[e] = '\0';
-+ #endif
-+ return serial_num;
-+}
-+
-+
-+char * tls_connection_peer_serial_num(void *tls_ctx,
-+ struct tls_connection *conn)
-+{
-+ const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
-+ if (crt == NULL)
-+ return NULL;
-+ size_t len = crt->serial.len * 2 + 1;
-+ char *serial_num = os_malloc(len);
-+ if (!serial_num)
-+ return NULL;
-+ return tls_mbedtls_peer_serial_num(crt, serial_num, len);
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn);
-+
-+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
-+{
-+ /* Note: this function called from eap_peer_tls_reauth_init()
-+ * for session resumption, not for connection shutdown */
-+
-+ if (conn == NULL)
-+ return -1;
-+
-+ tls_pull_buf_reset(conn);
-+ wpabuf_free(conn->push_buf);
-+ conn->push_buf = NULL;
-+ conn->established = 0;
-+ conn->resumed = 0;
-+ if (conn->tls_prf_type)
-+ tls_connection_deinit_expkey(conn);
-+
-+ /* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
-+
-+ return mbedtls_ssl_session_reset(&conn->ssl);
-+}
-+
-+
-+static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
-+ const unsigned char *data, size_t dlen)
-+{
-+ if (wpabuf_resize(buf, dlen) < 0)
-+ return 0;
-+ wpabuf_put_data(*buf, data, dlen);
-+ return 1;
-+}
-+
-+
-+static int tls_pull_buf_append(struct tls_connection *conn,
-+ const struct wpabuf *in_data)
-+{
-+ /*(interface does not lend itself to move semantics)*/
-+ return tls_wpabuf_resize_put_data(&conn->pull_buf,
-+ wpabuf_head(in_data),
-+ wpabuf_len(in_data));
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn)
-+{
-+ /*(future: might consider reusing conn->pull_buf)*/
-+ wpabuf_free(conn->pull_buf);
-+ conn->pull_buf = NULL;
-+ conn->pull_buf_offset = 0;
-+}
-+
-+
-+__attribute_cold__
-+static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
-+{
-+ size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+ if (discard)
-+ wpa_printf(MSG_DEBUG,
-+ "%s - %zu bytes remaining in pull_buf; discarding",
-+ func, discard);
-+ tls_pull_buf_reset(conn);
-+}
-+
-+
-+static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
-+{
-+ struct tls_connection *conn = (struct tls_connection *) ptr;
-+ if (conn->pull_buf == NULL)
-+ return MBEDTLS_ERR_SSL_WANT_READ;
-+ const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+ if (dlen == 0)
-+ return MBEDTLS_ERR_SSL_WANT_READ;
-+
-+ if (len > dlen)
-+ len = dlen;
-+ os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
-+
-+ if (len == dlen) {
-+ tls_pull_buf_reset(conn);
-+ /*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
-+ }
-+ else {
-+ conn->pull_buf_offset += len;
-+ /*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
-+ __func__, dlen - len);*/
-+ }
-+ return (int)len;
-+}
-+
-+
-+static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
-+{
-+ struct tls_connection *conn = (struct tls_connection *) ptr;
-+ return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
-+ ? (int)len
-+ : MBEDTLS_ERR_SSL_ALLOC_FAILED;
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
-+
-+
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
-+{
-+ #if 0
-+ /* mbedtls_ssl_setup() must be called only once */
-+ /* If this func might be called multiple times (e.g. via set_params),
-+ * then we should set a flag in conn that ssl was initialized */
-+ if (conn->ssl_is_init) {
-+ mbedtls_ssl_free(&conn->ssl);
-+ mbedtls_ssl_init(&conn->ssl);
-+ }
-+ #endif
-+
-+ int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_ssl_setup");
-+ return -1;
-+ }
-+
-+ mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ mbedtls_ssl_set_export_keys_cb(
-+ &conn->ssl, tls_connection_export_keys_cb, conn);
-+ #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+ mbedtls_ssl_conf_export_keys_ext_cb(
-+ &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
-+ #endif
-+ if (conn->verify_peer)
-+ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_data_is_pem(const u8 *data)
-+{
-+ return (NULL != os_strstr((char *)data, "-----"));
-+}
-+
-+
-+static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
-+ mbedtls_ssl_config *conf)
-+{
-+ #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
-+ #endif
-+
-+ /* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
-+ if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
-+ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
-+ }
-+
-+ const unsigned int flags = tls_conf->flags;
-+
-+ /* attempt to map flags to min and max TLS protocol version */
-+
-+ int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_3)
-+ ? 4
-+ : 3
-+ : 2
-+ : 1
-+ : 0;
-+
-+ int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+ ? (flags & TLS_CONN_DISABLE_TLSv1_0)
-+ ? -1
-+ : 0
-+ : 1
-+ : 2
-+ : 3;
-+
-+ if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
-+ if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
-+ if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
-+ if (max < min) {
-+ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+ return;
-+ }
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ /* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
-+ if (min < 2 || max < 2) {
-+ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+ if (min < 2) min = 2;
-+ if (max < 2) max = 2;
-+ }
-+ #endif
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ /* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
-+ /* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
-+ min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+ max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+ mbedtls_ssl_conf_min_tls_version(conf, min);
-+ mbedtls_ssl_conf_max_tls_version(conf, max);
-+ #else
-+ #ifndef MBEDTLS_SSL_MINOR_VERSION_4
-+ if (min == 3) min = 2;
-+ if (max == 3) max = 2;
-+ #endif
-+ /* MBEDTLS_SSL_MINOR_VERSION_0 0 *//*!< SSL v3.0 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_1 1 *//*!< TLS v1.0 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_2 2 *//*!< TLS v1.1 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_3 3 *//*!< TLS v1.2 */
-+ /* MBEDTLS_SSL_MINOR_VERSION_4 4 *//*!< TLS v1.3 */
-+ mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
-+ mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
-+ #endif
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
-+
-+
-+static int
-+tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
-+{
-+ size_t len;
-+ u8 *data;
-+ if (tls_mbedtls_readfile(dh_file, &data, &len))
-+ return 0;
-+
-+ /* parse only if DH parameters if in PEM format */
-+ if (tls_mbedtls_data_is_pem(data)
-+ && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
-+ if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
-+ wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
-+ else
-+ wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
-+ forced_memzero(data, len);
-+ os_free(data);
-+ return 0;
-+ }
-+
-+ /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
-+ mbedtls_dhm_context dhm;
-+ mbedtls_dhm_init(&dhm);
-+ int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
-+ if (0 == rc)
-+ rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
-+ if (0 != rc)
-+ elog(rc, dh_file);
-+ mbedtls_dhm_free(&dhm);
-+
-+ forced_memzero(data, len);
-+ os_free(data);
-+ return (0 == rc);
-+}
-+
-+
-+/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
-+ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+static int
-+tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
-+{
-+ if (1 >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR, "error: too many curves during list expand");
-+ return -1;
-+ }
-+ ids[++nids] = id;
-+ return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+ mbedtls_ecp_group_id ids[512];
-+ int nids = -1;
-+ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+ for (const char *e = curvelist-1; e; ) {
-+ const char * const n = e+1;
-+ e = os_strchr(n, ':');
-+ size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+ mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
-+ switch (len) {
-+ case 5:
-+ if (0 == os_memcmp("P-521", n, 5))
-+ grp_id = MBEDTLS_ECP_DP_SECP521R1;
-+ else if (0 == os_memcmp("P-384", n, 5))
-+ grp_id = MBEDTLS_ECP_DP_SECP384R1;
-+ else if (0 == os_memcmp("P-256", n, 5))
-+ grp_id = MBEDTLS_ECP_DP_SECP256R1;
-+ break;
-+ case 6:
-+ if (0 == os_memcmp("BP-521", n, 6))
-+ grp_id = MBEDTLS_ECP_DP_BP512R1;
-+ else if (0 == os_memcmp("BP-384", n, 6))
-+ grp_id = MBEDTLS_ECP_DP_BP384R1;
-+ else if (0 == os_memcmp("BP-256", n, 6))
-+ grp_id = MBEDTLS_ECP_DP_BP256R1;
-+ break;
-+ default:
-+ break;
-+ }
-+ if (grp_id != MBEDTLS_ECP_DP_NONE) {
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
-+ if (-1 == nids) return 0;
-+ continue;
-+ }
-+ /* similar to mbedtls_ecp_curve_info_from_name() */
-+ const mbedtls_ecp_curve_info *info;
-+ for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
-+ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+ break;
-+ }
-+ if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
-+ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+ return 0;
-+ }
-+
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
-+ if (-1 == nids) return 0;
-+ }
-+
-+ /* mod_openssl configures "prime256v1" if curve list not specified,
-+ * but mbedtls provides a list of supported curves if not explicitly set */
-+ if (-1 == nids) return 1; /* empty list; no-op */
-+
-+ ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
-+ ++nids;
-+
-+ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+ tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
-+ if (tls_conf->curves == NULL)
-+ return 0;
-+ os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
-+
-+ mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
-+ return 1;
-+}
-+#else
-+static int
-+tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
-+{
-+ if (1 >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR, "error: too many curves during list expand");
-+ return -1;
-+ }
-+ ids[++nids] = id;
-+ return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+ /* TLS Supported Groups (renamed from "EC Named Curve Registry")
-+ * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
-+ */
-+ uint16_t ids[512];
-+ int nids = -1;
-+ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+ for (const char *e = curvelist-1; e; ) {
-+ const char * const n = e+1;
-+ e = os_strchr(n, ':');
-+ size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+ uint16_t tls_id = 0;
-+ switch (len) {
-+ case 5:
-+ if (0 == os_memcmp("P-521", n, 5))
-+ tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
-+ else if (0 == os_memcmp("P-384", n, 5))
-+ tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
-+ else if (0 == os_memcmp("P-256", n, 5))
-+ tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
-+ break;
-+ case 6:
-+ if (0 == os_memcmp("BP-521", n, 6))
-+ tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
-+ else if (0 == os_memcmp("BP-384", n, 6))
-+ tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
-+ else if (0 == os_memcmp("BP-256", n, 6))
-+ tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
-+ break;
-+ default:
-+ break;
-+ }
-+ if (tls_id != 0) {
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
-+ if (-1 == nids) return 0;
-+ continue;
-+ }
-+ /* similar to mbedtls_ecp_curve_info_from_name() */
-+ const mbedtls_ecp_curve_info *info;
-+ for (info = curve_info; info->tls_id != 0; ++info) {
-+ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+ break;
-+ }
-+ if (info->tls_id == 0) {
-+ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+ return 0;
-+ }
-+
-+ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
-+ if (-1 == nids) return 0;
-+ }
-+
-+ /* mod_openssl configures "prime256v1" if curve list not specified,
-+ * but mbedtls provides a list of supported curves if not explicitly set */
-+ if (-1 == nids) return 1; /* empty list; no-op */
-+
-+ ids[++nids] = 0; /* terminate list */
-+ ++nids;
-+
-+ /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+ tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
-+ if (tls_conf->curves == NULL)
-+ return 0;
-+ os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
-+
-+ mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
-+ return 1;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
-+
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_256_ephemeral[] = {
-+ /* All AES-256 ephemeral suites */
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_128_ephemeral[] = {
-+ /* All AES-128 ephemeral suites */
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+/* HIGH cipher list (mapped from openssl list to mbedtls) */
-+static const int suite_HIGH[] = {
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
-+ MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
-+ MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+ MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
-+ MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
-+};
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
-+{
-+ if (xsz >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR, "error: too many ciphers during list expand");
-+ return -1;
-+ }
-+
-+ for (int i = 0; i < xsz; ++i)
-+ ids[++nids] = x[i];
-+
-+ return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
-+{
-+ const mbedtls_ssl_ciphersuite_t *info =
-+ mbedtls_ssl_ciphersuite_from_id(id);
-+ if (info == NULL)
-+ return 0;
-+ const char *name = mbedtls_ssl_ciphersuite_get_name(info);
-+ const size_t len = os_strlen(name);
-+ if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
-+ return 0;
-+ if (len >= buflen)
-+ return 0;
-+ os_strlcpy(buf, name, buflen);
-+
-+ /* attempt to translate mbedtls string to openssl string
-+ * (some heuristics; incomplete) */
-+ size_t i = 0, j = 0;
-+ if (buf[0] == 'T') {
-+ if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
-+ buf[3] = '-';
-+ j = 4; /* remove "1-3" from "TLS1-3-" prefix */
-+ i = 7;
-+ }
-+ else if (os_strncmp(buf, "TLS-", 4) == 0)
-+ i = 4; /* remove "TLS-" prefix */
-+ }
-+ for (; buf[i]; ++i) {
-+ if (buf[i] == '-') {
-+ if (i >= 3) {
-+ if (0 == os_memcmp(buf+i-3, "AES", 3))
-+ continue; /* "AES-" -> "AES" */
-+ }
-+ if (i >= 4) {
-+ if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
-+ j -= 4; /* remove "WITH-" */
-+ continue;
-+ }
-+ }
-+ }
-+ buf[j++] = buf[i];
-+ }
-+ buf[j] = '\0';
-+
-+ return j;
-+}
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
-+{
-+ /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
-+ os_free(tls_conf->ciphersuites);
-+ tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
-+ if (tls_conf->ciphersuites == NULL)
-+ return 0;
-+ os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
-+ mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
-+ return 1;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
-+{
-+ char buf[64];
-+ int ids[512];
-+ int nids = -1;
-+ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+ const char *next;
-+ size_t blen, clen;
-+ do {
-+ next = os_strchr(ciphers, ':');
-+ clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
-+ if (!clen)
-+ continue;
-+
-+ /* special-case a select set of openssl group names for hwsim tests */
-+ /* (review; remove excess code if tests are not run for non-OpenSSL?) */
-+ if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
-+ static int ssl_preset_suiteb192_ciphersuites[] = {
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+ 0
-+ };
-+ return tls_mbedtls_set_ciphersuites(tls_conf,
-+ ssl_preset_suiteb192_ciphersuites,
-+ 2);
-+ }
-+ if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
-+ static int ssl_preset_suiteb128_ciphersuites[] = {
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ 0
-+ };
-+ return tls_mbedtls_set_ciphersuites(tls_conf,
-+ ssl_preset_suiteb128_ciphersuites,
-+ 2);
-+ }
-+ if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
-+ continue;
-+ if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
-+ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+ suite_AES_128_ephemeral,
-+ (int)ARRAY_SIZE(suite_AES_128_ephemeral));
-+ if (nids == -1)
-+ return 0;
-+ continue;
-+ }
-+ if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
-+ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+ suite_AES_256_ephemeral,
-+ (int)ARRAY_SIZE(suite_AES_256_ephemeral));
-+ if (nids == -1)
-+ return 0;
-+ continue;
-+ }
-+ if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
-+ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
-+ (int)ARRAY_SIZE(suite_HIGH));
-+ if (nids == -1)
-+ return 0;
-+ continue;
-+ }
-+ /* ignore anonymous cipher group names (?not supported by mbedtls?) */
-+ if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
-+ continue;
-+ if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
-+ continue;
-+ if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
-+ continue;
-+
-+ /* attempt to match mbedtls cipher names
-+ * nb: does not support openssl group names or list manipulation syntax
-+ * (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
-+ * mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
-+ * note: not efficient to rewrite list for each ciphers entry,
-+ * but this code is expected to run only at startup
-+ */
-+ const int *list = mbedtls_ssl_list_ciphersuites();
-+ for (; *list; ++list) {
-+ blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
-+ if (!blen)
-+ continue;
-+
-+ /* matching heuristics additional to translate_ciphername above */
-+ if (blen == clen+4) {
-+ char *cbc = os_strstr(buf, "CBC-");
-+ if (cbc) {
-+ os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
-+ blen -= 4;
-+ }
-+ }
-+ if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
-+ && (blen == clen
-+ || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
-+ if (1 >= idsz - (nids + 1)) {
-+ emsg(MSG_ERROR,
-+ "error: too many ciphers during list expand");
-+ return 0;
-+ }
-+ ids[++nids] = *list;
-+ break;
-+ }
-+ }
-+ if (*list == 0) {
-+ wpa_printf(MSG_ERROR,
-+ "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
-+ return 0;
-+ }
-+ } while ((ciphers = next ? next+1 : NULL));
-+
-+ if (-1 == nids) return 1; /* empty list; no-op */
-+
-+ ids[++nids] = 0; /* terminate list */
-+ ++nids;
-+
-+ return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_set_item(char **config_item, const char *item)
-+{
-+ os_free(*config_item);
-+ *config_item = NULL;
-+ return item ? (*config_item = os_strdup(item)) != NULL : 1;
-+}
-+
-+
-+static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ int rc = 1;
-+ rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
-+ params->subject_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
-+ params->altsubject_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
-+ params->suffix_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
-+ params->domain_match);
-+ rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
-+ params->check_cert_subject);
-+ return rc;
-+}
-+
-+
-+/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+ #if 0 /* #ifdef MBEDTLS_FS_IO */
-+ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+ return -1;
-+ }
-+ #else
-+ /*(use os_readfile() so that we can use os_free()
-+ *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+ * on buf aborts in tests if buf not allocated via os_malloc())*/
-+ *buf = (u8 *)os_readfile(path, n);
-+ if (!*buf) {
-+ wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+ return -1;
-+ }
-+ u8 *buf0 = os_realloc(*buf, *n+1);
-+ if (!buf0) {
-+ bin_clear_free(*buf, *n);
-+ *buf = NULL;
-+ return -1;
-+ }
-+ buf0[(*n)++] = '\0';
-+ *buf = buf0;
-+ #endif
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
-+{
-+ /* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
-+ if (len && data[len-1] == '\0'
-+ && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
-+ && tls_mbedtls_data_is_pem(data))
-+ return 0;
-+
-+ mbedtls_x509_crl crl;
-+ mbedtls_x509_crl_init(&crl);
-+ int rc = mbedtls_x509_crl_parse(&crl, data, len);
-+ if (rc < 0) {
-+ mbedtls_x509_crl_free(&crl);
-+ return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
-+ }
-+
-+ mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
-+ if (crl_new == NULL) {
-+ mbedtls_x509_crl_free(&crl);
-+ return MBEDTLS_ERR_X509_ALLOC_FAILED;
-+ }
-+ os_memcpy(crl_new, &crl, sizeof(crl));
-+
-+ mbedtls_x509_crl *crl_old = tls_conf->crl;
-+ tls_conf->crl = crl_new;
-+ if (crl_old) {
-+ mbedtls_x509_crl_free(crl_old);
-+ os_free(crl_old);
-+ }
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
-+{
-+ /* load crt struct onto stack and then copy into tls_conf in
-+ * order to preserve existing tls_conf value if error occurs
-+ *
-+ * hostapd is not threaded, or else should allocate memory and swap in
-+ * pointer reduce race condition. (If threaded, would also need to
-+ * keep reference count of use to avoid freeing while still in use.) */
-+
-+ mbedtls_x509_crt crt;
-+ mbedtls_x509_crt_init(&crt);
-+ int rc = mbedtls_x509_crt_parse(&crt, data, len);
-+ if (rc < 0) {
-+ mbedtls_x509_crt_free(&crt);
-+ return rc;
-+ }
-+
-+ mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+ os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
-+{
-+ size_t len;
-+ u8 *data;
-+ if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
-+ return -1;
-+
-+ int rc;
-+ if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
-+ && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
-+ || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
-+ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+ &tls_conf->ca_cert,
-+ tls_conf->crl);
-+ }
-+ else {
-+ elog(rc, __func__);
-+ emsg(MSG_ERROR, ca_cert_file);
-+ }
-+
-+ forced_memzero(data, len);
-+ os_free(data);
-+ return rc;
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void)
-+{
-+ /* check for CRL refresh
-+ * continue even if error occurs; continue with previous cert, CRL */
-+ unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
-+ const char *ca_cert_file = tls_ctx_global.ca_cert_file;
-+ if (!crl_reload_interval || !ca_cert_file)
-+ return;
-+
-+ struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
-+ struct os_reltime now;
-+ if (os_get_reltime(&now) != 0
-+ || !os_reltime_expired(&now, previous, crl_reload_interval))
-+ return;
-+
-+ /* Note: modifying global state is not thread-safe
-+ * if in use by existing connections
-+ *
-+ * src/utils/os.h does not provide a portable stat()
-+ * or else it would be a good idea to check mtime and size,
-+ * and avoid reloading if file has not changed */
-+
-+ if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
-+ *previous = now;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ if (params->ca_cert) {
-+ if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
-+ tls_conf->ca_cert_probe = 1;
-+ tls_conf->has_ca_cert = 1;
-+ return 0;
-+ }
-+
-+ if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
-+ const char *pos = params->ca_cert + 7;
-+ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
-+ emsg(MSG_ERROR, "unsupported ca_cert hash value");
-+ return -1;
-+ }
-+ pos += 14;
-+ if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
-+ emsg(MSG_ERROR, "unexpected ca_cert hash length");
-+ return -1;
-+ }
-+ if (hexstr2bin(pos, tls_conf->ca_cert_hash,
-+ SHA256_DIGEST_LENGTH) < 0) {
-+ emsg(MSG_ERROR, "invalid ca_cert hash value");
-+ return -1;
-+ }
-+ emsg(MSG_DEBUG, "checking only server certificate match");
-+ tls_conf->verify_depth0_only = 1;
-+ tls_conf->has_ca_cert = 1;
-+ return 0;
-+ }
-+
-+ if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
-+ return -1;
-+ }
-+ if (params->ca_cert_blob) {
-+ size_t len = params->ca_cert_blob_len;
-+ int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
-+ if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
-+ ++len; /*(include '\0' in len for PEM)*/
-+ int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
-+ params->ca_cert_blob, len);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_x509_crt_parse");
-+ return -1;
-+ }
-+ if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
-+ ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_x509_crl_parse");
-+ return -1;
-+ }
-+ }
-+ }
-+
-+ if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
-+ || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
-+ emsg(MSG_WARNING, "ca_cert expired or not yet valid");
-+ if (params->ca_cert)
-+ emsg(MSG_WARNING, params->ca_cert);
-+ }
-+
-+ tls_conf->has_ca_cert = 1;
-+ return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ int ret;
-+
-+ if (params->ca_cert || params->ca_cert_blob) {
-+ if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
-+ return -1;
-+ }
-+ else if (params->ca_path) {
-+ emsg(MSG_INFO, "ca_path support not implemented");
-+ return -1;
-+ }
-+
-+ if (!tls_conf->has_ca_cert)
-+ mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
-+ else {
-+ /* Initial setting: REQUIRED for client, OPTIONAL for server
-+ * (see also tls_connection_set_verify()) */
-+ tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
-+ int authmode = tls_conf->verify_peer
-+ ? MBEDTLS_SSL_VERIFY_REQUIRED
-+ : MBEDTLS_SSL_VERIFY_OPTIONAL;
-+ mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
-+ mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+ &tls_conf->ca_cert,
-+ tls_conf->crl);
-+
-+ if (!tls_connection_set_subject_match(tls_conf, params))
-+ return -1;
-+ }
-+
-+ if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
-+ emsg(MSG_INFO, "server_cert2 support not implemented");
-+
-+ if (params->client_cert) {
-+ size_t len;
-+ u8 *data;
-+ if (tls_mbedtls_readfile(params->client_cert, &data, &len))
-+ return -1;
-+ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
-+ forced_memzero(data, len);
-+ os_free(data);
-+ }
-+ if (params->client_cert_blob) {
-+ size_t len = params->client_cert_blob_len;
-+ if (len && params->client_cert_blob[len-1] != '\0'
-+ && tls_mbedtls_data_is_pem(params->client_cert_blob))
-+ ++len; /*(include '\0' in len for PEM)*/
-+ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
-+ params->client_cert_blob, len);
-+ }
-+ if (params->client_cert || params->client_cert_blob) {
-+ if (ret < 0) {
-+ elog(ret, "mbedtls_x509_crt_parse");
-+ if (params->client_cert)
-+ emsg(MSG_ERROR, params->client_cert);
-+ return -1;
-+ }
-+ if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
-+ || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
-+ emsg(MSG_WARNING, "cert expired or not yet valid");
-+ if (params->client_cert)
-+ emsg(MSG_WARNING, params->client_cert);
-+ }
-+ tls_conf->has_client_cert = 1;
-+ }
-+
-+ if (params->private_key || params->private_key_blob) {
-+ size_t len = params->private_key_blob_len;
-+ u8 *data;
-+ *(const u8 **)&data = params->private_key_blob;
-+ if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
-+ ++len; /*(include '\0' in len for PEM)*/
-+ if (params->private_key
-+ && tls_mbedtls_readfile(params->private_key, &data, &len)) {
-+ return -1;
-+ }
-+ const char *pwd = params->private_key_passwd;
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+ data, len,
-+ (const unsigned char *)pwd,
-+ pwd ? os_strlen(pwd) : 0,
-+ mbedtls_ctr_drbg_random,
-+ tls_ctx_global.ctr_drbg);
-+ #else
-+ ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+ data, len,
-+ (const unsigned char *)pwd,
-+ pwd ? os_strlen(pwd) : 0);
-+ #endif
-+ if (params->private_key) {
-+ forced_memzero(data, len);
-+ os_free(data);
-+ }
-+ if (ret < 0) {
-+ elog(ret, "mbedtls_pk_parse_key");
-+ return -1;
-+ }
-+ tls_conf->has_private_key = 1;
-+ }
-+
-+ if (tls_conf->has_client_cert && tls_conf->has_private_key) {
-+ ret = mbedtls_ssl_conf_own_cert(
-+ &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
-+ if (ret < 0) {
-+ elog(ret, "mbedtls_ssl_conf_own_cert");
-+ return -1;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+
-+/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
-+{
-+ /* Only SHA-256 and 384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+ /* Only ECDSA */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+ /* Only NIST P-256 and P-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+ 0,
-+#endif
-+ 2048,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
-+{
-+ /* Only SHA-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+ /* Only ECDSA */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+ /* Only NIST P-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+ 0,
-+#endif
-+ 3072,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
-+{
-+ /* Only SHA-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+ 0xFFFFFFF, /* Any PK alg */
-+#if defined(MBEDTLS_ECP_C)
-+ /* Only NIST P-384 */
-+ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+ 0,
-+#endif
-+ 3072,
-+};
-+
-+
-+static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
-+ const struct tls_connection_params *params)
-+{
-+ tls_conf->flags = params->flags;
-+
-+ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
-+ emsg(MSG_INFO, "ocsp=3 not supported");
-+ return -1;
-+ }
-+
-+ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
-+ emsg(MSG_INFO, "ocsp not supported");
-+ return -1;
-+ }
-+
-+ int suiteb128 = 0;
-+ int suiteb192 = 0;
-+ if (params->openssl_ciphers) {
-+ if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
-+ suiteb192 = 1;
-+ tls_conf->flags |= TLS_CONN_SUITEB;
-+ }
-+ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
-+ suiteb128 = 1;
-+ tls_conf->flags |= TLS_CONN_SUITEB;
-+ }
-+ }
-+
-+ int ret = mbedtls_ssl_config_defaults(
-+ &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
-+ : MBEDTLS_SSL_IS_CLIENT,
-+ MBEDTLS_SSL_TRANSPORT_STREAM,
-+ (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
-+ : MBEDTLS_SSL_PRESET_DEFAULT);
-+ if (ret != 0) {
-+ elog(ret, "mbedtls_ssl_config_defaults");
-+ return -1;
-+ }
-+
-+ if (suiteb128) {
-+ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+ &tls_mbedtls_crt_profile_suiteb128);
-+ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
-+ }
-+ else if (suiteb192) {
-+ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+ &tls_mbedtls_crt_profile_suiteb192);
-+ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+ }
-+ else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ /* treat as suiteb192 while allowing any PK algorithm */
-+ mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+ &tls_mbedtls_crt_profile_suiteb192_anypk);
-+ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+ }
-+
-+ tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
-+ ret = tls_mbedtls_set_certs(tls_conf, params);
-+ if (ret != 0)
-+ return -1;
-+
-+ if (params->dh_file
-+ && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
-+ return -1;
-+ }
-+
-+ if (params->openssl_ecdh_curves
-+ && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
-+ return -1;
-+ }
-+
-+ if (params->openssl_ciphers) {
-+ if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
-+ return -1;
-+ }
-+ else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ /* special-case a select set of ciphers for hwsim tests */
-+ if (!tls_mbedtls_set_ciphers(tls_conf,
-+ (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
-+ ? "DHE-RSA-AES256-GCM-SHA384"
-+ : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-+
-+
-+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-+ const struct tls_connection_params *params)
-+{
-+ if (conn == NULL || params == NULL)
-+ return -1;
-+
-+ tls_conf_deinit(conn->tls_conf);
-+ struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
-+ if (tls_conf == NULL)
-+ return -1;
-+
-+ if (tls_ctx_global.tls_conf) {
-+ tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
-+ tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
-+ /*(tls_openssl.c inherits check_cert_subject from global conf)*/
-+ if (tls_ctx_global.tls_conf->check_cert_subject) {
-+ tls_conf->check_cert_subject =
-+ os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
-+ if (tls_conf->check_cert_subject == NULL)
-+ return -1;
-+ }
-+ }
-+
-+ if (tls_mbedtls_set_params(tls_conf, params) != 0)
-+ return -1;
-+ conn->verify_peer = tls_conf->verify_peer;
-+
-+ return tls_mbedtls_ssl_setup(conn);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
-+ const u8 *data, size_t len)
-+{
-+ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+ return -1;
-+ if (conn->clienthello_session_ticket)
-+ tls_connection_deinit_clienthello_session_ticket(conn);
-+ if (len) {
-+ conn->clienthello_session_ticket = mbedtls_calloc(1, len);
-+ if (conn->clienthello_session_ticket == NULL)
-+ return -1;
-+ conn->clienthello_session_ticket_len = len;
-+ os_memcpy(conn->clienthello_session_ticket, data, len);
-+ }
-+ return 0;
-+}
-+
-+
-+static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
-+{
-+ mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
-+ if (sess->MBEDTLS_PRIVATE(ticket)) {
-+ mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
-+ sess->MBEDTLS_PRIVATE(ticket_len));
-+ mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
-+ }
-+ sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
-+ sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
-+ sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
-+
-+ conn->clienthello_session_ticket = NULL;
-+ conn->clienthello_session_ticket_len = 0;
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
-+ const mbedtls_ssl_session *session,
-+ unsigned char *start,
-+ const unsigned char *end,
-+ size_t *tlen,
-+ uint32_t *lifetime)
-+{
-+ struct tls_connection *conn = p_ticket;
-+ if (conn && conn->session_ticket_cb) {
-+ /* see tls_mbedtls_clienthello_session_ticket_prep() */
-+ /* see tls_mbedtls_clienthello_session_ticket_set() */
-+ return 0;
-+ }
-+
-+ return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
-+ session, start, end, tlen, lifetime);
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
-+ mbedtls_ssl_session *session,
-+ unsigned char *buf,
-+ size_t len)
-+{
-+ /* XXX: TODO: not implemented in client;
-+ * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
-+
-+ if (len == 0)
-+ return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-+
-+ struct tls_connection *conn = p_ticket;
-+ if (conn && conn->session_ticket_cb) {
-+ /* XXX: have random and secret been initialized yet?
-+ * or must keys first be exported?
-+ * EAP-FAST uses all args, EAP-TEAP only uses secret */
-+ struct tls_random data;
-+ if (tls_connection_get_random(NULL, conn, &data) != 0)
-+ return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
-+ int ret =
-+ conn->session_ticket_cb(conn->session_ticket_cb_ctx,
-+ buf, len,
-+ data.client_random,
-+ data.server_random,
-+ conn->expkey_secret);
-+ if (ret == 1) {
-+ conn->resumed = 1;
-+ return 0;
-+ }
-+ emsg(MSG_ERROR, "EAP session ticket ext not implemented");
-+ return MBEDTLS_ERR_SSL_INVALID_MAC;
-+ /*(non-zero return used for mbedtls debug logging)*/
-+ }
-+
-+ /* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
-+ int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
-+ session, buf, len);
-+ if (conn)
-+ conn->resumed = (rc == 0);
-+ return rc;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+__attribute_cold__
-+int tls_global_set_params(void *tls_ctx,
-+ const struct tls_connection_params *params)
-+{
-+ /* XXX: why might global_set_params be called more than once? */
-+ if (tls_ctx_global.tls_conf)
-+ tls_conf_deinit(tls_ctx_global.tls_conf);
-+ tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
-+ if (tls_ctx_global.tls_conf == NULL)
-+ return -1;
-+
-+ #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+ #ifdef MBEDTLS_SSL_TICKET_C
-+ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+ tls_mbedtls_ssl_ticket_write,
-+ tls_mbedtls_ssl_ticket_parse,
-+ NULL);
-+ #else
-+ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+ mbedtls_ssl_ticket_write,
-+ mbedtls_ssl_ticket_parse,
-+ &tls_ctx_global.ticket_ctx);
-+ #endif
-+ #endif
-+ #endif
-+
-+ os_free(tls_ctx_global.ocsp_stapling_response);
-+ tls_ctx_global.ocsp_stapling_response = NULL;
-+ if (params->ocsp_stapling_response)
-+ tls_ctx_global.ocsp_stapling_response =
-+ os_strdup(params->ocsp_stapling_response);
-+
-+ os_free(tls_ctx_global.ca_cert_file);
-+ tls_ctx_global.ca_cert_file = NULL;
-+ if (params->ca_cert)
-+ tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
-+ return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
-+}
-+
-+
-+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
-+{
-+ tls_ctx_global.tls_conf->check_crl = check_crl;
-+ tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
-+ return 0;
-+}
-+
-+
-+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-+ int verify_peer, unsigned int flags,
-+ const u8 *session_ctx, size_t session_ctx_len)
-+{
-+ /*(EAP server-side calls this from eap_server_tls_ssl_init())*/
-+ if (conn == NULL)
-+ return -1;
-+
-+ conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
-+
-+ int authmode;
-+ switch (verify_peer) {
-+ case 2: authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
-+ case 1: authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
-+ default: authmode = MBEDTLS_SSL_VERIFY_NONE; break;
-+ }
-+ mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
-+
-+ if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
-+ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+ else
-+ mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
-+
-+ return 0;
-+}
-+
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static void tls_connection_export_keys_cb(
-+ void *p_expkey, mbedtls_ssl_key_export_type secret_type,
-+ const unsigned char *secret, size_t secret_len,
-+ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ mbedtls_tls_prf_types tls_prf_type)
-+{
-+ struct tls_connection *conn = p_expkey;
-+ conn->tls_prf_type = tls_prf_type;
-+ if (!tls_prf_type)
-+ return;
-+ if (secret_len > sizeof(conn->expkey_secret)) {
-+ emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
-+ conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
-+ return;
-+ }
-+ conn->expkey_secret_len = secret_len;
-+ os_memcpy(conn->expkey_secret, secret, secret_len);
-+ os_memcpy(conn->expkey_randbytes,
-+ client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+ server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+}
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static int tls_connection_export_keys_cb(
-+ void *p_expkey,
-+ const unsigned char *ms,
-+ const unsigned char *kb,
-+ size_t maclen,
-+ size_t keylen,
-+ size_t ivlen,
-+ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+ mbedtls_tls_prf_types tls_prf_type )
-+{
-+ struct tls_connection *conn = p_expkey;
-+ conn->tls_prf_type = tls_prf_type;
-+ if (!tls_prf_type)
-+ return -1; /*(return value ignored by mbedtls)*/
-+ conn->expkey_keyblock_size = maclen + keylen + ivlen;
-+ conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
-+ os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
-+ os_memcpy(conn->expkey_randbytes,
-+ client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+ server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+ return 0;
-+}
-+#endif
-+
-+
-+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
-+ struct tls_random *data)
-+{
-+ if (!conn || !conn->tls_prf_type)
-+ return -1;
-+ data->client_random = conn->expkey_randbytes;
-+ data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+ data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
-+ data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+ return 0;
-+}
-+
-+
-+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-+ const char *label, const u8 *context,
-+ size_t context_len, u8 *out, size_t out_len)
-+{
-+ /* (EAP-PEAP EAP-TLS EAP-TTLS) */
-+ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+ return (conn && conn->established && conn->tls_prf_type)
-+ ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+ conn->expkey_secret, conn->expkey_secret_len, label,
-+ conn->expkey_randbytes,
-+ sizeof(conn->expkey_randbytes), out, out_len)
-+ : -1;
-+ #else
-+ /* not implemented here for mbedtls < 2.18.0 */
-+ return -1;
-+ #endif
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+/* keyblock size info is not exposed in mbed TLS 3.0.0 */
-+/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
-+#include <mbedtls/ssl_ciphersuites.h>
-+#include <mbedtls/cipher.h>
-+static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
-+{
-+ #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
-+ #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+ if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3)
-+ return 0; /* (calculation not extracted) */
-+ #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
-+
-+ int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
-+ const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
-+ mbedtls_ssl_ciphersuite_from_id(ciphersuite);
-+ if (ciphersuite_info == NULL)
-+ return 0;
-+
-+ const mbedtls_cipher_info_t *cipher_info =
-+ mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
-+ if (cipher_info == NULL)
-+ return 0;
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+ size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
-+ mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
-+ #else
-+ size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
-+ mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
-+ #endif
-+ #if defined(MBEDTLS_GCM_C) || \
-+ defined(MBEDTLS_CCM_C) || \
-+ defined(MBEDTLS_CHACHAPOLY_C)
-+ if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
-+ return keylen + 4;
-+ else if (mode == MBEDTLS_MODE_CHACHAPOLY)
-+ return keylen + 12;
-+ else
-+ #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
-+ #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
-+ {
-+ const mbedtls_md_info_t *md_info =
-+ mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
-+ if (md_info == NULL)
-+ return 0;
-+ size_t mac_key_len = mbedtls_md_get_size(md_info);
-+ size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
-+ return keylen + mac_key_len + ivlen;
-+ }
-+ #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-+ #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
-+ return 0;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
-+
-+
-+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
-+ u8 *out, size_t out_len)
-+{
-+ /* XXX: has export keys callback been run? */
-+ if (!conn || !conn->tls_prf_type)
-+ return -1;
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
-+ if (conn->expkey_keyblock_size == 0)
-+ return -1;
-+ #endif
-+ size_t skip = conn->expkey_keyblock_size * 2;
-+ unsigned char *tmp_out = os_malloc(skip + out_len);
-+ if (!tmp_out)
-+ return -1;
-+
-+ /* server_random and then client_random */
-+ unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
-+ os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+ MBEDTLS_EXPKEY_RAND_LEN);
-+ os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
-+ MBEDTLS_EXPKEY_RAND_LEN);
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+ int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+ conn->expkey_secret, conn->expkey_secret_len,
-+ "key expansion", seed, sizeof(seed),
-+ tmp_out, skip + out_len);
-+ if (ret == 0)
-+ os_memcpy(out, tmp_out + skip, out_len);
-+ #else
-+ int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
-+ #endif
-+
-+ bin_clear_free(tmp_out, skip + out_len);
-+ forced_memzero(seed, sizeof(seed));
-+ return ret;
-+}
-+
-+#endif /* TLS_MBEDTLS_EAP_FAST */
-+
-+
-+__attribute_cold__
-+static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
-+{
-+ /* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
-+ if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
-+ return;
-+ if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
-+ return;
-+ #if 0
-+ /*(info not available on client;
-+ * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
-+ if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
-+ #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+ mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
-+ #else
-+ mbedtls_ssl_get_ciphersuite_id(
-+ mbedtls_ssl_get_ciphersuite(&conn->ssl))
-+ #endif
-+ && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
-+ < 384 /*(3072/8)*/)
-+ #endif
-+ {
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (init_conf->event_cb) {
-+ union tls_event_data ev;
-+ os_memset(&ev, 0, sizeof(ev));
-+ ev.alert.is_local = 1;
-+ ev.alert.type = "fatal";
-+ /*"internal error" string for tests/hwsim/test_suiteb.py */
-+ ev.alert.description = "internal error: handshake failure";
-+ /*ev.alert.description = "insufficient security";*/
-+ init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
-+ }
-+ }
-+}
-+
-+
-+struct wpabuf * tls_connection_handshake(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data,
-+ struct wpabuf **appl_data)
-+{
-+ if (appl_data)
-+ *appl_data = NULL;
-+
-+ if (in_data && wpabuf_len(in_data)) {
-+ /*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
-+ if (conn->pull_buf && 0) /* disable; appears unwise */
-+ tls_pull_buf_discard(conn, __func__);
-+ if (!tls_pull_buf_append(conn, in_data))
-+ return NULL;
-+ }
-+
-+ if (conn->tls_conf == NULL) {
-+ struct tls_connection_params params;
-+ os_memset(¶ms, 0, sizeof(params));
-+ params.openssl_ciphers =
-+ tls_ctx_global.init_conf.openssl_ciphers;
-+ params.flags = tls_ctx_global.tls_conf->flags;
-+ if (tls_connection_set_params(tls_ctx, conn, ¶ms) != 0)
-+ return NULL;
-+ }
-+
-+ if (conn->verify_peer) /*(call here might be redundant; nbd)*/
-+ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ if (conn->clienthello_session_ticket)
-+ /*(starting handshake for EAP-FAST and EAP-TEAP)*/
-+ tls_mbedtls_clienthello_session_ticket_set(conn);
-+
-+ /* (not thread-safe due to need to set userdata 'conn' for callback) */
-+ /* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
-+ * since ticket write and parse callbacks take (mbedtls_ssl_session *)
-+ * param instead of (mbedtls_ssl_context *) param) */
-+ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+ NULL, NULL, NULL);
-+ else
-+ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+ tls_mbedtls_ssl_ticket_write,
-+ tls_mbedtls_ssl_ticket_parse,
-+ conn);
-+ #endif
-+
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+ int ret = mbedtls_ssl_handshake(&conn->ssl);
-+ #else
-+ int ret = 0;
-+ while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
-+ ret = mbedtls_ssl_handshake_step(&conn->ssl);
-+ if (ret != 0)
-+ break;
-+ }
-+ #endif
-+
-+ #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+ tls_mbedtls_ssl_ticket_write,
-+ tls_mbedtls_ssl_ticket_parse,
-+ NULL);
-+ #endif
-+
-+ switch (ret) {
-+ case 0:
-+ conn->established = 1;
-+ if (conn->push_buf == NULL)
-+ /* Need to return something to get final TLS ACK. */
-+ conn->push_buf = wpabuf_alloc(0);
-+
-+ if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
-+ *appl_data = NULL; /* RFE: check for application data */
-+ break;
-+ case MBEDTLS_ERR_SSL_WANT_WRITE:
-+ case MBEDTLS_ERR_SSL_WANT_READ:
-+ case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
-+ case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
-+ if (tls_ctx_global.tls_conf /*(is server)*/
-+ && conn->established && conn->push_buf == NULL)
-+ /* Need to return something to trigger completion of EAP-TLS. */
-+ conn->push_buf = wpabuf_alloc(0);
-+ break;
-+ default:
-+ ++conn->failed;
-+ switch (ret) {
-+ case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
-+ case MBEDTLS_ERR_NET_CONN_RESET:
-+ case MBEDTLS_ERR_NET_SEND_FAILED:
-+ ++conn->write_alerts;
-+ break;
-+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+ case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
-+ #else
-+ case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
-+ #endif
-+ tls_mbedtls_suiteb_handshake_alert(conn);
-+ /* fall through */
-+ case MBEDTLS_ERR_NET_RECV_FAILED:
-+ case MBEDTLS_ERR_SSL_CONN_EOF:
-+ case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
-+ case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
-+ ++conn->read_alerts;
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ ilog(ret, "mbedtls_ssl_handshake");
-+ break;
-+ }
-+
-+ struct wpabuf *out_data = conn->push_buf;
-+ conn->push_buf = NULL;
-+ return out_data;
-+}
-+
-+
-+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data,
-+ struct wpabuf **appl_data)
-+{
-+ conn->is_server = 1;
-+ return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
-+}
-+
-+
-+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data)
-+{
-+ int res = mbedtls_ssl_write(&conn->ssl,
-+ wpabuf_head_u8(in_data), wpabuf_len(in_data));
-+ if (res < 0) {
-+ elog(res, "mbedtls_ssl_write");
-+ return NULL;
-+ }
-+
-+ struct wpabuf *buf = conn->push_buf;
-+ conn->push_buf = NULL;
-+ return buf;
-+}
-+
-+
-+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-+ struct tls_connection *conn,
-+ const struct wpabuf *in_data)
-+{
-+ int res;
-+ struct wpabuf *out;
-+
-+ /*assert(in_data != NULL);*/
-+ if (!tls_pull_buf_append(conn, in_data))
-+ return NULL;
-+
-+ #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
-+ /* Add extra buffer space to handle the possibility of decrypted
-+ * data being longer than input data due to TLS compression. */
-+ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-+ #else /* TLS compression is disabled in mbedtls 3.x */
-+ out = wpabuf_alloc(wpabuf_len(in_data));
-+ #endif
-+ if (out == NULL)
-+ return NULL;
-+
-+ res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
-+ if (res < 0) {
-+ #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
-+ if (res == MBEDTLS_ERR_SSL_WANT_READ)
-+ return out;
-+ #endif
-+ elog(res, "mbedtls_ssl_read");
-+ wpabuf_free(out);
-+ return NULL;
-+ }
-+ wpabuf_put(out, res);
-+
-+ return out;
-+}
-+
-+
-+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
-+{
-+ /* XXX: might need to detect if session resumed from TLS session ticket
-+ * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
-+ /* (?ssl->handshake->resume during session ticket validation?) */
-+ return conn && conn->resumed;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-+ u8 *ciphers)
-+{
-+ /* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
-+ int ids[7];
-+ const int idsz = (int)sizeof(ids);
-+ int nids = -1, id;
-+ for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
-+ switch (*ciphers) {
-+ case TLS_CIPHER_RC4_SHA:
-+ #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
-+ id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
-+ break;
-+ #else
-+ continue; /*(not supported in mbedtls 3.x; ignore)*/
-+ #endif
-+ case TLS_CIPHER_AES128_SHA:
-+ id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
-+ break;
-+ case TLS_CIPHER_RSA_DHE_AES128_SHA:
-+ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-+ break;
-+ case TLS_CIPHER_ANON_DH_AES128_SHA:
-+ continue; /*(not supported in mbedtls; ignore)*/
-+ case TLS_CIPHER_RSA_DHE_AES256_SHA:
-+ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-+ break;
-+ case TLS_CIPHER_AES256_SHA:
-+ id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
-+ break;
-+ default:
-+ return -1; /* should not happen */
-+ }
-+ if (++nids == idsz)
-+ return -1; /* should not happen */
-+ ids[nids] = id;
-+ }
-+ if (nids < 0)
-+ return 0; /* nothing to do */
-+ if (++nids == idsz)
-+ return -1; /* should not happen */
-+ ids[nids] = 0; /* terminate list */
-+ ++nids;
-+
-+ return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
-+}
-+#endif
-+
-+
-+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
-+ char *buf, size_t buflen)
-+{
-+ if (conn == NULL)
-+ return -1;
-+ os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
-+ return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
-+{
-+ if (conn == NULL)
-+ return 0;
-+ return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+}
-+#endif
-+
-+
-+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
-+ char *buf, size_t buflen)
-+{
-+ if (conn == NULL)
-+ return -1;
-+ const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+ return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+int tls_connection_enable_workaround(void *tls_ctx,
-+ struct tls_connection *conn)
-+{
-+ /* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
-+ /* XXX: is there a relevant setting for this in mbed TLS? */
-+ /* (do we even care that much about older CBC ciphers?) */
-+ return 0;
-+}
-+
-+
-+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
-+ int ext_type, const u8 *data,
-+ size_t data_len)
-+{
-+ /* (EAP-FAST and EAP-TEAP) */
-+ if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
-+ return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
-+ data_len);
-+
-+ return -1;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
-+{
-+ return conn ? conn->failed : -1;
-+}
-+
-+
-+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
-+{
-+ return conn ? conn->read_alerts : -1;
-+}
-+
-+
-+int tls_connection_get_write_alerts(void *tls_ctx,
-+ struct tls_connection *conn)
-+{
-+ return conn ? conn->write_alerts : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+int tls_connection_set_session_ticket_cb(
-+ void *tls_ctx, struct tls_connection *conn,
-+ tls_session_ticket_cb cb, void *ctx)
-+{
-+ if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
-+ /* (EAP-FAST and EAP-TEAP) */
-+ conn->session_ticket_cb = cb;
-+ conn->session_ticket_cb_ctx = ctx;
-+ return 0;
-+ }
-+ return -1;
-+}
-+#endif
-+
-+
-+int tls_get_library_version(char *buf, size_t buf_len)
-+{
-+ #ifndef MBEDTLS_VERSION_C
-+ const char * const ver = "n/a";
-+ #else
-+ char ver[9];
-+ mbedtls_version_get_string(ver);
-+ #endif
-+ return os_snprintf(buf, buf_len,
-+ "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
-+}
-+
-+
-+void tls_connection_set_success_data(struct tls_connection *conn,
-+ struct wpabuf *data)
-+{
-+ wpabuf_free(conn->success_data);
-+ conn->success_data = data;
-+}
-+
-+
-+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
-+{
-+}
-+
-+
-+const struct wpabuf *
-+tls_connection_get_success_data(struct tls_connection *conn)
-+{
-+ return conn->success_data;
-+}
-+
-+
-+void tls_connection_remove_session(struct tls_connection *conn)
-+{
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
-+{
-+ #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
-+ /* data from TLS handshake Finished message */
-+ size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
-+ char *verify_data = (conn->is_server ^ conn->resumed)
-+ ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
-+ : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
-+ if (verify_len && verify_len <= max_len) {
-+ os_memcpy(buf, verify_data, verify_len);
-+ return (int)verify_len;
-+ }
-+ #endif
-+ return -1;
-+}
-+#endif
-+
-+
-+__attribute_noinline__
-+static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
-+{
-+ if (conn->peer_subject)
-+ return;
-+ char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+ int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
-+ if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
-+ os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
-+{
-+ if (!conn)
-+ return NULL;
-+ if (!conn->peer_subject) { /*(if not set during cert verify)*/
-+ const mbedtls_x509_crt *peer_cert =
-+ mbedtls_ssl_get_peer_cert(&conn->ssl);
-+ if (peer_cert)
-+ tls_mbedtls_set_peer_subject(conn, peer_cert);
-+ }
-+ return conn->peer_subject;
-+}
-+#endif
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
-+{
-+ /* XXX: availability of cert does not necessary mean that client
-+ * received certificate request from server and then sent cert.
-+ * ? step handshake in tls_connection_handshake() looking for
-+ * MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
-+ const struct tls_conf * const tls_conf = conn->tls_conf;
-+ return (tls_conf->has_client_cert && tls_conf->has_private_key);
-+}
-+#endif
-+
-+
-+#if defined(CONFIG_FIPS)
-+#define TLS_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if defined(CONFIG_SHA256)
-+#define TLS_MBEDTLS_TLS_PRF_SHA256
-+#endif
-+
-+#if defined(CONFIG_SHA384)
-+#define TLS_MBEDTLS_TLS_PRF_SHA384
-+#endif
-+
-+
-+#ifndef TLS_MBEDTLS_CONFIG_FIPS
-+#if defined(CONFIG_MODULE_TESTS)
-+/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
-+ && MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+/* sha1-tlsprf.c */
-+#include "sha1.h"
-+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
-+ secret, secret_len, label,
-+ seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha1-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
-+/* sha256-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha256.h"
-+int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
-+ secret, secret_len, label,
-+ seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha256-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
-+/* sha384-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha384.h"
-+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
-+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
-+ secret, secret_len, label,
-+ seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha384-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
-+ ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
-+#endif
-+
-+struct mlist { const char *p; size_t n; };
-+
-+
-+static int
-+tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
-+{
-+ /* RFE: this could be pre-parsed into structured data at config time */
-+ struct mlist list[256]; /*(much larger than expected)*/
-+ int nlist = 0;
-+ if ( os_strncmp(match, "EMAIL:", 6) != 0
-+ && os_strncmp(match, "DNS:", 4) != 0
-+ && os_strncmp(match, "URI:", 4) != 0 ) {
-+ wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
-+ return 0;
-+ }
-+ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+ do { } while ((tok = os_strchr(s, ';'))
-+ && os_strncmp(tok+1, "EMAIL:", 6) != 0
-+ && os_strncmp(tok+1, "DNS:", 4) != 0
-+ && os_strncmp(tok+1, "URI:", 4) != 0);
-+ list[nlist].p = s;
-+ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+ wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
-+ match);
-+ break; /* truncate huge list and continue */
-+ }
-+ }
-+
-+ if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+ return 0;
-+
-+ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+ for (; cur != NULL; cur = cur->next) {
-+ const unsigned char san_type = (unsigned char)cur->buf.tag
-+ & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+ char t;
-+ size_t step = 4;
-+ switch (san_type) { /* "EMAIL:" or "DNS:" or "URI:" */
-+ case MBEDTLS_X509_SAN_RFC822_NAME: step = 6; t = 'E'; break;
-+ case MBEDTLS_X509_SAN_DNS_NAME: t = 'D'; break;
-+ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
-+ default: continue;
-+ }
-+
-+ for (int i = 0; i < nlist; ++i) {
-+ /* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
-+ /* Note: v is not '\0'-terminated, but is a known length vlen,
-+ * so okay to pass to os_strncasecmp() even though not z-string */
-+ if (cur->buf.len == list[i].n - step && t == *list[i].p
-+ && 0 == os_strncasecmp((char *)cur->buf.p,
-+ list[i].p+step, cur->buf.len)) {
-+ return 1; /* match */
-+ }
-+ }
-+ }
-+ return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffix(const char *v, size_t vlen,
-+ const struct mlist *list, int nlist, int full)
-+{
-+ /* Note: v is not '\0'-terminated, but is a known length vlen,
-+ * so okay to pass to os_strncasecmp() even though not z-string */
-+ for (int i = 0; i < nlist; ++i) {
-+ size_t n = list[i].n;
-+ if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
-+ && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
-+ return 1; /* match */
-+ }
-+ return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
-+{
-+ /* RFE: this could be pre-parsed into structured data at config time */
-+ struct mlist list[256]; /*(much larger than expected)*/
-+ int nlist = 0;
-+ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+ tok = os_strchr(s, ';');
-+ list[nlist].p = s;
-+ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+ wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
-+ break; /* truncate huge list and continue */
-+ }
-+ }
-+
-+ /* check subjectAltNames */
-+ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
-+ const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+ for (; cur != NULL; cur = cur->next) {
-+ const unsigned char san_type = (unsigned char)cur->buf.tag
-+ & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+ if (san_type == MBEDTLS_X509_SAN_DNS_NAME
-+ && tls_mbedtls_match_suffix((char *)cur->buf.p,
-+ cur->buf.len,
-+ list, nlist, full)) {
-+ return 1; /* match */
-+ }
-+ }
-+ }
-+
-+ /* check subject CN */
-+ const mbedtls_x509_name *name = &crt->subject;
-+ for (; name != NULL; name = name->next) {
-+ if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
-+ break;
-+ }
-+ if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
-+ list, nlist, full)) {
-+ return 1; /* match */
-+ }
-+
-+ return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
-+{
-+ /* RFE: this could be pre-parsed into structured data at config time */
-+ struct mlistoid { const char *p; size_t n;
-+ const char *oid; size_t olen;
-+ int prefix; };
-+ struct mlistoid list[32]; /*(much larger than expected)*/
-+ int nlist = 0;
-+ for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
-+ tok = os_strchr(s, '/');
-+ list[nlist].oid = NULL;
-+ list[nlist].olen = 0;
-+ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+ e = memchr(s, '=', list[nlist].n);
-+ if (e == NULL) {
-+ if (list[nlist].n == 0)
-+ continue; /* skip consecutive, repeated '/' */
-+ if (list[nlist].n == 1 && *s == '*') {
-+ /* special-case "*" to match any OID and value */
-+ s = e = "=*";
-+ list[nlist].n = 2;
-+ list[nlist].oid = "";
-+ }
-+ else {
-+ wpa_printf(MSG_INFO,
-+ "MTLS: invalid check_cert_subject '%s' missing '='",
-+ match);
-+ return 0;
-+ }
-+ }
-+ switch (e - s) {
-+ case 1:
-+ if (*s == 'C') {
-+ list[nlist].oid = MBEDTLS_OID_AT_COUNTRY;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
-+ }
-+ else if (*s == 'L') {
-+ list[nlist].oid = MBEDTLS_OID_AT_LOCALITY;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
-+ }
-+ else if (*s == 'O') {
-+ list[nlist].oid = MBEDTLS_OID_AT_ORGANIZATION;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
-+ }
-+ break;
-+ case 2:
-+ if (s[0] == 'C' && s[1] == 'N') {
-+ list[nlist].oid = MBEDTLS_OID_AT_CN;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
-+ }
-+ else if (s[0] == 'S' && s[1] == 'T') {
-+ list[nlist].oid = MBEDTLS_OID_AT_STATE;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
-+ }
-+ else if (s[0] == 'O' && s[1] == 'U') {
-+ list[nlist].oid = MBEDTLS_OID_AT_ORG_UNIT;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
-+ }
-+ break;
-+ case 12:
-+ if (os_memcmp(s, "emailAddress", 12) == 0) {
-+ list[nlist].oid = MBEDTLS_OID_PKCS9_EMAIL;
-+ list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
-+ }
-+ break;
-+ default:
-+ break;
-+ }
-+ if (list[nlist].oid == NULL) {
-+ wpa_printf(MSG_INFO,
-+ "MTLS: Unknown field in check_cert_subject '%s'",
-+ match);
-+ return 0;
-+ }
-+ list[nlist].n -= (size_t)(++e - s);
-+ list[nlist].p = e;
-+ if (list[nlist].n && e[list[nlist].n-1] == '*') {
-+ --list[nlist].n;
-+ list[nlist].prefix = 1;
-+ }
-+ /*(could easily add support for suffix matches if value begins with '*',
-+ * but suffix match is not currently supported by other TLS modules)*/
-+
-+ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+ wpa_printf(MSG_INFO,
-+ "MTLS: excessive check_cert_subject match '%s'",
-+ match);
-+ break; /* truncate huge list and continue */
-+ }
-+ }
-+
-+ /* each component in match string must match cert Subject in order listed
-+ * The behavior below preserves ordering but is slightly different than
-+ * the grossly inefficient contortions implemented in tls_openssl.c */
-+ const mbedtls_x509_name *name = &crt->subject;
-+ for (int i = 0; i < nlist; ++i) {
-+ int found = 0;
-+ for (; name != NULL && !found; name = name->next) {
-+ if (!name->oid.p)
-+ continue;
-+ /* special-case "*" to match any OID and value */
-+ if (list[i].olen == 0) {
-+ found = 1;
-+ continue;
-+ }
-+ /* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
-+ if (list[i].olen != name->oid.len
-+ || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
-+ continue;
-+ /* Note: v is not '\0'-terminated, but is a known length vlen,
-+ * so okay to pass to os_strncasecmp() even though not z-string */
-+ if ((list[i].prefix
-+ ? list[i].n <= name->val.len /* prefix match */
-+ : list[i].n == name->val.len) /* full match */
-+ && 0 == os_strncasecmp((char *)name->val.p,
-+ list[i].p, list[i].n)) {
-+ found = 1;
-+ continue;
-+ }
-+ }
-+ if (!found)
-+ return 0; /* no match */
-+ }
-+ return 1; /* match */
-+}
-+
-+
-+__attribute_cold__
-+static void
-+tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
-+ const char *errmsg, enum tls_fail_reason reason)
-+{
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (init_conf->event_cb == NULL)
-+ return;
-+
-+ struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+ subject[0] = '\0';
-+ union tls_event_data ev;
-+ os_memset(&ev, 0, sizeof(ev));
-+ ev.cert_fail.reason = reason;
-+ ev.cert_fail.depth = depth;
-+ ev.cert_fail.subject = subject;
-+ ev.cert_fail.reason_txt = errmsg;
-+ ev.cert_fail.cert = certbuf;
-+
-+ init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
-+
-+ wpabuf_free(certbuf);
-+}
-+
-+
-+__attribute_noinline__
-+static void
-+tls_mbedtls_verify_cert_event (struct tls_connection *conn,
-+ mbedtls_x509_crt *crt, int depth)
-+{
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (init_conf->event_cb == NULL)
-+ return;
-+
-+ struct wpabuf *certbuf = NULL;
-+ union tls_event_data ev;
-+ os_memset(&ev, 0, sizeof(ev));
-+
-+ #ifdef MBEDTLS_SHA256_C
-+ u8 hash[SHA256_DIGEST_LENGTH];
-+ const u8 *addr[] = { (u8 *)crt->raw.p };
-+ if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
-+ ev.peer_cert.hash = hash;
-+ ev.peer_cert.hash_len = sizeof(hash);
-+ }
-+ #endif
-+ ev.peer_cert.depth = depth;
-+ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+ if (depth == 0)
-+ ev.peer_cert.subject = conn->peer_subject;
-+ if (ev.peer_cert.subject == NULL) {
-+ ev.peer_cert.subject = subject;
-+ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+ subject[0] = '\0';
-+ }
-+
-+ char serial_num[128+1];
-+ ev.peer_cert.serial_num =
-+ tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
-+
-+ const mbedtls_x509_sequence *cur;
-+
-+ cur = NULL;
-+ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+ cur = &crt->subject_alt_names;
-+ for (; cur != NULL; cur = cur->next) {
-+ const unsigned char san_type = (unsigned char)cur->buf.tag
-+ & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+ size_t prelen = 4;
-+ const char *pre;
-+ switch (san_type) {
-+ case MBEDTLS_X509_SAN_RFC822_NAME: prelen = 6; pre = "EMAIL:";break;
-+ case MBEDTLS_X509_SAN_DNS_NAME: pre = "DNS:"; break;
-+ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:"; break;
-+ default: continue;
-+ }
-+
-+ char *pos = os_malloc(prelen + cur->buf.len + 1);
-+ if (pos == NULL)
-+ break;
-+ ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
-+ os_memcpy(pos, pre, prelen);
-+ /* data should be properly backslash-escaped if needed,
-+ * so code below does not re-escape, but does replace CTLs */
-+ /*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
-+ /*pos[prelen+cur->buf.len] = '\0';*/
-+ pos += prelen;
-+ for (size_t i = 0; i < cur->buf.len; ++i) {
-+ unsigned char c = cur->buf.p[i];
-+ *pos++ = (c >= 32 && c != 127) ? c : '?';
-+ }
-+ *pos = '\0';
-+
-+ if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
-+ break;
-+ }
-+
-+ cur = NULL;
-+ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
-+ cur = &crt->certificate_policies;
-+ for (; cur != NULL; cur = cur->next) {
-+ if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
-+ continue;
-+ /* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
-+ /* TOD-TOFU "1.3.6.1.4.1.40808.1.3.2" */
-+ #define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
-+ #define OID_TOD_TOFU "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
-+ if (os_memcmp(cur->buf.p,
-+ OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
-+ ev.peer_cert.tod = 1; /* TOD-STRICT */
-+ break;
-+ }
-+ if (os_memcmp(cur->buf.p,
-+ OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
-+ ev.peer_cert.tod = 2; /* TOD-TOFU */
-+ break;
-+ }
-+ }
-+
-+ struct tls_conf *tls_conf = conn->tls_conf;
-+ if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
-+ || init_conf->cert_in_cb) {
-+ certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+ ev.peer_cert.cert = certbuf;
-+ }
-+
-+ init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
-+
-+ wpabuf_free(certbuf);
-+ char **altsubject;
-+ *(const char ***)&altsubject = ev.peer_cert.altsubject;
-+ for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
-+ os_free(altsubject[i]);
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
-+{
-+ /* XXX: N.B. verify code not carefully tested besides hwsim tests
-+ *
-+ * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
-+ * RFE: review and add support for additional TLS_CONN_* flags
-+ * not handling OCSP (not available in mbedtls)
-+ * ... */
-+
-+ struct tls_connection *conn = (struct tls_connection *)arg;
-+ struct tls_conf *tls_conf = conn->tls_conf;
-+ uint32_t flags_in = *flags;
-+
-+ if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
-+ emsg(MSG_WARNING, "client cert chain too long");
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "client cert chain too long",
-+ TLS_FAIL_BAD_CERTIFICATE);
-+ }
-+ else if (tls_conf->verify_depth0_only) {
-+ if (depth > 0)
-+ *flags = 0;
-+ else {
-+ #ifdef MBEDTLS_SHA256_C
-+ u8 hash[SHA256_DIGEST_LENGTH];
-+ const u8 *addr[] = { (u8 *)crt->raw.p };
-+ if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
-+ || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
-+ *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "cert hash mismatch",
-+ TLS_FAIL_UNTRUSTED);
-+ }
-+ else /* hash matches; ignore other issues *except* if revoked)*/
-+ *flags &= MBEDTLS_X509_BADCERT_REVOKED;
-+ #endif
-+ }
-+ }
-+ else if (depth == 0) {
-+ if (!conn->peer_subject)
-+ tls_mbedtls_set_peer_subject(conn, crt);
-+ /*(use same labels to tls_mbedtls_verify_fail_event() as used in
-+ * other TLS modules so that hwsim tests find exact string match)*/
-+ if (!conn->peer_subject) { /* error copying subject string */
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "internal error",
-+ TLS_FAIL_UNSPECIFIED);
-+ }
-+ /*(use os_strstr() for subject match as is done in tls_mbedtls.c
-+ * to follow the same behavior, even though a suffix match would
-+ * make more sense. Also, note that strstr match does not
-+ * normalize whitespace (between components) for comparison)*/
-+ else if (tls_conf->subject_match
-+ && os_strstr(conn->peer_subject,
-+ tls_conf->subject_match) == NULL) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: Subject '%s' did not match with '%s'",
-+ conn->peer_subject, tls_conf->subject_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Subject mismatch",
-+ TLS_FAIL_SUBJECT_MISMATCH);
-+ }
-+ if (tls_conf->altsubject_match
-+ && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: altSubjectName match '%s' not found",
-+ tls_conf->altsubject_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "AltSubject mismatch",
-+ TLS_FAIL_ALTSUBJECT_MISMATCH);
-+ }
-+ if (tls_conf->suffix_match
-+ && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: Domain suffix match '%s' not found",
-+ tls_conf->suffix_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Domain suffix mismatch",
-+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
-+ }
-+ if (tls_conf->domain_match
-+ && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
-+ wpa_printf(MSG_WARNING,
-+ "MTLS: Domain match '%s' not found",
-+ tls_conf->domain_match);
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Domain mismatch",
-+ TLS_FAIL_DOMAIN_MISMATCH);
-+ }
-+ if (tls_conf->check_cert_subject
-+ && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
-+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Distinguished Name",
-+ TLS_FAIL_DN_MISMATCH);
-+ }
-+ if (tls_conf->flags & TLS_CONN_SUITEB) {
-+ /* check RSA modulus size (public key bitlen) */
-+ const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
-+ if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
-+ && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
-+ /* hwsim suite_b RSA tests expect 3072
-+ * suite_b_192_rsa_ecdhe_radius_rsa2048_client
-+ * suite_b_192_rsa_dhe_radius_rsa2048_client */
-+ *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "Insufficient RSA modulus size",
-+ TLS_FAIL_INSUFFICIENT_KEY_LEN);
-+ }
-+ }
-+ if (tls_conf->check_crl && tls_conf->crl == NULL) {
-+ /* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
-+ emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "check_crl set but no CRL loaded; "
-+ "reject all?",
-+ TLS_FAIL_BAD_CERTIFICATE);
-+ }
-+ }
-+ else {
-+ if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
-+ *flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
-+ }
-+
-+ if (!tls_conf->check_crl_strict) {
-+ *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
-+ *flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
-+ }
-+
-+ if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
-+ *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
-+ *flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
-+ }
-+
-+ tls_mbedtls_verify_cert_event(conn, crt, depth);
-+
-+ if (*flags) {
-+ if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
-+ |MBEDTLS_X509_BADCERT_CN_MISMATCH
-+ |MBEDTLS_X509_BADCERT_REVOKED)) {
-+ emsg(MSG_WARNING, "client cert not trusted");
-+ }
-+ /* report event if flags set but no additional flags set above */
-+ /* (could translate flags to more detailed TLS_FAIL_* if needed) */
-+ if (!(*flags & ~flags_in)) {
-+ enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
-+ const char *errmsg = "cert verify fail unspecified";
-+ if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
-+ reason = TLS_FAIL_UNTRUSTED;
-+ errmsg = "certificate not trusted";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
-+ reason = TLS_FAIL_REVOKED;
-+ errmsg = "certificate has been revoked";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
-+ reason = TLS_FAIL_NOT_YET_VALID;
-+ errmsg = "certificate not yet valid";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
-+ reason = TLS_FAIL_EXPIRED;
-+ errmsg = "certificate has expired";
-+ }
-+ if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
-+ reason = TLS_FAIL_BAD_CERTIFICATE;
-+ errmsg = "certificate uses insecure algorithm";
-+ }
-+ tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
-+ }
-+ #if 0
-+ /* ??? send (again) cert events for all certs in chain ???
-+ * (should already have been called for greater depths) */
-+ /* tls_openssl.c:tls_verify_cb() sends cert events for all certs
-+ * in chain if certificate validation fails, but sends all events
-+ * with depth set to 0 (might be a bug) */
-+ if (depth > 0) {
-+ int pdepth = depth + 1;
-+ for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
-+ tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
-+ }
-+ }
-+ #endif
-+ /*(do not preserve subject if verification failed but was optional)*/
-+ if (depth == 0 && conn->peer_subject) {
-+ os_free(conn->peer_subject);
-+ conn->peer_subject = NULL;
-+ }
-+ }
-+ else if (depth == 0) {
-+ struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+ if (tls_conf->ca_cert_probe) {
-+ /* reject server certificate on probe-only run */
-+ *flags |= MBEDTLS_X509_BADCERT_OTHER;
-+ tls_mbedtls_verify_fail_event(crt, depth,
-+ "server chain probe",
-+ TLS_FAIL_SERVER_CHAIN_PROBE);
-+ }
-+ else if (init_conf->event_cb) {
-+ /* ??? send event as soon as depth == 0 is verified ???
-+ * What about rest of chain?
-+ * Follows tls_mbedtls.c behavior: */
-+ init_conf->event_cb(init_conf->cb_ctx,
-+ TLS_CERT_CHAIN_SUCCESS, NULL);
-+ }
-+ }
-+
-+ return 0;
-+}
---- /dev/null
-+++ b/tests/build/build-wpa_supplicant-mbedtls.config
-@@ -0,0 +1,24 @@
-+CONFIG_TLS=mbedtls
-+
-+CONFIG_WPS=y
-+CONFIG_EAP_TLS=y
-+CONFIG_EAP_MSCHAPV2=y
-+
-+CONFIG_EAP_PSK=y
-+CONFIG_EAP_GPSK=y
-+CONFIG_EAP_AKA=y
-+CONFIG_EAP_SIM=y
-+CONFIG_EAP_SAKE=y
-+CONFIG_EAP_PAX=y
-+CONFIG_EAP_FAST=y
-+CONFIG_EAP_IKEV2=y
-+
-+CONFIG_SAE=y
-+CONFIG_FILS=y
-+CONFIG_FILS_SK_PFS=y
-+CONFIG_OWE=y
-+CONFIG_DPP=y
-+CONFIG_SUITEB=y
-+CONFIG_SUITEB192=y
-+
-+CFLAGS += -Werror
---- a/tests/hwsim/example-hostapd.config
-+++ b/tests/hwsim/example-hostapd.config
-@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
- CONFIG_DRIVER_NL80211=y
- CONFIG_RSN_PREAUTH=y
-
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -39,6 +40,9 @@ endif
- ifeq ($(CONFIG_TLS), wolfssl)
- CONFIG_EAP_PWD=y
- endif
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CONFIG_EAP_PWD=y
-+endif
- CONFIG_EAP_EKE=y
- CONFIG_PKCS12=y
- CONFIG_RADIUS_SERVER=y
---- a/tests/hwsim/example-wpa_supplicant.config
-+++ b/tests/hwsim/example-wpa_supplicant.config
-@@ -2,6 +2,7 @@
-
- CONFIG_TLS=openssl
- #CONFIG_TLS=wolfssl
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -41,6 +42,9 @@ endif
- ifeq ($(CONFIG_TLS), wolfssl)
- CONFIG_EAP_PWD=y
- endif
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CONFIG_EAP_PWD=y
-+endif
-
- CONFIG_USIM_SIMULATOR=y
- CONFIG_SIM_SIMULATOR=y
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -1163,6 +1163,29 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
-
-+ifeq ($(CONFIG_TLS), mbedtls)
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls -lmbedx509
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifdef NEED_FIPS186_2_PRF
-+OBJS += ../src/crypto/fips_prf_internal.o
-+SHA1OBJS += ../src/crypto/sha1-internal.o
-+endif
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+LIBS += -lmbedcrypto
-+LIBS_p += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
-@@ -1355,9 +1378,11 @@ endif
-
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_INTERNAL_AES_WRAP=y
- endif
- endif
-+endif
- ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
- # Seems to be needed at least with BoringSSL
- NEED_INTERNAL_AES_WRAP=y
-@@ -1371,9 +1396,11 @@ endif
-
- ifdef NEED_INTERNAL_AES_WRAP
- ifneq ($(CONFIG_TLS), linux)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -1383,35 +1410,45 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_WRAP
- NEED_AES_ENC=y
- ifdef NEED_INTERNAL_AES_WRAP
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_ENC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1426,12 +1463,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -1443,29 +1484,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
- else
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
-
- ifndef CONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- MD5OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
- MD5OBJS += ../src/crypto/md5-internal.o
-@@ -1520,12 +1569,17 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- SHA256OBJS += ../src/crypto/sha256-internal.o
- endif
-@@ -1538,50 +1592,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
- SHA256OBJS += ../src/crypto/sha512-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
- CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- OBJS += $(SHA256OBJS)
- ifdef NEED_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA512
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
-
- ifdef NEED_ASN1
- OBJS += ../src/tls/asn1.o
-@@ -1756,10 +1828,12 @@ ifdef CONFIG_FIPS
- CFLAGS += -DCONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
- endif
- endif
- endif
-+endif
-
- OBJS += $(SHA1OBJS) $(DESOBJS)
-
---- a/wpa_supplicant/defconfig
-+++ b/wpa_supplicant/defconfig
-@@ -10,8 +10,8 @@
- # to override previous values of the variables.
-
-
--# Uncomment following two lines and fix the paths if you have installed OpenSSL
--# or GnuTLS in non-default location
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
- #CFLAGS += -I/usr/local/openssl/include
- #LIBS += -L/usr/local/openssl/lib
-
-@@ -20,6 +20,7 @@
- # used to fix build issues on such systems (krb5.h not found).
- #CFLAGS += -I/usr/include/kerberos
-
-+
- # Driver interface for generic Linux wireless extensions
- # Note: WEXT is deprecated in the current Linux kernel version and no new
- # functionality is added to it. nl80211-based interface is the new
-@@ -326,6 +327,7 @@ CONFIG_BACKEND=file
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/120-mbedtls-fips186_2_prf.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/120-mbedtls-fips186_2_prf.patch
deleted file mode 100644
index a487252..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/120-mbedtls-fips186_2_prf.patch
+++ /dev/null
@@ -1,114 +0,0 @@
-From c8dba4bd750269bcc80fed3d546e2077cb4cdf0e Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 19 Jul 2022 20:02:21 -0400
-Subject: [PATCH 2/7] mbedtls: fips186_2_prf()
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- hostapd/Makefile | 4 ---
- src/crypto/crypto_mbedtls.c | 60 +++++++++++++++++++++++++++++++++++++
- wpa_supplicant/Makefile | 4 ---
- 3 files changed, 60 insertions(+), 8 deletions(-)
-
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -759,10 +759,6 @@ endif
- OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
--ifdef NEED_FIPS186_2_PRF
--OBJS += ../src/crypto/fips_prf_internal.o
--SHA1OBJS += ../src/crypto/sha1-internal.o
--endif
- ifeq ($(CONFIG_CRYPTO), mbedtls)
- ifdef CONFIG_DPP
- LIBS += -lmbedx509
---- a/src/crypto/crypto_mbedtls.c
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -132,6 +132,12 @@
- #define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
- #endif
-
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
-+/* EAP_SIM=y EAP_AKA=y */
-+#define CRYPTO_MBEDTLS_FIPS186_2_PRF
-+#endif
-+
- #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
- || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
- #define CRYPTO_MBEDTLS_SHA1_T_PRF
-@@ -813,6 +819,60 @@ int sha1_t_prf(const u8 *key, size_t key
-
- #endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
-
-+#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
-+
-+/* fips_prf_internal.c sha1-internal.c */
-+
-+/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
-+ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
-+ * where xlen is 160 */
-+
-+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-+{
-+ /* FIPS 186-2 + change notice 1 */
-+
-+ mbedtls_sha1_context ctx;
-+ u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
-+ u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
-+ const u32 xstate_init[] =
-+ { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
-+
-+ mbedtls_sha1_init(&ctx);
-+ os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
-+
-+ /* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
-+ for (; xlen >= 20; xlen -= 20) {
-+ /* XSEED_j = 0 */
-+ /* XVAL = (XKEY + XSEED_j) mod 2^b */
-+
-+ /* w_i = G(t, XVAL) */
-+ os_memcpy(xstate, xstate_init, sizeof(xstate_init));
-+ mbedtls_internal_sha1_process(&ctx, xkey);
-+
-+ #if __BYTE_ORDER == __LITTLE_ENDIAN
-+ xstate[0] = host_to_be32(xstate[0]);
-+ xstate[1] = host_to_be32(xstate[1]);
-+ xstate[2] = host_to_be32(xstate[2]);
-+ xstate[3] = host_to_be32(xstate[3]);
-+ xstate[4] = host_to_be32(xstate[4]);
-+ #endif
-+ os_memcpy(x, xstate, 20);
-+ if (xlen == 20) /*(done; skip prep for next loop)*/
-+ break;
-+
-+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
-+ for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
-+ xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
-+ x += 20;
-+ /* x_j = w_0|w_1 (each pair of iterations through loop)*/
-+ }
-+
-+ mbedtls_sha1_free(&ctx);
-+ return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
-+
- #endif /* MBEDTLS_SHA1_C */
-
-
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -1174,10 +1174,6 @@ endif
- OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
- OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
--ifdef NEED_FIPS186_2_PRF
--OBJS += ../src/crypto/fips_prf_internal.o
--SHA1OBJS += ../src/crypto/sha1-internal.o
--endif
- ifeq ($(CONFIG_CRYPTO), mbedtls)
- LIBS += -lmbedcrypto
- LIBS_p += -lmbedcrypto
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch
deleted file mode 100644
index ae7620b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch
+++ /dev/null
@@ -1,421 +0,0 @@
-From 31bd19e0e0254b910cccfd3ddc6a6a9222bbcfc0 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Sun, 9 Oct 2022 05:12:17 -0400
-Subject: [PATCH 3/7] mbedtls: annotate with TEST_FAIL() for hwsim tests
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- src/crypto/crypto_mbedtls.c | 124 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 124 insertions(+)
-
---- a/src/crypto/crypto_mbedtls.c
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -280,6 +280,9 @@ __attribute_noinline__
- static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
- u8 *mac, mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_md_context_t ctx;
- mbedtls_md_init(&ctx);
- if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
-@@ -343,6 +346,9 @@ __attribute_noinline__
- static int sha384_512_vector(size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac, int is384)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_sha512_context ctx;
- mbedtls_sha512_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -375,6 +381,9 @@ int sha384_vector(size_t num_elem, const
- #include <mbedtls/sha256.h>
- int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_sha256_context ctx;
- mbedtls_sha256_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -397,6 +406,9 @@ int sha256_vector(size_t num_elem, const
- #include <mbedtls/sha1.h>
- int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_sha1_context ctx;
- mbedtls_sha1_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -419,6 +431,9 @@ int sha1_vector(size_t num_elem, const u
- #include <mbedtls/md5.h>
- int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_md5_context ctx;
- mbedtls_md5_init(&ctx);
- #if MBEDTLS_VERSION_MAJOR >= 3
-@@ -441,6 +456,9 @@ int md5_vector(size_t num_elem, const u8
- #include <mbedtls/md4.h>
- int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- struct mbedtls_md4_context ctx;
- mbedtls_md4_init(&ctx);
- mbedtls_md4_starts_ret(&ctx);
-@@ -460,6 +478,9 @@ static int hmac_vector(const u8 *key, si
- const u8 *addr[], const size_t *len, u8 *mac,
- mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_md_context_t ctx;
- mbedtls_md_init(&ctx);
- if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
-@@ -571,6 +592,9 @@ static int hmac_kdf_expand(const u8 *prk
- const char *label, const u8 *info, size_t info_len,
- u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
- #ifdef MBEDTLS_HKDF_C
- if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */
-@@ -663,6 +687,9 @@ static int hmac_prf_bits(const u8 *key,
- const u8 *data, size_t data_len, u8 *buf,
- size_t buf_len_bits, mbedtls_md_type_t md_type)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_md_context_t ctx;
- mbedtls_md_init(&ctx);
- const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-@@ -938,6 +965,9 @@ int pbkdf2_sha1(const char *passphrase,
-
- static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
- if (!aes)
- return NULL;
-@@ -996,6 +1026,9 @@ void aes_decrypt_deinit(void *ctx)
- /* aes-wrap.c */
- int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_nist_kw_context ctx;
- mbedtls_nist_kw_init(&ctx);
- size_t olen;
-@@ -1010,6 +1043,9 @@ int aes_wrap(const u8 *kek, size_t kek_l
- /* aes-unwrap.c */
- int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_nist_kw_context ctx;
- mbedtls_nist_kw_init(&ctx);
- size_t olen;
-@@ -1041,6 +1077,9 @@ int omac1_aes_vector(
- const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_cipher_type_t cipher_type;
- switch (key_len) {
- case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
-@@ -1103,6 +1142,9 @@ int omac1_aes_256(const u8 *key, const u
- /* aes-encblock.c */
- int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_aes_context aes;
- mbedtls_aes_init(&aes);
- int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
-@@ -1118,6 +1160,9 @@ int aes_128_encrypt_block(const u8 *key,
- int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
- u8 *data, size_t data_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
- unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
- os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
-@@ -1160,11 +1205,17 @@ static int aes_128_cbc_oper(const u8 *ke
-
- int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
- }
-
- int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
- }
-
-@@ -1407,6 +1458,10 @@ int crypto_hash_finish(struct crypto_has
- }
- mbedtls_md_free(mctx);
- os_free(mctx);
-+
-+ if (TEST_FAIL())
-+ return -1;
-+
- return 0;
- }
-
-@@ -1421,6 +1476,9 @@ int crypto_hash_finish(struct crypto_has
-
- struct crypto_bignum *crypto_bignum_init(void)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_mpi *bn = os_malloc(sizeof(*bn));
- if (bn)
- mbedtls_mpi_init(bn);
-@@ -1429,6 +1487,9 @@ struct crypto_bignum *crypto_bignum_init
-
- struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_mpi *bn = os_malloc(sizeof(*bn));
- if (bn) {
- mbedtls_mpi_init(bn);
-@@ -1442,6 +1503,9 @@ struct crypto_bignum *crypto_bignum_init
-
- struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- #if 0 /*(hostap use of this interface passes int, not uint)*/
- val = host_to_be32(val);
- return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
-@@ -1467,6 +1531,9 @@ void crypto_bignum_deinit(struct crypto_
- int crypto_bignum_to_bin(const struct crypto_bignum *a,
- u8 *buf, size_t buflen, size_t padlen)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
- if (n < padlen)
- n = padlen;
-@@ -1477,6 +1544,9 @@ int crypto_bignum_to_bin(const struct cr
-
- int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
- #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
- return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
-@@ -1513,6 +1583,9 @@ int crypto_bignum_exptmod(const struct c
- const struct crypto_bignum *c,
- struct crypto_bignum *d)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /* (check if input params match d; d is the result) */
- /* (a == d) is ok in current mbedtls implementation */
- if (b == d || c == d) { /*(not ok; store result in intermediate)*/
-@@ -1540,6 +1613,9 @@ int crypto_bignum_inverse(const struct c
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b) ? -1 : 0;
-@@ -1549,6 +1625,9 @@ int crypto_bignum_sub(const struct crypt
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b) ? -1 : 0;
-@@ -1558,6 +1637,9 @@ int crypto_bignum_div(const struct crypt
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /*(most current use of this crypto.h interface has a == c (result),
- * so store result in an intermediate to avoid overwritten input)*/
- mbedtls_mpi R;
-@@ -1575,6 +1657,9 @@ int crypto_bignum_addmod(const struct cr
- const struct crypto_bignum *c,
- struct crypto_bignum *d)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b)
-@@ -1588,6 +1673,9 @@ int crypto_bignum_mulmod(const struct cr
- const struct crypto_bignum *c,
- struct crypto_bignum *d)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
- (const mbedtls_mpi *)a,
- (const mbedtls_mpi *)b)
-@@ -1600,6 +1688,9 @@ int crypto_bignum_sqrmod(const struct cr
- const struct crypto_bignum *b,
- struct crypto_bignum *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- #if 1
- return crypto_bignum_mulmod(a, a, b, c);
- #else
-@@ -1650,6 +1741,9 @@ int crypto_bignum_is_odd(const struct cr
- int crypto_bignum_legendre(const struct crypto_bignum *a,
- const struct crypto_bignum *p)
- {
-+ if (TEST_FAIL())
-+ return -2;
-+
- /* Security Note:
- * mbedtls_mpi_exp_mod() is not documented to run in constant time,
- * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
-@@ -1702,6 +1796,9 @@ int crypto_mod_exp(const u8 *base, size_
- const u8 *modulus, size_t modulus_len,
- u8 *result, size_t *result_len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
- mbedtls_mpi_init(&bn_base);
- mbedtls_mpi_init(&bn_exp);
-@@ -1769,6 +1866,9 @@ static int crypto_mbedtls_dh_init_public
- int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
- u8 *pubkey)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
- size_t pubkey_len, pad;
-
-@@ -1810,6 +1910,9 @@ int crypto_dh_derive_secret(u8 generator
- const u8 *pubkey, size_t pubkey_len,
- u8 *secret, size_t *len)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- #if 0
- if (pubkey_len > prime_len ||
- (pubkey_len == prime_len &&
-@@ -2512,6 +2615,9 @@ const struct crypto_ec_point * crypto_ec
-
- struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_ecp_point *p = os_malloc(sizeof(*p));
- if (p != NULL)
- mbedtls_ecp_point_init(p);
-@@ -2536,6 +2642,9 @@ int crypto_ec_point_x(struct crypto_ec *
- int crypto_ec_point_to_bin(struct crypto_ec *e,
- const struct crypto_ec_point *point, u8 *x, u8 *y)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
- size_t len = CRYPTO_EC_plen(e);
- if (x) {
-@@ -2563,6 +2672,9 @@ int crypto_ec_point_to_bin(struct crypto
- struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
- const u8 *val)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- size_t len = CRYPTO_EC_plen(e);
- mbedtls_ecp_point *p = os_malloc(sizeof(*p));
- u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-@@ -2615,6 +2727,9 @@ int crypto_ec_point_add(struct crypto_ec
- const struct crypto_ec_point *b,
- struct crypto_ec_point *c)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- /* mbedtls does not provide an mbedtls_ecp_point add function */
- mbedtls_mpi one;
- mbedtls_mpi_init(&one);
-@@ -2631,6 +2746,9 @@ int crypto_ec_point_mul(struct crypto_ec
- const struct crypto_bignum *b,
- struct crypto_ec_point *res)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- return mbedtls_ecp_mul(
- (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
- (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
-@@ -2639,6 +2757,9 @@ int crypto_ec_point_mul(struct crypto_ec
-
- int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
- {
-+ if (TEST_FAIL())
-+ return -1;
-+
- if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
- == MBEDTLS_ECP_TYPE_MONTGOMERY) {
- /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
-@@ -2751,6 +2872,9 @@ struct crypto_bignum *
- crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
- const struct crypto_bignum *x)
- {
-+ if (TEST_FAIL())
-+ return NULL;
-+
- mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
- if (y2 == NULL)
- return NULL;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/135-mbedtls-fix-owe-association.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/135-mbedtls-fix-owe-association.patch
deleted file mode 100644
index 0c29432..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/135-mbedtls-fix-owe-association.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-The code for hostapd-mbedtls did not work when used for OWE association.
-
-When handling association requests, the buffer offsets and length assumptions were incorrect, leading to never calculating the y point, thus denying association.
-
-Also when crafting the association response, the buffer contained the trailing key-type.
-
-Fix up both issues to adhere to the specification and make hostapd-mbedtls work with the OWE security type.
-
---- a/src/crypto/crypto_mbedtls.c
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -2299,25 +2299,30 @@ struct crypto_ecdh * crypto_ecdh_init2(i
- struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
- {
- mbedtls_ecp_group *grp = &ecdh->grp;
-- size_t len = CRYPTO_EC_plen(grp);
-+ size_t prime_len = CRYPTO_EC_plen(grp);
-+ size_t output_len = prime_len;
-+ u8 output_offset = 0;
-+ u8 buf[256];
-+
- #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
- /* len */
- #endif
- #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-- if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-- len = inc_y ? len*2+1 : len+1;
-+ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+ output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
-+ output_offset = 1;
-+ }
- #endif
-- struct wpabuf *buf = wpabuf_alloc(len);
-- if (buf == NULL)
-+
-+ if (output_len > sizeof(buf))
- return NULL;
-+
- inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
-- if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &len,
-- wpabuf_mhead_u8(buf), len) == 0) {
-- wpabuf_put(buf, len);
-- return buf;
-+ if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
-+ buf, output_len) == 0) {
-+ return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
- }
-
-- wpabuf_free(buf);
- return NULL;
- }
-
-@@ -2379,10 +2384,7 @@ struct wpabuf * crypto_ecdh_set_peerkey(
- os_memcpy(buf+2, key, len);
- }
- len >>= 1; /*(repurpose len to prime_len)*/
-- }
-- else if (key[0] == 0x02 || key[0] == 0x03) { /* (inc_y == 0) */
-- --len; /*(repurpose len to prime_len)*/
--
-+ } else { /* (inc_y == 0) */
- /* mbedtls_ecp_point_read_binary() does not currently support
- * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
- * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
-@@ -2390,22 +2392,21 @@ struct wpabuf * crypto_ecdh_set_peerkey(
- /* derive y, amend buf[] with y for UNCOMPRESSED format */
- if (sizeof(buf)-2 < len*2 || len == 0)
- return NULL;
-+
- buf[0] = (u8)(1+len*2);
- buf[1] = 0x04;
-+ os_memcpy(buf+2, key, len);
-+
- mbedtls_mpi bn;
- mbedtls_mpi_init(&bn);
-- int ret = mbedtls_mpi_read_binary(&bn, key+1, len)
-- || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn,
-- key[0] & 1)
-+ int ret = mbedtls_mpi_read_binary(&bn, key, len)
-+ || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
- || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
- mbedtls_mpi_free(&bn);
- if (ret != 0)
- return NULL;
- }
-
-- if (key[0] == 0) /*(repurpose len to prime_len)*/
-- len = CRYPTO_EC_plen(grp);
--
- if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
- return NULL;
- }
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch
deleted file mode 100644
index e967cff..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch
+++ /dev/null
@@ -1,1358 +0,0 @@
-From f24933dc175e0faf44a3cce3330c256a59649ca6 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 19 Jul 2022 23:01:17 -0400
-Subject: [PATCH 4/7] tests/Makefile make run-tests with CONFIG_TLS=...
-
-add test-crypto_module.c to run crypto_module_tests()
-
-adjust some tests/hwsim/*.py for mbed TLS (work in progress)
-
-option to build and run-tests with CONFIG_TLS=internal # (default)
-$ cd tests; make clean
-$ make run-tests
-
-option to build and run-tests with CONFIG_TLS=gnutls
-$ cd tests; make clean CONFIG_TLS=gnutls
-$ make run-tests CONFIG_TLS=gnutls
-
-option to build and run-tests with CONFIG_TLS=mbedtls
-$ cd tests; make clean CONFIG_TLS=mbedtls
-$ make run-tests CONFIG_TLS=mbedtls
-
-option to build and run-tests with CONFIG_TLS=openssl
-$ cd tests; make clean CONFIG_TLS=openssl
-$ make run-tests CONFIG_TLS=openssl
-
-option to build and run-tests with CONFIG_TLS=wolfssl
-$ cd tests; make clean CONFIG_TLS=wolfssl
-$ make run-tests CONFIG_TLS=wolfssl
-
-RFE: Makefile logic for crypto objects should be centralized
- instead of being duplicated in hostapd/Makefile,
- wpa_supplicant/Makefile, src/crypto/Makefile,
- tests/Makefile, ...
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- hostapd/Makefile | 6 +
- src/crypto/Makefile | 129 ++++++++++++++++++++-
- src/crypto/crypto_module_tests.c | 134 ++++++++++++++++++++++
- src/tls/Makefile | 11 ++
- tests/Makefile | 75 +++++++++---
- tests/hwsim/example-hostapd.config | 11 +-
- tests/hwsim/example-wpa_supplicant.config | 12 +-
- tests/hwsim/test_ap_eap.py | 114 +++++++++++++-----
- tests/hwsim/test_ap_ft.py | 4 +-
- tests/hwsim/test_authsrv.py | 9 +-
- tests/hwsim/test_dpp.py | 19 ++-
- tests/hwsim/test_erp.py | 16 +--
- tests/hwsim/test_fils.py | 5 +-
- tests/hwsim/test_pmksa_cache.py | 4 +-
- tests/hwsim/test_sae.py | 7 ++
- tests/hwsim/test_suite_b.py | 3 +
- tests/hwsim/test_wpas_ctrl.py | 2 +-
- tests/hwsim/utils.py | 8 +-
- tests/test-crypto_module.c | 16 +++
- tests/test-https.c | 12 +-
- tests/test-https_server.c | 12 +-
- wpa_supplicant/Makefile | 6 +
- 22 files changed, 524 insertions(+), 91 deletions(-)
- create mode 100644 tests/test-crypto_module.c
-
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -696,6 +696,7 @@ CFLAGS += -DCONFIG_TLSV12
- endif
-
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- CONFIG_CRYPTO=wolfssl
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -716,6 +717,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- CONFIG_CRYPTO=openssl
- ifdef TLS_FUNCS
-@@ -746,6 +748,7 @@ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONF
- endif
-
- ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=mbedtls
- endif
-@@ -776,6 +779,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -806,6 +810,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -884,6 +889,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/crypto_internal-rsa.o
---- a/src/crypto/Makefile
-+++ b/src/crypto/Makefile
-@@ -1,10 +1,121 @@
--CFLAGS += -DCONFIG_CRYPTO_INTERNAL
--CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
--CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- #CFLAGS += -DALL_DH_GROUPS
- CFLAGS += -DCONFIG_SHA256
- CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+
-+# crypto_module_tests.c
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
-+ifeq ($(CONFIG_TLS),mbedtls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DCONFIG_DES
-+CFLAGS += -DEAP_IKEV2
-+CFLAGS += -DEAP_MSCHAPv2
-+CFLAGS += -DEAP_SIM
-+
-+LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
-+LIB_OBJS+= \
-+ aes-eax.o \
-+ aes-siv.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o
-+
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
-+ifndef CONFIG_TLS_DEFAULT_CIPHERS
-+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
-+endif
-+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DEAP_TLS_OPENSSL
-+
-+LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
-+LIB_OBJS+= \
-+ aes-ctr.o \
-+ aes-eax.o \
-+ aes-encblock.o \
-+ aes-siv.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o \
-+ sha1-prf.o \
-+ sha1-tlsprf.o \
-+ sha1-tprf.o \
-+ sha256-kdf.o \
-+ sha256-prf.o \
-+ sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+
-+# (wolfssl libraries must be built with ./configure --enable-wpas)
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
-+CFLAGS += -DWOLFSSL_DER_LOAD
-+CFLAGS += -DCONFIG_DES
-+
-+LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
-+LIB_OBJS+= \
-+ aes-ctr.o \
-+ aes-eax.o \
-+ aes-encblock.o \
-+ aes-siv.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o \
-+ sha1-prf.o \
-+ sha1-tlsprf.o \
-+ sha1-tprf.o \
-+ sha256-kdf.o \
-+ sha256-prf.o \
-+ sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
-+LIB_OBJS = tls_gnutls.o crypto_gnutls.o
-+LIB_OBJS+= \
-+ aes-cbc.o \
-+ aes-ctr.o \
-+ aes-eax.o \
-+ aes-encblock.o \
-+ aes-omac1.o \
-+ aes-siv.o \
-+ aes-unwrap.o \
-+ aes-wrap.o \
-+ dh_group5.o \
-+ dh_groups.o \
-+ milenage.o \
-+ ms_funcs.o \
-+ rc4.o \
-+ sha1-pbkdf2.o \
-+ sha1-prf.o \
-+ fips_prf_internal.o \
-+ sha1-internal.o \
-+ sha1-tlsprf.o \
-+ sha1-tprf.o \
-+ sha256-kdf.o \
-+ sha256-prf.o \
-+ sha256-tlsprf.o
-+
-+else
-+
-+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- CFLAGS += -DCONFIG_INTERNAL_SHA384
-
- LIB_OBJS= \
-@@ -13,7 +124,6 @@ LIB_OBJS= \
- aes-ctr.o \
- aes-eax.o \
- aes-encblock.o \
-- aes-gcm.o \
- aes-internal.o \
- aes-internal-dec.o \
- aes-internal-enc.o \
-@@ -37,6 +147,7 @@ LIB_OBJS= \
- sha1-tlsprf.o \
- sha1-tprf.o \
- sha256.o \
-+ sha256-kdf.o \
- sha256-prf.o \
- sha256-tlsprf.o \
- sha256-internal.o \
-@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
- LIB_OBJS += crypto_internal-rsa.o
- LIB_OBJS += tls_internal.o
- LIB_OBJS += fips_prf_internal.o
-+
-+endif
-+endif
-+endif
-+endif
-+
-+
-+# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
-+LIB_OBJS += aes-gcm.o
-+
- ifndef TEST_FUZZ
- LIB_OBJS += random.o
- endif
---- a/src/crypto/crypto_module_tests.c
-+++ b/src/crypto/crypto_module_tests.c
-@@ -2469,6 +2469,139 @@ static int test_hpke(void)
- }
-
-
-+static int test_ecc(void)
-+{
-+#ifdef CONFIG_ECC
-+#ifndef CONFIG_TLS_INTERNAL
-+#ifndef CONFIG_TLS_GNUTLS
-+#if defined(CONFIG_TLS_MBEDTLS) \
-+ || defined(CONFIG_TLS_OPENSSL) \
-+ || defined(CONFIG_TLS_WOLFSSL)
-+ wpa_printf(MSG_INFO, "Testing ECC");
-+ /* Note: some tests below are valid on supported Short Weierstrass
-+ * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
-+ * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
-+ */
-+#ifdef CONFIG_TLS_MBEDTLS
-+ const int grps[] = {19, 20, 21, 25, 26, 28};
-+#endif
-+#ifdef CONFIG_TLS_OPENSSL
-+ const int grps[] = {19, 20, 21, 26};
-+#endif
-+#ifdef CONFIG_TLS_WOLFSSL
-+ const int grps[] = {19, 20, 21, 26};
-+#endif
-+ uint32_t i;
-+ struct crypto_ec *e = NULL;
-+ struct crypto_ec_point *p = NULL, *q = NULL;
-+ struct crypto_bignum *x = NULL, *y = NULL;
-+#ifdef CONFIG_DPP
-+ u8 bin[4096];
-+#endif
-+ for (i = 0; i < ARRAY_SIZE(grps); ++i) {
-+ e = crypto_ec_init(grps[i]);
-+ if (e == NULL
-+ || crypto_ec_prime_len(e) == 0
-+ || crypto_ec_prime_len_bits(e) == 0
-+ || crypto_ec_order_len(e) == 0
-+ || crypto_ec_get_prime(e) == NULL
-+ || crypto_ec_get_order(e) == NULL
-+ || crypto_ec_get_a(e) == NULL
-+ || crypto_ec_get_b(e) == NULL
-+ || crypto_ec_get_generator(e) == NULL) {
-+ break;
-+ }
-+#ifdef CONFIG_DPP
-+ struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
-+ if (key == NULL)
-+ break;
-+ p = crypto_ec_key_get_public_key(key);
-+ q = crypto_ec_key_get_public_key(key);
-+ crypto_ec_key_deinit(key);
-+ if (p == NULL || q == NULL)
-+ break;
-+ if (!crypto_ec_point_is_on_curve(e, p))
-+ break;
-+
-+ /* inverted point should not match original;
-+ * double-invert should match */
-+ if (crypto_ec_point_invert(e, q) != 0
-+ || crypto_ec_point_cmp(e, p, q) == 0
-+ || crypto_ec_point_invert(e, q) != 0
-+ || crypto_ec_point_cmp(e, p, q) != 0) {
-+ break;
-+ }
-+
-+ /* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
-+ * imbalanced interfaces? */
-+ size_t prime_len = crypto_ec_prime_len(e);
-+ if (prime_len * 2 > sizeof(bin))
-+ break;
-+ if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
-+ break;
-+ struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
-+ if (tmp == NULL)
-+ break;
-+ if (crypto_ec_point_cmp(e, p, tmp) != 0) {
-+ crypto_ec_point_deinit(tmp, 0);
-+ break;
-+ }
-+ crypto_ec_point_deinit(tmp, 0);
-+
-+ x = crypto_bignum_init();
-+ y = crypto_bignum_init_set(bin+prime_len, prime_len);
-+ if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
-+ break;
-+ struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
-+ if (y2 == NULL)
-+ break;
-+ if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
-+ || crypto_bignum_cmp(y, y2) != 0) {
-+ crypto_bignum_deinit(y2, 0);
-+ break;
-+ }
-+ crypto_bignum_deinit(y2, 0);
-+ crypto_bignum_deinit(x, 0);
-+ crypto_bignum_deinit(y, 0);
-+ x = NULL;
-+ y = NULL;
-+
-+ x = crypto_bignum_init();
-+ if (x == NULL)
-+ break;
-+ if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
-+ break;
-+ crypto_bignum_deinit(x, 0);
-+ x = NULL;
-+
-+ crypto_ec_point_deinit(p, 0);
-+ p = NULL;
-+ crypto_ec_point_deinit(q, 0);
-+ q = NULL;
-+#endif /* CONFIG_DPP */
-+ crypto_ec_deinit(e);
-+ e = NULL;
-+ }
-+ if (i != ARRAY_SIZE(grps)) {
-+ crypto_bignum_deinit(x, 0);
-+ crypto_bignum_deinit(y, 0);
-+ crypto_ec_point_deinit(p, 0);
-+ crypto_ec_point_deinit(q, 0);
-+ crypto_ec_deinit(e);
-+ wpa_printf(MSG_INFO,
-+ "ECC test case failed tls_id:%d", grps[i]);
-+ return -1;
-+ }
-+
-+ wpa_printf(MSG_INFO, "ECC test cases passed");
-+#endif
-+#endif /* !CONFIG_TLS_GNUTLS */
-+#endif /* !CONFIG_TLS_INTERNAL */
-+#endif /* CONFIG_ECC */
-+ return 0;
-+}
-+
-+
- static int test_ms_funcs(void)
- {
- #ifndef CONFIG_FIPS
-@@ -2590,6 +2723,7 @@ int crypto_module_tests(void)
- test_fips186_2_prf() ||
- test_extract_expand_hkdf() ||
- test_hpke() ||
-+ test_ecc() ||
- test_ms_funcs())
- ret = -1;
-
---- a/src/tls/Makefile
-+++ b/src/tls/Makefile
-@@ -1,3 +1,10 @@
-+LIB_OBJS= asn1.o
-+
-+ifneq ($(CONFIG_TLS),gnutls)
-+ifneq ($(CONFIG_TLS),mbedtls)
-+ifneq ($(CONFIG_TLS),openssl)
-+ifneq ($(CONFIG_TLS),wolfssl)
-+
- CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
- CFLAGS += -DCONFIG_CRYPTO_INTERNAL
- CFLAGS += -DCONFIG_TLSV11
-@@ -21,5 +28,9 @@ LIB_OBJS= \
- tlsv1_server_read.o \
- tlsv1_server_write.o \
- x509v3.o
-+endif
-+endif
-+endif
-+endif
-
- include ../lib.rules
---- a/tests/Makefile
-+++ b/tests/Makefile
-@@ -1,8 +1,10 @@
--ALL=test-base64 test-md4 test-milenage \
-- test-rsa-sig-ver \
-- test-sha1 \
-- test-https test-https_server \
-- test-sha256 test-aes test-x509v3 test-list test-rc4
-+RUN_TESTS= \
-+ test-list \
-+ test-md4 test-rc4 test-sha1 test-sha256 \
-+ test-milenage test-aes \
-+ test-crypto_module
-+
-+ALL=$(RUN_TESTS) test-base64 test-https test-https_server
-
- include ../src/build.rules
-
-@@ -24,13 +26,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
- CFLAGS += -DCONFIG_IEEE80211R
- CFLAGS += -DCONFIG_TDLS
-
-+# test-crypto_module
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DCONFIG_SHA256
-+CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
- CFLAGS += -I../src
- CFLAGS += -I../src/utils
-
- SLIBS = ../src/utils/libutils.a
-
--DLIBS = ../src/crypto/libcrypto.a \
-- ../src/tls/libtls.a
-+DLIBS = ../src/tls/libtls.a \
-+ ../src/crypto/libcrypto.a
-
- _OBJS_VAR := LLIBS
- include ../src/objs.mk
-@@ -42,12 +58,43 @@ include ../src/objs.mk
- LIBS = $(SLIBS) $(DLIBS)
- LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
-
-+ifeq ($(CONFIG_TLS),mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
-+LLIBS += -lssl -lcrypto
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
-+LLIBS += -lgnutls -lgpg-error -lgcrypt
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
-+LLIBS += -lwolfssl -lm
-+else
-+CFLAGS += -DCONFIG_TLS_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
-+ALL += test-rsa-sig-ver
-+ALL += test-x509v3
-+clean-config_tls_internal:
-+ rm -f test_x509v3_nist.out.*
-+ rm -f test_x509v3_nist2.out.*
-+endif
-+endif
-+endif
-+endif
-+
- # glibc < 2.17 needs -lrt for clock_gettime()
- LLIBS += -lrt
-
- test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
- $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
-
-+test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
-+ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
-+
- test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
- $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
-
-@@ -83,17 +130,11 @@ test-x509v3: $(call BUILDOBJ,test-x509v3
-
-
- run-tests: $(ALL)
-- ./test-aes
-- ./test-list
-- ./test-md4
-- ./test-milenage
-- ./test-rsa-sig-ver
-- ./test-sha1
-- ./test-sha256
-+ @set -ex; for i in $(RUN_TESTS); do ./$$i; done
- @echo
- @echo All tests completed successfully.
-
--clean: common-clean
-+clean: common-clean clean-config_tls_internal
- rm -f *~
-- rm -f test_x509v3_nist.out.*
-- rm -f test_x509v3_nist2.out.*
-+
-+.PHONY: run-tests clean-config_tls_internal
---- a/tests/hwsim/example-hostapd.config
-+++ b/tests/hwsim/example-hostapd.config
-@@ -34,15 +34,7 @@ CONFIG_EAP_TNC=y
- CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
- LIBS += -rdynamic
- CONFIG_EAP_UNAUTH_TLS=y
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), mbedtls)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
- CONFIG_EAP_EKE=y
- CONFIG_PKCS12=y
- CONFIG_RADIUS_SERVER=y
-@@ -89,6 +81,7 @@ CFLAGS += -DCONFIG_RADIUS_TEST
- CONFIG_MODULE_TESTS=y
-
- CONFIG_SUITEB=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
-
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
---- a/tests/hwsim/example-wpa_supplicant.config
-+++ b/tests/hwsim/example-wpa_supplicant.config
-@@ -35,16 +35,7 @@ LIBS += -rdynamic
- CONFIG_EAP_FAST=y
- CONFIG_EAP_TEAP=y
- CONFIG_EAP_IKEV2=y
--
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), mbedtls)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
-
- CONFIG_USIM_SIMULATOR=y
- CONFIG_SIM_SIMULATOR=y
-@@ -137,6 +128,7 @@ CONFIG_TESTING_OPTIONS=y
- CONFIG_MODULE_TESTS=y
-
- CONFIG_SUITEB=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
-
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
---- a/tests/hwsim/test_ap_eap.py
-+++ b/tests/hwsim/test_ap_eap.py
-@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
- res = dev.get_capability("eap")
- if method not in res:
- raise HwsimSkip("EAP method %s not supported in the build" % method)
-+ if method == "FAST" or method == "TEAP":
-+ tls = dev.request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
-
- def check_subject_match_support(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
-
- def check_check_cert_subject_support(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
-
- def check_altsubject_match_support(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
-
- def check_domain_match(dev):
-@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
-
- def check_domain_match_full(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("wolfSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
-
- def check_cert_probe_support(dev):
-@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
- raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
-
- def check_ext_cert_check_support(dev):
-+ if not openssl_imported:
-+ raise HwsimSkip("OpenSSL python method not available")
-+
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
-
- def check_ocsp_support(dev):
-@@ -91,14 +126,18 @@ def check_ocsp_support(dev):
- # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
- #if tls.startswith("wolfSSL"):
- # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-
- def check_pkcs5_v15_support(dev):
- tls = dev.request("GET tls_library")
-- if "BoringSSL" in tls or "GnuTLS" in tls:
-+ if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
- raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
-
- def check_tls13_support(dev):
- tls = dev.request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("TLS v1.3 not supported")
- if "run=OpenSSL 1.1.1" not in tls and "run=OpenSSL 3.0" not in tls and "wolfSSL" not in tls:
- raise HwsimSkip("TLS v1.3 not supported")
-
-@@ -118,11 +157,15 @@ def check_pkcs12_support(dev):
- # raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
- if tls.startswith("wolfSSL"):
- raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-
- def check_dh_dsa_support(dev):
- tls = dev.request("GET tls_library")
- if tls.startswith("internal"):
- raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
-
- def check_ec_support(dev):
- tls = dev.request("GET tls_library")
-@@ -1595,7 +1638,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_ma
- eap_connect(dev[0], hapd, "TTLS", "pap user",
- anonymous_identity="ttls", password="password",
- ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
-- subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
-+ check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
- altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
- eap_reauth(dev[0], "TTLS")
-
-@@ -2830,6 +2873,7 @@ def test_ap_wpa2_eap_tls_neg_domain_matc
-
- def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
- """WPA2-Enterprise negative test - subject mismatch"""
-+ check_subject_match_support(dev[0])
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hostapd.add_ap(apdev[0], params)
- dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-@@ -2890,6 +2934,7 @@ def test_ap_wpa2_eap_tls_neg_subject_mat
-
- def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
- """WPA2-Enterprise negative test - altsubject mismatch"""
-+ check_altsubject_match_support(dev[0])
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hostapd.add_ap(apdev[0], params)
-
-@@ -3430,7 +3475,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apde
- dev[0].request("REMOVE_NETWORK all")
-
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("wolfSSL"):
-+ if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
- tests = [(1, "os_get_random;dh_init")]
- else:
- tests = [(1, "crypto_dh_init;dh_init")]
-@@ -4744,7 +4789,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
- params["private_key"] = "auth_serv/iCA-server/server.key"
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4810,6 +4855,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
- run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
-
- def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
-+ check_ocsp_support(dev[0])
- params = int_eap_server_params()
- params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
- params["server_cert"] = "auth_serv/iCA-server/server.pem"
-@@ -4819,7 +4865,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_
- try:
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4855,7 +4901,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_
- try:
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4905,7 +4951,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
- try:
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -4972,7 +5018,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca
-
- hostapd.add_ap(apdev[0], params)
- tls = dev[0].request("GET tls_library")
-- if "GnuTLS" in tls or "wolfSSL" in tls:
-+ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
- ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
- client_cert = "auth_serv/iCA-user/user_and_ica.pem"
- else:
-@@ -5230,6 +5276,7 @@ def test_ap_wpa2_eap_ttls_server_cert_ek
-
- def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
- """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
-+ check_pkcs12_support(dev[0])
- skip_with_fips(dev[0])
- params = int_eap_server_params()
- del params["server_cert"]
-@@ -5242,6 +5289,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(
-
- def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
- """EAP-TTLS and server PKCS#12 file with extra certs"""
-+ check_pkcs12_support(dev[0])
- skip_with_fips(dev[0])
- params = int_eap_server_params()
- del params["server_cert"]
-@@ -5264,6 +5312,7 @@ def test_ap_wpa2_eap_ttls_dh_params_serv
-
- def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
- """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
-+ check_dh_dsa_support(dev[0])
- params = int_eap_server_params()
- params["dh_file"] = "auth_serv/dsaparam.pem"
- hapd = hostapd.add_ap(apdev[0], params)
-@@ -5575,8 +5624,8 @@ def test_ap_wpa2_eap_non_ascii_identity2
- def test_openssl_cipher_suite_config_wpas(dev, apdev):
- """OpenSSL cipher suite configuration on wpa_supplicant"""
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-- raise HwsimSkip("TLS library is not OpenSSL: " + tls)
-+ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+ raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hapd = hostapd.add_ap(apdev[0], params)
- eap_connect(dev[0], hapd, "TTLS", "pap user",
-@@ -5602,14 +5651,14 @@ def test_openssl_cipher_suite_config_wpa
- def test_openssl_cipher_suite_config_hapd(dev, apdev):
- """OpenSSL cipher suite configuration on hostapd"""
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-- raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
-+ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+ raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
- params = int_eap_server_params()
- params['openssl_ciphers'] = "AES256"
- hapd = hostapd.add_ap(apdev[0], params)
- tls = hapd.request("GET tls_library")
-- if not tls.startswith("OpenSSL"):
-- raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
-+ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+ raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
- eap_connect(dev[0], hapd, "TTLS", "pap user",
- anonymous_identity="ttls", password="password",
- ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
-@@ -6051,13 +6100,17 @@ def test_ap_wpa2_eap_tls_versions(dev, a
- check_tls_ver(dev[0], hapd,
- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
- "TLSv1.2")
-- elif tls.startswith("internal"):
-+ elif tls.startswith("internal") or tls.startswith("mbed TLS"):
- check_tls_ver(dev[0], hapd,
- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
-- check_tls_ver(dev[1], hapd,
-- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-- check_tls_ver(dev[2], hapd,
-- "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
-+ if tls.startswith("mbed TLS"):
-+ check_tls_ver(dev[2], hapd,
-+ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
-+ else:
-+ check_tls_ver(dev[1], hapd,
-+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-+ check_tls_ver(dev[2], hapd,
-+ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
- if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3.0" in tls:
- check_tls_ver(dev[0], hapd,
- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
-@@ -6079,6 +6132,11 @@ def test_ap_wpa2_eap_tls_versions_server
- tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
- ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
- ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+ #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
- for exp, flags in tests:
- hapd.disable()
- hapd.set("tls_flags", flags)
-@@ -7138,6 +7196,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apde
- def test_eap_tls_ext_cert_check(dev, apdev):
- """EAP-TLS and external server certification validation"""
- # With internal server certificate chain validation
-+ check_ext_cert_check_support(dev[0])
- id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
- identity="tls user",
- ca_cert="auth_serv/ca.pem",
-@@ -7150,6 +7209,7 @@ def test_eap_tls_ext_cert_check(dev, apd
- def test_eap_ttls_ext_cert_check(dev, apdev):
- """EAP-TTLS and external server certification validation"""
- # Without internal server certificate chain validation
-+ check_ext_cert_check_support(dev[0])
- id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
- identity="pap user", anonymous_identity="ttls",
- password="password", phase2="auth=PAP",
-@@ -7160,6 +7220,7 @@ def test_eap_ttls_ext_cert_check(dev, ap
- def test_eap_peap_ext_cert_check(dev, apdev):
- """EAP-PEAP and external server certification validation"""
- # With internal server certificate chain validation
-+ check_ext_cert_check_support(dev[0])
- id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
- identity="user", anonymous_identity="peap",
- ca_cert="auth_serv/ca.pem",
-@@ -7170,6 +7231,7 @@ def test_eap_peap_ext_cert_check(dev, ap
-
- def test_eap_fast_ext_cert_check(dev, apdev):
- """EAP-FAST and external server certification validation"""
-+ check_ext_cert_check_support(dev[0])
- check_eap_capa(dev[0], "FAST")
- # With internal server certificate chain validation
- dev[0].request("SET blob fast_pac_auth_ext ")
-@@ -7184,10 +7246,6 @@ def test_eap_fast_ext_cert_check(dev, ap
- run_ext_cert_check(dev, apdev, id)
-
- def run_ext_cert_check(dev, apdev, net_id):
-- check_ext_cert_check_support(dev[0])
-- if not openssl_imported:
-- raise HwsimSkip("OpenSSL python method not available")
--
- params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
- hapd = hostapd.add_ap(apdev[0], params)
-
---- a/tests/hwsim/test_ap_ft.py
-+++ b/tests/hwsim/test_ap_ft.py
-@@ -2474,11 +2474,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
- # This will fail to roam
- dev[0].roam(bssid1, check_bssid=False)
-
-- with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+ with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
- # This will fail to roam
- dev[0].roam(bssid1, check_bssid=False)
-
-- with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+ with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
- # This will fail to roam
- dev[0].roam(bssid1, check_bssid=False)
-
---- a/tests/hwsim/test_authsrv.py
-+++ b/tests/hwsim/test_authsrv.py
-@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
- if "FAIL" not in authsrv.request("ENABLE"):
- raise Exception("ENABLE succeeded during OOM")
-
-- with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
-- if "FAIL" not in authsrv.request("ENABLE"):
-- raise Exception("ENABLE succeeded during OOM")
-+ # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
-+ tls = dev[0].request("GET tls_library")
-+ if not tls.startswith("mbed TLS"):
-+ with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
-+ if "FAIL" not in authsrv.request("ENABLE"):
-+ raise Exception("ENABLE succeeded during OOM")
-
- for count in range(1, 3):
- with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
---- a/tests/hwsim/test_dpp.py
-+++ b/tests/hwsim/test_dpp.py
-@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False
- raise HwsimSkip("DPP not supported")
- if brainpool:
- tls = dev.request("GET tls_library")
-- if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
-+ if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
-+ and not tls.startswith("mbed TLS"):
- raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
- capa = dev.request("GET_CAPABILITY dpp")
- ver = 1
-@@ -3892,6 +3893,9 @@ def test_dpp_proto_auth_req_no_i_proto_k
-
- def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
- """DPP protocol testing - invalid I-proto key in Auth Req"""
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
- run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
-
- def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
-@@ -3987,7 +3991,12 @@ def test_dpp_proto_auth_resp_no_r_proto_
-
- def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
- """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
-- run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
-+ run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
-+ else:
-+ run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
-
- def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
- """DPP protocol testing - no R-nonce in Auth Resp"""
-@@ -4349,11 +4358,17 @@ def test_dpp_proto_pkex_exchange_resp_in
-
- def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
- """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
- run_dpp_proto_pkex_req_missing(dev, 47,
- "Peer bootstrapping key is invalid")
-
- def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
- """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
-+ tls = dev[0].request("GET tls_library")
-+ if tls.startswith("mbed TLS"):
-+ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
- run_dpp_proto_pkex_resp_missing(dev, 48,
- "Peer bootstrapping key is invalid")
-
---- a/tests/hwsim/test_erp.py
-+++ b/tests/hwsim/test_erp.py
-@@ -12,7 +12,7 @@ import time
-
- import hostapd
- from utils import *
--from test_ap_eap import int_eap_server_params, check_tls13_support
-+from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
- from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
-
- def test_erp_initiate_reauth_start(dev, apdev):
-@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apd
- params['erp_domain'] = 'example.com'
- params['disable_pmksa_caching'] = '1'
- hapd = hostapd.add_ap(apdev[0], params)
-+ tls = dev[0].request("GET tls_library")
-
- erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
- password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
-@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apd
- password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
- erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
- password="hello")
-- if "FAST" in eap_methods:
-+ if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
- erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
- password="password", ca_cert="auth_serv/ca.pem",
- phase2="auth=GTC",
-@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apd
- password="password")
- erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
- password_hex="0123456789abcdef0123456789abcdef")
-- if "MSCHAPV2" in eap_methods:
-+ if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
- erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
- password="password", ca_cert="auth_serv/ca.pem",
- phase2="auth=MSCHAPV2")
-- erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
-- password="password", ca_cert="auth_serv/ca.pem",
-- phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
-+ if check_eap_capa(dev[0], "TEAP"):
-+ erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
-+ password="password", ca_cert="auth_serv/ca.pem",
-+ phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
- erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
- password_hex="0123456789abcdef0123456789abcdef")
- if "PWD" in eap_methods:
-@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
- dev[0].request("REMOVE_NETWORK all")
- dev[0].wait_disconnected()
-
-- for count in range(1, 6):
-+ for count in range(1, 4):
- dev[0].request("ERP_FLUSH")
- with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
- dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
---- a/tests/hwsim/test_fils.py
-+++ b/tests/hwsim/test_fils.py
-@@ -1422,7 +1422,10 @@ def run_fils_sk_pfs(dev, apdev, group, p
- check_erp_capa(dev[0])
-
- tls = dev[0].request("GET tls_library")
-- if not tls.startswith("wolfSSL"):
-+ if tls.startswith("mbed TLS"):
-+ if int(group) == 27:
-+ raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
-+ elif not tls.startswith("wolfSSL"):
- if int(group) in [25]:
- if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
- raise HwsimSkip("EC group not supported")
---- a/tests/hwsim/test_pmksa_cache.py
-+++ b/tests/hwsim/test_pmksa_cache.py
-@@ -955,7 +955,7 @@ def test_pmksa_cache_preauth_wpas_oom(de
- eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
- password_hex="0123456789abcdef0123456789abcdef",
- bssid=apdev[0]['bssid'])
-- for i in range(1, 11):
-+ for i in range(1, 10):
- with alloc_fail(dev[0], i, "rsn_preauth_init"):
- res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
- logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
-@@ -963,7 +963,7 @@ def test_pmksa_cache_preauth_wpas_oom(de
- state = dev[0].request('GET_ALLOC_FAIL')
- if state.startswith('0:'):
- break
-- time.sleep(0.05)
-+ time.sleep(0.10)
-
- def test_pmksa_cache_ctrl(dev, apdev):
- """PMKSA cache control interface operations"""
---- a/tests/hwsim/test_sae.py
-+++ b/tests/hwsim/test_sae.py
-@@ -177,6 +177,11 @@ def test_sae_groups(dev, apdev):
- if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
- logger.info("Add Brainpool EC groups since OpenSSL is new enough")
- sae_groups += [27, 28, 29, 30]
-+ if tls.startswith("mbed TLS"):
-+ # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
-+ # does not have code to derive y from compressed format for those curves
-+ sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
-+ sae_groups += [27, 28, 29, 30]
- heavy_groups = [14, 15, 16]
- suitable_groups = [15, 16, 17, 18, 19, 20, 21]
- groups = [str(g) for g in sae_groups]
-@@ -2193,6 +2198,8 @@ def run_sae_pwe_group(dev, apdev, group)
- logger.info("Add Brainpool EC groups since OpenSSL is new enough")
- elif tls.startswith("wolfSSL"):
- logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
-+ elif tls.startswith("mbed TLS"):
-+ logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
- else:
- raise HwsimSkip("Brainpool curve not supported")
- start_sae_pwe_ap(apdev[0], group, 2)
---- a/tests/hwsim/test_suite_b.py
-+++ b/tests/hwsim/test_suite_b.py
-@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False
- return
- if tls.startswith("wolfSSL"):
- return
-+ if tls.startswith("mbed TLS"):
-+ return
- if not tls.startswith("OpenSSL"):
- raise HwsimSkip("TLS library not supported for Suite B: " + tls)
- supported = False
-@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh
-
- dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
- ieee80211w="2",
-+ openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
- phase1="tls_suiteb=1",
- eap="TLS", identity="tls user",
- ca_cert="auth_serv/rsa3072-ca.pem",
---- a/tests/hwsim/test_wpas_ctrl.py
-+++ b/tests/hwsim/test_wpas_ctrl.py
-@@ -1842,7 +1842,7 @@ def _test_wpas_ctrl_oom(dev):
- tls = dev[0].request("GET tls_library")
- if not tls.startswith("internal"):
- tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
-- 4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
-+ 3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
- for cmd, exp, count, func in tests:
- with alloc_fail(dev[0], count, func):
- res = dev[0].request(cmd)
---- a/tests/hwsim/utils.py
-+++ b/tests/hwsim/utils.py
-@@ -141,7 +141,13 @@ def check_imsi_privacy_support(dev):
-
- def check_tls_tod(dev):
- tls = dev.request("GET tls_library")
-- if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
-+ if tls.startswith("OpenSSL"):
-+ return
-+ elif tls.startswith("internal"):
-+ return
-+ elif tls.startswith("mbed TLS"):
-+ return
-+ else:
- raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
-
- def vht_supported():
---- /dev/null
-+++ b/tests/test-crypto_module.c
-@@ -0,0 +1,16 @@
-+/*
-+ * crypto module tests - test program
-+ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/module_tests.h"
-+#include "crypto/crypto_module_tests.c"
-+
-+int main(int argc, char *argv[])
-+{
-+ return crypto_module_tests();
-+}
---- a/tests/test-https.c
-+++ b/tests/test-https.c
-@@ -75,7 +75,7 @@ static int https_client(int s, const cha
- struct tls_connection *conn;
- struct wpabuf *in, *out, *appl;
- int res = -1;
-- int need_more_data;
-+ int need_more_data = 0;
-
- os_memset(&conf, 0, sizeof(conf));
- conf.event_cb = https_tls_event_cb;
-@@ -93,8 +93,12 @@ static int https_client(int s, const cha
-
- for (;;) {
- appl = NULL;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- out = tls_connection_handshake2(tls, conn, in, &appl,
- &need_more_data);
-+#else
-+ out = tls_connection_handshake(tls, conn, in, &appl);
-+#endif
- wpabuf_free(in);
- in = NULL;
- if (out == NULL) {
-@@ -152,11 +156,15 @@ static int https_client(int s, const cha
-
- wpa_printf(MSG_INFO, "Reading HTTP response");
- for (;;) {
-- int need_more_data;
-+ int need_more_data = 0;
- in = https_recv(s);
- if (in == NULL)
- goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+ out = tls_connection_decrypt(tls, conn, in);
-+#endif
- if (need_more_data)
- wpa_printf(MSG_DEBUG, "HTTP: Need more data");
- wpabuf_free(in);
---- a/tests/test-https_server.c
-+++ b/tests/test-https_server.c
-@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s,
- }
-
-
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- static void https_tls_log_cb(void *ctx, const char *msg)
- {
- wpa_printf(MSG_DEBUG, "TLS: %s", msg);
- }
-+#endif
-
-
- static int https_server(int s)
-@@ -79,7 +81,7 @@ static int https_server(int s)
- void *tls;
- struct tls_connection_params params;
- struct tls_connection *conn;
-- struct wpabuf *in, *out, *appl;
-+ struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
- int res = -1;
-
- os_memset(&conf, 0, sizeof(conf));
-@@ -106,7 +108,9 @@ static int https_server(int s)
- return -1;
- }
-
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
-+#endif
-
- for (;;) {
- in = https_recv(s, 5000);
-@@ -147,12 +151,16 @@ static int https_server(int s)
-
- wpa_printf(MSG_INFO, "Reading HTTP request");
- for (;;) {
-- int need_more_data;
-+ int need_more_data = 0;
-
- in = https_recv(s, 5000);
- if (!in)
- goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+ out = tls_connection_decrypt(tls, conn, in);
-+#endif
- wpabuf_free(in);
- in = NULL;
- if (need_more_data) {
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -1122,6 +1122,7 @@ CFLAGS += -DCONFIG_TLSV12
- endif
-
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- ifdef TLS_FUNCS
- CFLAGS += -DWOLFSSL_DER_LOAD
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -1137,6 +1138,7 @@ LIBS_p += -lwolfssl -lm
- endif
-
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- ifdef TLS_FUNCS
- CFLAGS += -DEAP_TLS_OPENSSL
-@@ -1164,6 +1166,7 @@ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONF
- endif
-
- ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=mbedtls
- endif
-@@ -1183,6 +1186,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -1213,6 +1217,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -1293,6 +1298,7 @@ endif
- endif
-
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- OBJS_p += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/150-add-NULL-checks-encountered-during-tests-hwsim.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/150-add-NULL-checks-encountered-during-tests-hwsim.patch
deleted file mode 100644
index c8c3ff3..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/150-add-NULL-checks-encountered-during-tests-hwsim.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 33afce36c54b0cad38643629ded10ff5d727f077 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Fri, 12 Aug 2022 05:34:47 -0400
-Subject: [PATCH 5/7] add NULL checks (encountered during tests/hwsim)
-
-sae_derive_commit_element_ecc NULL pwe_ecc check
-dpp_gen_keypair() NULL curve check
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- src/common/dpp_crypto.c | 6 ++++++
- src/common/sae.c | 7 +++++++
- 2 files changed, 13 insertions(+)
-
---- a/src/common/dpp_crypto.c
-+++ b/src/common/dpp_crypto.c
-@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec
-
- struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
- {
-+ if (curve == NULL) {
-+ wpa_printf(MSG_DEBUG,
-+ "DPP: %s curve must be initialized", __func__);
-+ return NULL;
-+ }
-+
- struct crypto_ec_key *key;
-
- wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
---- a/src/common/sae.c
-+++ b/src/common/sae.c
-@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
- static int sae_derive_commit_element_ecc(struct sae_data *sae,
- struct crypto_bignum *mask)
- {
-+ if (sae->tmp->pwe_ecc == NULL) {
-+ wpa_printf(MSG_DEBUG,
-+ "SAE: %s sae->tmp->pwe_ecc must be initialized",
-+ __func__);
-+ return -1;
-+ }
-+
- /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
- if (!sae->tmp->own_commit_element_ecc) {
- sae->tmp->own_commit_element_ecc =
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/160-dpp_pkex-EC-point-mul-w-value-prime.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/160-dpp_pkex-EC-point-mul-w-value-prime.patch
deleted file mode 100644
index db4fcfe..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/160-dpp_pkex-EC-point-mul-w-value-prime.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 54211caa2e0e5163aefef390daf88a971367a702 Mon Sep 17 00:00:00 2001
-From: Glenn Strauss <gstrauss@gluelogic.com>
-Date: Tue, 4 Oct 2022 17:09:24 -0400
-Subject: [PATCH 6/7] dpp_pkex: EC point mul w/ value < prime
-
-crypto_ec_point_mul() with mbedtls requires point
-be multiplied by a multiplicand with value < prime
-
-Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com>
----
- src/common/dpp_crypto.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
---- a/src/common/dpp_crypto.c
-+++ b/src/common/dpp_crypto.c
-@@ -1588,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curv
- Pr = crypto_ec_key_get_public_key(Pr_key);
- Qr = crypto_ec_point_init(ec);
- hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
-- if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
-+ if (!Pr || !Qr || !hash_bn ||
-+ crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
-+ crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
- goto fail;
-
- if (crypto_ec_point_is_at_infinity(ec, Qr)) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch
deleted file mode 100644
index b0151b0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch
+++ /dev/null
@@ -1,141 +0,0 @@
-From d4c4ef302f98fd6bce173b8636e7e350d8b44981 Mon Sep 17 00:00:00 2001
-From: P Praneesh <ppranees@codeaurora.org>
-Date: Fri, 19 Mar 2021 12:17:27 +0530
-Subject: [PATCH] hostapd: update cfs0 and cfs1 for 160MHz
-
-As per standard Draft P802.11ax_D8.0,( Table 26-9—Setting
-of the VHT Channel Width and VHT NSS at an HE STA
-transmitting the OM Control subfield ), center frequency of
-160MHz should be published in HT information subset 2 of
-HT information when EXT NSS BW field is enabled.
-
-If the supported number of NSS in 160MHz is at least max NSS
-support, then center_freq_seg0 indicates the center frequency of 80MHz and
-center_freq_seg1 indicates the center frequency of 160MHz.
-
-If the supported number of NSS in 160MHz is less than max NSS
-support, then center_freq_seg0 indicates the center frequency of 80MHz and
-center_freq_seg1 is 0. The center frequency of 160MHz is published in HT
-operation information element instead.
-
-Signed-off-by: P Praneesh <ppranees@codeaurora.org>
----
- hostapd/config_file.c | 2 ++
- src/ap/ieee802_11_ht.c | 7 +++++++
- src/ap/ieee802_11_vht.c | 16 ++++++++++++++++
- src/common/hw_features_common.c | 1 +
- src/common/ieee802_11_defs.h | 1 +
- 5 files changed, 27 insertions(+)
-
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1153,6 +1153,8 @@ static int hostapd_config_vht_capab(stru
- conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
- if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
- conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
-+ if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
-+ conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
- return 0;
- }
- #endif /* CONFIG_IEEE80211AC */
---- a/src/ap/ieee802_11_ht.c
-+++ b/src/ap/ieee802_11_ht.c
-@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct
- u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- struct ieee80211_ht_operation *oper;
-+ le32 vht_capabilities_info;
- u8 *pos = eid;
-+ u8 chwidth;
-
- if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
- is_6ghz_op_class(hapd->iconf->op_class))
-@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hos
- oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
- HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
-
-+ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
-+ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
-+ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+ oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
-+ }
-+
- pos += sizeof(*oper);
-
- return pos;
---- a/src/ap/ieee802_11_vht.c
-+++ b/src/ap/ieee802_11_vht.c
-@@ -25,6 +25,7 @@ u8 * hostapd_eid_vht_capabilities(struct
- struct ieee80211_vht_capabilities *cap;
- struct hostapd_hw_modes *mode = hapd->iface->current_mode;
- u8 *pos = eid;
-+ u8 chwidth;
-
- if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
- return eid;
-@@ -62,6 +63,17 @@ u8 * hostapd_eid_vht_capabilities(struct
- host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
- }
-
-+ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+ if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+ cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
-+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
-+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
-+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
-+ } else {
-+ cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
-+ }
-+
- /* Supported MCS set comes from hw */
- os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
-
-@@ -74,6 +86,7 @@ u8 * hostapd_eid_vht_capabilities(struct
- u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- struct ieee80211_vht_operation *oper;
-+ le32 vht_capabilities_info;
- u8 *pos = eid;
- enum oper_chan_width oper_chwidth =
- hostapd_get_oper_chwidth(hapd->iconf);
-@@ -106,6 +119,7 @@ u8 * hostapd_eid_vht_operation(struct ho
- oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
-
- oper->vht_op_info_chwidth = oper_chwidth;
-+ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
- if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
- /*
- * Convert 160 MHz channel width to new style as interop
-@@ -119,6 +133,9 @@ u8 * hostapd_eid_vht_operation(struct ho
- oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
- else
- oper->vht_op_info_chan_center_freq_seg0_idx += 8;
-+
-+ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+ oper->vht_op_info_chan_center_freq_seg1_idx = 0;
- } else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
- /*
- * Convert 80+80 MHz channel width to new style as interop
---- a/src/common/hw_features_common.c
-+++ b/src/common/hw_features_common.c
-@@ -811,6 +811,7 @@ int ieee80211ac_cap_check(u32 hw, u32 co
- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
- VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
- VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
-+ VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
-
- #undef VHT_CAP_CHECK
- #undef VHT_CAP_CHECK_MAX
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -1349,6 +1349,8 @@ struct ieee80211_ampe_ie {
- #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27))
- #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28))
- #define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT ((u32) BIT(30))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK ((u32) BIT(30) | BIT(31))
-
- #define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1))
- #define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch
deleted file mode 100644
index 4929c58..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 14 Sep 2023 10:53:50 +0200
-Subject: [PATCH] driver_nl80211: fix setting QoS map on secondary BSSs
-
-The setting is per-BSS, not per PHY
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -11341,7 +11341,7 @@ static int nl80211_set_qos_map(void *pri
- wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
- qos_map_set, qos_map_set_len);
-
-- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
-+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_QOS_MAP)) ||
- nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
- nlmsg_free(msg);
- return -ENOBUFS;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch
deleted file mode 100644
index adfb21f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 14 Sep 2023 11:28:03 +0200
-Subject: [PATCH] driver_nl80211: update drv->ifindex on removing the first
- BSS
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -8867,6 +8867,7 @@ static int wpa_driver_nl80211_if_remove(
- if (drv->first_bss->next) {
- drv->first_bss = drv->first_bss->next;
- drv->ctx = drv->first_bss->ctx;
-+ drv->ifindex = drv->first_bss->ifindex;
- os_free(bss);
- } else {
- wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch
deleted file mode 100644
index 395c645..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 18 Sep 2023 16:47:41 +0200
-Subject: [PATCH] nl80211: move nl80211_put_freq_params call outside of
- 802.11ax #ifdef
-
-The relevance of this call is not specific to 802.11ax, so it should be done
-even with CONFIG_IEEE80211AX disabled.
-
-Fixes: b3921db426ea ("nl80211: Add frequency info in start AP command")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -5226,6 +5226,9 @@ static int wpa_driver_nl80211_set_ap(voi
- nla_nest_end(msg, ftm);
- }
-
-+ if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
-+ goto fail;
-+
- #ifdef CONFIG_IEEE80211AX
- if (params->he_spr_ctrl) {
- struct nlattr *spr;
-@@ -5260,9 +5263,6 @@ static int wpa_driver_nl80211_set_ap(voi
- nla_nest_end(msg, spr);
- }
-
-- if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
-- goto fail;
--
- if (params->freq && params->freq->he_enabled) {
- struct nlattr *bss_color;
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch
deleted file mode 100644
index fe81318..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 20 Sep 2023 13:41:10 +0200
-Subject: [PATCH] hostapd: cancel channel_list_update_timeout in
- hostapd_cleanup_iface_partial
-
-Fixes a crash when disabling an interface during channel list update
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -569,6 +569,7 @@ static void sta_track_deinit(struct host
- void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
- {
- wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
- #ifdef NEED_AP_MLME
- hostapd_stop_setup_timers(iface);
- #endif /* NEED_AP_MLME */
-@@ -598,7 +599,6 @@ void hostapd_cleanup_iface_partial(struc
- static void hostapd_cleanup_iface(struct hostapd_iface *iface)
- {
- wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-- eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
- eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
- NULL);
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/200-multicall.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/200-multicall.patch
deleted file mode 100644
index e3ed00f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/200-multicall.patch
+++ /dev/null
@@ -1,355 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -1,6 +1,7 @@
- ALL=hostapd hostapd_cli
- CONFIG_FILE = .config
-
-+-include $(if $(MULTICALL), ../wpa_supplicant/.config)
- include ../src/build.rules
-
- ifdef LIBS
-@@ -199,7 +200,8 @@ endif
-
- ifdef CONFIG_NO_VLAN
- CFLAGS += -DCONFIG_NO_VLAN
--else
-+endif
-+ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
- OBJS += ../src/ap/vlan_init.o
- OBJS += ../src/ap/vlan_ifconfig.o
- OBJS += ../src/ap/vlan.o
-@@ -357,10 +359,14 @@ CFLAGS += -DCONFIG_MBO
- OBJS += ../src/ap/mbo_ap.o
- endif
-
-+ifndef MULTICALL
-+CFLAGS += -DNO_SUPPLICANT
-+endif
-+
- include ../src/drivers/drivers.mak
--OBJS += $(DRV_AP_OBJS)
--CFLAGS += $(DRV_AP_CFLAGS)
--LDFLAGS += $(DRV_AP_LDFLAGS)
-+OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
-+CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
-+LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
- LIBS += $(DRV_AP_LIBS)
-
- ifdef CONFIG_L2_PACKET
-@@ -1380,6 +1386,12 @@ install: $(addprefix $(DESTDIR)$(BINDIR)
- _OBJS_VAR := OBJS
- include ../src/objs.mk
-
-+hostapd_multi.a: $(BCHECK) $(OBJS)
-+ $(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
-+ @$(E) " CC " $<
-+ @rm -f $@
-+ @$(AR) cr $@ hostapd_multi.o $(OBJS)
-+
- hostapd: $(OBJS)
- $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
- @$(E) " LD " $@
-@@ -1460,6 +1472,12 @@ include ../src/objs.mk
- _OBJS_VAR := SOBJS
- include ../src/objs.mk
-
-+dump_cflags:
-+ @printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- nt_password_hash: $(NOBJS)
- $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
- @$(E) " LD " $@
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.servic
- EXTRA_TARGETS=dynamic_eap_methods
-
- CONFIG_FILE=.config
-+-include $(if $(MULTICALL),../hostapd/.config)
- include ../src/build.rules
-
- ifdef CONFIG_BUILD_PASN_SO
-@@ -382,7 +383,9 @@ endif
- ifdef CONFIG_IBSS_RSN
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_IBSS_RSN
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ibss_rsn.o
- endif
-
-@@ -924,6 +927,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
- CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
- LIBS += -ldl -rdynamic
- endif
-+else
-+ ifdef MULTICALL
-+ OBJS += ../src/eap_common/eap_common.o
-+ endif
- endif
-
- ifdef CONFIG_AP
-@@ -931,9 +938,11 @@ NEED_EAP_COMMON=y
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_AP
- OBJS += ap.o
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
- CFLAGS += -DCONFIG_NO_ACCOUNTING
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ../src/ap/hostapd.o
- OBJS += ../src/ap/wpa_auth_glue.o
- OBJS += ../src/ap/utils.o
-@@ -1022,6 +1031,12 @@ endif
- ifdef CONFIG_HS20
- OBJS += ../src/ap/hs20.o
- endif
-+else
-+ ifdef MULTICALL
-+ OBJS += ../src/eap_server/eap_server.o
-+ OBJS += ../src/eap_server/eap_server_identity.o
-+ OBJS += ../src/eap_server/eap_server_methods.o
-+ endif
- endif
-
- ifdef CONFIG_MBO
-@@ -1030,7 +1045,9 @@ CFLAGS += -DCONFIG_MBO
- endif
-
- ifdef NEED_RSN_AUTHENTICATOR
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
-+endif
- NEED_AES_WRAP=y
- OBJS += ../src/ap/wpa_auth.o
- OBJS += ../src/ap/wpa_auth_ie.o
-@@ -2010,6 +2027,12 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
-
- _OBJS_VAR := OBJS
- include ../src/objs.mk
-+wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
-+ $(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
-+ @$(E) " CC " $<
-+ @rm -f $@
-+ @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
-+
- wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
- $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
- @$(E) " LD " $@
-@@ -2142,6 +2165,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
- $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
- @$(E) " sed" $<
-
-+dump_cflags:
-+ @printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+ @printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- wpa_supplicant.exe: wpa_supplicant
- mv -f $< $@
- wpa_cli.exe: wpa_cli
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -6667,8 +6667,8 @@ union wpa_event_data {
- * Driver wrapper code should call this function whenever an event is received
- * from the driver.
- */
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data);
-+extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- /**
- * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
-@@ -6680,7 +6680,7 @@ void wpa_supplicant_event(void *ctx, enu
- * Same as wpa_supplicant_event(), but we search for the interface in
- * wpa_global.
- */
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data);
-
- /*
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2184,8 +2184,8 @@ err:
- #endif /* CONFIG_OWE */
-
-
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data)
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data)
- {
- struct hostapd_data *hapd = ctx;
- #ifndef CONFIG_NO_STDOUT_DEBUG
-@@ -2489,7 +2489,7 @@ void wpa_supplicant_event(void *ctx, enu
- }
-
-
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data)
- {
- struct hapd_interfaces *interfaces = ctx;
---- a/wpa_supplicant/wpa_priv.c
-+++ b/wpa_supplicant/wpa_priv.c
-@@ -1039,8 +1039,8 @@ static void wpa_priv_send_ft_response(st
- }
-
-
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data)
-+static void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data)
- {
- struct wpa_priv_interface *iface = ctx;
-
-@@ -1103,7 +1103,7 @@ void wpa_supplicant_event(void *ctx, enu
- }
-
-
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data)
- {
- struct wpa_priv_global *global = ctx;
-@@ -1217,6 +1217,8 @@ int main(int argc, char *argv[])
- if (os_program_init())
- return -1;
-
-+ wpa_supplicant_event = supplicant_event;
-+ wpa_supplicant_event_global = supplicant_event_global;
- wpa_priv_fd_workaround();
-
- os_memset(&global, 0, sizeof(global));
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -5353,8 +5353,8 @@ static void wpas_link_reconfig(struct wp
- }
-
-
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-- union wpa_event_data *data)
-+void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data)
- {
- struct wpa_supplicant *wpa_s = ctx;
- int resched;
-@@ -6272,7 +6272,7 @@ void wpa_supplicant_event(void *ctx, enu
- }
-
-
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data)
- {
- struct wpa_supplicant *wpa_s;
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -7462,7 +7462,6 @@ struct wpa_interface * wpa_supplicant_ma
- return NULL;
- }
-
--
- /**
- * wpa_supplicant_match_existing - Match existing interfaces
- * @global: Pointer to global data from wpa_supplicant_init()
-@@ -7497,6 +7496,11 @@ static int wpa_supplicant_match_existing
-
- #endif /* CONFIG_MATCH_IFACE */
-
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- /**
- * wpa_supplicant_add_iface - Add a new network interface
-@@ -7753,6 +7757,8 @@ struct wpa_global * wpa_supplicant_init(
- #ifndef CONFIG_NO_WPA_MSG
- wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
- #endif /* CONFIG_NO_WPA_MSG */
-+ wpa_supplicant_event = supplicant_event;
-+ wpa_supplicant_event_global = supplicant_event_global;
-
- if (params->wpa_debug_file_path)
- wpa_debug_open_file(params->wpa_debug_file_path);
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -698,6 +698,11 @@ fail:
- return -1;
- }
-
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- #ifdef CONFIG_WPS
- static int gen_uuid(const char *txt_addr)
-@@ -791,6 +796,8 @@ int main(int argc, char *argv[])
- return -1;
- #endif /* CONFIG_DPP */
-
-+ wpa_supplicant_event = hostapd_wpa_event;
-+ wpa_supplicant_event_global = hostapd_wpa_event_global;
- for (;;) {
- c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
- if (c < 0)
---- a/src/drivers/drivers.c
-+++ b/src/drivers/drivers.c
-@@ -10,6 +10,10 @@
- #include "utils/common.h"
- #include "driver.h"
-
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- const struct wpa_driver_ops *const wpa_drivers[] =
- {
---- a/wpa_supplicant/eapol_test.c
-+++ b/wpa_supplicant/eapol_test.c
-@@ -31,7 +31,12 @@
- #include "ctrl_iface.h"
- #include "pcsc_funcs.h"
- #include "wpas_glue.h"
-+#include "drivers/driver.h"
-
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
-
-@@ -1303,6 +1308,10 @@ static void usage(void)
- "option several times.\n");
- }
-
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+ union wpa_event_data *data);
-
- int main(int argc, char *argv[])
- {
-@@ -1323,6 +1332,8 @@ int main(int argc, char *argv[])
- if (os_program_init())
- return -1;
-
-+ wpa_supplicant_event = supplicant_event;
-+ wpa_supplicant_event_global = supplicant_event_global;
- hostapd_logger_register_cb(hostapd_logger_cb);
-
- os_memset(&eapol_test, 0, sizeof(eapol_test));
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/300-noscan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/300-noscan.patch
deleted file mode 100644
index 3b5f432..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/300-noscan.patch
+++ /dev/null
@@ -1,58 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3448,6 +3448,10 @@ static int hostapd_config_fill(struct ho
- if (bss->ocv && !bss->ieee80211w)
- bss->ieee80211w = 1;
- #endif /* CONFIG_OCV */
-+ } else if (os_strcmp(buf, "noscan") == 0) {
-+ conf->noscan = atoi(pos);
-+ } else if (os_strcmp(buf, "ht_coex") == 0) {
-+ conf->no_ht_coex = !atoi(pos);
- } else if (os_strcmp(buf, "ieee80211n") == 0) {
- conf->ieee80211n = atoi(pos);
- } else if (os_strcmp(buf, "ht_capab") == 0) {
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1075,6 +1075,8 @@ struct hostapd_config {
-
- int ht_op_mode_fixed;
- u16 ht_capab;
-+ int noscan;
-+ int no_ht_coex;
- int ieee80211n;
- int secondary_channel;
- int no_pri_sec_switch;
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -546,7 +546,8 @@ static int ieee80211n_check_40mhz(struct
- int ret;
-
- /* Check that HT40 is used and PRI / SEC switch is allowed */
-- if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
-+ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
-+ iface->conf->noscan)
- return 0;
-
- hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
---- a/src/ap/ieee802_11_ht.c
-+++ b/src/ap/ieee802_11_ht.c
-@@ -239,6 +239,9 @@ void hostapd_2040_coex_action(struct hos
- return;
- }
-
-+ if (iface->conf->noscan || iface->conf->no_ht_coex)
-+ return;
-+
- if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
- wpa_printf(MSG_DEBUG,
- "Ignore too short 20/40 BSS Coexistence Management frame");
-@@ -399,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_
- if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
- return;
-
-+ if (iface->conf->noscan || iface->conf->no_ht_coex)
-+ return;
-+
- wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
- " in Association Request", MAC2STR(sta->addr));
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/301-mesh-noscan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/301-mesh-noscan.patch
deleted file mode 100644
index ceb6d0c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/301-mesh-noscan.patch
+++ /dev/null
@@ -1,80 +0,0 @@
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -2600,6 +2600,7 @@ static const struct parse_data ssid_fiel
- #else /* CONFIG_MESH */
- { INT_RANGE(mode, 0, 4) },
- #endif /* CONFIG_MESH */
-+ { INT_RANGE(noscan, 0, 1) },
- { INT_RANGE(proactive_key_caching, 0, 1) },
- { INT_RANGE(disabled, 0, 2) },
- { STR(id_str) },
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -775,6 +775,7 @@ static void wpa_config_write_network(FIL
- #endif /* IEEE8021X_EAPOL */
- INT(mode);
- INT(no_auto_peer);
-+ INT(noscan);
- INT(mesh_fwding);
- INT(frequency);
- INT(enable_edmg);
---- a/wpa_supplicant/mesh.c
-+++ b/wpa_supplicant/mesh.c
-@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(stru
- frequency);
- goto out_free;
- }
-+ if (conf->noscan)
-+ ssid->noscan = 1;
-
- if (ssid->mesh_basic_rates == NULL) {
- /*
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -2710,7 +2710,7 @@ static bool ibss_mesh_can_use_vht(struct
- const struct wpa_ssid *ssid,
- struct hostapd_hw_modes *mode)
- {
-- if (mode->mode != HOSTAPD_MODE_IEEE80211A)
-+ if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
- return false;
-
- if (!drv_supports_vht(wpa_s, ssid))
-@@ -2783,7 +2783,7 @@ static void ibss_mesh_select_40mhz(struc
- int i, res;
- unsigned int j;
- static const int ht40plus[] = {
-- 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 165, 173,
-+ 1, 2, 3, 4, 5, 6, 7, 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 165, 173,
- 184, 192
- };
- int ht40 = -1;
-@@ -3033,7 +3033,7 @@ void ibss_mesh_setup_freq(struct wpa_sup
- int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
- enum hostapd_hw_mode hw_mode;
- struct hostapd_hw_modes *mode = NULL;
-- int i, obss_scan = 1;
-+ int i, obss_scan = !(ssid->noscan);
- u8 channel;
- bool is_6ghz;
- bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
-@@ -3080,6 +3080,8 @@ void ibss_mesh_setup_freq(struct wpa_sup
- freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
- ieee80211_mode);
- freq->channel = channel;
-+ if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
-+ ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- /* Setup higher BW only for 5 GHz */
- if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
- ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
---- a/wpa_supplicant/config_ssid.h
-+++ b/wpa_supplicant/config_ssid.h
-@@ -1035,6 +1035,8 @@ struct wpa_ssid {
- */
- int no_auto_peer;
-
-+ int noscan;
-+
- /**
- * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
- *
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/310-rescan_immediately.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/310-rescan_immediately.patch
deleted file mode 100644
index 6e0244b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/310-rescan_immediately.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -5769,7 +5769,7 @@ wpa_supplicant_alloc(struct wpa_supplica
- if (wpa_s == NULL)
- return NULL;
- wpa_s->scan_req = INITIAL_SCAN_REQ;
-- wpa_s->scan_interval = 5;
-+ wpa_s->scan_interval = 1;
- wpa_s->new_connection = 1;
- wpa_s->parent = parent ? parent : wpa_s;
- wpa_s->p2pdev = wpa_s->parent;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/320-optional_rfkill.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/320-optional_rfkill.patch
deleted file mode 100644
index 0153779..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/320-optional_rfkill.patch
+++ /dev/null
@@ -1,61 +0,0 @@
---- a/src/drivers/drivers.mak
-+++ b/src/drivers/drivers.mak
-@@ -54,7 +54,6 @@ NEED_SME=y
- NEED_AP_MLME=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- NEED_RADIOTAP=y
- NEED_LIBNL=y
- endif
-@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
- CONFIG_WIRELESS_EXTENSION=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- endif
-
- ifdef CONFIG_DRIVER_NDIS
-@@ -137,7 +135,6 @@ endif
- ifdef CONFIG_WIRELESS_EXTENSION
- DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
- DRV_WPA_OBJS += ../src/drivers/driver_wext.o
--NEED_RFKILL=y
- endif
-
- ifdef NEED_NETLINK
-@@ -146,6 +143,7 @@ endif
-
- ifdef NEED_RFKILL
- DRV_OBJS += ../src/drivers/rfkill.o
-+DRV_WPA_CFLAGS += -DCONFIG_RFKILL
- endif
-
- ifdef NEED_RADIOTAP
---- a/src/drivers/rfkill.h
-+++ b/src/drivers/rfkill.h
-@@ -18,8 +18,24 @@ struct rfkill_config {
- void (*unblocked_cb)(void *ctx);
- };
-
-+#ifdef CONFIG_RFKILL
- struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
- void rfkill_deinit(struct rfkill_data *rfkill);
- int rfkill_is_blocked(struct rfkill_data *rfkill);
-+#else
-+static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
-+{
-+ return (void *) 1;
-+}
-+
-+static inline void rfkill_deinit(struct rfkill_data *rfkill)
-+{
-+}
-+
-+static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
-+{
-+ return 0;
-+}
-+#endif
-
- #endif /* RFKILL_H */
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/330-nl80211_fix_set_freq.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/330-nl80211_fix_set_freq.patch
deleted file mode 100644
index c11c957..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/330-nl80211_fix_set_freq.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -5407,7 +5407,7 @@ static int nl80211_set_channel(struct i8
- freq->he_enabled, freq->eht_enabled, freq->bandwidth,
- freq->center_freq1, freq->center_freq2);
-
-- msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
-+ msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
- NL80211_CMD_SET_WIPHY);
- if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
- nlmsg_free(msg);
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/341-mesh-ctrl-iface-channel-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/341-mesh-ctrl-iface-channel-switch.patch
deleted file mode 100644
index 8784452..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/341-mesh-ctrl-iface-channel-switch.patch
+++ /dev/null
@@ -1,39 +0,0 @@
---- a/wpa_supplicant/ap.c
-+++ b/wpa_supplicant/ap.c
-@@ -1825,15 +1825,35 @@ int ap_switch_channel(struct wpa_supplic
-
-
- #ifdef CONFIG_CTRL_IFACE
-+
-+static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
-+ struct csa_settings *settings)
-+{
-+#ifdef NEED_AP_MLME
-+ if (!iface || !iface->bss[0])
-+ return 0;
-+
-+ return hostapd_switch_channel(iface->bss[0], settings);
-+#else
-+ return -1;
-+#endif
-+}
-+
-+
- int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
- {
- struct csa_settings settings;
- int ret = hostapd_parse_csa_settings(pos, &settings);
-
-+ if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
-+ !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
-+ return -1;
-+
-+ ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
- if (ret)
- return ret;
-
-- return ap_switch_channel(wpa_s, &settings);
-+ return __ap_ctrl_iface_chanswitch(wpa_s->ifmsh, &settings);
- }
- #endif /* CONFIG_CTRL_IFACE */
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/350-nl80211_del_beacon_bss.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/350-nl80211_del_beacon_bss.patch
deleted file mode 100644
index 647ca2c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/350-nl80211_del_beacon_bss.patch
+++ /dev/null
@@ -1,35 +0,0 @@
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -3008,12 +3008,12 @@ static int wpa_driver_nl80211_del_beacon
- return 0;
-
- wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
-- drv->ifindex);
-+ bss->ifindex);
- link->beacon_set = 0;
- link->freq = 0;
-
- nl80211_put_wiphy_data_ap(bss);
-- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
-+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
- if (!msg)
- return -ENOBUFS;
-
-@@ -6100,7 +6100,7 @@ static void nl80211_teardown_ap(struct i
- nl80211_mgmt_unsubscribe(bss, "AP teardown");
-
- nl80211_put_wiphy_data_ap(bss);
-- bss->flink->beacon_set = 0;
-+ wpa_driver_nl80211_del_beacon_all(bss);
- }
-
-
-@@ -8859,8 +8859,6 @@ static int wpa_driver_nl80211_if_remove(
- } else {
- wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
- nl80211_teardown_ap(bss);
-- if (!bss->added_if && !drv->first_bss->next)
-- wpa_driver_nl80211_del_beacon_all(bss);
- nl80211_destroy_bss(bss);
- if (!bss->added_if)
- i802_set_iface_flags(bss, 0);
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/380-disable_ctrl_iface_mib.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/380-disable_ctrl_iface_mib.patch
deleted file mode 100644
index f7720fc..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/380-disable_ctrl_iface_mib.patch
+++ /dev/null
@@ -1,239 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -221,6 +221,9 @@ endif
- ifdef CONFIG_NO_CTRL_IFACE
- CFLAGS += -DCONFIG_NO_CTRL_IFACE
- else
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- ifeq ($(CONFIG_CTRL_IFACE), udp)
- CFLAGS += -DCONFIG_CTRL_IFACE_UDP
- else
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3314,6 +3314,7 @@ static int hostapd_ctrl_iface_receive_pr
- reply_size);
- } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
- reply_len = hostapd_drv_status(hapd, reply, reply_size);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strcmp(buf, "MIB") == 0) {
- reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
- if (reply_len >= 0) {
-@@ -3355,6 +3356,7 @@ static int hostapd_ctrl_iface_receive_pr
- } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
- reply_size);
-+#endif
- } else if (os_strcmp(buf, "ATTACH") == 0) {
- if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
- reply_len = -1;
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -983,6 +983,9 @@ ifdef CONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- endif
- ifdef CONFIG_CTRL_IFACE
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- OBJS += ../src/ap/ctrl_iface_ap.o
- endif
-
---- a/wpa_supplicant/ctrl_iface.c
-+++ b/wpa_supplicant/ctrl_iface.c
-@@ -2326,7 +2326,7 @@ static int wpa_supplicant_ctrl_iface_sta
- pos += ret;
- }
-
--#ifdef CONFIG_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
- if (wpa_s->ap_iface) {
- pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
- end - pos,
-@@ -12087,6 +12087,7 @@ char * wpa_supplicant_ctrl_iface_process
- reply_len = -1;
- } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
- wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strcmp(buf, "MIB") == 0) {
- reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
- if (reply_len >= 0) {
-@@ -12099,6 +12100,7 @@ char * wpa_supplicant_ctrl_iface_process
- reply_size - reply_len);
- #endif /* CONFIG_MACSEC */
- }
-+#endif
- } else if (os_strncmp(buf, "STATUS", 6) == 0) {
- reply_len = wpa_supplicant_ctrl_iface_status(
- wpa_s, buf + 6, reply, reply_size);
-@@ -12587,6 +12589,7 @@ char * wpa_supplicant_ctrl_iface_process
- reply_len = wpa_supplicant_ctrl_iface_bss(
- wpa_s, buf + 4, reply, reply_size);
- #ifdef CONFIG_AP
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strcmp(buf, "STA-FIRST") == 0) {
- reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
- } else if (os_strncmp(buf, "STA ", 4) == 0) {
-@@ -12595,12 +12598,15 @@ char * wpa_supplicant_ctrl_iface_process
- } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
- reply_size);
-+#endif
-+#ifdef CONFIG_CTRL_IFACE_MIB
- } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
- if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
- reply_len = -1;
- } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
- if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
- reply_len = -1;
-+#endif
- } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
- if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
- reply_len = -1;
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -26,6 +26,26 @@
- #include "taxonomy.h"
- #include "wnm_ap.h"
-
-+static const char * hw_mode_str(enum hostapd_hw_mode mode)
-+{
-+ switch (mode) {
-+ case HOSTAPD_MODE_IEEE80211B:
-+ return "b";
-+ case HOSTAPD_MODE_IEEE80211G:
-+ return "g";
-+ case HOSTAPD_MODE_IEEE80211A:
-+ return "a";
-+ case HOSTAPD_MODE_IEEE80211AD:
-+ return "ad";
-+ case HOSTAPD_MODE_IEEE80211ANY:
-+ return "any";
-+ case NUM_HOSTAPD_MODES:
-+ return "invalid";
-+ }
-+ return "unknown";
-+}
-+
-+#ifdef CONFIG_CTRL_IFACE_MIB
-
- static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
- size_t curr_len, const u8 *mcs_set)
-@@ -212,26 +232,6 @@ static const char * timeout_next_str(int
- }
-
-
--static const char * hw_mode_str(enum hostapd_hw_mode mode)
--{
-- switch (mode) {
-- case HOSTAPD_MODE_IEEE80211B:
-- return "b";
-- case HOSTAPD_MODE_IEEE80211G:
-- return "g";
-- case HOSTAPD_MODE_IEEE80211A:
-- return "a";
-- case HOSTAPD_MODE_IEEE80211AD:
-- return "ad";
-- case HOSTAPD_MODE_IEEE80211ANY:
-- return "any";
-- case NUM_HOSTAPD_MODES:
-- return "invalid";
-- }
-- return "unknown";
--}
--
--
- static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
- struct sta_info *sta,
- char *buf, size_t buflen)
-@@ -493,6 +493,7 @@ int hostapd_ctrl_iface_sta_next(struct h
- return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
- }
-
-+#endif
-
- #ifdef CONFIG_P2P_MANAGER
- static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
-@@ -884,12 +885,12 @@ int hostapd_ctrl_iface_status(struct hos
- return len;
- len += ret;
- }
--
-+#ifdef CONFIG_CTRL_IFACE_MIB
- if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
- len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
- mode->mcs_set);
- }
--
-+#endif /* CONFIG_CTRL_IFACE_MIB */
- if (iface->current_rates && iface->num_rates) {
- ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
- if (os_snprintf_error(buflen - len, ret))
---- a/src/ap/ieee802_1x.c
-+++ b/src/ap/ieee802_1x.c
-@@ -2834,6 +2834,7 @@ static const char * bool_txt(bool val)
- return val ? "TRUE" : "FALSE";
- }
-
-+#ifdef CONFIG_CTRL_IFACE_MIB
-
- int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
- {
-@@ -3020,6 +3021,7 @@ int ieee802_1x_get_mib_sta(struct hostap
- return len;
- }
-
-+#endif
-
- #ifdef CONFIG_HS20
- static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -5328,6 +5328,7 @@ static const char * wpa_bool_txt(int val
- return val ? "TRUE" : "FALSE";
- }
-
-+#ifdef CONFIG_CTRL_IFACE_MIB
-
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
-@@ -5480,7 +5481,7 @@ int wpa_get_mib_sta(struct wpa_state_mac
-
- return len;
- }
--
-+#endif
-
- void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
- {
---- a/src/rsn_supp/wpa.c
-+++ b/src/rsn_supp/wpa.c
-@@ -3834,6 +3834,8 @@ static u32 wpa_key_mgmt_suite(struct wpa
- }
-
-
-+#ifdef CONFIG_CTRL_IFACE_MIB
-+
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
- ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
-@@ -3915,6 +3917,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, ch
-
- return (int) len;
- }
-+#endif
- #endif /* CONFIG_CTRL_IFACE */
-
-
---- a/wpa_supplicant/ap.c
-+++ b/wpa_supplicant/ap.c
-@@ -1499,7 +1499,7 @@ int wpas_ap_wps_nfc_report_handover(stru
- #endif /* CONFIG_WPS */
-
-
--#ifdef CONFIG_CTRL_IFACE
-+#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
-
- int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
- char *buf, size_t buflen)
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/381-hostapd_cli_UNKNOWN-COMMAND.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/381-hostapd_cli_UNKNOWN-COMMAND.patch
deleted file mode 100644
index e9083f6..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/381-hostapd_cli_UNKNOWN-COMMAND.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -757,7 +757,7 @@ static int wpa_ctrl_command_sta(struct w
- }
-
- buf[len] = '\0';
-- if (memcmp(buf, "FAIL", 4) == 0)
-+ if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
- return -1;
- if (print)
- printf("%s", buf);
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/390-wpa_ie_cap_workaround.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/390-wpa_ie_cap_workaround.patch
deleted file mode 100644
index 4592c34..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/390-wpa_ie_cap_workaround.patch
+++ /dev/null
@@ -1,56 +0,0 @@
---- a/src/common/wpa_common.c
-+++ b/src/common/wpa_common.c
-@@ -2841,6 +2841,31 @@ u32 wpa_akm_to_suite(int akm)
- }
-
-
-+static void wpa_fixup_wpa_ie_rsn(u8 *assoc_ie, const u8 *wpa_msg_ie,
-+ size_t rsn_ie_len)
-+{
-+ int pos, count;
-+
-+ pos = sizeof(struct rsn_ie_hdr) + RSN_SELECTOR_LEN;
-+ if (rsn_ie_len < pos + 2)
-+ return;
-+
-+ count = WPA_GET_LE16(wpa_msg_ie + pos);
-+ pos += 2 + count * RSN_SELECTOR_LEN;
-+ if (rsn_ie_len < pos + 2)
-+ return;
-+
-+ count = WPA_GET_LE16(wpa_msg_ie + pos);
-+ pos += 2 + count * RSN_SELECTOR_LEN;
-+ if (rsn_ie_len < pos + 2)
-+ return;
-+
-+ if (!assoc_ie[pos] && !assoc_ie[pos + 1] &&
-+ (wpa_msg_ie[pos] || wpa_msg_ie[pos + 1]))
-+ memcpy(&assoc_ie[pos], &wpa_msg_ie[pos], 2);
-+}
-+
-+
- int wpa_compare_rsn_ie(int ft_initial_assoc,
- const u8 *ie1, size_t ie1len,
- const u8 *ie2, size_t ie2len)
-@@ -2848,8 +2873,19 @@ int wpa_compare_rsn_ie(int ft_initial_as
- if (ie1 == NULL || ie2 == NULL)
- return -1;
-
-- if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
-- return 0; /* identical IEs */
-+ if (ie1len == ie2len) {
-+ u8 *ie_tmp;
-+
-+ if (os_memcmp(ie1, ie2, ie1len) == 0)
-+ return 0; /* identical IEs */
-+
-+ ie_tmp = alloca(ie1len);
-+ memcpy(ie_tmp, ie1, ie1len);
-+ wpa_fixup_wpa_ie_rsn(ie_tmp, ie2, ie1len);
-+
-+ if (os_memcmp(ie_tmp, ie2, ie1len) == 0)
-+ return 0; /* only mismatch in RSN capabilties */
-+ }
-
- #ifdef CONFIG_IEEE80211R
- if (ft_initial_assoc) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/400-wps_single_auth_enc_type.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/400-wps_single_auth_enc_type.patch
deleted file mode 100644
index edcd985..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/400-wps_single_auth_enc_type.patch
+++ /dev/null
@@ -1,23 +0,0 @@
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(s
- bss->wpa_pairwise |= WPA_CIPHER_GCMP;
- else
- bss->wpa_pairwise |= WPA_CIPHER_CCMP;
-- }
- #ifndef CONFIG_NO_TKIP
-- if (cred->encr_type & WPS_ENCR_TKIP)
-+ } else if (cred->encr_type & WPS_ENCR_TKIP)
- bss->wpa_pairwise |= WPA_CIPHER_TKIP;
- #endif /* CONFIG_NO_TKIP */
- bss->rsn_pairwise = bss->wpa_pairwise;
-@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data
- WPA_CIPHER_GCMP_256)) {
- wps->encr_types |= WPS_ENCR_AES;
- wps->encr_types_rsn |= WPS_ENCR_AES;
-- }
-- if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
-+ } else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
- #ifdef CONFIG_NO_TKIP
- wpa_printf(MSG_INFO, "WPS: TKIP not supported");
- goto fail;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/410-limit_debug_messages.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/410-limit_debug_messages.patch
deleted file mode 100644
index 48a5589..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/410-limit_debug_messages.patch
+++ /dev/null
@@ -1,210 +0,0 @@
---- a/src/utils/wpa_debug.c
-+++ b/src/utils/wpa_debug.c
-@@ -206,7 +206,7 @@ void wpa_debug_close_linux_tracing(void)
- *
- * Note: New line '\n' is added to the end of the text when printing to stdout.
- */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- {
- va_list ap;
-
-@@ -255,7 +255,7 @@ void wpa_printf(int level, const char *f
- }
-
-
--static void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
- size_t len, int show, int only_syslog)
- {
- size_t i;
-@@ -382,19 +382,7 @@ static void _wpa_hexdump(int level, cons
- #endif /* CONFIG_ANDROID_LOG */
- }
-
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
--{
-- _wpa_hexdump(level, title, buf, len, 1, 0);
--}
--
--
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
--{
-- _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
--}
--
--
--static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
- size_t len, int show)
- {
- size_t i, llen;
-@@ -507,20 +495,6 @@ file_done:
- }
-
-
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
-- size_t len)
--{
-- _wpa_hexdump_ascii(level, title, buf, len, 1);
--}
--
--
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
-- size_t len)
--{
-- _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
--}
--
--
- #ifdef CONFIG_DEBUG_FILE
- static char *last_path = NULL;
- #endif /* CONFIG_DEBUG_FILE */
-@@ -644,7 +618,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_
- }
-
-
--void wpa_msg(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...)
- {
- va_list ap;
- char *buf;
-@@ -682,7 +656,7 @@ void wpa_msg(void *ctx, int level, const
- }
-
-
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- {
- va_list ap;
- char *buf;
---- a/src/utils/wpa_debug.h
-+++ b/src/utils/wpa_debug.h
-@@ -51,6 +51,17 @@ void wpa_debug_close_file(void);
- void wpa_debug_setup_stdout(void);
- void wpa_debug_stop_log(void);
-
-+/* internal */
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+ size_t len, int show, int only_syslog);
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+ size_t len, int show);
-+extern int wpa_debug_show_keys;
-+
-+#ifndef CONFIG_MSG_MIN_PRIORITY
-+#define CONFIG_MSG_MIN_PRIORITY 0
-+#endif
-+
- /**
- * wpa_debug_printf_timestamp - Print timestamp for debug output
- *
-@@ -71,9 +82,15 @@ void wpa_debug_print_timestamp(void);
- *
- * Note: New line '\n' is added to the end of the text when printing to stdout.
- */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- PRINTF_FORMAT(2, 3);
-
-+#define wpa_printf(level, ...) \
-+ do { \
-+ if (level >= CONFIG_MSG_MIN_PRIORITY) \
-+ _wpa_printf(level, __VA_ARGS__); \
-+ } while(0)
-+
- /**
- * wpa_hexdump - conditional hex dump
- * @level: priority level (MSG_*) of the message
-@@ -85,7 +102,13 @@ PRINTF_FORMAT(2, 3);
- * output may be directed to stdout, stderr, and/or syslog based on
- * configuration. The contents of buf is printed out has hex dump.
- */
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump(level, title, buf, len, 1, 1);
-+}
-
- static inline void wpa_hexdump_buf(int level, const char *title,
- const struct wpabuf *buf)
-@@ -107,7 +130,13 @@ static inline void wpa_hexdump_buf(int l
- * like wpa_hexdump(), but by default, does not include secret keys (passwords,
- * etc.) in debug output.
- */
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
-+}
-
- static inline void wpa_hexdump_buf_key(int level, const char *title,
- const struct wpabuf *buf)
-@@ -129,8 +158,14 @@ static inline void wpa_hexdump_buf_key(i
- * the hex numbers and ASCII characters (for printable range) are shown. 16
- * bytes per line will be shown.
- */
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
-- size_t len);
-+static inline void wpa_hexdump_ascii(int level, const char *title,
-+ const u8 *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump_ascii(level, title, buf, len, 1);
-+}
-
- /**
- * wpa_hexdump_ascii_key - conditional hex dump, hide keys
-@@ -146,8 +181,14 @@ void wpa_hexdump_ascii(int level, const
- * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
- * default, does not include secret keys (passwords, etc.) in debug output.
- */
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
-- size_t len);
-+static inline void wpa_hexdump_ascii_key(int level, const char *title,
-+ const u8 *buf, size_t len)
-+{
-+ if (level < CONFIG_MSG_MIN_PRIORITY)
-+ return;
-+
-+ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
-+}
-
- /*
- * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
-@@ -184,7 +225,12 @@ void wpa_hexdump_ascii_key(int level, co
- *
- * Note: New line '\n' is added to the end of the text when printing to stdout.
- */
--void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+#define wpa_msg(ctx, level, ...) \
-+ do { \
-+ if (level >= CONFIG_MSG_MIN_PRIORITY) \
-+ _wpa_msg(ctx, level, __VA_ARGS__); \
-+ } while(0)
-
- /**
- * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
-@@ -198,8 +244,13 @@ void wpa_msg(void *ctx, int level, const
- * attached ctrl_iface monitors. In other words, it can be used for frequent
- * events that do not need to be sent to syslog.
- */
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- PRINTF_FORMAT(3, 4);
-+#define wpa_msg_ctrl(ctx, level, ...) \
-+ do { \
-+ if (level >= CONFIG_MSG_MIN_PRIORITY) \
-+ _wpa_msg_ctrl(ctx, level, __VA_ARGS__); \
-+ } while(0)
-
- /**
- * wpa_msg_global - Global printf for ctrl_iface monitors
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/420-indicate-features.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/420-indicate-features.patch
deleted file mode 100644
index 07df8e5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/420-indicate-features.patch
+++ /dev/null
@@ -1,63 +0,0 @@
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -31,7 +31,7 @@
- #include "config_file.h"
- #include "eap_register.h"
- #include "ctrl_iface.h"
--
-+#include "build_features.h"
-
- struct hapd_global {
- void **drv_priv;
-@@ -799,7 +799,7 @@ int main(int argc, char *argv[])
- wpa_supplicant_event = hostapd_wpa_event;
- wpa_supplicant_event_global = hostapd_wpa_event_global;
- for (;;) {
-- c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
-+ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
- if (c < 0)
- break;
- switch (c) {
-@@ -836,6 +836,8 @@ int main(int argc, char *argv[])
- break;
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
- case 'v':
-+ if (optarg)
-+ exit(!has_feature(optarg));
- show_version();
- exit(1);
- case 'g':
---- a/wpa_supplicant/main.c
-+++ b/wpa_supplicant/main.c
-@@ -12,6 +12,7 @@
- #endif /* __linux__ */
-
- #include "common.h"
-+#include "build_features.h"
- #include "crypto/crypto.h"
- #include "fst/fst.h"
- #include "wpa_supplicant_i.h"
-@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
-
- for (;;) {
- c = getopt(argc, argv,
-- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
-+ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuv::W");
- if (c < 0)
- break;
- switch (c) {
-@@ -302,8 +303,12 @@ int main(int argc, char *argv[])
- break;
- #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
- case 'v':
-- printf("%s\n", wpa_supplicant_version);
-- exitcode = 0;
-+ if (optarg) {
-+ exitcode = !has_feature(optarg);
-+ } else {
-+ printf("%s\n", wpa_supplicant_version);
-+ exitcode = 0;
-+ }
- goto out;
- case 'W':
- params.wait_for_monitor++;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/430-hostapd_cli_ifdef.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/430-hostapd_cli_ifdef.patch
deleted file mode 100644
index a21f0bf..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/430-hostapd_cli_ifdef.patch
+++ /dev/null
@@ -1,56 +0,0 @@
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -401,7 +401,6 @@ static int hostapd_cli_cmd_disassociate(
- }
-
-
--#ifdef CONFIG_TAXONOMY
- static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
- {
-@@ -414,7 +413,6 @@ static int hostapd_cli_cmd_signature(str
- os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
- return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_TAXONOMY */
-
-
- static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
-@@ -431,7 +429,6 @@ static int hostapd_cli_cmd_sa_query(stru
- }
-
-
--#ifdef CONFIG_WPS
- static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
- {
-@@ -657,7 +654,6 @@ static int hostapd_cli_cmd_wps_config(st
- ssid_hex, argv[1]);
- return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_WPS */
-
-
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
-@@ -1610,13 +1606,10 @@ static const struct hostapd_cli_cmd host
- { "disassociate", hostapd_cli_cmd_disassociate,
- hostapd_complete_stations,
- "<addr> = disassociate a station" },
--#ifdef CONFIG_TAXONOMY
- { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
- "<addr> = get taxonomy signature for a station" },
--#endif /* CONFIG_TAXONOMY */
- { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
- "<addr> = send SA Query to a station" },
--#ifdef CONFIG_WPS
- { "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
- "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
- { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
-@@ -1641,7 +1634,6 @@ static const struct hostapd_cli_cmd host
- "<SSID> <auth> <encr> <key> = configure AP" },
- { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
- "= show current WPS status" },
--#endif /* CONFIG_WPS */
- { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
- "= send Disassociation Imminent notification" },
- { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/431-wpa_cli_ifdef.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/431-wpa_cli_ifdef.patch
deleted file mode 100644
index 65c31c5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/431-wpa_cli_ifdef.patch
+++ /dev/null
@@ -1,18 +0,0 @@
---- a/wpa_supplicant/wpa_cli.c
-+++ b/wpa_supplicant/wpa_cli.c
-@@ -26,6 +26,15 @@
- #include <cutils/properties.h>
- #endif /* ANDROID */
-
-+#ifndef CONFIG_P2P
-+#define CONFIG_P2P
-+#endif
-+#ifndef CONFIG_AP
-+#define CONFIG_AP
-+#endif
-+#ifndef CONFIG_MESH
-+#define CONFIG_MESH
-+#endif
-
- static const char *const wpa_cli_version =
- "wpa_cli v" VERSION_STR "\n"
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch
deleted file mode 100644
index dc19553..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From 4bb69d15477e0f2b00e166845341dc933de47c58 Mon Sep 17 00:00:00 2001
-From: Antonio Quartulli <ordex@autistici.org>
-Date: Sun, 3 Jun 2012 18:22:56 +0200
-Subject: [PATCHv2 601/602] wpa_supplicant: add new config params to be used
- with the ibss join command
-
-Signed-hostap: Antonio Quartulli <ordex@autistici.org>
----
- src/drivers/driver.h | 6 +++
- wpa_supplicant/config.c | 96 +++++++++++++++++++++++++++++++++++++++
- wpa_supplicant/config_ssid.h | 6 +++
- wpa_supplicant/wpa_supplicant.c | 23 +++++++---
- 4 files changed, 124 insertions(+), 7 deletions(-)
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -19,6 +19,7 @@
-
- #define WPA_SUPPLICANT_DRIVER_VERSION 4
-
-+#include "ap/sta_info.h"
- #include "common/defs.h"
- #include "common/ieee802_11_defs.h"
- #include "common/wpa_common.h"
-@@ -953,6 +954,9 @@ struct wpa_driver_associate_params {
- * responsible for selecting with which BSS to associate. */
- const u8 *bssid;
-
-+ unsigned char rates[WLAN_SUPP_RATES_MAX];
-+ int mcast_rate;
-+
- /**
- * bssid_hint - BSSID of a proposed AP
- *
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -18,6 +18,7 @@
- #include "eap_peer/eap.h"
- #include "p2p/p2p.h"
- #include "fst/fst.h"
-+#include "ap/sta_info.h"
- #include "config.h"
-
-
-@@ -2389,6 +2390,97 @@ static char * wpa_config_write_mac_value
- #endif /* NO_CONFIG_WRITE */
-
-
-+static int wpa_config_parse_mcast_rate(const struct parse_data *data,
-+ struct wpa_ssid *ssid, int line,
-+ const char *value)
-+{
-+ ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
-+
-+ return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_mcast_rate(const struct parse_data *data,
-+ struct wpa_ssid *ssid)
-+{
-+ char *value;
-+ int res;
-+
-+ if (!ssid->mcast_rate == 0)
-+ return NULL;
-+
-+ value = os_malloc(6); /* longest: 300.0 */
-+ if (value == NULL)
-+ return NULL;
-+ res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
-+ if (res < 0) {
-+ os_free(value);
-+ return NULL;
-+ }
-+ return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
-+static int wpa_config_parse_rates(const struct parse_data *data,
-+ struct wpa_ssid *ssid, int line,
-+ const char *value)
-+{
-+ int i;
-+ char *pos, *r, *sptr, *end;
-+ double rate;
-+
-+ pos = (char *)value;
-+ r = strtok_r(pos, ",", &sptr);
-+ i = 0;
-+ while (pos && i < WLAN_SUPP_RATES_MAX) {
-+ rate = 0.0;
-+ if (r)
-+ rate = strtod(r, &end);
-+ ssid->rates[i] = rate * 2;
-+ if (*end != '\0' || rate * 2 != ssid->rates[i])
-+ return 1;
-+
-+ i++;
-+ r = strtok_r(NULL, ",", &sptr);
-+ }
-+
-+ return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_rates(const struct parse_data *data,
-+ struct wpa_ssid *ssid)
-+{
-+ char *value, *pos;
-+ int res, i;
-+
-+ if (ssid->rates[0] <= 0)
-+ return NULL;
-+
-+ value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
-+ if (value == NULL)
-+ return NULL;
-+ pos = value;
-+ for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
-+ res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
-+ if (res < 0) {
-+ os_free(value);
-+ return NULL;
-+ }
-+ pos += res;
-+ }
-+ res = os_snprintf(pos, 6, "%.1f",
-+ (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
-+ if (res < 0) {
-+ os_free(value);
-+ return NULL;
-+ }
-+
-+ value[6 * WLAN_SUPP_RATES_MAX] = '\0';
-+ return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
- /* Helper macros for network block parser */
-
- #ifdef OFFSET
-@@ -2674,6 +2766,8 @@ static const struct parse_data ssid_fiel
- { INT(ap_max_inactivity) },
- { INT(dtim_period) },
- { INT(beacon_int) },
-+ { FUNC(rates) },
-+ { FUNC(mcast_rate) },
- #ifdef CONFIG_MACSEC
- { INT_RANGE(macsec_policy, 0, 1) },
- { INT_RANGE(macsec_integ_only, 0, 1) },
---- a/wpa_supplicant/config_ssid.h
-+++ b/wpa_supplicant/config_ssid.h
-@@ -10,8 +10,10 @@
- #define CONFIG_SSID_H
-
- #include "common/defs.h"
-+#include "ap/sta_info.h"
- #include "utils/list.h"
- #include "eap_peer/eap_config.h"
-+#include "drivers/nl80211_copy.h"
-
-
- #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
-@@ -879,6 +881,9 @@ struct wpa_ssid {
- */
- void *parent_cred;
-
-+ unsigned char rates[WLAN_SUPP_RATES_MAX];
-+ double mcast_rate;
-+
- #ifdef CONFIG_MACSEC
- /**
- * macsec_policy - Determines the policy for MACsec secure session
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -4177,6 +4177,12 @@ static void wpas_start_assoc_cb(struct w
- params.beacon_int = ssid->beacon_int;
- else
- params.beacon_int = wpa_s->conf->beacon_int;
-+ int i = 0;
-+ while (i < WLAN_SUPP_RATES_MAX) {
-+ params.rates[i] = ssid->rates[i];
-+ i++;
-+ }
-+ params.mcast_rate = ssid->mcast_rate;
- }
-
- if (bss && ssid->enable_edmg)
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/463-add-mcast_rate-to-11s.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/463-add-mcast_rate-to-11s.patch
deleted file mode 100644
index daa36c2..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/463-add-mcast_rate-to-11s.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From: Sven Eckelmann <sven.eckelmann@openmesh.com>
-Date: Thu, 11 May 2017 08:21:45 +0200
-Subject: [PATCH] set mcast_rate in mesh mode
-
-The wpa_supplicant code for IBSS allows to set the mcast rate. It is
-recommended to increase this value from 1 or 6 Mbit/s to something higher
-when using a mesh protocol on top which uses the multicast packet loss as
-indicator for the link quality.
-
-This setting was unfortunately not applied for mesh mode. But it would be
-beneficial when wpa_supplicant would behave similar to IBSS mode and set
-this argument during mesh join like authsae already does. At least it is
-helpful for companies/projects which are currently switching to 802.11s
-(without mesh_fwding and with mesh_ttl set to 1) as replacement for IBSS
-because newer drivers seem to support 802.11s but not IBSS anymore.
-
-Signed-off-by: Sven Eckelmann <sven.eckelmann@openmesh.com>
-Tested-by: Simon Wunderlich <simon.wunderlich@openmesh.com>
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -1827,6 +1827,7 @@ struct wpa_driver_mesh_join_params {
- #define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008
- unsigned int flags;
- bool handle_dfs;
-+ int mcast_rate;
- };
-
- struct wpa_driver_set_key_params {
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -11667,6 +11667,18 @@ static int nl80211_put_mesh_id(struct nl
- }
-
-
-+static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
-+{
-+ if (mcast_rate > 0) {
-+ wpa_printf(MSG_DEBUG, " * mcast_rate=%.1f",
-+ (double)mcast_rate / 10);
-+ return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
-+ }
-+
-+ return 0;
-+}
-+
-+
- static int nl80211_put_mesh_config(struct nl_msg *msg,
- struct wpa_driver_mesh_bss_params *params)
- {
-@@ -11728,6 +11740,7 @@ static int nl80211_join_mesh(struct i802
- nl80211_put_basic_rates(msg, params->basic_rates) ||
- nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
- nl80211_put_beacon_int(msg, params->beacon_int) ||
-+ nl80211_put_mcast_rate(msg, params->mcast_rate) ||
- nl80211_put_dtim_period(msg, params->dtim_period))
- goto fail;
-
---- a/wpa_supplicant/mesh.c
-+++ b/wpa_supplicant/mesh.c
-@@ -632,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_
-
- params->meshid = ssid->ssid;
- params->meshid_len = ssid->ssid_len;
-+ params->mcast_rate = ssid->mcast_rate;
- ibss_mesh_setup_freq(wpa_s, ssid, ¶ms->freq);
- wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
- wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/464-fix-mesh-obss-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/464-fix-mesh-obss-check.patch
deleted file mode 100644
index 4d7d85f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/464-fix-mesh-obss-check.patch
+++ /dev/null
@@ -1,13 +0,0 @@
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -3040,6 +3040,10 @@ void ibss_mesh_setup_freq(struct wpa_sup
-
- freq->freq = ssid->frequency;
-
-+ if (ssid->fixed_freq) {
-+ obss_scan = 0;
-+ }
-+
- if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
- struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/465-hostapd-config-support-random-BSS-color.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/465-hostapd-config-support-random-BSS-color.patch
deleted file mode 100644
index 7d3d946..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/465-hostapd-config-support-random-BSS-color.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From c9304d3303d563ad6d2619f4e07864ed12f96889 Mon Sep 17 00:00:00 2001
-From: David Bauer <mail@david-bauer.net>
-Date: Sat, 14 May 2022 21:41:03 +0200
-Subject: [PATCH] hostapd: config: support random BSS color
-
-Configure the HE BSS color to a random value in case the config defines
-a BSS color which exceeds the max BSS color (63).
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- hostapd/config_file.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3500,6 +3500,8 @@ static int hostapd_config_fill(struct ho
- } else if (os_strcmp(buf, "he_bss_color") == 0) {
- conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- conf->he_op.he_bss_color_disabled = 0;
-+ if (atoi(pos) > 63)
-+ conf->he_op.he_bss_color = os_random() % 63 + 1;
- } else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
- conf->he_op.he_bss_color_partial = atoi(pos);
- } else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/470-survey_data_fallback.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/470-survey_data_fallback.patch
deleted file mode 100644
index 79ab48c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/470-survey_data_fallback.patch
+++ /dev/null
@@ -1,30 +0,0 @@
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -455,17 +455,17 @@ static int acs_get_bw_center_chan(int fr
- static int acs_survey_is_sufficient(struct freq_survey *survey)
- {
- if (!(survey->filled & SURVEY_HAS_NF)) {
-+ survey->nf = -95;
- wpa_printf(MSG_INFO,
- "ACS: Survey for freq %d is missing noise floor",
- survey->freq);
-- return 0;
- }
-
- if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
-+ survey->channel_time = 0;
- wpa_printf(MSG_INFO,
- "ACS: Survey for freq %d is missing channel time",
- survey->freq);
-- return 0;
- }
-
- if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
-@@ -473,7 +473,6 @@ static int acs_survey_is_sufficient(stru
- wpa_printf(MSG_INFO,
- "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
- survey->freq);
-- return 0;
- }
-
- return 1;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/500-lto-jobserver-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/500-lto-jobserver-support.patch
deleted file mode 100644
index 67312c5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/500-lto-jobserver-support.patch
+++ /dev/null
@@ -1,59 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -1396,7 +1396,7 @@ hostapd_multi.a: $(BCHECK) $(OBJS)
- @$(AR) cr $@ hostapd_multi.o $(OBJS)
-
- hostapd: $(OBJS)
-- $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
-+ +$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
- @$(E) " LD " $@
-
- ifdef CONFIG_WPA_TRACE
-@@ -1407,7 +1407,7 @@ _OBJS_VAR := OBJS_c
- include ../src/objs.mk
-
- hostapd_cli: $(OBJS_c)
-- $(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
-+ +$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
- @$(E) " LD " $@
-
- NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -2037,31 +2037,31 @@ wpa_supplicant_multi.a: .config $(BCHECK
- @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
-
- wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
-- $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_t
- include ../src/objs.mk
- eapol_test: $(OBJS_t)
-- $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_t2
- include ../src/objs.mk
- preauth_test: $(OBJS_t2)
-- $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_p
- include ../src/objs.mk
- wpa_passphrase: $(OBJS_p)
-- $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
-+ +$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
- @$(E) " LD " $@
-
- _OBJS_VAR := OBJS_c
- include ../src/objs.mk
- wpa_cli: $(OBJS_c)
-- $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
-+ +$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
- @$(E) " LD " $@
-
- LIBCTRL += ../src/common/wpa_ctrl.o
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/590-rrm-wnm-statistics.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/590-rrm-wnm-statistics.patch
deleted file mode 100644
index 0efa6db..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/590-rrm-wnm-statistics.patch
+++ /dev/null
@@ -1,92 +0,0 @@
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -163,6 +163,21 @@ struct hostapd_sae_commit_queue {
- };
-
- /**
-+ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
-+ */
-+struct hostapd_openwrt_stats {
-+ struct {
-+ u64 neighbor_report_tx;
-+ } rrm;
-+
-+ struct {
-+ u64 bss_transition_query_rx;
-+ u64 bss_transition_request_tx;
-+ u64 bss_transition_response_rx;
-+ } wnm;
-+};
-+
-+/**
- * struct hostapd_data - hostapd per-BSS data structure
- */
- struct hostapd_data {
-@@ -182,6 +197,9 @@ struct hostapd_data {
-
- struct hostapd_data *mld_first_bss;
-
-+ /* OpenWrt specific statistics */
-+ struct hostapd_openwrt_stats openwrt_stats;
-+
- int num_sta; /* number of entries in sta_list */
- struct sta_info *sta_list; /* STA info list head */
- #define STA_HASH_SIZE 256
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -386,6 +386,7 @@ static int ieee802_11_send_bss_trans_mgm
- mgmt->u.action.u.bss_tm_req.validity_interval = 1;
- pos = mgmt->u.action.u.bss_tm_req.variable;
-
-+ 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 "
- "validity_interval=%u",
-@@ -790,10 +791,12 @@ int ieee802_11_rx_wnm_action_ap(struct h
- plen);
- return 0;
- case WNM_BSS_TRANS_MGMT_QUERY:
-+ hapd->openwrt_stats.wnm.bss_transition_query_rx++;
- ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
- plen);
- return 0;
- case WNM_BSS_TRANS_MGMT_RESP:
-+ hapd->openwrt_stats.wnm.bss_transition_response_rx++;
- ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
- plen);
- return 0;
-@@ -840,6 +843,7 @@ int wnm_send_disassoc_imminent(struct ho
-
- pos = mgmt->u.action.u.bss_tm_req.variable;
-
-+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
- MACSTR, disassoc_timer, MAC2STR(sta->addr));
- if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
-@@ -921,6 +925,7 @@ int wnm_send_ess_disassoc_imminent(struc
- return -1;
- }
-
-+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- if (disassoc_timer) {
- /* send disassociation frame after time-out */
- set_disassoc_timer(hapd, sta, disassoc_timer);
-@@ -1001,6 +1006,7 @@ int wnm_send_bss_tm_req(struct hostapd_d
- }
- os_free(buf);
-
-+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- if (disassoc_timer) {
- /* send disassociation frame after time-out */
- set_disassoc_timer(hapd, sta, disassoc_timer);
---- a/src/ap/rrm.c
-+++ b/src/ap/rrm.c
-@@ -269,6 +269,8 @@ static void hostapd_send_nei_report_resp
- }
- }
-
-+ hapd->openwrt_stats.rrm.neighbor_report_tx++;
-+
- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
- wpabuf_head(buf), wpabuf_len(buf));
- wpabuf_free(buf);
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/599-wpa_supplicant-fix-warnings.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/599-wpa_supplicant-fix-warnings.patch
deleted file mode 100644
index e70dc61..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/599-wpa_supplicant-fix-warnings.patch
+++ /dev/null
@@ -1,19 +0,0 @@
---- a/wpa_supplicant/wps_supplicant.h
-+++ b/wpa_supplicant/wps_supplicant.h
-@@ -9,6 +9,7 @@
- #ifndef WPS_SUPPLICANT_H
- #define WPS_SUPPLICANT_H
-
-+struct wpa_bss;
- struct wpa_scan_results;
-
- #ifdef CONFIG_WPS
-@@ -16,8 +17,6 @@ struct wpa_scan_results;
- #include "wps/wps.h"
- #include "wps/wps_defs.h"
-
--struct wpa_bss;
--
- struct wps_new_ap_settings {
- const char *ssid_hex;
- const char *auth;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/600-ubus_support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/600-ubus_support.patch
deleted file mode 100644
index a6ccf83..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/600-ubus_support.patch
+++ /dev/null
@@ -1,748 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -166,6 +166,12 @@ OBJS += ../src/common/hw_features_common
-
- OBJS += ../src/eapol_auth/eapol_auth_sm.o
-
-+ifdef CONFIG_UBUS
-+CFLAGS += -DUBUS_SUPPORT
-+OBJS += ../src/utils/uloop.o
-+OBJS += ../src/ap/ubus.o
-+LIBS += -lubox -lubus
-+endif
-
- ifdef CONFIG_CODE_COVERAGE
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -18,6 +18,7 @@
- #include "utils/list.h"
- #include "ap_config.h"
- #include "drivers/driver.h"
-+#include "ubus.h"
-
- #define OCE_STA_CFON_ENABLED(hapd) \
- ((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -184,6 +185,7 @@ struct hostapd_data {
- struct hostapd_iface *iface;
- struct hostapd_config *iconf;
- struct hostapd_bss_config *conf;
-+ struct hostapd_ubus_bss ubus;
- int interface_added; /* virtual interface added for this BSS */
- unsigned int started:1;
- unsigned int disabled:1;
-@@ -695,6 +697,7 @@ hostapd_alloc_bss_data(struct hostapd_if
- struct hostapd_bss_config *bss);
- int hostapd_setup_interface(struct hostapd_iface *iface);
- int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
-+void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
- void hostapd_interface_deinit(struct hostapd_iface *iface);
- void hostapd_interface_free(struct hostapd_iface *iface);
- struct hostapd_iface * hostapd_alloc_iface(void);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -435,6 +435,7 @@ void hostapd_free_hapd_data(struct hosta
- hapd->beacon_set_done = 0;
-
- wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
-+ hostapd_ubus_free_bss(hapd);
- accounting_deinit(hapd);
- hostapd_deinit_wpa(hapd);
- vlan_deinit(hapd);
-@@ -1187,6 +1188,8 @@ static int hostapd_start_beacon(struct h
- if (hapd->driver && hapd->driver->set_operstate)
- hapd->driver->set_operstate(hapd->drv_priv, 1);
-
-+ hostapd_ubus_add_bss(hapd);
-+
- return 0;
- }
-
-@@ -2275,6 +2278,7 @@ static int hostapd_setup_interface_compl
- if (err)
- goto fail;
-
-+ hostapd_ubus_add_iface(iface);
- wpa_printf(MSG_DEBUG, "Completing interface initialization");
- if (iface->freq) {
- #ifdef NEED_AP_MLME
-@@ -2494,6 +2498,7 @@ dfs_offload:
-
- fail:
- wpa_printf(MSG_ERROR, "Interface initialization failed");
-+ hostapd_ubus_free_iface(iface);
-
- if (iface->is_no_ir) {
- hostapd_set_state(iface, HAPD_IFACE_NO_IR);
-@@ -2984,6 +2989,7 @@ void hostapd_interface_deinit_free(struc
- (unsigned int) iface->conf->num_bss);
- driver = iface->bss[0]->driver;
- drv_priv = iface->bss[0]->drv_priv;
-+ hostapd_ubus_free_iface(iface);
- hostapd_interface_deinit(iface);
- wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
- __func__, driver, drv_priv);
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -2786,7 +2786,7 @@ static void handle_auth(struct hostapd_d
- u16 auth_alg, auth_transaction, status_code;
- u16 resp = WLAN_STATUS_SUCCESS;
- struct sta_info *sta = NULL;
-- int res, reply_res;
-+ int res, reply_res, ubus_resp;
- u16 fc;
- const u8 *challenge = NULL;
- u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
-@@ -2795,6 +2795,11 @@ static void handle_auth(struct hostapd_d
- struct radius_sta rad_info;
- const u8 *dst, *sa, *bssid;
- bool mld_sta = false;
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_AUTH_REQ,
-+ .mgmt_frame = mgmt,
-+ .ssi_signal = rssi,
-+ };
-
- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
-@@ -2986,6 +2991,13 @@ static void handle_auth(struct hostapd_d
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto fail;
- }
-+ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+ if (ubus_resp) {
-+ wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
-+ MAC2STR(mgmt->sa));
-+ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+ goto fail;
-+ }
- if (res == HOSTAPD_ACL_PENDING)
- return;
-
-@@ -5161,7 +5173,7 @@ static void handle_assoc(struct hostapd_
- int resp = WLAN_STATUS_SUCCESS;
- u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
- const u8 *pos;
-- int left, i;
-+ int left, i, ubus_resp;
- struct sta_info *sta;
- u8 *tmp = NULL;
- #ifdef CONFIG_FILS
-@@ -5374,6 +5386,11 @@ static void handle_assoc(struct hostapd_
- left = res;
- }
- #endif /* CONFIG_FILS */
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_ASSOC_REQ,
-+ .mgmt_frame = mgmt,
-+ .ssi_signal = rssi,
-+ };
-
- /* followed by SSID and Supported rates; and HT capabilities if 802.11n
- * is used */
-@@ -5472,6 +5489,13 @@ static void handle_assoc(struct hostapd_
- }
- #endif /* CONFIG_FILS */
-
-+ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+ if (ubus_resp) {
-+ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+ MAC2STR(mgmt->sa));
-+ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+ goto fail;
-+ }
- fail:
-
- /*
-@@ -5753,6 +5777,7 @@ static void handle_disassoc(struct hosta
- (unsigned long) len);
- return;
- }
-+ hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
-
- sta = ap_get_sta(hapd, mgmt->sa);
- if (!sta) {
-@@ -5784,6 +5809,8 @@ static void handle_deauth(struct hostapd
- /* Clear the PTKSA cache entries for PASN */
- ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
-
-+ hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
-+
- sta = ap_get_sta(hapd, mgmt->sa);
- if (!sta) {
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1036,6 +1036,12 @@ void handle_probe_req(struct hostapd_dat
- u16 csa_offs[2];
- size_t csa_offs_len;
- struct radius_sta rad_info;
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_PROBE_REQ,
-+ .mgmt_frame = mgmt,
-+ .ssi_signal = ssi_signal,
-+ .elems = &elems,
-+ };
-
- if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
- ssi_signal < hapd->iconf->rssi_ignore_probe_request)
-@@ -1222,6 +1228,12 @@ void handle_probe_req(struct hostapd_dat
- }
- #endif /* CONFIG_P2P */
-
-+ if (hostapd_ubus_handle_event(hapd, &req)) {
-+ wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
-+ MAC2STR(mgmt->sa));
-+ return;
-+ }
-+
- /* TODO: verify that supp_rates contains at least one matching rate
- * with AP configuration */
-
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -260,6 +260,10 @@ int hostapd_notif_assoc(struct hostapd_d
- u16 reason = WLAN_REASON_UNSPECIFIED;
- int status = WLAN_STATUS_SUCCESS;
- const u8 *p2p_dev_addr = NULL;
-+ struct hostapd_ubus_request req = {
-+ .type = HOSTAPD_UBUS_ASSOC_REQ,
-+ .addr = addr,
-+ };
-
- if (addr == NULL) {
- /*
-@@ -396,6 +400,12 @@ int hostapd_notif_assoc(struct hostapd_d
- goto fail;
- }
-
-+ if (hostapd_ubus_handle_event(hapd, &req)) {
-+ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+ MAC2STR(req.addr));
-+ goto fail;
-+ }
-+
- #ifdef CONFIG_P2P
- if (elems.p2p) {
- wpabuf_free(sta->p2p_ie);
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -471,6 +471,7 @@ void ap_handle_timer(void *eloop_ctx, vo
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "deauthenticated due to "
- "local deauth request");
-+ hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
- ap_free_sta(hapd, sta);
- return;
- }
-@@ -626,6 +627,7 @@ skip_poll:
- mlme_deauthenticate_indication(
- hapd, sta,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-+ hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
- ap_free_sta(hapd, sta);
- break;
- }
-@@ -1344,15 +1346,28 @@ void ap_sta_set_authorized(struct hostap
- sta->addr, authorized, dev_addr);
-
- if (authorized) {
-+ static const char * const auth_algs[] = {
-+ [WLAN_AUTH_OPEN] = "open",
-+ [WLAN_AUTH_SHARED_KEY] = "shared",
-+ [WLAN_AUTH_FT] = "ft",
-+ [WLAN_AUTH_SAE] = "sae",
-+ [WLAN_AUTH_FILS_SK] = "fils-sk",
-+ [WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
-+ [WLAN_AUTH_FILS_PK] = "fils-pk",
-+ [WLAN_AUTH_PASN] = "pasn",
-+ };
-+ const char *auth_alg = NULL;
- const u8 *dpp_pkhash;
- const char *keyid;
- char dpp_pkhash_buf[100];
- char keyid_buf[100];
- char ip_addr[100];
-+ char alg_buf[100];
-
- dpp_pkhash_buf[0] = '\0';
- keyid_buf[0] = '\0';
- ip_addr[0] = '\0';
-+ alg_buf[0] = '\0';
- #ifdef CONFIG_P2P
- if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
- os_snprintf(ip_addr, sizeof(ip_addr),
-@@ -1362,6 +1377,13 @@ void ap_sta_set_authorized(struct hostap
- }
- #endif /* CONFIG_P2P */
-
-+ if (sta->auth_alg < ARRAY_SIZE(auth_algs))
-+ auth_alg = auth_algs[sta->auth_alg];
-+
-+ if (auth_alg)
-+ os_snprintf(alg_buf, sizeof(alg_buf),
-+ " auth_alg=%s", auth_alg);
-+
- keyid = ap_sta_wpa_get_keyid(hapd, sta);
- if (keyid) {
- os_snprintf(keyid_buf, sizeof(keyid_buf),
-@@ -1380,17 +1402,19 @@ void ap_sta_set_authorized(struct hostap
- dpp_pkhash, SHA256_MAC_LEN);
- }
-
-- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
-- buf, ip_addr, keyid_buf, dpp_pkhash_buf);
-+ hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
-+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
-+ buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
-
- if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx)
- wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-- AP_STA_CONNECTED "%s%s%s%s",
-+ AP_STA_CONNECTED "%s%s%s%s%s",
- buf, ip_addr, keyid_buf,
-- dpp_pkhash_buf);
-+ dpp_pkhash_buf, alg_buf);
- } else {
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
-+ hostapd_ubus_notify(hapd, "disassoc", sta->addr);
-
- if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx)
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -269,6 +269,7 @@ static void hostapd_wpa_auth_psk_failure
- struct hostapd_data *hapd = ctx;
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
- MAC2STR(addr));
-+ hostapd_ubus_notify(hapd, "key-mismatch", addr);
- }
-
-
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -192,6 +192,13 @@ ifdef CONFIG_EAPOL_TEST
- CFLAGS += -Werror -DEAPOL_TEST
- endif
-
-+ifdef CONFIG_UBUS
-+CFLAGS += -DUBUS_SUPPORT
-+OBJS += ubus.o
-+OBJS += ../src/utils/uloop.o
-+LIBS += -lubox -lubus
-+endif
-+
- ifdef CONFIG_CODE_COVERAGE
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage
- LIBS += -lgcov
-@@ -987,6 +994,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
- CFLAGS += -DCONFIG_CTRL_IFACE_MIB
- endif
- OBJS += ../src/ap/ctrl_iface_ap.o
-+ifdef CONFIG_UBUS
-+OBJS += ../src/ap/ubus.o
-+endif
- endif
-
- CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -7595,6 +7595,8 @@ struct wpa_supplicant * wpa_supplicant_a
- }
- #endif /* CONFIG_P2P */
-
-+ wpas_ubus_add_bss(wpa_s);
-+
- return wpa_s;
- }
-
-@@ -7621,6 +7623,8 @@ int wpa_supplicant_remove_iface(struct w
- struct wpa_supplicant *parent = wpa_s->parent;
- #endif /* CONFIG_MESH */
-
-+ wpas_ubus_free_bss(wpa_s);
-+
- /* Remove interface from the global list of interfaces */
- prev = global->ifaces;
- if (prev == wpa_s) {
-@@ -7967,8 +7971,12 @@ int wpa_supplicant_run(struct wpa_global
- eloop_register_signal_terminate(wpa_supplicant_terminate, global);
- eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
-
-+ wpas_ubus_add(global);
-+
- eloop_run();
-
-+ wpas_ubus_free(global);
-+
- return 0;
- }
-
---- a/wpa_supplicant/wpa_supplicant_i.h
-+++ b/wpa_supplicant/wpa_supplicant_i.h
-@@ -21,6 +21,7 @@
- #include "config_ssid.h"
- #include "wmm_ac.h"
- #include "pasn/pasn_common.h"
-+#include "ubus.h"
-
- extern const char *const wpa_supplicant_version;
- extern const char *const wpa_supplicant_license;
-@@ -319,6 +320,8 @@ struct wpa_global {
- #endif /* CONFIG_WIFI_DISPLAY */
-
- struct psk_list_entry *add_psk; /* From group formation */
-+
-+ struct ubus_object ubus_global;
- };
-
-
-@@ -685,6 +688,7 @@ struct wpa_supplicant {
- unsigned char own_addr[ETH_ALEN];
- unsigned char perm_addr[ETH_ALEN];
- char ifname[100];
-+ struct wpas_ubus_bss ubus;
- #ifdef CONFIG_MATCH_IFACE
- int matched;
- #endif /* CONFIG_MATCH_IFACE */
---- a/wpa_supplicant/wps_supplicant.c
-+++ b/wpa_supplicant/wps_supplicant.c
-@@ -33,6 +33,7 @@
- #include "p2p/p2p.h"
- #include "p2p_supplicant.h"
- #include "wps_supplicant.h"
-+#include "ubus.h"
-
-
- #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
-@@ -402,6 +403,8 @@ static int wpa_supplicant_wps_cred(void
- wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
- cred->cred_attr, cred->cred_attr_len);
-
-+ wpas_ubus_notify(wpa_s, cred);
-+
- if (wpa_s->conf->wps_cred_processing == 1)
- return 0;
-
---- a/wpa_supplicant/main.c
-+++ b/wpa_supplicant/main.c
-@@ -203,7 +203,7 @@ int main(int argc, char *argv[])
-
- for (;;) {
- c = getopt(argc, argv,
-- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuv::W");
-+ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
- if (c < 0)
- break;
- switch (c) {
-@@ -268,6 +268,9 @@ int main(int argc, char *argv[])
- params.conf_p2p_dev = optarg;
- break;
- #endif /* CONFIG_P2P */
-+ case 'n':
-+ iface_count = 0;
-+ break;
- case 'o':
- params.override_driver = optarg;
- break;
---- a/src/ap/rrm.c
-+++ b/src/ap/rrm.c
-@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report
- return;
- wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
- MAC2STR(addr), token, rep_mode, report);
-+ if (len < sizeof(struct rrm_measurement_beacon_report))
-+ return;
-+ hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
- }
-
-
-@@ -352,6 +355,9 @@ void hostapd_handle_radio_measurement(st
- mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
-
- switch (mgmt->u.action.u.rrm.action) {
-+ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
-+ hostapd_ubus_handle_link_measurement(hapd, buf, len);
-+ break;
- case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
- hostapd_handle_radio_msmt_report(hapd, buf, len);
- break;
---- a/src/ap/vlan_init.c
-+++ b/src/ap/vlan_init.c
-@@ -22,6 +22,7 @@
- static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- int existsok)
- {
-+ bool vlan_exists = iface_exists(vlan->ifname);
- int ret;
- #ifdef CONFIG_WEP
- int i;
-@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_da
- }
- #endif /* CONFIG_WEP */
-
-- if (!iface_exists(vlan->ifname))
-+ if (!vlan_exists)
- ret = hostapd_vlan_if_add(hapd, vlan->ifname);
- else if (!existsok)
- return -1;
-@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_da
- if (hapd->wpa_auth)
- ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
-
-+ if (!ret && !vlan_exists)
-+ hostapd_ubus_add_vlan(hapd, vlan);
-+
- if (ret == 0)
- return ret;
-
-@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *
- "WPA deinitialization for VLAN %d failed (%d)",
- vlan->vlan_id, ret);
-
-+ hostapd_ubus_remove_vlan(hapd, vlan);
-+
- return hostapd_vlan_if_remove(hapd, vlan->ifname);
- }
-
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1216,6 +1216,8 @@ int hostapd_dfs_pre_cac_expired(struct h
- "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
- freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
-
-+ hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
-+
- /* Proceed only if DFS is not offloaded to the driver */
- if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
- return 0;
---- a/src/ap/airtime_policy.c
-+++ b/src/ap/airtime_policy.c
-@@ -112,8 +112,14 @@ static void set_sta_weights(struct hosta
- {
- struct sta_info *sta;
-
-- for (sta = hapd->sta_list; sta; sta = sta->next)
-- sta_set_airtime_weight(hapd, sta, weight);
-+ for (sta = hapd->sta_list; sta; sta = sta->next) {
-+ unsigned int sta_weight = weight;
-+
-+ if (sta->dyn_airtime_weight)
-+ sta_weight = (weight * sta->dyn_airtime_weight) / 256;
-+
-+ sta_set_airtime_weight(hapd, sta, sta_weight);
-+ }
- }
-
-
-@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostap
- unsigned int weight;
-
- if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
-- weight = get_weight_for_sta(hapd, sta->addr);
-+ if (sta->dyn_airtime_weight)
-+ weight = sta->dyn_airtime_weight;
-+ else
-+ weight = get_weight_for_sta(hapd, sta->addr);
- if (weight)
- return sta_set_airtime_weight(hapd, sta, weight);
- }
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -322,6 +322,7 @@ struct sta_info {
- #endif /* CONFIG_TESTING_OPTIONS */
- #ifdef CONFIG_AIRTIME_POLICY
- unsigned int airtime_weight;
-+ unsigned int dyn_airtime_weight;
- struct os_reltime backlogged_until;
- #endif /* CONFIG_AIRTIME_POLICY */
-
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -455,7 +455,8 @@ static void ieee802_11_rx_bss_trans_mgmt
- MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
- os_free(hex);
-
-- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
-+ if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
-+ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
- }
-
-
-@@ -477,7 +478,7 @@ static void ieee802_11_rx_bss_trans_mgmt
- size_t len)
- {
- u8 dialog_token, status_code, bss_termination_delay;
-- const u8 *pos, *end;
-+ const u8 *pos, *end, *target_bssid = NULL;
- int enabled = hapd->conf->bss_transition;
- struct sta_info *sta;
-
-@@ -524,6 +525,7 @@ static void ieee802_11_rx_bss_trans_mgmt
- wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
- return;
- }
-+ target_bssid = pos;
- sta->agreed_to_steer = 1;
- eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
- eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
-@@ -543,6 +545,10 @@ static void ieee802_11_rx_bss_trans_mgmt
- MAC2STR(addr), status_code, bss_termination_delay);
- }
-
-+ hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
-+ status_code, bss_termination_delay,
-+ target_bssid, pos, end - pos);
-+
- wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
- pos, end - pos);
- }
---- a/src/utils/eloop.c
-+++ b/src/utils/eloop.c
-@@ -77,6 +77,9 @@ struct eloop_sock_table {
- struct eloop_data {
- int max_sock;
-
-+ eloop_timeout_poll_handler timeout_poll_cb;
-+ eloop_poll_handler poll_cb;
-+
- size_t count; /* sum of all table counts */
- #ifdef CONFIG_ELOOP_POLL
- size_t max_pollfd_map; /* number of pollfds_map currently allocated */
-@@ -1121,6 +1124,12 @@ void eloop_run(void)
- os_reltime_sub(&timeout->time, &now, &tv);
- else
- tv.sec = tv.usec = 0;
-+ }
-+
-+ if (eloop.timeout_poll_cb && eloop.timeout_poll_cb(&tv, !!timeout))
-+ timeout = (void *)1;
-+
-+ if (timeout) {
- #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
- timeout_ms = tv.sec * 1000 + tv.usec / 1000;
- #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
-@@ -1190,7 +1199,8 @@ void eloop_run(void)
- eloop.exceptions.changed = 0;
-
- eloop_process_pending_signals();
--
-+ if (eloop.poll_cb)
-+ eloop.poll_cb();
-
- /* check if some registered timeouts have occurred */
- timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
-@@ -1252,6 +1262,14 @@ out:
- return;
- }
-
-+int eloop_register_cb(eloop_poll_handler poll_cb,
-+ eloop_timeout_poll_handler timeout_cb)
-+{
-+ eloop.poll_cb = poll_cb;
-+ eloop.timeout_poll_cb = timeout_cb;
-+
-+ return 0;
-+}
-
- void eloop_terminate(void)
- {
---- a/src/utils/eloop.h
-+++ b/src/utils/eloop.h
-@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(vo
- */
- typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
-
-+typedef bool (*eloop_timeout_poll_handler)(struct os_reltime *tv, bool tv_set);
-+typedef void (*eloop_poll_handler)(void);
-+
- /**
- * eloop_init() - Initialize global event loop data
- * Returns: 0 on success, -1 on failure
-@@ -73,6 +76,9 @@ typedef void (*eloop_signal_handler)(int
- */
- int eloop_init(void);
-
-+int eloop_register_cb(eloop_poll_handler poll_cb,
-+ eloop_timeout_poll_handler timeout_cb);
-+
- /**
- * eloop_register_read_sock - Register handler for read events
- * @sock: File descriptor number for the socket
-@@ -320,6 +326,8 @@ int eloop_register_signal_reconfig(eloop
- */
- int eloop_sock_requeue(void);
-
-+void eloop_add_uloop(void);
-+
- /**
- * eloop_run - Start the event loop
- *
---- /dev/null
-+++ b/src/utils/uloop.c
-@@ -0,0 +1,64 @@
-+#include <libubox/uloop.h>
-+#include "includes.h"
-+#include "common.h"
-+#include "eloop.h"
-+
-+static void eloop_uloop_event_cb(int sock, void *eloop_ctx, void *sock_ctx)
-+{
-+}
-+
-+static void eloop_uloop_fd_cb(struct uloop_fd *fd, unsigned int events)
-+{
-+ unsigned int changed = events ^ fd->flags;
-+
-+ if (changed & ULOOP_READ) {
-+ if (events & ULOOP_READ)
-+ eloop_register_sock(fd->fd, EVENT_TYPE_READ, eloop_uloop_event_cb, fd, fd);
-+ else
-+ eloop_unregister_sock(fd->fd, EVENT_TYPE_READ);
-+ }
-+
-+ if (changed & ULOOP_WRITE) {
-+ if (events & ULOOP_WRITE)
-+ eloop_register_sock(fd->fd, EVENT_TYPE_WRITE, eloop_uloop_event_cb, fd, fd);
-+ else
-+ eloop_unregister_sock(fd->fd, EVENT_TYPE_WRITE);
-+ }
-+}
-+
-+static bool uloop_timeout_poll_handler(struct os_reltime *tv, bool tv_set)
-+{
-+ struct os_reltime tv_uloop;
-+ int timeout_ms = uloop_get_next_timeout();
-+
-+ if (timeout_ms < 0)
-+ return false;
-+
-+ tv_uloop.sec = timeout_ms / 1000;
-+ tv_uloop.usec = (timeout_ms % 1000) * 1000;
-+
-+ if (!tv_set || os_reltime_before(&tv_uloop, tv)) {
-+ *tv = tv_uloop;
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+static void uloop_poll_handler(void)
-+{
-+ uloop_run_timeout(0);
-+}
-+
-+void eloop_add_uloop(void)
-+{
-+ static bool init_done = false;
-+
-+ if (!init_done) {
-+ uloop_init();
-+ uloop_fd_set_cb = eloop_uloop_fd_cb;
-+ init_done = true;
-+ }
-+
-+ eloop_register_cb(uloop_poll_handler, uloop_timeout_poll_handler);
-+}
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/601-ucode_support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/601-ucode_support.patch
deleted file mode 100644
index cfdb51f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/601-ucode_support.patch
+++ /dev/null
@@ -1,671 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -168,9 +168,21 @@ OBJS += ../src/eapol_auth/eapol_auth_sm.
-
- ifdef CONFIG_UBUS
- CFLAGS += -DUBUS_SUPPORT
--OBJS += ../src/utils/uloop.o
- OBJS += ../src/ap/ubus.o
--LIBS += -lubox -lubus
-+LIBS += -lubus
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef CONFIG_UCODE
-+CFLAGS += -DUCODE_SUPPORT
-+OBJS += ../src/utils/ucode.o
-+OBJS += ../src/ap/ucode.o
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef NEED_ULOOP
-+OBJS += ../src/utils/uloop.o
-+LIBS += -lubox
- endif
-
- ifdef CONFIG_CODE_COVERAGE
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -1007,6 +1007,7 @@ int main(int argc, char *argv[])
- }
-
- hostapd_global_ctrl_iface_init(&interfaces);
-+ hostapd_ucode_init(&interfaces);
-
- if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
- wpa_printf(MSG_ERROR, "Failed to start eloop");
-@@ -1016,6 +1017,7 @@ int main(int argc, char *argv[])
- ret = 0;
-
- out:
-+ hostapd_ucode_free();
- hostapd_global_ctrl_iface_deinit(&interfaces);
- /* Deinitialize all interfaces */
- for (i = 0; i < interfaces.count; i++) {
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -19,6 +19,7 @@
- #include "ap_config.h"
- #include "drivers/driver.h"
- #include "ubus.h"
-+#include "ucode.h"
-
- #define OCE_STA_CFON_ENABLED(hapd) \
- ((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -51,6 +52,10 @@ struct hapd_interfaces {
- struct hostapd_config * (*config_read_cb)(const char *config_fname);
- int (*ctrl_iface_init)(struct hostapd_data *hapd);
- void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
-+ int (*ctrl_iface_recv)(struct hostapd_data *hapd,
-+ char *buf, char *reply, int reply_size,
-+ struct sockaddr_storage *from,
-+ socklen_t fromlen);
- int (*for_each_interface)(struct hapd_interfaces *interfaces,
- int (*cb)(struct hostapd_iface *iface,
- void *ctx), void *ctx);
-@@ -186,6 +191,7 @@ struct hostapd_data {
- struct hostapd_config *iconf;
- struct hostapd_bss_config *conf;
- struct hostapd_ubus_bss ubus;
-+ struct hostapd_ucode_bss ucode;
- int interface_added; /* virtual interface added for this BSS */
- unsigned int started:1;
- unsigned int disabled:1;
-@@ -506,6 +512,7 @@ struct hostapd_sta_info {
- */
- struct hostapd_iface {
- struct hapd_interfaces *interfaces;
-+ struct hostapd_ucode_iface ucode;
- void *owner;
- char *config_fname;
- struct hostapd_config *conf;
-@@ -706,6 +713,8 @@ struct hostapd_iface * hostapd_init(stru
- struct hostapd_iface *
- hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
- const char *config_fname, int debug);
-+int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
-+void hostapd_bss_deinit(struct hostapd_data *hapd);
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- int reassoc);
- void hostapd_interface_deinit_free(struct hostapd_iface *iface);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -252,6 +252,8 @@ int hostapd_reload_config(struct hostapd
- struct hostapd_config *newconf, *oldconf;
- size_t j;
-
-+ hostapd_ucode_reload_bss(hapd);
-+
- if (iface->config_fname == NULL) {
- /* Only in-memory config in use - assume it has been updated */
- hostapd_clear_old(iface);
-@@ -435,6 +437,7 @@ void hostapd_free_hapd_data(struct hosta
- hapd->beacon_set_done = 0;
-
- wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
-+ hostapd_ucode_free_bss(hapd);
- hostapd_ubus_free_bss(hapd);
- accounting_deinit(hapd);
- hostapd_deinit_wpa(hapd);
-@@ -600,6 +603,7 @@ void hostapd_cleanup_iface_partial(struc
- static void hostapd_cleanup_iface(struct hostapd_iface *iface)
- {
- wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-+ hostapd_ucode_free_iface(iface);
- eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
- NULL);
-
-@@ -1189,6 +1193,7 @@ static int hostapd_start_beacon(struct h
- hapd->driver->set_operstate(hapd->drv_priv, 1);
-
- hostapd_ubus_add_bss(hapd);
-+ hostapd_ucode_add_bss(hapd);
-
- return 0;
- }
-@@ -1211,8 +1216,7 @@ static int hostapd_start_beacon(struct h
- * initialized. Most of the modules that are initialized here will be
- * deinitialized in hostapd_cleanup().
- */
--static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
-- bool start_beacon)
-+int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
- {
- struct hostapd_bss_config *conf = hapd->conf;
- u8 ssid[SSID_MAX_LEN + 1];
-@@ -2698,7 +2702,7 @@ hostapd_alloc_bss_data(struct hostapd_if
- }
-
-
--static void hostapd_bss_deinit(struct hostapd_data *hapd)
-+void hostapd_bss_deinit(struct hostapd_data *hapd)
- {
- if (!hapd)
- return;
-@@ -3491,7 +3495,8 @@ int hostapd_remove_iface(struct hapd_int
- hapd_iface = interfaces->iface[i];
- if (hapd_iface == NULL)
- return -1;
-- if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
-+ if (!os_strcmp(hapd_iface->phy, buf) ||
-+ !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
- wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
- hapd_iface->driver_ap_teardown =
- !!(hapd_iface->drv_flags &
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -195,8 +195,20 @@ endif
- ifdef CONFIG_UBUS
- CFLAGS += -DUBUS_SUPPORT
- OBJS += ubus.o
-+LIBS += -lubus
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef CONFIG_UCODE
-+CFLAGS += -DUCODE_SUPPORT
-+OBJS += ../src/utils/ucode.o
-+OBJS += ucode.o
-+NEED_ULOOP:=y
-+endif
-+
-+ifdef NEED_ULOOP
- OBJS += ../src/utils/uloop.o
--LIBS += -lubox -lubus
-+LIBS += -lubox
- endif
-
- ifdef CONFIG_CODE_COVERAGE
-@@ -997,6 +1009,9 @@ OBJS += ../src/ap/ctrl_iface_ap.o
- ifdef CONFIG_UBUS
- OBJS += ../src/ap/ubus.o
- endif
-+ifdef CONFIG_UCODE
-+OBJS += ../src/ap/ucode.o
-+endif
- endif
-
- CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -1044,6 +1044,7 @@ void wpa_supplicant_set_state(struct wpa
- sme_sched_obss_scan(wpa_s, 0);
- }
- wpa_s->wpa_state = state;
-+ wpas_ucode_update_state(wpa_s);
-
- #ifdef CONFIG_BGSCAN
- if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
-@@ -7594,6 +7595,7 @@ struct wpa_supplicant * wpa_supplicant_a
- #endif /* CONFIG_P2P */
-
- wpas_ubus_add_bss(wpa_s);
-+ wpas_ucode_add_bss(wpa_s);
-
- return wpa_s;
- }
-@@ -7621,6 +7623,7 @@ int wpa_supplicant_remove_iface(struct w
- struct wpa_supplicant *parent = wpa_s->parent;
- #endif /* CONFIG_MESH */
-
-+ wpas_ucode_free_bss(wpa_s);
- wpas_ubus_free_bss(wpa_s);
-
- /* Remove interface from the global list of interfaces */
-@@ -7931,6 +7934,7 @@ struct wpa_global * wpa_supplicant_init(
-
- eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
- wpas_periodic, global, NULL);
-+ wpas_ucode_init(global);
-
- return global;
- }
-@@ -7969,12 +7973,8 @@ int wpa_supplicant_run(struct wpa_global
- eloop_register_signal_terminate(wpa_supplicant_terminate, global);
- eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
-
-- wpas_ubus_add(global);
--
- eloop_run();
-
-- wpas_ubus_free(global);
--
- return 0;
- }
-
-@@ -8007,6 +8007,8 @@ void wpa_supplicant_deinit(struct wpa_gl
-
- wpas_notify_supplicant_deinitialized(global);
-
-+ wpas_ucode_free();
-+
- eap_peer_unregister_methods();
- #ifdef CONFIG_AP
- eap_server_unregister_methods();
---- a/wpa_supplicant/wpa_supplicant_i.h
-+++ b/wpa_supplicant/wpa_supplicant_i.h
-@@ -22,6 +22,7 @@
- #include "wmm_ac.h"
- #include "pasn/pasn_common.h"
- #include "ubus.h"
-+#include "ucode.h"
-
- extern const char *const wpa_supplicant_version;
- extern const char *const wpa_supplicant_license;
-@@ -689,6 +690,7 @@ struct wpa_supplicant {
- unsigned char perm_addr[ETH_ALEN];
- char ifname[100];
- struct wpas_ubus_bss ubus;
-+ struct wpas_ucode_bss ucode;
- #ifdef CONFIG_MATCH_IFACE
- int matched;
- #endif /* CONFIG_MATCH_IFACE */
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4856,6 +4856,7 @@ try_again:
- return -1;
- }
-
-+ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
- wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
-
- return 0;
-@@ -4957,6 +4958,7 @@ fail:
- os_free(fname);
-
- interface->global_ctrl_sock = s;
-+ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
- eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
- interface, NULL);
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -3787,6 +3787,25 @@ struct wpa_driver_ops {
- const char *ifname);
-
- /**
-+ * if_rename - Rename a virtual interface
-+ * @priv: Private driver interface data
-+ * @type: Interface type
-+ * @ifname: Interface name of the virtual interface to be renamed
-+ * (NULL when renaming the AP BSS interface)
-+ * @new_name: New interface name of the virtual interface
-+ * Returns: 0 on success, -1 on failure
-+ */
-+ int (*if_rename)(void *priv, enum wpa_driver_if_type type,
-+ const char *ifname, const char *new_name);
-+
-+ /**
-+ * set_first_bss - Make a virtual interface the first (primary) bss
-+ * @priv: Private driver interface data
-+ * Returns: 0 on success, -1 on failure
-+ */
-+ int (*set_first_bss)(void *priv);
-+
-+ /**
- * set_sta_vlan - Bind a station into a specific interface (AP only)
- * @priv: Private driver interface data
- * @ifname: Interface (main or virtual BSS or VLAN)
-@@ -6440,6 +6459,7 @@ union wpa_event_data {
-
- /**
- * struct ch_switch
-+ * @count: Count until channel switch activates
- * @freq: Frequency of new channel in MHz
- * @ht_enabled: Whether this is an HT channel
- * @ch_offset: Secondary channel offset
-@@ -6450,6 +6470,7 @@ union wpa_event_data {
- * @punct_bitmap: Puncturing bitmap
- */
- struct ch_switch {
-+ int count;
- int freq;
- int ht_enabled;
- int ch_offset;
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1202,6 +1202,7 @@ static void mlme_event_ch_switch(struct
- struct nlattr *bw, struct nlattr *cf1,
- struct nlattr *cf2,
- struct nlattr *punct_bitmap,
-+ struct nlattr *count,
- int finished)
- {
- struct i802_bss *bss;
-@@ -1265,6 +1266,8 @@ static void mlme_event_ch_switch(struct
- data.ch_switch.cf1 = nla_get_u32(cf1);
- if (cf2)
- data.ch_switch.cf2 = nla_get_u32(cf2);
-+ if (count)
-+ data.ch_switch.count = nla_get_u32(count);
-
- if (finished)
- bss->flink->freq = data.ch_switch.freq;
-@@ -3912,6 +3915,7 @@ static void do_process_drv_event(struct
- tb[NL80211_ATTR_CENTER_FREQ1],
- tb[NL80211_ATTR_CENTER_FREQ2],
- tb[NL80211_ATTR_PUNCT_BITMAP],
-+ tb[NL80211_ATTR_CH_SWITCH_COUNT],
- 0);
- break;
- case NL80211_CMD_CH_SWITCH_NOTIFY:
-@@ -3924,6 +3928,7 @@ static void do_process_drv_event(struct
- tb[NL80211_ATTR_CENTER_FREQ1],
- tb[NL80211_ATTR_CENTER_FREQ2],
- tb[NL80211_ATTR_PUNCT_BITMAP],
-+ NULL,
- 1);
- break;
- case NL80211_CMD_DISCONNECT:
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -5389,6 +5389,7 @@ void supplicant_event(void *ctx, enum wp
- event_to_string(event), event);
- #endif /* CONFIG_NO_STDOUT_DEBUG */
-
-+ wpas_ucode_event(wpa_s, event, data);
- switch (event) {
- case EVENT_AUTH:
- #ifdef CONFIG_FST
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -393,6 +393,23 @@ static inline int hostapd_drv_stop_ap(st
- return hapd->driver->stop_ap(hapd->drv_priv);
- }
-
-+static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
-+ enum wpa_driver_if_type type,
-+ const char *ifname,
-+ const char *new_name)
-+{
-+ if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
-+ return -1;
-+ return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
-+}
-+
-+static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
-+ return 0;
-+ return hapd->driver->set_first_bss(hapd->drv_priv);
-+}
-+
- static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
- struct wpa_channel_info *ci)
- {
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -73,6 +73,16 @@ enum nlmsgerr_attrs {
-
- #endif /* ANDROID */
-
-+static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
-+{
-+ const struct nlmsghdr *nlh;
-+
-+ if (!wpa_netlink_hook)
-+ return;
-+
-+ nlh = nlmsg_hdr(msg);
-+ wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
-+}
-
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
-@@ -379,6 +389,11 @@ static int no_seq_check(struct nl_msg *m
- return NL_OK;
- }
-
-+static int debug_handler(struct nl_msg *msg, void *arg)
-+{
-+ handle_nl_debug_hook(msg, 0);
-+ return NL_OK;
-+}
-
- static void nl80211_nlmsg_clear(struct nl_msg *msg)
- {
-@@ -415,6 +430,7 @@ static int send_and_recv(struct nl80211_
- if (!msg)
- return -ENOMEM;
-
-+ handle_nl_debug_hook(msg, 1);
- cb = nl_cb_clone(global->nl_cb);
- if (!cb)
- goto out;
-@@ -443,6 +459,7 @@ static int send_and_recv(struct nl80211_
-
- err = 1;
-
-+ nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
- nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
- if (ack_handler_custom) {
-@@ -919,6 +936,7 @@ nl80211_get_wiphy_data_ap(struct i802_bs
- os_free(w);
- return NULL;
- }
-+ nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -1333,7 +1351,7 @@ static void wpa_driver_nl80211_event_rtm
- }
- wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
- namebuf, ifname);
-- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+ if (drv->first_bss->ifindex != ifi->ifi_index) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Not the main interface (%s) - do not indicate interface down",
- drv->first_bss->ifname);
-@@ -1369,7 +1387,7 @@ static void wpa_driver_nl80211_event_rtm
- }
- wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
- namebuf, ifname);
-- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+ if (drv->first_bss->ifindex != ifi->ifi_index) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Not the main interface (%s) - do not indicate interface up",
- drv->first_bss->ifname);
-@@ -1992,6 +2010,7 @@ static int wpa_driver_nl80211_init_nl_gl
- /* Continue without vendor events */
- }
-
-+ nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -2160,6 +2179,7 @@ static int nl80211_init_bss(struct i802_
- if (!bss->nl_cb)
- return -1;
-
-+ nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -8432,6 +8452,7 @@ static void *i802_init(struct hostapd_da
- char master_ifname[IFNAMSIZ];
- int ifindex, br_ifindex = 0;
- int br_added = 0;
-+ int err;
-
- bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
- params->global_priv, 1,
-@@ -8491,21 +8512,17 @@ static void *i802_init(struct hostapd_da
- (params->num_bridge == 0 || !params->bridge[0]))
- add_ifidx(drv, br_ifindex, drv->ifindex);
-
-- if (bss->added_if_into_bridge || bss->already_in_bridge) {
-- int err;
--
-- drv->rtnl_sk = nl_socket_alloc();
-- if (drv->rtnl_sk == NULL) {
-- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
-- goto failed;
-- }
-+ drv->rtnl_sk = nl_socket_alloc();
-+ if (drv->rtnl_sk == NULL) {
-+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
-+ goto failed;
-+ }
-
-- err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
-- if (err) {
-- wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
-- nl_geterror(err));
-- goto failed;
-- }
-+ err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
-+ if (err) {
-+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
-+ nl_geterror(err));
-+ goto failed;
- }
-
- if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
-@@ -8875,6 +8892,50 @@ static int wpa_driver_nl80211_if_remove(
- return 0;
- }
-
-+static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
-+ enum wpa_driver_if_type type,
-+ const char *ifname, const char *new_name)
-+{
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct ifinfomsg ifi = {
-+ .ifi_family = AF_UNSPEC,
-+ .ifi_index = bss->ifindex,
-+ };
-+ struct nl_msg *msg;
-+ int res = -ENOMEM;
-+
-+ if (ifname)
-+ ifi.ifi_index = if_nametoindex(ifname);
-+
-+ msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
-+ if (!msg)
-+ return res;
-+
-+ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
-+ goto out;
-+
-+ if (nla_put_string(msg, IFLA_IFNAME, new_name))
-+ goto out;
-+
-+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+ if (res < 0)
-+ goto out;
-+
-+ res = nl_wait_for_ack(drv->rtnl_sk);
-+ if (res) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Renaming device %s to %s failed: %s",
-+ ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
-+ goto out;
-+ }
-+
-+ if (type == WPA_IF_AP_BSS && !ifname)
-+ os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
-+
-+out:
-+ nlmsg_free(msg);
-+ return res;
-+}
-
- static int cookie_handler(struct nl_msg *msg, void *arg)
- {
-@@ -10513,6 +10574,37 @@ static int driver_nl80211_if_remove(void
- }
-
-
-+static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
-+ const char *ifname, const char *new_name)
-+{
-+ struct i802_bss *bss = priv;
-+ return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
-+}
-+
-+
-+static int driver_nl80211_set_first_bss(void *priv)
-+{
-+ struct i802_bss *bss = priv, *tbss;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+
-+ if (drv->first_bss == bss)
-+ return 0;
-+
-+ for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
-+ if (tbss->next != bss)
-+ continue;
-+
-+ tbss->next = bss->next;
-+ bss->next = drv->first_bss;
-+ drv->first_bss = bss;
-+ drv->ctx = bss->ctx;
-+ return 0;
-+ }
-+
-+ return -1;
-+}
-+
-+
- static int driver_nl80211_send_mlme(void *priv, const u8 *data,
- size_t data_len, int noack,
- unsigned int freq,
-@@ -13697,6 +13789,8 @@ const struct wpa_driver_ops wpa_driver_n
- .set_acl = wpa_driver_nl80211_set_acl,
- .if_add = wpa_driver_nl80211_if_add,
- .if_remove = driver_nl80211_if_remove,
-+ .if_rename = driver_nl80211_if_rename,
-+ .set_first_bss = driver_nl80211_set_first_bss,
- .send_mlme = driver_nl80211_send_mlme,
- .get_hw_feature_data = nl80211_get_hw_feature_data,
- .sta_add = wpa_driver_nl80211_sta_add,
---- a/src/utils/wpa_debug.c
-+++ b/src/utils/wpa_debug.c
-@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NU
- #define WPAS_TRACE_PFX "wpas <%d>: "
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
-
-+void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
-+ size_t len);
-+void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
-
- int wpa_debug_level = MSG_INFO;
- int wpa_debug_show_keys = 0;
-@@ -210,6 +214,12 @@ void _wpa_printf(int level, const char *
- {
- va_list ap;
-
-+ if (wpa_printf_hook) {
-+ va_start(ap, fmt);
-+ wpa_printf_hook(level, fmt, ap);
-+ va_end(ap);
-+ }
-+
- if (level >= wpa_debug_level) {
- #ifdef CONFIG_ANDROID_LOG
- va_start(ap, fmt);
-@@ -260,6 +270,9 @@ void _wpa_hexdump(int level, const char
- {
- size_t i;
-
-+ if (wpa_hexdump_hook)
-+ wpa_hexdump_hook(level, title, buf, len);
-+
- #ifdef CONFIG_DEBUG_LINUX_TRACING
- if (wpa_debug_tracing_file != NULL) {
- fprintf(wpa_debug_tracing_file,
---- a/src/utils/wpa_debug.h
-+++ b/src/utils/wpa_debug.h
-@@ -11,6 +11,10 @@
-
- #include "wpabuf.h"
-
-+extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+extern void (*wpa_hexdump_hook)(int level, const char *title,
-+ const void *buf, size_t len);
-+extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
- extern int wpa_debug_level;
- extern int wpa_debug_show_keys;
- extern int wpa_debug_timestamp;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/610-hostapd_cli_ujail_permission.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/610-hostapd_cli_ujail_permission.patch
deleted file mode 100644
index a03fcc9..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/610-hostapd_cli_ujail_permission.patch
+++ /dev/null
@@ -1,33 +0,0 @@
---- a/src/common/wpa_ctrl.c
-+++ b/src/common/wpa_ctrl.c
-@@ -135,7 +135,7 @@ try_again:
- return NULL;
- }
- tries++;
--#ifdef ANDROID
-+
- /* Set client socket file permissions so that bind() creates the client
- * socket with these permissions and there is no need to try to change
- * them with chmod() after bind() which would have potential issues with
-@@ -147,7 +147,7 @@ try_again:
- * operations to allow the response to go through. Those are using the
- * no-deference-symlinks version to avoid races. */
- fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
--#endif /* ANDROID */
-+
- if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
- sizeof(ctrl->local)) < 0) {
- if (errno == EADDRINUSE && tries < 2) {
-@@ -165,7 +165,11 @@ try_again:
- return NULL;
- }
-
--#ifdef ANDROID
-+#ifndef ANDROID
-+ /* Set group even if we do not have privileges to change owner */
-+ lchown(ctrl->local.sun_path, -1, 101);
-+ lchown(ctrl->local.sun_path, 101, 101);
-+#else
- /* Set group even if we do not have privileges to change owner */
- lchown(ctrl->local.sun_path, -1, AID_WIFI);
- lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/701-reload_config_inline.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/701-reload_config_inline.patch
deleted file mode 100644
index 3c62bf6..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/701-reload_config_inline.patch
+++ /dev/null
@@ -1,33 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4816,7 +4816,12 @@ struct hostapd_config * hostapd_config_r
- int errors = 0;
- size_t i;
-
-- f = fopen(fname, "r");
-+ if (!strncmp(fname, "data:", 5)) {
-+ f = fmemopen((void *)(fname + 5), strlen(fname + 5), "r");
-+ fname = "<inline>";
-+ } else {
-+ f = fopen(fname, "r");
-+ }
- if (f == NULL) {
- wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
- "for reading.", fname);
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(cons
- while (cred_tail && cred_tail->next)
- cred_tail = cred_tail->next;
-
-+ if (!strncmp(name, "data:", 5)) {
-+ f = fmemopen((void *)(name + 5), strlen(name + 5), "r");
-+ name = "<inline>";
-+ } else {
-+ f = fopen(name, "r");
-+ }
- wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
-- f = fopen(name, "r");
- if (f == NULL) {
- wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
- "error: %s", name, strerror(errno));
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/710-vlan_no_bridge.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/710-vlan_no_bridge.patch
deleted file mode 100644
index 63d1b8a..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/710-vlan_no_bridge.patch
+++ /dev/null
@@ -1,41 +0,0 @@
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -121,6 +121,7 @@ struct hostapd_ssid {
- #define DYNAMIC_VLAN_OPTIONAL 1
- #define DYNAMIC_VLAN_REQUIRED 2
- int dynamic_vlan;
-+ int vlan_no_bridge;
- #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
- #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
- #define DYNAMIC_VLAN_NAMING_END 2
---- a/src/ap/vlan_full.c
-+++ b/src/ap/vlan_full.c
-@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, st
- if (!vlan)
- return;
-
-+ if (hapd->conf->ssid.vlan_no_bridge)
-+ goto out;
-+
- vlan->configured = 1;
-
- notempty = vlan->vlan_desc.notempty;
-@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, st
- ifname, br_name, tagged[i], hapd);
- }
-
-+out:
- ifconfig_up(ifname);
- }
-
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3351,6 +3351,8 @@ static int hostapd_config_fill(struct ho
- #ifndef CONFIG_NO_VLAN
- } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
- bss->ssid.dynamic_vlan = atoi(pos);
-+ } else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
-+ bss->ssid.vlan_no_bridge = atoi(pos);
- } else if (os_strcmp(buf, "per_sta_vif") == 0) {
- bss->ssid.per_sta_vif = atoi(pos);
- } else if (os_strcmp(buf, "vlan_file") == 0) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/711-wds_bridge_force.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/711-wds_bridge_force.patch
deleted file mode 100644
index c0f2c31..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/711-wds_bridge_force.patch
+++ /dev/null
@@ -1,22 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2318,6 +2318,8 @@ static int hostapd_config_fill(struct ho
- sizeof(conf->bss[0]->iface));
- } else if (os_strcmp(buf, "bridge") == 0) {
- os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
-+ if (!bss->wds_bridge[0])
-+ os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
- } else if (os_strcmp(buf, "bridge_hairpin") == 0) {
- bss->bridge_hairpin = atoi(pos);
- } else if (os_strcmp(buf, "vlan_bridge") == 0) {
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -348,8 +348,6 @@ int hostapd_set_wds_sta(struct hostapd_d
- return -1;
- if (hapd->conf->wds_bridge[0])
- bridge = hapd->conf->wds_bridge;
-- else if (hapd->conf->bridge[0])
-- bridge = hapd->conf->bridge;
- return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
- bridge, ifname_wds);
- }
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/720-iface_max_num_sta.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/720-iface_max_num_sta.patch
deleted file mode 100644
index 089c1dd..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/720-iface_max_num_sta.patch
+++ /dev/null
@@ -1,81 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2848,6 +2848,14 @@ static int hostapd_config_fill(struct ho
- line, bss->max_num_sta, MAX_STA_COUNT);
- return 1;
- }
-+ } else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
-+ conf->max_num_sta = atoi(pos);
-+ if (conf->max_num_sta < 0 ||
-+ conf->max_num_sta > MAX_STA_COUNT) {
-+ wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
-+ line, conf->max_num_sta, MAX_STA_COUNT);
-+ return 1;
-+ }
- } else if (os_strcmp(buf, "wpa") == 0) {
- bss->wpa = atoi(pos);
- } else if (os_strcmp(buf, "extended_key_id") == 0) {
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -742,6 +742,7 @@ void hostapd_cleanup_cs_params(struct ho
- void hostapd_periodic_iface(struct hostapd_iface *iface);
- int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
- void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
-+int hostapd_check_max_sta(struct hostapd_data *hapd);
-
- void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
- void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -244,6 +244,29 @@ static int hostapd_iface_conf_changed(st
- return 0;
- }
-
-+static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
-+{
-+ int num_sta = 0;
-+ int i;
-+
-+ for (i = 0; i < iface->num_bss; i++)
-+ num_sta += iface->bss[i]->num_sta;
-+
-+ return num_sta;
-+}
-+
-+
-+int hostapd_check_max_sta(struct hostapd_data *hapd)
-+{
-+ if (hapd->num_sta >= hapd->conf->max_num_sta)
-+ return 1;
-+
-+ if (hapd->iconf->max_num_sta &&
-+ hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
-+ return 1;
-+
-+ return 0;
-+}
-
- int hostapd_reload_config(struct hostapd_iface *iface)
- {
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1252,7 +1252,7 @@ void handle_probe_req(struct hostapd_dat
- if (hapd->conf->no_probe_resp_if_max_sta &&
- is_multicast_ether_addr(mgmt->da) &&
- is_multicast_ether_addr(mgmt->bssid) &&
-- hapd->num_sta >= hapd->conf->max_num_sta &&
-+ hostapd_check_max_sta(hapd) &&
- !ap_get_sta(hapd, mgmt->sa)) {
- wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
- " since no room for additional STA",
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1039,6 +1039,8 @@ struct hostapd_config {
- unsigned int track_sta_max_num;
- unsigned int track_sta_max_age;
-
-+ int max_num_sta;
-+
- char country[3]; /* first two octets: country code as described in
- * ISO/IEC 3166-1. Third octet:
- * ' ' (ascii 32): all environments
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/730-ft_iface.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/730-ft_iface.patch
deleted file mode 100644
index 0795ed1..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/730-ft_iface.patch
+++ /dev/null
@@ -1,38 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3007,6 +3007,8 @@ static int hostapd_config_fill(struct ho
- wpa_printf(MSG_INFO,
- "Line %d: Obsolete peerkey parameter ignored", line);
- #ifdef CONFIG_IEEE80211R_AP
-+ } else if (os_strcmp(buf, "ft_iface") == 0) {
-+ os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
- } else if (os_strcmp(buf, "mobility_domain") == 0) {
- if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
- hexstr2bin(pos, bss->mobility_domain,
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -283,6 +283,7 @@ struct airtime_sta_weight {
- struct hostapd_bss_config {
- char iface[IFNAMSIZ + 1];
- char bridge[IFNAMSIZ + 1];
-+ char ft_iface[IFNAMSIZ + 1];
- char vlan_bridge[IFNAMSIZ + 1];
- char wds_bridge[IFNAMSIZ + 1];
- int bridge_hairpin; /* hairpin_mode on bridge members */
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -1727,8 +1727,12 @@ int hostapd_setup_wpa(struct hostapd_dat
- wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
- const char *ft_iface;
-
-- ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
-- hapd->conf->iface;
-+ if (hapd->conf->ft_iface[0])
-+ ft_iface = hapd->conf->ft_iface;
-+ else if (hapd->conf->bridge[0])
-+ ft_iface = hapd->conf->bridge;
-+ else
-+ ft_iface = hapd->conf->iface;
- hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
- hostapd_rrb_receive, hapd, 1);
- if (!hapd->l2) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/740-snoop_iface.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/740-snoop_iface.patch
deleted file mode 100644
index ce64513..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/740-snoop_iface.patch
+++ /dev/null
@@ -1,139 +0,0 @@
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -284,6 +284,7 @@ struct hostapd_bss_config {
- char iface[IFNAMSIZ + 1];
- char bridge[IFNAMSIZ + 1];
- char ft_iface[IFNAMSIZ + 1];
-+ char snoop_iface[IFNAMSIZ + 1];
- char vlan_bridge[IFNAMSIZ + 1];
- char wds_bridge[IFNAMSIZ + 1];
- int bridge_hairpin; /* hairpin_mode on bridge members */
---- a/src/ap/x_snoop.c
-+++ b/src/ap/x_snoop.c
-@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *ha
-
- hapd->x_snoop_initialized = true;
-
-- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
-+ if (!conf->snoop_iface[0] &&
-+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
- 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable hairpin_mode on the bridge port");
- return -1;
- }
-
-- if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
-+ if (!conf->snoop_iface[0] &&
-+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable proxyarp on the bridge port");
- return -1;
- }
-
- if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
-- 1)) {
-+ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
- return -1;
- }
-
- #ifdef CONFIG_IPV6
-- if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
-+ if (!conf->snoop_iface[0] &&
-+ hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to enable multicast snooping on the bridge");
- return -1;
-@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_dat
- {
- struct hostapd_bss_config *conf = hapd->conf;
- struct l2_packet_data *l2;
-+ const char *ifname = conf->bridge;
-+
-+ if (conf->snoop_iface[0])
-+ ifname = conf->snoop_iface;
-
-- l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
-+ l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
- if (l2 == NULL) {
- wpa_printf(MSG_DEBUG,
- "x_snoop: Failed to initialize L2 packet processing %s",
-@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send
-
- void x_snoop_deinit(struct hostapd_data *hapd)
- {
-+ struct hostapd_bss_config *conf = hapd->conf;
-+
- if (!hapd->x_snoop_initialized)
- return;
-- hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
-+ hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
-+ conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
- hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
- hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
- hapd->x_snoop_initialized = false;
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2322,6 +2322,8 @@ static int hostapd_config_fill(struct ho
- os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
- } else if (os_strcmp(buf, "bridge_hairpin") == 0) {
- bss->bridge_hairpin = atoi(pos);
-+ } else if (os_strcmp(buf, "snoop_iface") == 0) {
-+ os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
- } else if (os_strcmp(buf, "vlan_bridge") == 0) {
- os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
- } else if (os_strcmp(buf, "wds_bridge") == 0) {
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -366,12 +366,12 @@ static inline int hostapd_drv_br_port_se
-
- static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
- enum drv_br_net_param param,
-- unsigned int val)
-+ const char *ifname, unsigned int val)
- {
- if (hapd->driver == NULL || hapd->drv_priv == NULL ||
- hapd->driver->br_set_net_param == NULL)
- return -1;
-- return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
-+ return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
- }
-
- static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -4209,7 +4209,7 @@ struct wpa_driver_ops {
- * Returns: 0 on success, negative (<0) on failure
- */
- int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
-- unsigned int val);
-+ const char *ifname, unsigned int val);
-
- /**
- * get_wowlan - Get wake-on-wireless status
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -12168,7 +12168,7 @@ static const char * drv_br_net_param_str
-
-
- static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
-- unsigned int val)
-+ const char *ifname, unsigned int val)
- {
- struct i802_bss *bss = priv;
- char path[128];
-@@ -12194,8 +12194,11 @@ static int wpa_driver_br_set_net_param(v
- return -EINVAL;
- }
-
-+ if (!ifname)
-+ ifname = bss->brname;
-+
- os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
-- ip_version, bss->brname, param_txt);
-+ ip_version, ifname, param_txt);
-
- set_val:
- if (linux_write_system_file(path, val))
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/750-qos_map_set_without_interworking.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
deleted file mode 100644
index 97c32df..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
+++ /dev/null
@@ -1,97 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1604,6 +1604,8 @@ static int parse_anqp_elem(struct hostap
- return 0;
- }
-
-+#endif /* CONFIG_INTERWORKING */
-+
-
- static int parse_qos_map_set(struct hostapd_bss_config *bss,
- char *buf, int line)
-@@ -1645,8 +1647,6 @@ static int parse_qos_map_set(struct host
- return 0;
- }
-
--#endif /* CONFIG_INTERWORKING */
--
-
- #ifdef CONFIG_HS20
- static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
-@@ -4062,10 +4062,10 @@ static int hostapd_config_fill(struct ho
- bss->gas_frag_limit = val;
- } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
- bss->gas_comeback_delay = atoi(pos);
-+#endif /* CONFIG_INTERWORKING */
- } else if (os_strcmp(buf, "qos_map_set") == 0) {
- if (parse_qos_map_set(bss, pos, line) < 0)
- return 1;
--#endif /* CONFIG_INTERWORKING */
- #ifdef CONFIG_RADIUS_TEST
- } else if (os_strcmp(buf, "dump_msk_file") == 0) {
- os_free(bss->dump_msk_file);
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1486,6 +1486,7 @@ int hostapd_setup_bss(struct hostapd_dat
- wpa_printf(MSG_ERROR, "GAS server initialization failed");
- return -1;
- }
-+#endif /* CONFIG_INTERWORKING */
-
- if (conf->qos_map_set_len &&
- hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
-@@ -1493,7 +1494,6 @@ int hostapd_setup_bss(struct hostapd_dat
- wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
- return -1;
- }
--#endif /* CONFIG_INTERWORKING */
-
- if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
- wpa_printf(MSG_ERROR, "BSS Load initialization failed");
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -2683,8 +2683,6 @@ void wnm_bss_keep_alive_deinit(struct wp
- }
-
-
--#ifdef CONFIG_INTERWORKING
--
- static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
- size_t len)
- {
-@@ -2717,8 +2715,6 @@ static void interworking_process_assoc_r
- }
- }
-
--#endif /* CONFIG_INTERWORKING */
--
-
- static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
- {
-@@ -3098,10 +3094,8 @@ static int wpa_supplicant_event_associnf
- wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- data->assoc_info.resp_ies_len);
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_INTERWORKING
- interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- data->assoc_info.resp_ies_len);
--#endif /* CONFIG_INTERWORKING */
- if (wpa_s->hw_capab == CAPAB_VHT &&
- get_ie(data->assoc_info.resp_ies,
- data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
---- a/src/ap/ieee802_11_shared.c
-+++ b/src/ap/ieee802_11_shared.c
-@@ -1116,13 +1116,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_da
- u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *ext_capab_ie, size_t ext_capab_ie_len)
- {
--#ifdef CONFIG_INTERWORKING
- /* check for QoS Map support */
- if (ext_capab_ie_len >= 5) {
- if (ext_capab_ie[4] & 0x01)
- sta->qos_map_enabled = 1;
- }
--#endif /* CONFIG_INTERWORKING */
-
- if (ext_capab_ie_len > 0) {
- sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/751-qos_map_ignore_when_unsupported.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/751-qos_map_ignore_when_unsupported.patch
deleted file mode 100644
index f5ebab7..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/751-qos_map_ignore_when_unsupported.patch
+++ /dev/null
@@ -1,12 +0,0 @@
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -927,7 +927,8 @@ int hostapd_start_dfs_cac(struct hostapd
- int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
- const u8 *qos_map_set, u8 qos_map_set_len)
- {
-- if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
-+ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
-+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
- return 0;
- return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
- qos_map_set_len);
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/760-dynamic_own_ip.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/760-dynamic_own_ip.patch
deleted file mode 100644
index 2c705a6..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/760-dynamic_own_ip.patch
+++ /dev/null
@@ -1,109 +0,0 @@
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -310,6 +310,7 @@ struct hostapd_bss_config {
- unsigned int eap_sim_db_timeout;
- int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
- struct hostapd_ip_addr own_ip_addr;
-+ int dynamic_own_ip_addr;
- char *nas_identifier;
- struct hostapd_radius_servers *radius;
- int acct_interim_interval;
---- a/src/radius/radius_client.c
-+++ b/src/radius/radius_client.c
-@@ -163,6 +163,8 @@ struct radius_client_data {
- */
- void *ctx;
-
-+ struct hostapd_ip_addr local_ip;
-+
- /**
- * conf - RADIUS client configuration (list of RADIUS servers to use)
- */
-@@ -720,6 +722,30 @@ static void radius_client_list_add(struc
-
-
- /**
-+ * radius_client_send - Get local address for the RADIUS auth socket
-+ * @radius: RADIUS client context from radius_client_init()
-+ * @addr: pointer to store the address
-+ *
-+ * This function returns the local address for the connection to the RADIUS
-+ * auth server. It also opens the socket if it's not available yet.
-+ */
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+ struct hostapd_ip_addr *addr)
-+{
-+ struct hostapd_radius_servers *conf = radius->conf;
-+
-+ if (conf->auth_server && radius->auth_sock < 0)
-+ radius_client_init_auth(radius);
-+
-+ if (radius->auth_sock < 0)
-+ return -1;
-+
-+ memcpy(addr, &radius->local_ip, sizeof(*addr));
-+
-+ return 0;
-+}
-+
-+/**
- * radius_client_send - Send a RADIUS request
- * @radius: RADIUS client context from radius_client_init()
- * @msg: RADIUS message to be sent
-@@ -1238,6 +1264,10 @@ radius_change_server(struct radius_clien
- wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
- inet_ntoa(claddr.sin_addr),
- ntohs(claddr.sin_port));
-+ if (auth) {
-+ radius->local_ip.af = AF_INET;
-+ radius->local_ip.u.v4 = claddr.sin_addr;
-+ }
- }
- break;
- #ifdef CONFIG_IPV6
-@@ -1249,6 +1279,10 @@ radius_change_server(struct radius_clien
- inet_ntop(AF_INET6, &claddr6.sin6_addr,
- abuf, sizeof(abuf)),
- ntohs(claddr6.sin6_port));
-+ if (auth) {
-+ radius->local_ip.af = AF_INET6;
-+ radius->local_ip.u.v6 = claddr6.sin6_addr;
-+ }
- }
- break;
- }
---- a/src/radius/radius_client.h
-+++ b/src/radius/radius_client.h
-@@ -249,6 +249,8 @@ int radius_client_register(struct radius
- void radius_client_set_interim_error_cb(struct radius_client_data *radius,
- void (*cb)(const u8 *addr, void *ctx),
- void *ctx);
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+ struct hostapd_ip_addr * addr);
- int radius_client_send(struct radius_client_data *radius,
- struct radius_msg *msg,
- RadiusType msg_type, const u8 *addr);
---- a/src/ap/ieee802_1x.c
-+++ b/src/ap/ieee802_1x.c
-@@ -598,6 +598,10 @@ int add_common_radius_attr(struct hostap
- struct hostapd_radius_attr *attr;
- int len;
-
-+ if (hapd->conf->dynamic_own_ip_addr)
-+ radius_client_get_local_addr(hapd->radius,
-+ &hapd->conf->own_ip_addr);
-+
- if (!hostapd_config_get_radius_attr(req_attr,
- RADIUS_ATTR_NAS_IP_ADDRESS) &&
- hapd->conf->own_ip_addr.af == AF_INET &&
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2688,6 +2688,8 @@ static int hostapd_config_fill(struct ho
- } else if (os_strcmp(buf, "iapp_interface") == 0) {
- wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
- #endif /* CONFIG_IAPP */
-+ } else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
-+ bss->dynamic_own_ip_addr = atoi(pos);
- } else if (os_strcmp(buf, "own_ip_addr") == 0) {
- if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
- wpa_printf(MSG_ERROR,
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/761-shared_das_port.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/761-shared_das_port.patch
deleted file mode 100644
index cbb2a1b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/761-shared_das_port.patch
+++ /dev/null
@@ -1,298 +0,0 @@
---- a/src/radius/radius_das.h
-+++ b/src/radius/radius_das.h
-@@ -44,6 +44,7 @@ struct radius_das_attrs {
- struct radius_das_conf {
- int port;
- const u8 *shared_secret;
-+ const u8 *nas_identifier;
- size_t shared_secret_len;
- const struct hostapd_ip_addr *client_addr;
- unsigned int time_window;
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1423,6 +1423,7 @@ int hostapd_setup_bss(struct hostapd_dat
-
- os_memset(&das_conf, 0, sizeof(das_conf));
- das_conf.port = conf->radius_das_port;
-+ das_conf.nas_identifier = conf->nas_identifier;
- das_conf.shared_secret = conf->radius_das_shared_secret;
- das_conf.shared_secret_len =
- conf->radius_das_shared_secret_len;
---- a/src/radius/radius_das.c
-+++ b/src/radius/radius_das.c
-@@ -12,13 +12,26 @@
- #include "utils/common.h"
- #include "utils/eloop.h"
- #include "utils/ip_addr.h"
-+#include "utils/list.h"
- #include "radius.h"
- #include "radius_das.h"
-
-
--struct radius_das_data {
-+static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
-+
-+struct radius_das_port {
-+ struct dl_list list;
-+ struct dl_list das_data;
-+
-+ int port;
- int sock;
-+};
-+
-+struct radius_das_data {
-+ struct dl_list list;
-+ struct radius_das_port *port;
- u8 *shared_secret;
-+ u8 *nas_identifier;
- size_t shared_secret_len;
- struct hostapd_ip_addr client_addr;
- unsigned int time_window;
-@@ -378,56 +391,17 @@ fail:
- }
-
-
--static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+static void
-+radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
-+ struct sockaddr *from, socklen_t fromlen,
-+ char *abuf, int from_port)
- {
-- struct radius_das_data *das = eloop_ctx;
-- u8 buf[1500];
-- union {
-- struct sockaddr_storage ss;
-- struct sockaddr_in sin;
--#ifdef CONFIG_IPV6
-- struct sockaddr_in6 sin6;
--#endif /* CONFIG_IPV6 */
-- } from;
-- char abuf[50];
-- int from_port = 0;
-- socklen_t fromlen;
-- int len;
-- struct radius_msg *msg, *reply = NULL;
-+ struct radius_msg *reply = NULL;
- struct radius_hdr *hdr;
- struct wpabuf *rbuf;
-+ struct os_time now;
- u32 val;
- int res;
-- struct os_time now;
--
-- fromlen = sizeof(from);
-- len = recvfrom(sock, buf, sizeof(buf), 0,
-- (struct sockaddr *) &from.ss, &fromlen);
-- if (len < 0) {
-- wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
-- return;
-- }
--
-- os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
-- from_port = ntohs(from.sin.sin_port);
--
-- wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
-- len, abuf, from_port);
-- if (das->client_addr.u.v4.s_addr &&
-- das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
-- wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
-- return;
-- }
--
-- msg = radius_msg_parse(buf, len);
-- if (msg == NULL) {
-- wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
-- "from %s:%d failed", abuf, from_port);
-- return;
-- }
--
-- if (wpa_debug_level <= MSG_MSGDUMP)
-- radius_msg_dump(msg);
-
- if (radius_msg_verify_das_req(msg, das->shared_secret,
- das->shared_secret_len,
-@@ -494,9 +468,8 @@ static void radius_das_receive(int sock,
- radius_msg_dump(reply);
-
- rbuf = radius_msg_get_buf(reply);
-- res = sendto(das->sock, wpabuf_head(rbuf),
-- wpabuf_len(rbuf), 0,
-- (struct sockaddr *) &from.ss, fromlen);
-+ res = sendto(das->port->sock, wpabuf_head(rbuf),
-+ wpabuf_len(rbuf), 0, from, fromlen);
- if (res < 0) {
- wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
- abuf, from_port, strerror(errno));
-@@ -508,6 +481,72 @@ fail:
- radius_msg_free(reply);
- }
-
-+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+{
-+ struct radius_das_port *p = eloop_ctx;
-+ struct radius_das_data *das;
-+ u8 buf[1500];
-+ union {
-+ struct sockaddr_storage ss;
-+ struct sockaddr_in sin;
-+#ifdef CONFIG_IPV6
-+ struct sockaddr_in6 sin6;
-+#endif /* CONFIG_IPV6 */
-+ } from;
-+ struct radius_msg *msg;
-+ size_t nasid_len = 0;
-+ u8 *nasid_buf = NULL;
-+ char abuf[50];
-+ int from_port = 0;
-+ socklen_t fromlen;
-+ int found = 0;
-+ int len;
-+
-+ fromlen = sizeof(from);
-+ len = recvfrom(sock, buf, sizeof(buf), 0,
-+ (struct sockaddr *) &from.ss, &fromlen);
-+ if (len < 0) {
-+ wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
-+ return;
-+ }
-+
-+ os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
-+ from_port = ntohs(from.sin.sin_port);
-+
-+ msg = radius_msg_parse(buf, len);
-+ if (msg == NULL) {
-+ wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
-+ "from %s:%d failed", abuf, from_port);
-+ return;
-+ }
-+
-+ wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
-+ len, abuf, from_port);
-+
-+ if (wpa_debug_level <= MSG_MSGDUMP)
-+ radius_msg_dump(msg);
-+
-+ radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
-+ &nasid_buf, &nasid_len, NULL);
-+ dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
-+ if (das->client_addr.u.v4.s_addr &&
-+ das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
-+ continue;
-+
-+ if (das->nas_identifier && nasid_buf &&
-+ (nasid_len != os_strlen(das->nas_identifier) ||
-+ os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
-+ continue;
-+
-+ found = 1;
-+ radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
-+ fromlen, abuf, from_port);
-+ }
-+
-+ if (!found)
-+ wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
-+}
-+
-
- static int radius_das_open_socket(int port)
- {
-@@ -533,6 +572,49 @@ static int radius_das_open_socket(int po
- }
-
-
-+static struct radius_das_port *
-+radius_das_open_port(int port)
-+{
-+ struct radius_das_port *p;
-+
-+ dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
-+ if (p->port == port)
-+ return p;
-+ }
-+
-+ p = os_zalloc(sizeof(*p));
-+ if (p == NULL)
-+ return NULL;
-+
-+ dl_list_init(&p->das_data);
-+ p->port = port;
-+ p->sock = radius_das_open_socket(port);
-+ if (p->sock < 0)
-+ goto free_port;
-+
-+ if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
-+ goto close_port;
-+
-+ dl_list_add(&das_ports, &p->list);
-+
-+ return p;
-+
-+close_port:
-+ close(p->sock);
-+free_port:
-+ os_free(p);
-+
-+ return NULL;
-+}
-+
-+static void radius_das_close_port(struct radius_das_port *p)
-+{
-+ dl_list_del(&p->list);
-+ eloop_unregister_read_sock(p->sock);
-+ close(p->sock);
-+ free(p);
-+}
-+
- struct radius_das_data *
- radius_das_init(struct radius_das_conf *conf)
- {
-@@ -553,6 +635,8 @@ radius_das_init(struct radius_das_conf *
- das->ctx = conf->ctx;
- das->disconnect = conf->disconnect;
- das->coa = conf->coa;
-+ if (conf->nas_identifier)
-+ das->nas_identifier = os_strdup(conf->nas_identifier);
-
- os_memcpy(&das->client_addr, conf->client_addr,
- sizeof(das->client_addr));
-@@ -565,19 +649,15 @@ radius_das_init(struct radius_das_conf *
- }
- das->shared_secret_len = conf->shared_secret_len;
-
-- das->sock = radius_das_open_socket(conf->port);
-- if (das->sock < 0) {
-+ das->port = radius_das_open_port(conf->port);
-+ if (!das->port) {
- wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
- "DAS");
- radius_das_deinit(das);
- return NULL;
- }
-
-- if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
-- {
-- radius_das_deinit(das);
-- return NULL;
-- }
-+ dl_list_add(&das->port->das_data, &das->list);
-
- return das;
- }
-@@ -588,11 +668,14 @@ void radius_das_deinit(struct radius_das
- if (das == NULL)
- return;
-
-- if (das->sock >= 0) {
-- eloop_unregister_read_sock(das->sock);
-- close(das->sock);
-+ if (das->port) {
-+ dl_list_del(&das->list);
-+
-+ if (dl_list_empty(&das->port->das_data))
-+ radius_das_close_port(das->port);
- }
-
-+ os_free(das->nas_identifier);
- os_free(das->shared_secret);
- os_free(das);
- }
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/770-radius_server.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/770-radius_server.patch
deleted file mode 100644
index 8837a26..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/770-radius_server.patch
+++ /dev/null
@@ -1,154 +0,0 @@
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -63,6 +63,10 @@ endif
- OBJS += main.o
- OBJS += config_file.o
-
-+ifdef CONFIG_RADIUS_SERVER
-+OBJS += radius.o
-+endif
-+
- OBJS += ../src/ap/hostapd.o
- OBJS += ../src/ap/wpa_auth_glue.o
- OBJS += ../src/ap/drv_callbacks.o
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -40,6 +40,7 @@ struct hapd_global {
-
- static struct hapd_global global;
-
-+extern int radius_main(int argc, char **argv);
-
- #ifndef CONFIG_NO_HOSTAPD_LOGGER
- static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
-@@ -771,6 +772,11 @@ int main(int argc, char *argv[])
- if (os_program_init())
- return -1;
-
-+#ifdef RADIUS_SERVER
-+ if (strstr(argv[0], "radius"))
-+ return radius_main(argc, argv);
-+#endif
-+
- os_memset(&interfaces, 0, sizeof(interfaces));
- interfaces.reload_config = hostapd_reload_config;
- interfaces.config_read_cb = hostapd_config_read;
---- a/src/radius/radius_server.c
-+++ b/src/radius/radius_server.c
-@@ -63,6 +63,12 @@ struct radius_server_counters {
- u32 unknown_acct_types;
- };
-
-+struct radius_accept_attr {
-+ u8 type;
-+ u16 len;
-+ void *data;
-+};
-+
- /**
- * struct radius_session - Internal RADIUS server data for a session
- */
-@@ -90,7 +96,7 @@ struct radius_session {
- unsigned int macacl:1;
- unsigned int t_c_filtering:1;
-
-- struct hostapd_radius_attr *accept_attr;
-+ struct radius_accept_attr *accept_attr;
-
- u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
- };
-@@ -394,6 +400,7 @@ static void radius_server_session_free(s
- radius_msg_free(sess->last_reply);
- os_free(sess->username);
- os_free(sess->nas_ip);
-+ os_free(sess->accept_attr);
- os_free(sess);
- data->num_sess--;
- }
-@@ -554,6 +561,36 @@ radius_server_erp_find_key(struct radius
- }
- #endif /* CONFIG_ERP */
-
-+static struct radius_accept_attr *
-+radius_server_copy_attr(const struct hostapd_radius_attr *data)
-+{
-+ const struct hostapd_radius_attr *attr;
-+ struct radius_accept_attr *attr_new;
-+ size_t data_size = 0;
-+ void *data_buf;
-+ int n_attr = 1;
-+
-+ for (attr = data; attr; attr = attr->next) {
-+ n_attr++;
-+ data_size += wpabuf_len(attr->val);
-+ }
-+
-+ attr_new = os_zalloc(n_attr * sizeof(*attr) + data_size);
-+ if (!attr_new)
-+ return NULL;
-+
-+ data_buf = &attr_new[n_attr];
-+ for (n_attr = 0, attr = data; attr; attr = attr->next) {
-+ struct radius_accept_attr *cur = &attr_new[n_attr++];
-+
-+ cur->type = attr->type;
-+ cur->len = wpabuf_len(attr->val);
-+ cur->data = memcpy(data_buf, wpabuf_head(attr->val), cur->len);
-+ data_buf += cur->len;
-+ }
-+
-+ return attr_new;
-+}
-
- static struct radius_session *
- radius_server_get_new_session(struct radius_server_data *data,
-@@ -607,7 +644,7 @@ radius_server_get_new_session(struct rad
- eap_user_free(tmp);
- return NULL;
- }
-- sess->accept_attr = tmp->accept_attr;
-+ sess->accept_attr = radius_server_copy_attr(tmp->accept_attr);
- sess->macacl = tmp->macacl;
- eap_user_free(tmp);
-
-@@ -1118,11 +1155,10 @@ radius_server_encapsulate_eap(struct rad
- }
-
- if (code == RADIUS_CODE_ACCESS_ACCEPT) {
-- struct hostapd_radius_attr *attr;
-- for (attr = sess->accept_attr; attr; attr = attr->next) {
-- if (!radius_msg_add_attr(msg, attr->type,
-- wpabuf_head(attr->val),
-- wpabuf_len(attr->val))) {
-+ struct radius_accept_attr *attr;
-+ for (attr = sess->accept_attr; attr->data; attr++) {
-+ if (!radius_msg_add_attr(msg, attr->type, attr->data,
-+ attr->len)) {
- wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
- radius_msg_free(msg);
- return NULL;
-@@ -1211,11 +1247,10 @@ radius_server_macacl(struct radius_serve
- }
-
- if (code == RADIUS_CODE_ACCESS_ACCEPT) {
-- struct hostapd_radius_attr *attr;
-- for (attr = sess->accept_attr; attr; attr = attr->next) {
-- if (!radius_msg_add_attr(msg, attr->type,
-- wpabuf_head(attr->val),
-- wpabuf_len(attr->val))) {
-+ struct radius_accept_attr *attr;
-+ for (attr = sess->accept_attr; attr->data; attr++) {
-+ if (!radius_msg_add_attr(msg, attr->type, attr->data,
-+ attr->len)) {
- wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
- radius_msg_free(msg);
- return NULL;
-@@ -2512,7 +2547,7 @@ static int radius_server_get_eap_user(vo
- ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
- phase2, user);
- if (ret == 0 && user) {
-- sess->accept_attr = user->accept_attr;
-+ sess->accept_attr = radius_server_copy_attr(user->accept_attr);
- sess->remediation = user->remediation;
- sess->macacl = user->macacl;
- sess->t_c_timestamp = user->t_c_timestamp;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch
deleted file mode 100644
index 5809a3b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From f0e9f5aab52b3eab85d28338cc996972ced4c39c Mon Sep 17 00:00:00 2001
-From: David Bauer <mail@david-bauer.net>
-Date: Tue, 17 May 2022 23:07:59 +0200
-Subject: [PATCH] ctrl: make WNM_AP functions dependant on CONFIG_AP
-
-This fixes linking errors found when compiling wpa_supplicant with
-CONFIG_WNM_AP enabled but CONFIG_AP disabled.
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- wpa_supplicant/ctrl_iface.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/wpa_supplicant/ctrl_iface.c
-+++ b/wpa_supplicant/ctrl_iface.c
-@@ -12763,7 +12763,7 @@ char * wpa_supplicant_ctrl_iface_process
- if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
- reply_len = -1;
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_WNM_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
- } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
- if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
- reply_len = -1;
-@@ -12773,7 +12773,7 @@ char * wpa_supplicant_ctrl_iface_process
- } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
- if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
- reply_len = -1;
--#endif /* CONFIG_WNM_AP */
-+#endif /* CONFIG_AP && CONFIG_WNM_AP */
- } else if (os_strcmp(buf, "FLUSH") == 0) {
- wpa_supplicant_ctrl_iface_flush(wpa_s);
- } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/991-Fix-OpenWrt-13156.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/991-Fix-OpenWrt-13156.patch
deleted file mode 100644
index 097d62a..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/991-Fix-OpenWrt-13156.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From 26cd9bafc1d25e602952ee86cd2a5b8c3a995490 Mon Sep 17 00:00:00 2001
-From: Stijn Tintel <stijn@linux-ipv6.be>
-Date: Fri, 28 Jul 2023 16:27:47 +0300
-Subject: [PATCH] Revert "Do prune_association only after the STA is
- authorized"
-
-Commit e978072baaca ("Do prune_association only after the STA is
-authorized") causes issues when an STA roams from one interface to
-another interface on the same PHY. The mt7915 driver is not able to
-handle this properly. While the commits fixes a DoS, there are other
-devices and drivers with the same limitation, so revert to the orginal
-behavior for now, until we have a better solution in place.
-
-Ref: https://github.com/openwrt/openwrt/issues/13156
-Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
----
- src/ap/hostapd.c | 14 +++++++++++---
- src/ap/sta_info.c | 3 ---
- 2 files changed, 11 insertions(+), 6 deletions(-)
-
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -3564,6 +3564,8 @@ int hostapd_remove_iface(struct hapd_int
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- int reassoc)
- {
-+ int mld_assoc_link_id = -1;
-+
- if (hapd->tkip_countermeasures) {
- hostapd_drv_sta_deauth(hapd, sta->addr,
- WLAN_REASON_MICHAEL_MIC_FAILURE);
-@@ -3571,10 +3573,16 @@ void hostapd_new_assoc_sta(struct hostap
- }
-
- #ifdef CONFIG_IEEE80211BE
-- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
-- sta->mld_assoc_link_id != hapd->mld_link_id)
-- return;
-+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
-+ if (sta->mld_assoc_link_id == hapd->mld_link_id) {
-+ mld_assoc_link_id = sta->mld_assoc_link_id;
-+ } else {
-+ return;
-+ }
-+ }
- #endif /* CONFIG_IEEE80211BE */
-+ if (mld_assoc_link_id != -2)
-+ hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
-
- ap_sta_clear_disconnect_timeouts(hapd, sta);
- sta->post_csa_sa_query = 0;
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -1318,9 +1318,6 @@ void ap_sta_set_authorized(struct hostap
- mld_assoc_link_id = -2;
- }
- #endif /* CONFIG_IEEE80211BE */
-- if (mld_assoc_link_id != -2)
-- hostapd_prune_associations(hapd, sta->addr,
-- mld_assoc_link_id);
- sta->flags |= WLAN_STA_AUTHORIZED;
- } else {
- sta->flags &= ~WLAN_STA_AUTHORIZED;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch
deleted file mode 100644
index c7b595d..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From: David Bauer <mail@david-bauer.net>
-To: hostap@lists.infradead.org
-Cc: =?utf-8?q?=C3=89tienne_Morice?= <neon.emorice@mail.com>
-Subject: [PATCH] nl80211: add extra-ies only if allowed by driver
-Date: Sun, 30 Jan 2022 20:22:00 +0100
-Message-Id: <20220130192200.10883-1-mail@david-bauer.net>
-List-Id: <hostap.lists.infradead.org>
-
-Upgrading wpa_supplicant from 2.9 to 2.10 breaks broadcom-wl
-based adapters. The reason for it is hostapd tries to install additional
-IEs for scanning while the driver does not support this.
-
-The kernel indicates the maximum number of bytes for additional scan IEs
-using the NL80211_ATTR_MAX_SCAN_IE_LEN attribute. Save this value and
-only add additional scan IEs in case the driver can accommodate these
-additional IEs.
-
-Reported-by: Étienne Morice <neon.emorice@mail.com>
-Tested-by: Étienne Morice <neon.emorice@mail.com>
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
- src/drivers/driver.h | 3 +++
- src/drivers/driver_nl80211_capa.c | 4 ++++
- src/drivers/driver_nl80211_scan.c | 2 +-
- 3 files changed, 8 insertions(+), 1 deletion(-)
-
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -2283,6 +2283,9 @@ struct wpa_driver_capa {
- /** Maximum number of iterations in a single scan plan */
- u32 max_sched_scan_plan_iterations;
-
-+ /** Maximum number of extra IE bytes for scans */
-+ u16 max_scan_ie_len;
-+
- /** Whether sched_scan (offloaded scanning) is supported */
- int sched_scan_supported;
-
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -949,6 +949,10 @@ static int wiphy_info_handler(struct nl_
- nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
- }
-
-+ if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
-+ capa->max_scan_ie_len =
-+ nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
-+
- if (tb[NL80211_ATTR_MAX_MATCH_SETS])
- capa->max_match_sets =
- nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
---- a/src/drivers/driver_nl80211_scan.c
-+++ b/src/drivers/driver_nl80211_scan.c
-@@ -222,7 +222,7 @@ nl80211_scan_common(struct i802_bss *bss
- wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
- }
-
-- if (params->extra_ies) {
-+ if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
- wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
- params->extra_ies, params->extra_ies_len);
- if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch
deleted file mode 100644
index 948c51b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 7a733993211ad46cf3032038c1e7e6bdc2322336 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 5 Sep 2023 09:43:25 +0800
-Subject: [PATCH] ACS: Fix typo in bw_40 frequency array
-
-The range for the 5 GHz channel 118 was encoded with an incorrect
-channel number.
-
-Fixes: ed8e13decc71 (ACS: Extract bw40/80/160 freqs out of acs_usable_bwXXX_chan())
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/acs.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -256,7 +256,7 @@ struct bw_item {
- 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 },
-+ { 5580, 5600, 118 }, { 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 },
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
deleted file mode 100644
index 25892c8..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
+++ /dev/null
@@ -1,437 +0,0 @@
-From 154b32c66ff22838dc619d85332eadae16cb0353 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 01/54] mtk: hostapd: Add neighbor report and BSS Termination
- for MBO certification
-
-1. 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.
-
-2. Support including neighbor report elements in ANQP response
-3. Support including neignbor report elements in BTM response
-4. Support configuring BSS Termination TSF by using hostapd_cli command
-5. Disable interface if BSS Termination TSF is set
-6. Support including neighbor report elements in BTM request
-7. Add hostapd_neighbor_set_own_report_pref()
-8. Add hostapd_neighbor_set_pref_by_non_pref_chan()
-
-Revert set_send_disassoc_frame_timer
----
- hostapd/ctrl_iface.c | 5 ++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ctrl_iface_ap.c | 19 ++++++-
- src/ap/gas_serv.c | 29 ++++++++++
- src/ap/gas_serv.h | 2 +
- src/ap/neighbor_db.c | 119 +++++++++++++++++++++++++++++++++++++++++
- src/ap/neighbor_db.h | 9 ++++
- src/ap/wnm_ap.c | 42 ++++++++++++++-
- 9 files changed, 223 insertions(+), 4 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 0a892c915..98d598ea9 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1290,6 +1290,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 298216a47..73b33b42a 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -171,6 +171,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
- bss->pasn_comeback_after = 10;
- bss->pasn_noauth = 1;
- #endif /* CONFIG_PASN */
-+ bss->bss_termination_tsf = 0;
- }
-
-
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 0790478ee..82338e213 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -558,6 +558,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;
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 42c959387..0e173f174 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1274,6 +1274,10 @@ 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));
- }
-@@ -1300,14 +1304,25 @@ 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"))
-+ 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
-+ hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
-+
- pos = os_strstr(cmd, "mbo=");
- if (pos) {
- unsigned int mbo_reason, cell_pref, reassoc_delay;
-diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
-index 4642e4927..cce6df41c 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 7646a98a4..ce492b53f 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.
-diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
-index 5b276e8da..1c14b3201 100644
---- a/src/ap/neighbor_db.c
-+++ b/src/ap/neighbor_db.c
-@@ -89,6 +89,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);
-@@ -325,3 +357,90 @@ 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;
-+ }
-+ }
-+ }
-+ }
-+}
-+
-+#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 992671b62..a1ddc075b 100644
---- a/src/ap/neighbor_db.h
-+++ b/src/ap/neighbor_db.h
-@@ -24,4 +24,13 @@ 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);
-+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 */
-diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index ba1dd2ed1..939d4471b 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 "
-@@ -890,6 +911,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)
-@@ -979,6 +1016,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/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
deleted file mode 100644
index 976c625..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 135e2f4368bba88d8823da63a134fdcc587fe698 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 20 Sep 2022 19:33:45 +0800
-Subject: [PATCH 02/54] mtk: hostapd: print sae groups by hostapd ctrl
-
----
- hostapd/ctrl_iface.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 98d598ea9..c03e6f608 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1364,6 +1364,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
- if (os_snprintf_error(buflen, res))
- return -1;
- return res;
-+ } else if (os_strcmp(cmd, "sae_group_capability") == 0) {
-+#ifdef CONFIG_SAE
-+ /* see sae_set_group() */
-+ res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
-+ dh_groups_get(15) ? "15 ": "",
-+ dh_groups_get(16) ? "16 ": "",
-+ dh_groups_get(17) ? "17 ": "",
-+ dh_groups_get(18) ? "18 ": "");
-+
-+ if (os_snprintf_error(buflen, res))
-+ return -1;
-+ return res;
-+#endif /* CONFIG_SAE */
- }
-
- return -1;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
deleted file mode 100644
index 29b4c03..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
+++ /dev/null
@@ -1,211 +0,0 @@
-From 55220dcc0fcd43270edf583720e0b36e453dc2d7 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 31 May 2022 21:15:54 +0800
-Subject: [PATCH 03/54] mtk: hostapd: add support for runtime set in-band
- discovery
-
-Usage:
-hostapd_cli unsolic_probe_resp [tx_type] [interval]
-
-0: disable all in-band discovery
-1: enable unsolicited probe response
-2: enable FILS discovery
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
----
- hostapd/ctrl_iface.c | 66 ++++++++++++++++++++++++++++++++++++
- hostapd/hostapd_cli.c | 20 +++++++++++
- src/ap/beacon.c | 5 ++-
- src/drivers/driver_nl80211.c | 10 ++++--
- src/drivers/nl80211_copy.h | 1 +
- 5 files changed, 98 insertions(+), 4 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index c03e6f608..ee6d492f8 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -770,6 +770,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
-
- #endif /* CONFIG_INTERWORKING */
-
-+static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
-+ const char *cmd)
-+{
-+ struct hostapd_bss_config *conf = hapd->conf;
-+ const char *pos = cmd;
-+ int tx_type, interval, ret;
-+
-+ tx_type = atoi(pos);
-+ if (tx_type < 0 || tx_type > 2) {
-+ wpa_printf(MSG_ERROR, "Invalid tx type\n");
-+ return -1;
-+ }
-+
-+ pos = os_strchr(pos, ' ');
-+ if(!pos)
-+ return -1;
-+ pos++;
-+ interval = atoi(pos);
-+ if (interval < 0 || interval > 20) {
-+ wpa_printf(MSG_ERROR, "Invalid interval value\n");
-+ return -1;
-+ }
-+
-+ wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
-+ tx_type, interval);
-+
-+#define DISABLE_INBAND_DISC 0
-+#define UNSOL_PROBE_RESP 1
-+#define FILS_DISCOVERY 2
-+
-+#ifdef CONFIG_FILS
-+ conf->fils_discovery_max_int = 0;
-+ conf->fils_discovery_min_int = 0;
-+#endif /* CONFIG_FILS */
-+ conf->unsol_bcast_probe_resp_interval = 0;
-+
-+ switch (tx_type) {
-+ case DISABLE_INBAND_DISC:
-+ default:
-+ /* Disable both Unsolicited probe response and FILS discovery*/
-+ break;
-+ case UNSOL_PROBE_RESP:
-+ /* Enable Unsolicited probe response */
-+ conf->unsol_bcast_probe_resp_interval = interval;
-+ break;
-+#ifdef CONFIG_FILS
-+ case FILS_DISCOVERY:
-+ /* Enable FILS discovery */
-+ conf->fils_discovery_min_int = interval;
-+ conf->fils_discovery_max_int = interval;
-+ break;
-+#endif /* CONFIG_FILS */
-+ }
-+
-+ ret = ieee802_11_update_beacons(hapd->iface);
-+ if(ret) {
-+ wpa_printf(MSG_DEBUG,
-+ "Failed to update with inband discovery parameters\n");
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-
- #ifdef CONFIG_WNM_AP
-
-@@ -3483,6 +3546,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
- reply_len = -1;
- #endif /* CONFIG_WNM_AP */
-+ } else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
-+ if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
-+ reply_len = -1;
- } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
- reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
- reply_size);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 61f8cba12..dfc996d49 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -655,6 +655,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
- return wpa_ctrl_command(ctrl, buf);
- }
-
-+static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ char buf[300];
-+ int res;
-+
-+ if (argc < 2) {
-+ printf("Invalid 'inband_discovery' command - two arguments"
-+ "tx_type interval\n");
-+ return -1;
-+ }
-+
-+ res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
-+ argv[0], argv[1]);
-+ if (os_snprintf_error(sizeof(buf), res))
-+ return -1;
-+ return wpa_ctrl_command(ctrl, buf);
-+}
-
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
-@@ -1773,6 +1791,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- { "driver", hostapd_cli_cmd_driver, NULL,
- "<driver sub command> [<hex formatted data>] = send driver command data" },
- #endif /* ANDROID */
-+ { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
-+ "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 6366d77f0..d160675cb 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1648,6 +1648,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- struct wpa_driver_ap_params *params)
- {
- params->fd_max_int = hapd->conf->fils_discovery_max_int;
-+ params->unsol_bcast_probe_resp_interval =
-+ hapd->conf->unsol_bcast_probe_resp_interval;
- if (is_6ghz_op_class(hapd->iconf->op_class) &&
- params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
- params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
-@@ -1656,7 +1658,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- if (params->fd_min_int > params->fd_max_int)
- params->fd_min_int = params->fd_max_int;
-
-- if (params->fd_max_int)
-+ if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
-+ !params->unsol_bcast_probe_resp_interval))
- return hostapd_gen_fils_discovery(hapd,
- ¶ms->fd_frame_tmpl_len);
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 98510f1cf..a3e436e95 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -4706,9 +4706,10 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
- params->fd_max_int) ||
- (params->fd_frame_tmpl &&
- nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
-- params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
-+ params->fd_frame_tmpl_len, params->fd_frame_tmpl)) ||
-+ nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INTE,
-+ params->unsol_bcast_probe_resp_interval))
- return -1;
--
- nla_nest_end(msg, attr);
- return 0;
- }
-@@ -5320,7 +5321,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
- #endif /* CONFIG_SAE */
-
- #ifdef CONFIG_FILS
-- if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
-+ if ((params->fd_max_int ||
-+ ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
-+ !(params->unsol_bcast_probe_resp_interval))) &&
-+ nl80211_fils_discovery(bss, msg, params) < 0)
- goto fail;
- #endif /* CONFIG_FILS */
-
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index c59fec406..82860ae32 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -7591,6 +7591,7 @@ enum nl80211_fils_discovery_attributes {
- NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
- NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
- NL80211_FILS_DISCOVERY_ATTR_TMPL,
-+ NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INTE,
-
- /* keep last */
- __NL80211_FILS_DISCOVERY_ATTR_LAST,
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch
deleted file mode 100644
index 16949b7..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch
+++ /dev/null
@@ -1,216 +0,0 @@
-From 2bba6f165367d21c44cb4da8b74904ecee956d55 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 15:04:57 +0800
-Subject: [PATCH 04/54] mtk: hostapd: Add mtk_vendor.h
-
----
- src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 197 insertions(+)
- create mode 100644 src/common/mtk_vendor.h
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-new file mode 100644
-index 000000000..4a19d2fc9
---- /dev/null
-+++ b/src/common/mtk_vendor.h
-@@ -0,0 +1,197 @@
-+// SPDX-License-Identifier: ISC
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+#ifndef MTK_VENDOR_H
-+#define MTK_VENDOR_H
-+
-+#define OUI_MTK 0x000ce7
-+
-+enum mtk_nl80211_vendor_subcmds {
-+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+ MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
-+ MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
-+ MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl {
-+ MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
-+
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
-+ MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+ EDCCA_CTRL_SET_EN = 0,
-+ EDCCA_CTRL_SET_THERS,
-+ EDCCA_CTRL_GET_EN,
-+ EDCCA_CTRL_GET_THERS,
-+ EDCCA_CTRL_NUM,
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+enum mtk_vendor_attr_csi_ctrl {
-+ MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
-+ MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
-+ MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
-+ MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
-+
-+ MTK_VENDOR_ATTR_CSI_CTRL_DATA,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
-+ MTK_VENDOR_ATTR_CSI_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_csi_data {
-+ MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
-+ MTK_VENDOR_ATTR_CSI_DATA_PAD,
-+
-+ MTK_VENDOR_ATTR_CSI_DATA_VER,
-+ MTK_VENDOR_ATTR_CSI_DATA_TS,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSSI,
-+ MTK_VENDOR_ATTR_CSI_DATA_SNR,
-+ MTK_VENDOR_ATTR_CSI_DATA_BW,
-+ MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
-+ MTK_VENDOR_ATTR_CSI_DATA_TA,
-+ MTK_VENDOR_ATTR_CSI_DATA_I,
-+ MTK_VENDOR_ATTR_CSI_DATA_Q,
-+ MTK_VENDOR_ATTR_CSI_DATA_INFO,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
-+ MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
-+ MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
-+ MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
-+ MTK_VENDOR_ATTR_CSI_DATA_MODE,
-+ MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-+ MTK_VENDOR_ATTR_CSI_DATA_MAX =
-+ NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_ctrl {
-+ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_CTRL_SET,
-+ MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
-+ MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_set {
-+ MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_SET_INDEX,
-+ MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_SET,
-+ MTK_VENDOR_ATTR_AMNT_SET_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_dump {
-+ MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+ MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_ctrl {
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_rfeature_ctrl {
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
-+};
-+
-+#define CSI_MAX_COUNT 256
-+#define ETH_ALEN 6
-+
-+struct csi_data {
-+ s16 data_i[CSI_MAX_COUNT];
-+ s16 data_q[CSI_MAX_COUNT];
-+ s8 rssi;
-+ u8 snr;
-+ u32 ts;
-+ u8 data_bw;
-+ u8 pri_ch_idx;
-+ u8 ta[ETH_ALEN];
-+ u32 info;
-+ u8 rx_mode;
-+ u32 h_idx;
-+ u16 tx_idx;
-+ u16 rx_idx;
-+};
-+
-+struct amnt_data {
-+ u8 idx;
-+ u8 addr[ETH_ALEN];
-+ s8 rssi[4];
-+ u32 last_seen;
-+};
-+#endif /* MTK_VENDOR_H */
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
deleted file mode 100644
index 37f5172..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
+++ /dev/null
@@ -1,631 +0,0 @@
-From 7e1b6b0dc2167af5b9d58466ce693b67e6b5dbf2 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 16:31:34 +0800
-Subject: [PATCH 05/54] mtk: hostapd: Support EDCCA hostapd configuration
-
-edcca_enable and edcca_compensation and implement edcca related handlers.
----
- hostapd/config_file.c | 34 ++++++
- hostapd/ctrl_iface.c | 125 +++++++++++++++++++++
- src/ap/ap_config.c | 4 +
- src/ap/ap_config.h | 30 ++++++
- src/ap/ap_drv_ops.c | 24 +++++
- src/ap/ap_drv_ops.h | 4 +
- src/ap/hostapd.c | 7 ++
- src/common/mtk_vendor.h | 20 ++--
- src/drivers/driver.h | 4 +
- src/drivers/driver_nl80211.c | 174 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 7 ++
- 12 files changed, 428 insertions(+), 6 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 4b0f99fd2..d281026e8 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4809,6 +4809,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- #endif /* CONFIG_IEEE80211BE */
-+ } else if (os_strcmp(buf, "edcca_threshold") == 0) {
-+ if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
-+ conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
-+ conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
-+ conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
-+ conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
-+ conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
-+ wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
-+ line);
-+ return 1;
-+ }
-+ } else if (os_strcmp(buf, "edcca_enable") == 0) {
-+ int mode = atoi(pos);
-+ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+ wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
-+ " allowed value 0 (Force Disable) or 1(Auto) ",
-+ line, mode);
-+ return 1;
-+ }
-+ conf->edcca_enable = (u8) mode;
-+ } else if (os_strcmp(buf, "edcca_compensation") == 0) {
-+ int val = atoi(pos);
-+ if (val < EDCCA_MIN_COMPENSATION ||
-+ val > EDCCA_MAX_COMPENSATION) {
-+ wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
-+ " value %d; allowed value %d ~ %d.",
-+ line, val, EDCCA_MIN_COMPENSATION,
-+ EDCCA_MAX_COMPENSATION);
-+ return 1;
-+ }
-+ conf->edcca_compensation = (s8) val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ee6d492f8..cad3f863c 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -542,6 +542,19 @@ static const char * pbc_status_str(enum pbc_status status)
- }
-
-
-+static const char *edcca_mode_str(enum edcca_mode status)
-+{
-+ switch (status) {
-+ case EDCCA_MODE_FORCE_DISABLE:
-+ return "Force Disable";
-+ case EDCCA_MODE_AUTO:
-+ return "Auto";
-+ default:
-+ return "Unknown";
-+ }
-+}
-+
-+
- static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
- char *buf, size_t buflen)
- {
-@@ -3369,6 +3382,112 @@ static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
- #endif /* ANDROID */
-
-
-+static int
-+hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *config, *value;
-+ config = cmd;
-+ pos = os_strchr(config, ' ');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if (pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strcmp(config, "enable") == 0) {
-+ int mode = atoi(value);
-+ if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+ wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
-+ return -1;
-+ }
-+ hapd->iconf->edcca_enable = (u8) mode;
-+ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+ return -1;
-+ } else if (os_strcmp(config, "compensation") == 0) {
-+ int compensation = atoi(value);
-+ if (compensation < EDCCA_MIN_COMPENSATION ||
-+ compensation > EDCCA_MAX_COMPENSATION) {
-+ wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
-+ return -1;
-+ }
-+ hapd->iconf->edcca_compensation = (s8) compensation;
-+ if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+ return -1;
-+ } else if (os_strcmp(config, "threshold") == 0) {
-+ char *thres_value;
-+ thres_value = os_strchr(value, ':');
-+ if (thres_value == NULL)
-+ return -1;
-+ *thres_value++ = '\0';
-+
-+ if (thres_value == NULL)
-+ return -1;
-+ int bw_idx = atoi(value);
-+ int threshold = atoi(thres_value);
-+
-+ if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported Bandwidth idx %d for SET_EDCCA",
-+ bw_idx);
-+ return -1;
-+ }
-+ if (threshold < EDCCA_MIN_CONFIG_THRES ||
-+ threshold > EDCCA_MAX_CONFIG_THRES) {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported threshold %d for SET_EDCCA",
-+ threshold);
-+ return -1;
-+ }
-+
-+ int threshold_arr[EDCCA_MAX_BW_NUM];
-+ /* 0x7f means keep the origival value in firmware */
-+ os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
-+ threshold_arr[bw_idx] = threshold;
-+
-+ if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
-+ return -1;
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for SET_EDCCA", config);
-+ return -1;
-+ }
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
-+ size_t buflen)
-+{
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+ u8 value[EDCCA_MAX_BW_NUM] = {0};
-+
-+ if (os_strcmp(cmd, "enable") == 0) {
-+ return os_snprintf(pos, end - pos, "Enable: %s\n",
-+ edcca_mode_str(hapd->iconf->edcca_enable));
-+ } else if (os_strcmp(cmd, "compensation") == 0) {
-+ return os_snprintf(pos, end - pos, "Compensation: %d\n",
-+ hapd->iconf->edcca_compensation);
-+ } else if (os_strcmp(cmd, "threshold") == 0) {
-+ if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
-+ return -1;
-+ return os_snprintf(pos, end - pos,
-+ "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
-+ value[0], value[1], value[2], value[3]);
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for GET_EDCCA", cmd);
-+ return -1;
-+ }
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -3922,6 +4041,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
- reply_size);
- #endif /* ANDROID */
-+ } else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
-+ reply_size);
-+ } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
-+ reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 73b33b42a..8e56d1082 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -295,6 +295,9 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
- #endif /* CONFIG_AIRTIME_POLICY */
-
-+ conf->edcca_enable = EDCCA_MODE_AUTO;
-+ conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+
- return conf;
- }
-
-@@ -1008,6 +1011,7 @@ void hostapd_config_free(struct hostapd_config *conf)
- #ifdef CONFIG_ACS
- os_free(conf->acs_chan_bias);
- #endif /* CONFIG_ACS */
-+ os_free(conf->edcca_threshold);
- wpabuf_free(conf->lci);
- wpabuf_free(conf->civic);
-
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 82338e213..24d540dbf 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1193,8 +1193,38 @@ struct hostapd_config {
- MBSSID_ENABLED = 1,
- ENHANCED_MBSSID_ENABLED = 2,
- } mbssid;
-+
-+ u8 edcca_enable;
-+ s8 edcca_compensation;
-+ int *edcca_threshold;
-+};
-+
-+enum edcca_mode {
-+ EDCCA_MODE_FORCE_DISABLE = 0,
-+ EDCCA_MODE_AUTO = 1,
-+};
-+
-+enum edcca_bw_id {
-+ EDCCA_BW_20 = 0,
-+ EDCCA_BW_40,
-+ EDCCA_BW_80,
-+ EDCCA_BW_160,
-+ EDCCA_MAX_BW_NUM,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+ EDCCA_CTRL_SET_EN = 0,
-+ EDCCA_CTRL_SET_THRES,
-+ EDCCA_CTRL_GET_EN,
-+ EDCCA_CTRL_GET_THRES,
-+ EDCCA_CTRL_NUM,
- };
-
-+#define EDCCA_DEFAULT_COMPENSATION -6
-+#define EDCCA_MIN_COMPENSATION -126
-+#define EDCCA_MAX_COMPENSATION 126
-+#define EDCCA_MIN_CONFIG_THRES -126
-+#define EDCCA_MAX_CONFIG_THRES 0
-
- static inline enum oper_chan_width
- hostapd_get_oper_chwidth(struct hostapd_config *conf)
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 75ddfa15c..99ba973aa 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1137,3 +1137,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms);
- }
- #endif /* CONFIG_PASN */
-+
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->configure_edcca_enable)
-+ return 0;
-+ return hapd->driver->configure_edcca_enable(hapd->drv_priv,
-+ hapd->iconf->edcca_enable,
-+ hapd->iconf->edcca_compensation);
-+}
-+
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+ const int *threshold)
-+{
-+ if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
-+ return 0;
-+ return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
-+}
-+
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
-+{
-+ if (!hapd->driver || !hapd->driver->get_edcca)
-+ return 0;
-+ return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 96c8c4e2c..6ca693b0b 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -144,6 +144,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- u8 ltf_keyseed_len,
- const u8 *ltf_keyseed, u32 action);
-
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+ const int *threshold);
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index a203546b6..f7c80c73b 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2511,6 +2511,13 @@ dfs_offload:
- }
- #endif /* CONFIG_MESH */
-
-+ if (hostapd_drv_configure_edcca_enable(hapd) < 0)
-+ goto fail;
-+
-+ if (hostapd_drv_configure_edcca_threshold(hapd,
-+ hapd->iconf->edcca_threshold) < 0)
-+ goto fail;
-+
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
- if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 4a19d2fc9..6121857dd 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
- NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
- };
-
--enum mtk_vendor_attr_edcca_ctrl_mode {
-- EDCCA_CTRL_SET_EN = 0,
-- EDCCA_CTRL_SET_THERS,
-- EDCCA_CTRL_GET_EN,
-- EDCCA_CTRL_GET_THERS,
-- EDCCA_CTRL_NUM,
-+enum mtk_vendor_attr_edcca_dump {
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
-+
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
-+ MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
-
-+
- static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
- [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index c5cc26737..7d71aa783 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5105,6 +5105,10 @@ struct wpa_driver_ops {
- const u8 *match, size_t match_len,
- bool multicast);
- #endif /* CONFIG_TESTING_OPTIONS */
-+ int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
-+ const s8 edcca_compensation);
-+ int (*configure_edcca_threshold)(void *priv, const int *threshold);
-+ int (*get_edcca)(void *priv, const u8 mode, u8 *value);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index a3e436e95..1a2f52b77 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -37,6 +37,8 @@
- #include "radiotap_iter.h"
- #include "rfkill.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
-+#include "ap/ap_config.h"
-
-
- #ifndef NETLINK_CAP_ACK
-@@ -13768,6 +13770,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
-
- #endif /* CONFIG_TESTING_OPTIONS */
-
-+static int nl80211_configure_edcca_enable(void *priv,
-+ const u8 edcca_enable,
-+ const s8 edcca_compensation)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_edcca_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting EDCCA enable");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+ edcca_compensation)) {
-+ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_edcca_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting EDCCA threshold");
-+ return 0;
-+ }
-+
-+ if (!threshold) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Input EDCCA threshold is empty!");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
-+ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+
-+static int edcca_info_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *info = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info++ = nla_get_u8(attr);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info++ = nla_get_u8(attr);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info++ = nla_get_u8(attr);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
-+ return NL_SKIP;
-+ }
-+
-+ *info = nla_get_u8(attr);
-+ return NL_SKIP;
-+}
-+
-+
-+static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_edcca_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting EDCCA threshold");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
-+ wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, edcca_info_handler, value, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
-@@ -13924,4 +14094,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .register_frame = testing_nl80211_register_frame,
- .radio_disable = testing_nl80211_radio_disable,
- #endif /* CONFIG_TESTING_OPTIONS */
-+/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
-+ .configure_edcca_enable = nl80211_configure_edcca_enable,
-+ .configure_edcca_threshold = nl80211_configure_edcca_threshold,
-+ .get_edcca = nl80211_get_edcca,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index aee8c4512..51b3fbec8 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
- unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
- unsigned int puncturing:1;
- unsigned int qca_ap_allowed_freqs:1;
-+ unsigned int mtk_edcca_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index f01a526a0..47654f65b 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -18,6 +18,7 @@
- #include "common/qca-vendor-attr.h"
- #include "common/brcm_vendor.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
-
-
- static int protocol_feature_handler(struct nl_msg *msg, void *arg)
-@@ -1111,6 +1112,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- break;
- }
- #endif /* CONFIG_DRIVER_NL80211_BRCM */
-+ } else if (vinfo->vendor_id == OUI_MTK) {
-+ switch (vinfo->subcmd) {
-+ case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
-+ drv->mtk_edcca_vendor_cmd_avail = 1;
-+ break;
-+ }
- }
-
- wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
deleted file mode 100644
index e0bdc50..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
+++ /dev/null
@@ -1,454 +0,0 @@
-From 8ed06968aea1170b6fbb7d88fbf546764c404eae Mon Sep 17 00:00:00 2001
-From: TomLiu <tomml.liu@mediatek.com>
-Date: Tue, 9 Aug 2022 10:23:44 -0700
-Subject: [PATCH 06/54] mtk: hostapd: Add hostapd MU SET/GET control
-
----
- hostapd/config_file.c | 9 +++
- hostapd/ctrl_iface.c | 66 ++++++++++++++++++
- hostapd/hostapd_cli.c | 18 +++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ap_drv_ops.c | 14 ++++
- src/ap/ap_drv_ops.h | 2 +
- src/ap/hostapd.c | 2 +
- src/common/mtk_vendor.h | 15 ++++
- src/drivers/driver.h | 13 ++++
- src/drivers/driver_nl80211.c | 110 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 13 files changed, 255 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index d281026e8..ec3b41abe 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3677,6 +3677,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- conf->mbssid = mbssid;
-+ } else if (os_strcmp(buf, "mu_onoff") == 0) {
-+ int val = atoi(pos);
-+ if (val < 0 || val > 15) {
-+ wpa_printf(MSG_ERROR,
-+ "Line %d: invalid mu_onoff value",
-+ line);
-+ return 1;
-+ }
-+ conf->mu_onoff = val;
- #endif /* CONFIG_IEEE80211AX */
- } else if (os_strcmp(buf, "max_listen_interval") == 0) {
- bss->max_listen_interval = atoi(pos);
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index cad3f863c..b10483652 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3488,6 +3488,67 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
- }
-
-
-+static int
-+hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *config, *value;
-+ config = cmd;
-+ pos = os_strchr(config, ' ');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if(pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strcmp(config, "onoff") == 0) {
-+ int mu = atoi(value);
-+ if (mu < 0 || mu > 15) {
-+ wpa_printf(MSG_ERROR, "Invalid value for mu");
-+ return -1;
-+ }
-+ hapd->iconf->mu_onoff = (u8) mu;
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for SET_MU", config);
-+ return -1;
-+ }
-+
-+ if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+ return os_snprintf(buf, buflen, "OK\n");
-+ } else {
-+ return -1;
-+ }
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ u8 mu_onoff;
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hapd->iface->state != HAPD_IFACE_ENABLED)
-+ return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
-+ hostapd_state_text(hapd->iface->state));
-+
-+ if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
-+ hapd->iconf->mu_onoff = mu_onoff;
-+ return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
-+ !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
-+ } else {
-+ wpa_printf(MSG_INFO, "ctrl iface failed to call");
-+ return -1;
-+ }
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4047,6 +4108,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
- reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- reply_size);
-+ } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
-+ reply_size);
-+ } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index dfc996d49..98892ee9d 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1400,6 +1400,20 @@ static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
-+}
-+
-+
-+static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
-+}
-+
-+
- #ifdef CONFIG_DPP
-
- static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
-@@ -1729,6 +1743,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- " = send FTM range request"},
- { "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
- " = show supported driver flags"},
-+ { "set_mu", hostapd_cli_cmd_set_mu, NULL,
-+ "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
-+ { "get_mu", hostapd_cli_cmd_get_mu, NULL,
-+ " = show mu onoff value in 0-15 bitmap"},
- #ifdef CONFIG_DPP
- { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
- "report a scanned DPP URI from a QR Code" },
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 8e56d1082..cf7f56392 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -281,6 +281,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->he_6ghz_max_ampdu_len_exp = 7;
- conf->he_6ghz_rx_ant_pat = 1;
- conf->he_6ghz_tx_ant_pat = 1;
-+ conf->mu_onoff = 15;
- #endif /* CONFIG_IEEE80211AX */
-
- /* The third octet of the country string uses an ASCII space character
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 24d540dbf..421e6a647 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1146,6 +1146,7 @@ struct hostapd_config {
- u8 he_6ghz_tx_ant_pat;
- u8 he_6ghz_reg_pwr_type;
- bool require_he;
-+ u8 mu_onoff;
- #endif /* CONFIG_IEEE80211AX */
-
- /* VHT enable/disable config from CHAN_SWITCH */
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 99ba973aa..44f494ed9 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1161,3 +1161,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- return 0;
- return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-+
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->mu_ctrl)
-+ return 0;
-+ return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+}
-+
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-+{
-+ if (!hapd->driver || !hapd->driver->mu_dump)
-+ return 0;
-+ return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 6ca693b0b..8a7d98128 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -148,6 +148,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f7c80c73b..65fdc47da 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2517,6 +2517,8 @@ dfs_offload:
- if (hostapd_drv_configure_edcca_threshold(hapd,
- hapd->iconf->edcca_threshold) < 0)
- goto fail;
-+ if (hostapd_drv_mu_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6121857dd..60bc4cd4c 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
- MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
- MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+ MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- };
-
-@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
- NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
- };
-
-+enum mtk_vendor_attr_mu_ctrl {
-+ MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
-+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-+ MTK_VENDOR_ATTR_MU_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
-+};
-+
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 7d71aa783..a23de244f 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -176,6 +176,11 @@ struct hostapd_channel_data {
- * punct_bitmap - RU puncturing bitmap
- */
- u16 punct_bitmap;
-+
-+ /**
-+ * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
-+ */
-+ u8 mu_onoff;
- };
-
- #define HE_MAC_CAPAB_0 0
-@@ -5109,6 +5114,14 @@ struct wpa_driver_ops {
- const s8 edcca_compensation);
- int (*configure_edcca_threshold)(void *priv, const int *threshold);
- int (*get_edcca)(void *priv, const u8 mode, u8 *value);
-+
-+ /**
-+ * mu_ctrl - ctrl on off for UL/DL MURU
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+ int (*mu_dump)(void *priv, u8 *mu_onoff);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 1a2f52b77..f7f157bf0 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13633,6 +13633,114 @@ fail:
- }
-
-
-+#ifdef CONFIG_IEEE80211AX
-+static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_mu_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting mu control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if(ret){
-+ wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+
-+static int mu_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *mu_onoff = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ static const struct nla_policy
-+ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
-+ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+ };
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
-+ return NL_SKIP;
-+ }
-+
-+ *mu_onoff = nla_get_u8(attr);
-+ wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
-+
-+ return 0;
-+}
-+
-+static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *attr;
-+ int ret;
-+
-+ if (!drv->mtk_mu_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting mu control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+
-+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!attr) {
-+ nlmsg_free(msg);
-+ return -1;
-+ }
-+
-+ nla_nest_end(msg, attr);
-+
-+ ret = send_and_recv_msgs(drv, msg, mu_dump_handler, mu_onoff, NULL, NULL);
-+
-+ if(ret){
-+ wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+}
-+#endif /* CONFIG_IEEE80211AX */
-+
-+
- #ifdef CONFIG_DPP
- static int nl80211_dpp_listen(void *priv, bool enable)
- {
-@@ -14085,6 +14193,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .update_connect_params = nl80211_update_connection_params,
- .send_external_auth_status = nl80211_send_external_auth_status,
- .set_4addr_mode = nl80211_set_4addr_mode,
-+ .mu_ctrl = nl80211_mu_onoff,
-+ .mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- .dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 51b3fbec8..bd5d28404 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
- unsigned int puncturing:1;
- unsigned int qca_ap_allowed_freqs:1;
- unsigned int mtk_edcca_vendor_cmd_avail:1;
-+ unsigned int mtk_mu_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 47654f65b..07f6cb133 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1117,6 +1117,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
- drv->mtk_edcca_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
-+ drv->mtk_mu_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
deleted file mode 100644
index c24b78a..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 022b58d6277d12517ada28d8b5581a75e501d779 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 2 Sep 2022 01:03:23 +0800
-Subject: [PATCH 07/54] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
- command
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c | 4 ++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 13 ++++++++++++
- src/ap/ap_drv_ops.c | 11 +++++++++++
- src/ap/ap_drv_ops.h | 1 +
- src/ap/hostapd.c | 2 ++
- src/common/mtk_vendor.h | 16 +++++++++++++++
- src/drivers/driver.h | 8 ++++++++
- src/drivers/driver_nl80211.c | 33 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +++
- 11 files changed, 93 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index ec3b41abe..d515b6ea9 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4852,6 +4852,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- conf->edcca_compensation = (s8) val;
-+ } else if (os_strcmp(buf, "three_wire_enable") == 0) {
-+ u8 en = atoi(pos);
-+
-+ conf->three_wire_enable = en;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index cf7f56392..8b1154553 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -298,6 +298,7 @@ struct hostapd_config * hostapd_config_defaults(void)
-
- conf->edcca_enable = EDCCA_MODE_AUTO;
- conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+ conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 421e6a647..52df2e0c0 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1198,6 +1198,19 @@ struct hostapd_config {
- u8 edcca_enable;
- s8 edcca_compensation;
- int *edcca_threshold;
-+ u8 three_wire_enable;
-+};
-+
-+enum three_wire_mode {
-+ THREE_WIRE_MODE_DISABLE,
-+ THREE_WIRE_MODE_EXT0_ENABLE,
-+ THREE_WIRE_MODE_EXT1_ENABLE,
-+ THREE_WIRE_MODE_ALL_ENABLE,
-+
-+ /* keep last */
-+ NUM_THREE_WIRE_MODE,
-+ THREE_WIRE_MODE_MAX =
-+ NUM_THREE_WIRE_MODE - 1
- };
-
- enum edcca_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 44f494ed9..2f15f99f4 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1175,3 +1175,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
- return 0;
- return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
- }
-+
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->three_wire_ctrl)
-+ return 0;
-+ if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
-+ wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
-+ return 0;
-+ }
-+ return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 8a7d98128..ed3b4cf11 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -150,6 +150,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 65fdc47da..5487c9489 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2519,6 +2519,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_mu_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_three_wire_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 60bc4cd4c..99ecbaf71 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
- };
-
-+enum mtk_vendor_attr_3wire_ctrl {
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
-+ MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_csi_ctrl {
- MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index a23de244f..03d268b2e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5122,6 +5122,14 @@ struct wpa_driver_ops {
- */
- int (*mu_ctrl)(void *priv, u8 mu_onoff);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-+
-+ /**
-+ * three_wire_ctrl - set three_wire_ctrl mode
-+ * @priv: Private driver interface data
-+ * @three_wire_enable: three_wire_ctrl mode
-+ *
-+ */
-+ int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index f7f157bf0..d5c0ea81a 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14046,6 +14046,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
- return ret;
- }
-
-+static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ /* Prepare nl80211 cmd */
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_3wire_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting three wire control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
-@@ -14208,4 +14240,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .configure_edcca_enable = nl80211_configure_edcca_enable,
- .configure_edcca_threshold = nl80211_configure_edcca_threshold,
- .get_edcca = nl80211_get_edcca,
-+ .three_wire_ctrl = nl80211_enable_three_wire,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index bd5d28404..99af8b075 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
- unsigned int qca_ap_allowed_freqs:1;
- unsigned int mtk_edcca_vendor_cmd_avail:1;
- unsigned int mtk_mu_vendor_cmd_avail:1;
-+ unsigned int mtk_3wire_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 07f6cb133..47ba17933 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1120,6 +1120,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
- drv->mtk_mu_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
-+ drv->mtk_3wire_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch
deleted file mode 100644
index 8ff3fe9..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch
+++ /dev/null
@@ -1,431 +0,0 @@
-From 23191149c4b3be30766166cd68db4beb6d57af78 Mon Sep 17 00:00:00 2001
-From: mtk27835 <shurong.wen@mediatek.com>
-Date: Wed, 7 Sep 2022 14:41:51 -0700
-Subject: [PATCH 08/54] mtk: hostapd: Add hostapd iBF control
-
-Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
----
- hostapd/config_file.c | 3 +
- hostapd/ctrl_iface.c | 26 +++++++
- hostapd/hostapd_cli.c | 9 +++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 2 +
- src/ap/ap_drv_ops.c | 14 ++++
- src/ap/ap_drv_ops.h | 2 +
- src/ap/hostapd.c | 2 +
- src/common/mtk_vendor.h | 35 +++++++++-
- src/drivers/driver.h | 19 ++++++
- src/drivers/driver_nl80211.c | 108 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 13 files changed, 224 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index d515b6ea9..f8560a721 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4856,6 +4856,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- u8 en = atoi(pos);
-
- conf->three_wire_enable = en;
-+ } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
-+ int val = atoi(pos);
-+ conf->ibf_enable = !!val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index b10483652..cf7cc3923 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3549,6 +3549,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
- }
-
-
-+static int
-+hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ u8 ibf_enable;
-+ int ret;
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
-+ hapd->iconf->ibf_enable = ibf_enable;
-+ ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
-+ ibf_enable);
-+ }
-+
-+ if (os_snprintf_error(end - pos, ret))
-+ return 0;
-+
-+ return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4113,6 +4137,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_size);
- } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 98892ee9d..4fa2d323d 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1606,6 +1606,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- #endif /* ANDROID */
-
-
-+static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- const char *cmd;
- int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1811,6 +1818,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- #endif /* ANDROID */
- { "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
- "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
-+ { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
-+ " = show iBF state (enabled/disabled)"},
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 8b1154553..c9b9683bb 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -299,6 +299,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->edcca_enable = EDCCA_MODE_AUTO;
- conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
-+ conf->ibf_enable = IBF_DEFAULT_ENABLE;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 52df2e0c0..ffbc4fb4f 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1199,6 +1199,7 @@ struct hostapd_config {
- s8 edcca_compensation;
- int *edcca_threshold;
- u8 three_wire_enable;
-+ u8 ibf_enable;
- };
-
- enum three_wire_mode {
-@@ -1324,6 +1325,7 @@ hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
- conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
- }
-
-+#define IBF_DEFAULT_ENABLE 0
-
- int hostapd_mac_comp(const void *a, const void *b);
- struct hostapd_config * hostapd_config_defaults(void);
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 2f15f99f4..41e76aa54 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1186,3 +1186,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
- }
- return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
- }
-+
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->ibf_ctrl)
-+ return 0;
-+ return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
-+}
-+
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
-+{
-+ if (!hapd->driver || !hapd->driver->ibf_dump)
-+ return 0;
-+ return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index ed3b4cf11..295866134 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -151,6 +151,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 5487c9489..15bc9f486 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2521,6 +2521,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_ibf_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99ecbaf71..9811f266e 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
-+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
-+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
- NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
- };
-
-+enum mtk_vendor_attr_ibf_ctrl {
-+ MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
-+ MTK_VENDOR_ATTR_IBF_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_ibf_dump {
-+ MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
-+ MTK_VENDOR_ATTR_IBF_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
-+};
-+
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
-
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 03d268b2e..58a681b7a 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -181,6 +181,11 @@ struct hostapd_channel_data {
- * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
- */
- u8 mu_onoff;
-+
-+ /**
-+ * ibf_enable=<val>
-+ */
-+ u8 ibf_enable;
- };
-
- #define HE_MAC_CAPAB_0 0
-@@ -5130,6 +5135,20 @@ struct wpa_driver_ops {
- *
- */
- int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
-+
-+ /**
-+ * ibf_ctrl - ctrl disable/enable for ibf
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*ibf_ctrl)(void *priv, u8 ibf_enable);
-+
-+ /**
-+ * ibf_dump - dump ibf
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*ibf_dump)(void *priv, u8 *ibf_enable);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index d5c0ea81a..daa05882f 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14079,6 +14079,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
- return ret;
- }
-
-+static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_ibf_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ibf control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int ibf_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *ibf_enable = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
-+ if (!attr) {
-+ wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
-+ return NL_SKIP;
-+ }
-+
-+ *ibf_enable = nla_get_u8(attr);
-+
-+ return NL_SKIP;
-+}
-+
-+static int
-+nl80211_ibf_dump(void *priv, u8 *ibf_enable)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+ if (!data)
-+ goto fail;
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, ibf_dump_handler, ibf_enable, NULL, NULL);
-+
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14241,4 +14347,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .configure_edcca_threshold = nl80211_configure_edcca_threshold,
- .get_edcca = nl80211_get_edcca,
- .three_wire_ctrl = nl80211_enable_three_wire,
-+ .ibf_ctrl = nl80211_ibf_enable,
-+ .ibf_dump = nl80211_ibf_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 99af8b075..4e64e7d31 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_edcca_vendor_cmd_avail:1;
- unsigned int mtk_mu_vendor_cmd_avail:1;
- unsigned int mtk_3wire_vendor_cmd_avail:1;
-+ unsigned int mtk_ibf_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 47ba17933..5b659f490 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1123,6 +1123,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
- drv->mtk_3wire_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
-+ drv->mtk_ibf_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
deleted file mode 100644
index e8ad881..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 037b1cdac457c03ba44a470a7df38798ba1d5d88 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 22 Sep 2022 16:08:09 +0800
-Subject: [PATCH 09/54] mtk: hostapd: Do not include HE capab IE if associated
- sta's HE capab IE is invalid
-
----
- src/ap/ieee802_11.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index db404a6c7..110ad8c2e 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4863,7 +4863,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- #endif /* CONFIG_IEEE80211AC */
-
- #ifdef CONFIG_IEEE80211AX
-- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
-+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
-+ sta->flags & WLAN_STA_HE) {
- p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
- p = hostapd_eid_he_operation(hapd, p);
- p = hostapd_eid_cca(hapd, p);
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch
deleted file mode 100644
index a0f9de1..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From 8ee7b5e713067c29aab2f7a4389cc806b545c5d8 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:55:49 +0800
-Subject: [PATCH 10/54] mtk: hostapd: Add DFS detection mode
-
-Add DFS detection mode for testing radar detection rate.
-If DFS detection mode is on, AP will not switch channels when receiving
-a radar signal.
-This detection mode also supports background chain.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c | 4 ++++
- hostapd/ctrl_iface.c | 23 +++++++++++++++++++++++
- src/ap/ap_config.h | 13 +++++++++++++
- src/ap/dfs.c | 10 ++++++++++
- 4 files changed, 50 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index f8560a721..50e299303 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4859,6 +4859,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- } else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
- int val = atoi(pos);
- conf->ibf_enable = !!val;
-+ } else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
-+ u8 en = strtol(pos, NULL, 10);
-+
-+ conf->dfs_detect_mode = en;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index cf7cc3923..327533f80 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3573,6 +3573,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
- }
-
-
-+static int
-+hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
-+ char *buf, size_t buflen)
-+{
-+ u8 dfs_detect_mode;
-+
-+ if (!value)
-+ return -1;
-+
-+ dfs_detect_mode = strtol(value, NULL, 10);
-+ if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
-+ wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
-+ return -1;
-+ }
-+ hapd->iconf->dfs_detect_mode = dfs_detect_mode;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4139,6 +4159,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
- reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
-+ reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index ffbc4fb4f..6576d791d 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1200,6 +1200,7 @@ struct hostapd_config {
- int *edcca_threshold;
- u8 three_wire_enable;
- u8 ibf_enable;
-+ u8 dfs_detect_mode;
- };
-
- enum three_wire_mode {
-@@ -1214,6 +1215,18 @@ enum three_wire_mode {
- NUM_THREE_WIRE_MODE - 1
- };
-
-+enum dfs_mode {
-+ DFS_DETECT_MODE_DISABLE,
-+ DFS_DETECT_MODE_AP_ENABLE,
-+ DFS_DETECT_MODE_BACKGROUND_ENABLE,
-+ DFS_DETECT_MODE_ALL_ENABLE,
-+
-+ /* keep last */
-+ NUM_DFS_DETECT_MODE,
-+ DFS_DETECT_MODE_MAX =
-+ NUM_DFS_DETECT_MODE - 1
-+};
-+
- enum edcca_mode {
- EDCCA_MODE_FORCE_DISABLE = 0,
- EDCCA_MODE_AUTO = 1,
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 29d268351..2e138e225 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1327,6 +1327,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- __func__, iface->radar_background.cac_started ? "yes" : "no",
- hostapd_csa_in_progress(iface) ? "yes" : "no");
-
-+ /* Skip channel switch when background dfs detect mode is on */
-+ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
-+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+ return 0;
-+
- /* Check if CSA in progress */
- if (hostapd_csa_in_progress(iface))
- return 0;
-@@ -1375,6 +1380,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
- __func__, iface->cac_started ? "yes" : "no",
- hostapd_csa_in_progress(iface) ? "yes" : "no");
-
-+ /* Skip channel switch when dfs detect mode is on */
-+ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
-+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+ return 0;
-+
- /* Check if CSA in progress */
- if (hostapd_csa_in_progress(iface))
- return 0;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
deleted file mode 100644
index ae9c59e..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
+++ /dev/null
@@ -1,192 +0,0 @@
-From 60b4911627763adee4fba3107acd2979ff024f10 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:56:55 +0800
-Subject: [PATCH 11/54] mtk: hostapd: Add DFS offchan channel switch
-
-Add DFS background chain channel switch command for testing purpose.
-This feature is implemented via hostapd_cli command.
-Command format:
-hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
- src/ap/dfs.c | 25 ++++++---------
- src/ap/dfs.h | 15 +++++++++
- 3 files changed, 96 insertions(+), 16 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 327533f80..84a6127d1 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3593,6 +3593,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
- }
-
-
-+static int
-+hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ struct hostapd_iface *iface = hapd->iface;
-+ char *pos, *param;
-+ enum hostapd_hw_mode hw_mode;
-+ bool chan_found = false;
-+ int i, num_available_chandefs, channel, chan_width, sec = 0;
-+ int sec_chan_idx_80p80 = -1;
-+ u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
-+ struct hostapd_channel_data *chan;
-+ enum dfs_channel_type type = DFS_NO_CAC_YET;
-+
-+ param = os_strchr(cmd, ' ');
-+ if (!param)
-+ return -1;
-+ *param++ = '\0';
-+
-+ pos = os_strstr(param, "chan=");
-+ if (pos)
-+ channel = strtol(pos + 5, NULL, 10);
-+ else
-+ return -1;
-+
-+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
-+ for (i = 0; i < num_available_chandefs; i++) {
-+ dfs_find_channel(iface, &chan, i, type);
-+ if (chan->chan == channel) {
-+ chan_found = true;
-+ break;
-+ }
-+ }
-+
-+ if (!chan_found)
-+ return -1;
-+
-+ if (iface->conf->secondary_channel)
-+ sec = 1;
-+
-+ dfs_adjust_center_freq(iface, chan,
-+ sec,
-+ sec_chan_idx_80p80,
-+ &oper_centr_freq_seg0_idx,
-+ &oper_centr_freq_seg1_idx);
-+
-+ if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+ chan->freq, chan->chan,
-+ iface->conf->ieee80211n,
-+ iface->conf->ieee80211ac,
-+ iface->conf->ieee80211ax,
-+ iface->conf->ieee80211be,
-+ sec, hostapd_get_oper_chwidth(iface->conf),
-+ oper_centr_freq_seg0_idx,
-+ oper_centr_freq_seg1_idx, true)) {
-+ wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
-+ iface->radar_background.channel = -1;
-+ return -1;
-+ }
-+
-+ iface->radar_background.channel = chan->chan;
-+ iface->radar_background.freq = chan->freq;
-+ iface->radar_background.secondary_channel = sec;
-+ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
-+ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4162,6 +4232,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
- reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 2e138e225..23e6527b3 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -19,13 +19,6 @@
- #include "dfs.h"
- #include "crypto/crypto.h"
-
--
--enum dfs_channel_type {
-- DFS_ANY_CHANNEL,
-- DFS_AVAILABLE, /* non-radar or radar-available */
-- DFS_NO_CAC_YET, /* radar-not-yet-available */
--};
--
- static struct hostapd_channel_data *
- dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
- u8 *oper_centr_freq_seg0_idx,
-@@ -238,9 +231,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
- * - hapd->vht/he_oper_centr_freq_seg0_idx
- * - hapd->vht/he_oper_centr_freq_seg1_idx
- */
--static int dfs_find_channel(struct hostapd_iface *iface,
-- struct hostapd_channel_data **ret_chan,
-- int idx, enum dfs_channel_type type)
-+int dfs_find_channel(struct hostapd_iface *iface,
-+ struct hostapd_channel_data **ret_chan,
-+ int idx, enum dfs_channel_type type)
- {
- struct hostapd_hw_modes *mode;
- struct hostapd_channel_data *chan;
-@@ -300,12 +293,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
- }
-
-
--static void dfs_adjust_center_freq(struct hostapd_iface *iface,
-- struct hostapd_channel_data *chan,
-- int secondary_channel,
-- int sec_chan_idx_80p80,
-- u8 *oper_centr_freq_seg0_idx,
-- u8 *oper_centr_freq_seg1_idx)
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+ struct hostapd_channel_data *chan,
-+ int secondary_channel,
-+ int sec_chan_idx_80p80,
-+ u8 *oper_centr_freq_seg0_idx,
-+ u8 *oper_centr_freq_seg1_idx)
- {
- if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
- return;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 606c1b393..c2556d2d9 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -9,6 +9,12 @@
- #ifndef DFS_H
- #define DFS_H
-
-+enum dfs_channel_type {
-+ DFS_ANY_CHANNEL,
-+ DFS_AVAILABLE, /* non-radar or radar-available */
-+ DFS_NO_CAC_YET, /* radar-not-yet-available */
-+};
-+
- int hostapd_handle_dfs(struct hostapd_iface *iface);
-
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
-@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
- int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
- int center_freq);
-+int dfs_find_channel(struct hostapd_iface *iface,
-+ struct hostapd_channel_data **ret_chan,
-+ int idx, enum dfs_channel_type type);
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+ struct hostapd_channel_data *chan,
-+ int secondary_channel,
-+ int sec_chan_idx_80p80,
-+ u8 *oper_centr_freq_seg0_idx,
-+ u8 *oper_centr_freq_seg1_idx);
-
- #endif /* DFS_H */
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
deleted file mode 100644
index f2d0484..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
+++ /dev/null
@@ -1,400 +0,0 @@
-From cfd3d079808b3a7d5585da349c1426351728b442 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 16 Dec 2022 03:57:11 +0800
-Subject: [PATCH 12/54] mtk: hostapd: Add amsdu set get ctrl
-
----
- hostapd/config_file.c | 9 +++
- hostapd/ctrl_iface.c | 26 +++++++
- hostapd/hostapd_cli.c | 9 +++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ap_drv_ops.c | 14 ++++
- src/ap/ap_drv_ops.h | 2 +
- src/ap/hostapd.c | 2 +
- src/common/mtk_vendor.h | 17 ++++-
- src/drivers/driver.h | 9 +++
- src/drivers/driver_nl80211.c | 114 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 13 files changed, 207 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 50e299303..0b2f3dc32 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4863,6 +4863,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- u8 en = strtol(pos, NULL, 10);
-
- conf->dfs_detect_mode = en;
-+ } else if (os_strcmp(buf, "amsdu") == 0) {
-+ int val = atoi(pos);
-+ if (val < 0 || val > 1) {
-+ wpa_printf(MSG_ERROR,
-+ "Line %d: invalid amsdu value",
-+ line);
-+ return 1;
-+ }
-+ conf->amsdu = val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 84a6127d1..57addb22d 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3663,6 +3663,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
- }
-
-
-+static int
-+hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ u8 amsdu;
-+ int ret;
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
-+ hapd->iconf->amsdu = amsdu;
-+ ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
-+ hapd->iconf->amsdu);
-+ }
-+
-+ if (os_snprintf_error(end - pos, ret))
-+ return 0;
-+
-+ return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4234,6 +4258,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply, reply_size);
- } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
- reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
-+ } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 4fa2d323d..d59373062 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1613,6 +1613,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- const char *cmd;
- int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1820,6 +1827,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- { "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
- " = show iBF state (enabled/disabled)"},
-+ { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
-+ " = show AMSDU state"},
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index c9b9683bb..f519a769b 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -300,6 +300,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- conf->ibf_enable = IBF_DEFAULT_ENABLE;
-+ conf->amsdu = 1;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 6576d791d..9f3cea205 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1201,6 +1201,7 @@ struct hostapd_config {
- u8 three_wire_enable;
- u8 ibf_enable;
- u8 dfs_detect_mode;
-+ u8 amsdu;
- };
-
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 41e76aa54..a7226cfa9 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1199,4 +1199,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
- if (!hapd->driver || !hapd->driver->ibf_dump)
- return 0;
- return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-+
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->amsdu_ctrl)
-+ return 0;
-+ return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
-+}
-+
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
-+{
-+ if (!hapd->driver || !hapd->driver->amsdu_dump)
-+ return 0;
-+ return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
- }
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 295866134..88bc430d2 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,6 +153,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 15bc9f486..fcf346d36 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2523,6 +2523,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_ibf_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_amsdu_ctrl(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 9811f266e..7b4d7c11a 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
- MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-
-@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
- NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
- };
-
-+enum mtk_vendor_attr_wireless_dump {
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
-+ MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
-+ NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_rfeature_ctrl {
- MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 58a681b7a..577c34c07 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5149,6 +5149,15 @@ struct wpa_driver_ops {
- *
- */
- int (*ibf_dump)(void *priv, u8 *ibf_enable);
-+
-+ /**
-+ * amsdu_ctrl - enable/disable amsdu
-+ * amsdu_dump - get current amsdu status
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*amsdu_ctrl)(void *priv, u8 amsdu);
-+ int (*amsdu_dump)(void *priv, u8 *amsdu);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index daa05882f..c1e3fcb69 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14185,6 +14185,118 @@ fail:
- return -ENOBUFS;
- }
-
-+static int nl80211_enable_amsdu(void *priv, u8 amsdu)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_wireless_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap wireless control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
-+{
-+ u8 *amsdu = (u8 *) arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr_amsdu;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
-+ if (!attr_amsdu ){
-+ wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
-+ return NL_SKIP;
-+ }
-+
-+ *amsdu = nla_get_u8(attr_amsdu);
-+
-+ return NL_SKIP;
-+}
-+
-+static int
-+nl80211_dump_amsdu(void *priv, u8 *amsdu)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_wireless_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support ap_wireless control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, dump_amsdu_handler, amsdu, NULL, NULL);
-+
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
-+ }
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14349,4 +14461,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .three_wire_ctrl = nl80211_enable_three_wire,
- .ibf_ctrl = nl80211_ibf_enable,
- .ibf_dump = nl80211_ibf_dump,
-+ .amsdu_ctrl = nl80211_enable_amsdu,
-+ .amsdu_dump = nl80211_dump_amsdu,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 4e64e7d31..0100314ba 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_mu_vendor_cmd_avail:1;
- unsigned int mtk_3wire_vendor_cmd_avail:1;
- unsigned int mtk_ibf_vendor_cmd_avail:1;
-+ unsigned int mtk_wireless_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 5b659f490..0e70b7321 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1126,6 +1126,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
- drv->mtk_ibf_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
-+ drv->mtk_wireless_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch
deleted file mode 100644
index 32ee385..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From 6d57a4121c23048f3473991435aa8673b51763ad Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Thu, 12 Jan 2023 15:18:19 +0800
-Subject: [PATCH 13/54] mtk: hostapd: Add he_ldpc configuration
-
----
- hostapd/config_file.c | 2 ++
- hostapd/hostapd.conf | 5 +++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 1 +
- src/ap/ieee802_11_he.c | 7 +++++++
- src/common/ieee802_11_defs.h | 3 +++
- 6 files changed, 19 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 0b2f3dc32..9e3dbb24a 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3515,6 +3515,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- conf->he_phy_capab.he_su_beamformee = atoi(pos);
- } else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
- conf->he_phy_capab.he_mu_beamformer = atoi(pos);
-+ } else if (os_strcmp(buf, "he_ldpc") == 0) {
-+ conf->he_phy_capab.he_ldpc = atoi(pos);
- } else if (os_strcmp(buf, "he_bss_color") == 0) {
- conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- conf->he_op.he_bss_color_disabled = 0;
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index bafc9232b..f16e3b08d 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -833,6 +833,11 @@ wmm_ac_vo_acm=0
- # 1 = supported
- #he_mu_beamformer=1
-
-+#he_ldpc: HE LDPC support
-+# 0 = not supported
-+# 1 = supported (default)
-+#he_ldpc=1
-+
- # he_bss_color: BSS color (1-63)
- #he_bss_color=1
-
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index f519a769b..223db56eb 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -269,6 +269,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- #endif /* CONFIG_ACS */
-
- #ifdef CONFIG_IEEE80211AX
-+ conf->he_phy_capab.he_ldpc = 1;
- conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
- HE_OPERATION_RTS_THRESHOLD_OFFSET;
- /* Set default basic MCS/NSS set to single stream MCS 0-7 */
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 9f3cea205..d0e27b28d 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -959,6 +959,7 @@ struct hostapd_bss_config {
- * struct he_phy_capabilities_info - HE PHY capabilities
- */
- struct he_phy_capabilities_info {
-+ bool he_ldpc;
- bool he_su_beamformer;
- bool he_su_beamformee;
- bool he_mu_beamformer;
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index 548a44821..9407dd6e5 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -138,6 +138,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
- os_memcpy(&cap->optional[mcs_nss_size],
- mode->he_capab[opmode].ppet, ppet_size);
-
-+ if (hapd->iface->conf->he_phy_capab.he_ldpc)
-+ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
-+ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+ else
-+ cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
-+ ~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+
- if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
- cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
- HE_PHYCAP_SU_BEAMFORMER_CAPAB;
-diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
-index e7c3f17e1..69f15913e 100644
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -2358,6 +2358,9 @@ struct ieee80211_spatial_reuse {
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G ((u8) BIT(3))
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G ((u8) BIT(4))
-
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX 1
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD ((u8) BIT(5))
-+
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7))
- #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
deleted file mode 100644
index cd53d89..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 45bfb188573dae7bcc4c24fb22311c51c82a22d1 Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Tue, 24 Jan 2023 19:06:44 +0800
-Subject: [PATCH 14/54] mtk: hostapd: Add vendor command attribute for RTS BW
- signaling.
-
-Signed-off-by: himanshu.goyal <himanshu.goyal@mediatek.com>
----
- src/common/mtk_vendor.h | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 7b4d7c11a..ace993bc8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -172,6 +172,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch
deleted file mode 100644
index 653ca37..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 808d6d8261761d9cab0cf1500eddd812344bf7bf Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 13 Feb 2023 11:03:53 +0800
-Subject: [PATCH 15/54] mtk: hostapd: 6G band does not require DFS
-
----
- src/ap/dfs.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 23e6527b3..0a8486a1e 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1516,6 +1516,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- !iface->conf->ieee80211h) ||
- !iface->current_mode ||
-+ is_6ghz_freq(iface->freq) ||
- iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
- return 0;
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
deleted file mode 100644
index 9e68c12..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From bae45334df7f087ea31ddd4bc419610636ab45ea Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 11:01:18 +0800
-Subject: [PATCH 16/54] mtk: hostapd: Fix sending wrong VHT operation IE in CSA
- while using ZWDFS
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 14 +++++++++-----
- 1 file changed, 9 insertions(+), 5 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 0a8486a1e..cfc350879 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1120,6 +1120,14 @@ static int
- hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- {
- u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
-+ int ret;
-+
-+ ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
-+ iface->radar_background.freq,
-+ iface->radar_background.secondary_channel,
-+ current_vht_oper_chwidth,
-+ iface->radar_background.centr_freq_seg0_idx,
-+ iface->radar_background.centr_freq_seg1_idx);
-
- iface->conf->channel = iface->radar_background.channel;
- iface->freq = iface->radar_background.freq;
-@@ -1132,11 +1140,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
-
- hostpad_dfs_update_background_chain(iface);
-
-- return hostapd_dfs_request_channel_switch(
-- iface, iface->conf->channel, iface->freq,
-- iface->conf->secondary_channel, current_vht_oper_chwidth,
-- hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
-- hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
-+ return ret;
- }
-
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
deleted file mode 100644
index 3c36b27..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From 7782f39aa86e067030ee8277c7942423c6e64389 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 10:51:47 +0800
-Subject: [PATCH 17/54] mtk: hostapd: Add sta-assisted DFS state update
- mechanism
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 20 ++++++++++++++++++++
- src/ap/dfs.h | 3 +++
- src/ap/drv_callbacks.c | 28 ++++++++++++++++++++++++++++
- src/common/wpa_ctrl.h | 1 +
- src/drivers/driver.h | 14 ++++++++++++++
- src/drivers/driver_nl80211_event.c | 6 ++++++
- src/drivers/nl80211_copy.h | 6 ++++++
- 7 files changed, 78 insertions(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index cfc350879..9d002cfad 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1513,6 +1513,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- }
-
-
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, u32 state)
-+{
-+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
-+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
-+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
-+ (state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
-+
-+ /* Proceed only if DFS is not offloaded to the driver */
-+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
-+ return 0;
-+
-+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
-+ cf1, cf2, state);
-+
-+ return 0;
-+}
-+
-+
- int hostapd_is_dfs_required(struct hostapd_iface *iface)
- {
- int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index c2556d2d9..25ba29ca1 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- int ht_enabled,
- int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, u32 state);
- int hostapd_is_dfs_required(struct hostapd_iface *iface);
- int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index f6093c11c..e7f1f19ce 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2086,6 +2086,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- radar->cf1, radar->cf2);
- }
-
-+static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
-+ struct dfs_event *radar)
-+{
-+ wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
-+ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+ radar->chan_offset, radar->chan_width,
-+ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
-+}
-+
-+static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
-+ struct dfs_event *radar)
-+{
-+ wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
-+ hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+ radar->chan_offset, radar->chan_width,
-+ radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
-+}
-+
- #endif /* NEED_AP_MLME */
-
-
-@@ -2407,6 +2425,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- break;
- hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- break;
-+ case EVENT_DFS_STA_CAC_SKIPPED:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
-+ break;
-+ case EVENT_DFS_STA_CAC_EXPIRED:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
-+ break;
- case EVENT_CHANNEL_LIST_CHANGED:
- /* channel list changed (regulatory?), update channel list */
- /* TODO: check this. hostapd_get_hw_features() initializes
-diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
-index 416e0d6a8..62f042e05 100644
---- a/src/common/wpa_ctrl.h
-+++ b/src/common/wpa_ctrl.h
-@@ -374,6 +374,7 @@ extern "C" {
- #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
- #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
- #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
-+#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
-
- #define AP_CSA_FINISHED "AP-CSA-FINISHED "
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 577c34c07..24ab656fa 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5785,6 +5785,20 @@ enum wpa_event_type {
- * EVENT_LINK_RECONFIG - Notification that AP links removed
- */
- EVENT_LINK_RECONFIG,
-+
-+ /**
-+ * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
-+ *
-+ * The channel in the notification is now marked as available.
-+ */
-+ EVENT_DFS_STA_CAC_SKIPPED,
-+
-+ /**
-+ * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
-+ *
-+ * The channel in the notification is now marked as usable.
-+ */
-+ EVENT_DFS_STA_CAC_EXPIRED,
- };
-
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 701c32e7b..63d44017c 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2514,6 +2514,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- case NL80211_RADAR_CAC_STARTED:
- wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- break;
-+ case NL80211_RADAR_STA_CAC_SKIPPED:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
-+ break;
-+ case NL80211_RADAR_STA_CAC_EXPIRED:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
-+ break;
- default:
- wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
- "received", event_type);
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index 82860ae32..225864b94 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6643,6 +6643,10 @@ enum nl80211_smps_mode {
- * applicable for ETSI dfs domain where pre-CAC is valid for ever.
- * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
- * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-+ * when receiving CSA/assoc resp
-+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-+ * when STA is disconnected or leaving the channel
- */
- enum nl80211_radar_event {
- NL80211_RADAR_DETECTED,
-@@ -6651,6 +6655,8 @@ enum nl80211_radar_event {
- NL80211_RADAR_NOP_FINISHED,
- NL80211_RADAR_PRE_CAC_EXPIRED,
- NL80211_RADAR_CAC_STARTED,
-+ NL80211_RADAR_STA_CAC_SKIPPED,
-+ NL80211_RADAR_STA_CAC_EXPIRED,
- };
-
- /**
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
deleted file mode 100644
index ef35c2d..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 8a52855a8e3425d62b7ffba05ccf1ddd08223f78 Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Fri, 3 Mar 2023 12:45:42 +0800
-Subject: [PATCH 18/54] mtk: hostapd: Mark DFS channel as available for CSA.
-
----
- hostapd/ctrl_iface.c | 10 ++++++++++
- hostapd/hostapd_cli.c | 2 +-
- src/ap/ctrl_iface_ap.c | 1 +
- 3 files changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 57addb22d..ba2137969 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -2751,6 +2751,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
- break;
- }
-
-+ if (settings.freq_params.radar_background) {
-+ hostapd_dfs_sta_update_state(iface,
-+ settings.freq_params.freq,
-+ settings.freq_params.ht_enabled,
-+ settings.freq_params.sec_channel_offset,
-+ bandwidth, settings.freq_params.center_freq1,
-+ settings.freq_params.center_freq2,
-+ HOSTAPD_CHAN_DFS_AVAILABLE);
-+ }
-+
- if (settings.freq_params.center_freq1)
- dfs_range += hostapd_is_dfs_overlap(
- iface, bandwidth, settings.freq_params.center_freq1);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index d59373062..0a374be8e 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1715,7 +1715,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<addr> = send QoS Map Configure frame" },
- { "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
- "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
-- " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
-+ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
- " = initiate channel switch announcement" },
- { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
- "<addr> <url>\n"
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 0e173f174..7bdefb4cf 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1014,6 +1014,7 @@ int hostapd_parse_csa_settings(const char *pos,
- settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
- settings->freq_params.he_enabled = !!os_strstr(pos, " he");
- settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
-+ settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
- settings->block_tx = !!os_strstr(pos, " blocktx");
- #undef SET_CSA_SETTING
- #undef SET_CSA_SETTING_EXT
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch
deleted file mode 100644
index 4f2c43b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch
+++ /dev/null
@@ -1,477 +0,0 @@
-From 41e104c896bfa30a027924302a39401245e02767 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Thu, 26 Jan 2023 09:16:00 +0800
-Subject: [PATCH 19/54] mtk: hostapd: Add available color bitmap
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
----
- hostapd/ctrl_iface.c | 74 +++++++++++
- hostapd/hostapd_cli.c | 18 +++
- src/ap/ap_drv_ops.c | 10 +-
- src/ap/ap_drv_ops.h | 2 +
- src/common/mtk_vendor.h | 11 ++
- src/drivers/driver.h | 8 ++
- src/drivers/driver_nl80211.c | 199 +++++++++++++++++++++++++++++-
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 9 files changed, 324 insertions(+), 2 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ba2137969..e45e574be 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3696,6 +3696,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
- return ret;
- }
-
-+static int
-+hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ int ret;
-+ char *pos, *end;
-+ int i;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (hapd->iface->conf->he_op.he_bss_color_disabled)
-+ ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
-+ else
-+ ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
-+ hapd->iface->conf->he_op.he_bss_color);
-+
-+ pos += ret;
-+
-+ return pos - buf;
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
-+ size_t buflen)
-+{
-+ int ret;
-+ char *pos, *end;
-+ int i;
-+ u64 aval_color_bmp = 0;
-+
-+ hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
-+ hapd->color_collision_bitmap = ~aval_color_bmp;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ ret = os_snprintf(buf, buflen,
-+ "available color bitmap=0x%llx\n",
-+ aval_color_bmp);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
-+ int bit = !!((aval_color_bmp >> i) & 1LLU);
-+
-+ if (i % 8 == 0) {
-+ ret = os_snprintf(pos, end - pos, "%2d: ", i);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "%d ", bit);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ if (i % 8 == 7) {
-+ ret = os_snprintf(pos, end - pos, "\n");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+ }
-+ return pos - buf;
-+}
-+
-
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
-@@ -4270,6 +4340,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- } else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
- reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 0a374be8e..e9e156d28 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1598,6 +1598,20 @@ static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
-+}
-+
-+
-+static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
-+}
-+
-+
- #ifdef ANDROID
- static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
-@@ -1819,6 +1833,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
- { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
- "= reload wpa_psk_file only" },
-+ { "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
-+ "= get current BSS color" },
-+ { "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
-+ "= get available BSS color bitmap" },
- #ifdef ANDROID
- { "driver", hostapd_cli_cmd_driver, NULL,
- "<driver sub command> [<hex formatted data>] = send driver command data" },
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index a7226cfa9..9615ca8ce 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1213,4 +1213,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
- if (!hapd->driver || !hapd->driver->amsdu_dump)
- return 0;
- return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
--}
-\ No newline at end of file
-+}
-+
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
-+{
-+ if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
-+ hapd->iface->conf->he_op.he_bss_color_disabled)
-+ return 0;
-+ return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 88bc430d2..ecaa71f99 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -155,6 +155,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
- int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
-+ u64 *aval_color_bmp);
-
- #include "drivers/driver.h"
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index ace993bc8..e27fe69b3 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
-+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
- [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
- };
-
-+enum mtk_vendor_attr_bss_color_ctrl {
-+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
-+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
-+};
-
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 24ab656fa..869b0442f 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5158,6 +5158,14 @@ struct wpa_driver_ops {
- */
- int (*amsdu_ctrl)(void *priv, u8 amsdu);
- int (*amsdu_dump)(void *priv, u8 *amsdu);
-+
-+ /**
-+ * get_aval_color_bmp - get available BSS color bitmap
-+ * @priv: Private driver interface data
-+ * @aval_color_bmp: available bss color bitmap
-+ *
-+ */
-+ int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c1e3fcb69..7b5a50ea6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -12869,7 +12869,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
- num, MAC2STR(candidate->bssid), buf);
- }
-
--
- static int
- nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
- {
-@@ -14297,6 +14296,203 @@ fail:
- return -ENOBUFS;
- }
-
-+static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
-+{
-+ u64 *aval_color_bmp = arg;
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct nlattr *nl_vend, *attr;
-+
-+ static const struct nla_policy
-+ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
-+ [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
-+ };
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!nl_vend)
-+ return NL_SKIP;
-+
-+ nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
-+ nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+ *aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
-+
-+ return 0;
-+}
-+
-+static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *attr;
-+ int ret;
-+
-+ if (!drv->mtk_bss_color_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support BSS COLOR vendor cmd");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
-+ return -ENOBUFS;
-+
-+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!attr) {
-+ nlmsg_free(msg);
-+ return -1;
-+ }
-+
-+ nla_nest_end(msg, attr);
-+
-+ ret = send_and_recv_msgs(drv, msg,
-+ nl80211_get_aval_color_bmp_handler, aval_color_bmp, NULL, NULL);
-+
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
-+static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_wireless_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap wireless control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
-+ nla_put_u16(msg, sub_vendor_id, (u16) value);
-+ else
-+ nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap rfeatures control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data, *data2;
-+ int ret;
-+
-+ if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting ap rfeatures control");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
-+ if (!data2)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
-+
-+ nla_nest_end(msg, data2);
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14463,4 +14659,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .ibf_dump = nl80211_ibf_dump,
- .amsdu_ctrl = nl80211_enable_amsdu,
- .amsdu_dump = nl80211_dump_amsdu,
-+ .get_aval_color_bmp = nl80211_get_aval_color_bmp,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 0100314ba..fd1e57cc2 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_3wire_vendor_cmd_avail:1;
- unsigned int mtk_ibf_vendor_cmd_avail:1;
- unsigned int mtk_wireless_vendor_cmd_avail:1;
-+ unsigned int mtk_bss_color_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 0e70b7321..3e8eb8cb1 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1129,6 +1129,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
- drv->mtk_wireless_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
-+ drv->mtk_bss_color_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
deleted file mode 100644
index 3e30ba2..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
+++ /dev/null
@@ -1,210 +0,0 @@
-From 0e01d6aa229b84b92c9462f7101314db62857b86 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Mar 2023 16:08:30 +0800
-Subject: [PATCH 20/54] mtk: hostapd: Fix ZWDFS issue in BW 160
-
-When background radar is enabled and bandwidth is set to 160, AP will
-fail to startup due to the lack of non-DFS channel.
-Under this circumstance, AP should perform CAC itself, and the background
-chain could also perform CAC simultaneously.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
- 1 file changed, 79 insertions(+), 19 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 9d002cfad..3b1df6dc6 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -69,15 +69,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
- static int dfs_channel_available(struct hostapd_channel_data *chan,
- enum dfs_channel_type type)
- {
-+ int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
-+
-+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
-+ return -1;
-+
- if (type == DFS_NO_CAC_YET) {
- /* 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)
-+ if (!(chan->flag & HOSTAPD_CHAN_RADAR))
-+ return 0;
-+
-+ if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
- return 1;
-- return 0;
-+
-+ return -1;
- }
-
- /*
-@@ -86,16 +93,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
- * channel for CSA, unless they are available for immediate use.
- */
- if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
-- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
-- HOSTAPD_CHAN_DFS_AVAILABLE))
-- return 0;
-+ (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
-+ return -1;
-
-- if (chan->flag & HOSTAPD_CHAN_DISABLED)
-- return 0;
- if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
-- ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
-- HOSTAPD_CHAN_DFS_UNAVAILABLE))
-- return 0;
-+ ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
-+ (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
-+ return -1;
-+
- return 1;
- }
-
-@@ -167,7 +172,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- enum dfs_channel_type type)
- {
- struct hostapd_channel_data *first_chan, *chan;
-- int i;
-+ int i, available = 0, ret = 0;
- u32 bw = num_chan_to_bw(num_chans);
-
- if (first_chan_idx + num_chans > mode->num_channels) {
-@@ -203,14 +208,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- return 0;
- }
-
-- if (!dfs_channel_available(chan, type)) {
-+ ret = dfs_channel_available(chan, type);
-+ if (ret < 0) {
- wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
- first_chan->freq + i * 20);
- return 0;
- }
-+
-+ available |= ret;
- }
-
-- return 1;
-+ return available;
- }
-
-
-@@ -836,8 +844,12 @@ 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 sec = 0, skip_radar = 0;
-+ u8 cf1 = 0, cf2 = 0;
-+ bool use_radar_background = dfs_use_radar_background(iface);
-+ enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
-
- if (is_6ghz_freq(iface->freq))
- return 1;
-@@ -900,7 +912,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- /* Finally start CAC */
- hostapd_set_state(iface, HAPD_IFACE_DFS);
- wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
-- dfs_use_radar_background(iface) ? " (background)" : "");
-+ use_radar_background ? " (background)" : "");
- 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,
-@@ -910,6 +922,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
- iface->dfs_cac_ms / 1000);
-
-+ if (use_radar_background) {
-+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
-+ /*
-+ * AP cannot get any random available channel.
-+ * Let AP and dedicated radar chain both perform CAC.
-+ */
-+ if (!channel)
-+ use_radar_background = false;
-+ }
-+
- res = hostapd_start_dfs_cac(
- iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
- iface->conf->ieee80211n, iface->conf->ieee80211ac,
-@@ -918,14 +940,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- hostapd_get_oper_chwidth(iface->conf),
- hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
- hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
-- dfs_use_radar_background(iface));
-+ use_radar_background);
-
- if (res) {
- wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
- return -1;
- }
-
-- if (dfs_use_radar_background(iface)) {
-+ if (use_radar_background) {
- /* Cache background radar parameters. */
- iface->radar_background.channel = iface->conf->channel;
- iface->radar_background.secondary_channel =
-@@ -946,6 +968,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
-
- iface->radar_background.temp_ch = 1;
- return 1;
-+ } else if (dfs_use_radar_background(iface)) {
-+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
-+ channel_type = DFS_ANY_CHANNEL;
-+
-+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
-+
-+ if (!channel ||
-+ (channel->chan == iface->conf->channel &&
-+ cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
-+ cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
-+ wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
-+ iface->radar_background.channel = -1;
-+ return 0;
-+ }
-+
-+ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+ channel->freq, channel->chan,
-+ iface->conf->ieee80211n,
-+ iface->conf->ieee80211ac,
-+ iface->conf->ieee80211ax,
-+ iface->conf->ieee80211be,
-+ sec, hostapd_get_oper_chwidth(iface->conf),
-+ cf1, cf2, true);
-+
-+ iface->radar_background.channel = channel->chan;
-+ iface->radar_background.freq = channel->freq;
-+ iface->radar_background.secondary_channel = sec;
-+ iface->radar_background.centr_freq_seg0_idx = cf1;
-+ iface->radar_background.centr_freq_seg1_idx = cf2;
- }
-
- return 0;
-@@ -1195,6 +1246,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- hostapd_setup_interface_complete(iface, 0);
- iface->cac_started = 0;
- }
-+
-+ /*
-+ * When background radar is enabled but the CAC completion
-+ * is not received from the background chain.
-+ * Then, reset radar background chain.
-+ */
-+ if (dfs_use_radar_background(iface) &&
-+ iface->radar_background.channel == -1)
-+ hostpad_dfs_update_background_chain(iface);
- }
- } else if (hostapd_dfs_is_background_event(iface, freq)) {
- iface->radar_background.cac_started = 0;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
deleted file mode 100644
index 804aa16..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
+++ /dev/null
@@ -1,396 +0,0 @@
-From 6dee226e8db428c4434115b4c792f7ffd8a759dd Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 17 Mar 2023 16:17:14 +0800
-Subject: [PATCH 21/54] mtk: hostapd: Add vendor for CAPI certification
- commands
-
----
- hostapd/ctrl_iface.c | 99 +++++++++++++++++++++++++++++++
- src/ap/ap_drv_ops.c | 21 +++++++
- src/ap/ap_drv_ops.h | 3 +
- src/common/mtk_vendor.h | 33 +----------
- src/drivers/driver.h | 22 +++++++
- src/drivers/driver_nl80211.c | 55 +++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +
- 8 files changed, 206 insertions(+), 31 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index e45e574be..05606eb43 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -69,6 +69,7 @@
- #include "config_file.h"
- #include "ctrl_iface.h"
-
-+#include "common/mtk_vendor.h"
-
- #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
-
-@@ -3766,6 +3767,100 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
- return pos - buf;
- }
-
-+static int
-+hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *value, *config = cmd;
-+ enum mtk_vendor_attr_wireless_ctrl sub_cmd;
-+
-+ pos = os_strchr(config, '=');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if(pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strncmp(config, "fixed_mcs", 9) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
-+ else if (os_strncmp(config, "ofdma", 5) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
-+ else if (os_strncmp(config, "ppdu_type", 9) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
-+ else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
-+ else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
-+ else if (os_strncmp(config, "mimo", 4) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
-+ else if (os_strncmp(config, "cert", 4) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
-+ else if (os_strncmp(config, "amsdu", 5) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
-+ else if (os_strncmp(config, "rts_sigta", 9) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
-+ else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for ap_wireless", config);
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+ return -1;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *pos, *value, *type, *config = cmd;
-+ enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
-+
-+ pos = os_strchr(config, '=');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if(pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strncmp(config, "he_gi", 5) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
-+ else if (os_strncmp(config, "he_ltf", 6) == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
-+ else if (os_strncmp(config, "trig_type", 9) == 0) {
-+ pos = os_strchr(value, ',');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+ if(pos == NULL)
-+ return -1;
-+ type = pos;
-+ goto trigtype;
-+ } else if (os_strcmp(config, "ack_policy") == 0)
-+ sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
-+ else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for ap_rfeatures", config);
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+ return -1;
-+ goto exit;
-+
-+trigtype:
-+ if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
-+ return -1;
-+
-+exit:
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
-@@ -4344,6 +4439,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
- } else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
- reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
-+ } else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
-+ reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
-+ } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
-+ reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 9615ca8ce..11444c7eb 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1222,3 +1222,24 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
- return 0;
- return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
- }
-+
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+ if (!hapd->driver || !hapd->driver->ap_wireless)
-+ return 0;
-+ return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+ if (!hapd->driver || !hapd->driver->ap_rfeatures)
-+ return 0;
-+ return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
-+{
-+ if (!hapd->driver || !hapd->driver->ap_trigtype)
-+ return 0;
-+ return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index ecaa71f99..32e6fc151 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -157,6 +157,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
- int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
- u64 *aval_color_bmp);
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
-
- #include "drivers/driver.h"
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e27fe69b3..0b23c76ad 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
- NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
-
--
--static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-- [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_3wire_ctrl {
- MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-
-@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
- NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
- };
-
--static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-- [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_csi_ctrl {
- MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-
-@@ -172,7 +157,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-- MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+ MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
- MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
-
- /* keep last */
-@@ -192,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
- NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
- };
-
--static const struct nla_policy
--wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-- [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_rfeature_ctrl {
- MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-
-@@ -206,6 +186,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
- MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
- MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
- MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+ MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-@@ -247,16 +228,6 @@ enum mtk_vendor_attr_ibf_dump {
- NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
- };
-
--static struct nla_policy
--ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-- [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
--};
--
--static struct nla_policy
--ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-- [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_bss_color_ctrl {
- MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 869b0442f..2ef1a3fcd 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5166,6 +5166,28 @@ struct wpa_driver_ops {
- *
- */
- int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
-+
-+ /**
-+ * ap_wireless - set wireless command
-+ * @priv: Private driver interface data
-+ * @value: value
-+ */
-+ int (*ap_wireless)(void *priv, u8 mode, int value);
-+
-+ /**
-+ * ap_rfeatures - set ap rf features command
-+ * @priv: Private driver interface data
-+ * @value: value
-+ */
-+ int (*ap_rfeatures)(void *priv, u8 mode, int value);
-+
-+ /**
-+ * ap_trigtype - set trigger type
-+ * @priv: Private driver interface data
-+ * @enable: enable or disable
-+ * @type: trigger type
-+ */
-+ int (*ap_trigtype)(void *priv, u8 enable, u8 type);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 7b5a50ea6..3ee3ec1eb 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -86,6 +86,58 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
- wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
- }
-
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+ [MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- struct nl_sock *handle;
-@@ -14660,4 +14712,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .amsdu_ctrl = nl80211_enable_amsdu,
- .amsdu_dump = nl80211_dump_amsdu,
- .get_aval_color_bmp = nl80211_get_aval_color_bmp,
-+ .ap_wireless = nl80211_ap_wireless,
-+ .ap_rfeatures = nl80211_ap_rfeatures,
-+ .ap_trigtype = nl80211_ap_trigtype,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index fd1e57cc2..fc5217d61 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_ibf_vendor_cmd_avail:1;
- unsigned int mtk_wireless_vendor_cmd_avail:1;
- unsigned int mtk_bss_color_vendor_cmd_avail:1;
-+ unsigned int mtk_rfeatures_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 3e8eb8cb1..16306a121 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1132,6 +1132,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- drv->mtk_bss_color_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
-+ drv->mtk_rfeatures_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
deleted file mode 100644
index e2d1e7f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
+++ /dev/null
@@ -1,506 +0,0 @@
-From 8a578ff51002e783b4d9a64d24402c78529b244b Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:18:48 +0800
-Subject: [PATCH 22/54] mtk: hostapd: Air Monitor support in hostapd by vendor
-
-Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
----
- hostapd/ctrl_iface.c | 113 +++++++++++++++++++
- hostapd/hostapd_cli.c | 15 +++
- src/ap/ap_drv_ops.c | 14 +++
- src/ap/ap_drv_ops.h | 3 +
- src/common/mtk_vendor.h | 8 ++
- src/drivers/driver.h | 16 +++
- src/drivers/driver_nl80211.c | 180 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 2 +
- 9 files changed, 352 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 05606eb43..ab2768e76 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3809,6 +3809,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
-
- if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
- return -1;
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *tmp, sta_mac[ETH_ALEN] = {0};
-+ int amnt_idx = 0;
-+
-+ tmp = strtok_r(cmd, " ", &cmd);
-+
-+ if (!tmp) {
-+ wpa_printf(MSG_ERROR, "Error in command format\n");
-+ return -1;
-+ }
-+
-+ amnt_idx = strtol(tmp, &tmp, 10);
-+
-+ if (amnt_idx < 0 || amnt_idx > 15) {
-+ wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
-+ return -1;
-+ }
-+
-+ if (!cmd) {
-+ wpa_printf(MSG_ERROR, "Error in command format\n");
-+ return -1;
-+ }
-+
-+ if (hwaddr_aton(cmd, sta_mac) < 0) {
-+ wpa_printf(MSG_ERROR, "station mac is not right.\n");
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
-+ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+ return -1;
-+ }
-
- return os_snprintf(buf, buflen, "OK\n");
- }
-@@ -3862,6 +3900,75 @@ exit:
- return os_snprintf(buf, buflen, "OK\n");
- }
-
-+static int
-+hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ char *tmp;
-+ int amnt_idx = 0, ret = 0;
-+ struct amnt_resp_data *resp_buf;
-+ char *pos, *end;
-+ struct amnt_data *res;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ tmp = strtok_r(cmd, " ", &cmd);
-+
-+ if (!tmp) {
-+ wpa_printf(MSG_ERROR, "Error in command format\n");
-+ return -1;
-+ }
-+
-+ amnt_idx = strtoul(tmp, &tmp, 0);
-+
-+ if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
-+ wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
-+ return -1;
-+ }
-+
-+ if (amnt_idx == 0xff)
-+ resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
-+ * sizeof(struct amnt_data) + 1);
-+ else
-+ resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
-+
-+ if (resp_buf == NULL) {
-+ wpa_printf(MSG_ERROR, "Error in memory allocation\n");
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
-+ wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+ os_free(resp_buf);
-+ return -1;
-+ }
-+
-+ for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
-+ res = &resp_buf->resp_data[i];
-+ ret = os_snprintf(pos, end - pos,
-+ "[hostapd_cli] amnt_idx: %d, addr="MACSTR
-+ ", rssi=%d/%d/%d/%d, last_seen=%u\n",
-+ res->idx,
-+ MAC2STR(res->addr), res->rssi[0],
-+ res->rssi[1], res->rssi[2],
-+ res->rssi[3], res->last_seen);
-+ if (os_snprintf_error(end - pos, ret)) {
-+ os_free(resp_buf);
-+ return 0;
-+ }
-+ pos = pos + ret;
-+ }
-+
-+ os_free(resp_buf);
-+
-+ if (pos == buf)
-+ return os_snprintf(buf, buflen, "Index %d is not monitored\n",
-+ amnt_idx);
-+ else
-+ return pos - buf;
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4443,6 +4550,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
- } else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
- reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
-+ } else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
-+ reply, reply_size);
-+ } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
-+ reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
-+ reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index e9e156d28..6d763f327 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1633,6 +1633,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
- return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
- }
-
-+static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
-+}
-
- struct hostapd_cli_cmd {
- const char *cmd;
-@@ -1847,6 +1858,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- " = show iBF state (enabled/disabled)"},
- { "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
- " = show AMSDU state"},
-+ { "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
-+ " = Set Station index and mac to monitor"},
-+ { "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
-+ " = Dump RSSI of monitoring Station"},
- { NULL, NULL, NULL, NULL }
- };
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 11444c7eb..b90dd5722 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1243,3 +1243,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
- return 0;
- return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
- }
-+
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+ if (!hapd->driver || !hapd->driver->amnt_set)
-+ return 0;
-+ return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
-+}
-+
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
-+{
-+ if (!hapd->driver || !hapd->driver->amnt_dump)
-+ return 0;
-+ return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 32e6fc151..8a97e0fea 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -161,6 +161,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
- int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
- int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
-
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+
- #include "drivers/driver.h"
-
- int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 0b23c76ad..dd1ca2164 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -258,10 +258,18 @@ struct csi_data {
- u16 rx_idx;
- };
-
-+#define AIR_MONITOR_MAX_ENTRY 16
-+
- struct amnt_data {
- u8 idx;
- u8 addr[ETH_ALEN];
- s8 rssi[4];
- u32 last_seen;
- };
-+
-+struct amnt_resp_data {
-+ u8 sta_num;
-+ struct amnt_data resp_data[0];
-+};
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 2ef1a3fcd..24c9b96df 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5188,6 +5188,22 @@ struct wpa_driver_ops {
- * @type: trigger type
- */
- int (*ap_trigtype)(void *priv, u8 enable, u8 type);
-+
-+ /**
-+ * amnt_set - add/delete station from monitoring
-+ * @priv: Private driver interface data
-+ * @amnt_idx: Monitor Index
-+ * @amnt_sta_mac: station mac address
-+ */
-+ int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
-+
-+ /**
-+ * amnt_dump - Dump particular/ all station
-+ * @priv: Private driver interface data
-+ * @amnt_idx: Monitor Index
-+ * @amnt_dump_buf: Buffer to print
-+ */
-+ int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3ee3ec1eb..3a1c32a6d 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -138,6 +138,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
- [MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
- };
-
-+static struct nla_policy
-+amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
-+ [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
-+ [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
-+};
-+
-+static struct nla_policy
-+amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
-+ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- struct nl_sock *handle;
-@@ -14545,6 +14558,171 @@ fail:
- return -ENOBUFS;
- }
-
-+static int
-+nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ void *tb1;
-+ int ret;
-+
-+ if (!drv->mtk_amnt_vendor_cmd_avail) {
-+ wpa_printf(MSG_ERROR,
-+ "nl80211: Driver does not support air monitor");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+ if (!data)
-+ goto fail;
-+
-+ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
-+ if (!tb1)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
-+
-+ nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
-+
-+ nla_nest_end(msg, tb1);
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
-+ ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+
-+}
-+
-+static int
-+mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
-+{
-+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
-+ struct nlattr *attr, *cur, *data;
-+ struct amnt_data *res;
-+ int len = 0, rem;
-+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+ struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
-+
-+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+ genlmsg_attrlen(gnlh, 0), NULL);
-+
-+ attr = tb[NL80211_ATTR_VENDOR_DATA];
-+ if (!attr)
-+ return NL_SKIP;
-+
-+ nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
-+ attr, amnt_ctrl_policy);
-+
-+ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
-+ return NL_SKIP;
-+
-+ nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
-+
-+ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
-+ return NL_SKIP;
-+
-+ len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
-+ if (!len)
-+ return 0;
-+
-+ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
-+ return NL_SKIP;
-+
-+ data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
-+
-+ nla_for_each_nested(cur, data, rem) {
-+ if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
-+ return NL_SKIP;
-+ res = (struct amnt_data *) nla_data(cur);
-+ wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
-+ "addr="MACSTR", "
-+ "rssi=%d/%d/%d/%d, last_seen=%u\n",
-+ res->idx,
-+ MAC2STR(res->addr),
-+ res->rssi[0], res->rssi[1], res->rssi[2],
-+ res->rssi[3], res->last_seen);
-+ os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
-+ sizeof(struct amnt_data));
-+ amnt_dump->sta_num++;
-+ }
-+ return 0;
-+}
-+
-+static int
-+nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ void *tb1;
-+ int ret;
-+
-+ if (!drv->mtk_amnt_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support air monitor");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+ if (!data)
-+ goto fail;
-+
-+ tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
-+ | NLA_F_NESTED);
-+ if (!tb1)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
-+
-+ nla_nest_end(msg, tb1);
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, mt76_amnt_dump_cb,
-+ dump_buf, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
-+ , ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14715,4 +14893,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .ap_wireless = nl80211_ap_wireless,
- .ap_rfeatures = nl80211_ap_rfeatures,
- .ap_trigtype = nl80211_ap_trigtype,
-+ .amnt_set = nl80211_amnt_set,
-+ .amnt_dump = nl80211_amnt_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index fc5217d61..0d85adfee 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_wireless_vendor_cmd_avail:1;
- unsigned int mtk_bss_color_vendor_cmd_avail:1;
- unsigned int mtk_rfeatures_vendor_cmd_avail:1;
-+ unsigned int mtk_amnt_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 16306a121..4bd15f348 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1131,6 +1131,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- break;
- case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- drv->mtk_bss_color_vendor_cmd_avail = 1;
-+ case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
-+ drv->mtk_amnt_vendor_cmd_avail = 1;
- break;
- case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- drv->mtk_rfeatures_vendor_cmd_avail = 1;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch
deleted file mode 100644
index 84be9e0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From da108d74412d83264355fe453a8d2ffc6f99fa86 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:23:00 +0800
-Subject: [PATCH 23/54] mtk: hostapd: Fix setting wrong seg0 index for 5G
- center chan 159 BW40
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/ap_config.h | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d0e27b28d..f03a957b7 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1308,7 +1308,8 @@ hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
- #ifdef CONFIG_IEEE80211BE
- if (conf->ieee80211be)
- conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
-- if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
-+ if (is_6ghz_op_class(conf->op_class) &&
-+ center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
- oper_centr_freq_seg0_idx +=
- conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
- #endif /* CONFIG_IEEE80211BE */
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch
deleted file mode 100644
index d1c6e2c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch
+++ /dev/null
@@ -1,228 +0,0 @@
-From 906bc021d4e4ddd62edc985dd37d7ad61d39fdb7 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:24:19 +0800
-Subject: [PATCH 24/54] mtk: hostapd: Add muru user number debug command
-
----
- hostapd/ctrl_iface.c | 13 ++++++++++++-
- src/ap/ap_drv_ops.c | 4 ++--
- src/ap/ap_drv_ops.h | 2 +-
- src/ap/hostapd.c | 3 ++-
- src/common/mtk_vendor.h | 7 +++++++
- src/drivers/driver.h | 4 ++--
- src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
- 7 files changed, 55 insertions(+), 15 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ab2768e76..4515583cf 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3398,6 +3398,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
- char *buf, size_t buflen)
- {
- char *pos, *config, *value;
-+ u8 mode;
-+
- config = cmd;
- pos = os_strchr(config, ' ');
- if (pos == NULL)
-@@ -3504,6 +3506,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- char *buf, size_t buflen)
- {
- char *pos, *config, *value;
-+ u8 mode;
-+
- config = cmd;
- pos = os_strchr(config, ' ');
- if (pos == NULL)
-@@ -3521,13 +3525,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- return -1;
- }
- hapd->iconf->mu_onoff = (u8) mu;
-+ mode = MU_CTRL_ONOFF;
-+ } else if (os_strcmp(config, "ul_user_cnt") == 0) {
-+ mode = MU_CTRL_UL_USER_CNT;
-+ wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
-+ } else if (os_strcmp(config, "dl_user_cnt") == 0) {
-+ mode = MU_CTRL_DL_USER_CNT;
-+ wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
- } else {
- wpa_printf(MSG_ERROR,
- "Unsupported parameter %s for SET_MU", config);
- return -1;
- }
-
-- if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+ if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
- return os_snprintf(buf, buflen, "OK\n");
- } else {
- return -1;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index b90dd5722..0aec9e925 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1162,11 +1162,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
- {
- if (!hapd->driver || !hapd->driver->mu_ctrl)
- return 0;
-- return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
- }
-
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 8a97e0fea..464efbae1 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -148,7 +148,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index fcf346d36..2d7fb6d39 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -57,6 +57,7 @@
- #include "wpa_auth_kay.h"
- #include "hw_features.h"
-
-+#include "common/mtk_vendor.h"
-
- static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
- #ifdef CONFIG_WEP
-@@ -2517,7 +2518,7 @@ dfs_offload:
- if (hostapd_drv_configure_edcca_threshold(hapd,
- hapd->iconf->edcca_threshold) < 0)
- goto fail;
-- if (hostapd_drv_mu_ctrl(hapd) < 0)
-+ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
- goto fail;
- if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index dd1ca2164..99371bf73 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,6 +199,8 @@ enum mtk_vendor_attr_mu_ctrl {
-
- MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
-+ MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -272,4 +274,9 @@ struct amnt_resp_data {
- struct amnt_data resp_data[0];
- };
-
-+enum {
-+ MU_CTRL_ONOFF,
-+ MU_CTRL_DL_USER_CNT,
-+ MU_CTRL_UL_USER_CNT,
-+};
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 24c9b96df..83d347338 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5121,11 +5121,11 @@ struct wpa_driver_ops {
- int (*get_edcca)(void *priv, const u8 mode, u8 *value);
-
- /**
-- * mu_ctrl - ctrl on off for UL/DL MURU
-+ * mu_ctrl - ctrl for UL/DL MURU
- * @priv: Private driver interface data
- *
- */
-- int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+ int (*mu_ctrl)(void *priv, u8 mode, u8 val);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3a1c32a6d..8226bebc4 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13698,13 +13698,13 @@ fail:
-
-
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl_msg *msg;
- struct nlattr *data;
-- int ret;
-+ int ret = -ENOBUFS;
-
- if (!drv->mtk_mu_vendor_cmd_avail) {
- wpa_printf(MSG_INFO,
-@@ -13715,17 +13715,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-- !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
-- nlmsg_free(msg);
-- return -ENOBUFS;
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
-+ goto fail;
-+
-+ switch (mode) {
-+ case MU_CTRL_ONOFF:
-+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
-+ goto fail;
-+ break;
-+ case MU_CTRL_UL_USER_CNT:
-+ case MU_CTRL_DL_USER_CNT:
-+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
-+ goto fail;
-+ break;
-+ default:
-+ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+ ret = -EINVAL;
-+ goto fail;
- }
-+
- nla_nest_end(msg, data);
-+
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
- if(ret){
-- wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+ wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
- }
- return ret;
-+
-+fail:
-+ nl80211_nlmsg_clear(msg);
-+ nlmsg_free(msg);
-+ return ret;
- }
-
-
-@@ -14869,7 +14890,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .update_connect_params = nl80211_update_connection_params,
- .send_external_auth_status = nl80211_send_external_auth_status,
- .set_4addr_mode = nl80211_set_4addr_mode,
-- .mu_ctrl = nl80211_mu_onoff,
-+ .mu_ctrl = nl80211_mu_ctrl,
- .mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- .dpp_listen = nl80211_dpp_listen,
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
deleted file mode 100644
index db8ae73..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
+++ /dev/null
@@ -1,639 +0,0 @@
-From 738370924d0153163300f44c87a68e19a5220272 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Sat, 3 Jun 2023 17:12:15 +0800
-Subject: [PATCH 25/54] mtk: hostapd: add connac3 PHY MURU manual mode config
- support
-
-This commit supports read the following two formats to set MU/RU manual
-mode:
-1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
-2. hostapd_cli -i <intf> set_mu <field> <value>
-
-For the <field>, we support the following field:
-1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
-2. ul_comm_bw/dl_comm_bw: set the bandwith
-3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
-allocate idx
-4. ul_user_mcs/dl_user_mcs: set the mcs for each user
-5. ul_user_ssAlloc_raru: set the number of ss for each user
-6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
-7. dl_comm_toneplan: fix ru toneplan allocation
-8. dl_comm_ack_policy: fix station ack policy
-9. update : trigger driver to send mcu command to set muru manual mode.
-
-For the value of each field, please check wiki to learn the details:
-https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
-
-For the fields that mt76 support to use, we will update in this wiki:
-https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
-
-Please noted that this commit is only for connac 3 gen chips. If this
-feature is to be used in other generations, the following actions must
-be taken:
-1. Different data structue needs to be defined for different
-generations, e.g. connac4_muru_comm, connac4_muru_dl.
-2. hostapd_ctrl_iface_set_mu() shall be modified.
-3. A new code level configuration shall be defined to differentiate the
-code flow that different generations will go through.
----
- hostapd/ctrl_iface.c | 235 +++++++++++++++++++++++++++++++----
- src/ap/ap_config.h | 1 +
- src/ap/ap_drv_ops.c | 4 +-
- src/ap/ap_drv_ops.h | 2 +-
- src/ap/hostapd.c | 2 +-
- src/common/mtk_vendor.h | 166 ++++++++++++++++++++++++-
- src/drivers/driver.h | 2 +-
- src/drivers/driver_nl80211.c | 21 ++--
- 8 files changed, 390 insertions(+), 43 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 4515583cf..ae61cf625 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3500,22 +3500,61 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
- }
- }
-
-+static int
-+hostapd_parse_argument_helper(char *value, u16 **ptr_input)
-+{
-+#define MAX_MU_CTRL_NUM 17
-+
-+ u16 *input;
-+ char *endptr;
-+ int cnt = 0;
-+
-+ input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
-+ if (input == NULL) {
-+ wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
-+ return -1;
-+ }
-+ while (value) {
-+ u8 val = strtol(value, &endptr, 10);
-+
-+ if (value != endptr) {
-+ input[cnt++] = val;
-+ value = os_strchr(endptr, ':');
-+ if (value)
-+ value++;
-+ } else {
-+ break;
-+ }
-+ }
-
-+ *ptr_input = input;
-+ return cnt;
-+}
-+
-+#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do { \
-+ if ((le_to_host32(_val) & (_mask)) != _mask) { \
-+ wpa_printf(MSG_ERROR, "Set %s first\n", #_mask); \
-+ goto fail; \
-+ } \
-+ } while(0)
- static int
- hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
-- char *buf, size_t buflen)
-+ char *buf, size_t buflen)
- {
- char *pos, *config, *value;
-- u8 mode;
-+ u8 i;
-+ int cnt = 0, ret;
-+ u16 *val;
-+ struct connac3_muru *muru;
-+ struct connac3_muru_dl *dl;
-+ struct connac3_muru_ul *ul;
-+ struct connac3_muru_comm *comm;
-
- config = cmd;
- pos = os_strchr(config, ' ');
-- if (pos == NULL)
-- return -1;
-- *pos++ = '\0';
-+ if (pos != NULL)
-+ *pos++ = '\0';
-
-- if(pos == NULL)
-- return -1;
- value = pos;
-
- if (os_strcmp(config, "onoff") == 0) {
-@@ -3525,24 +3564,167 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- return -1;
- }
- hapd->iconf->mu_onoff = (u8) mu;
-- mode = MU_CTRL_ONOFF;
-- } else if (os_strcmp(config, "ul_user_cnt") == 0) {
-- mode = MU_CTRL_UL_USER_CNT;
-- wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
-- } else if (os_strcmp(config, "dl_user_cnt") == 0) {
-- mode = MU_CTRL_DL_USER_CNT;
-- wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
-- } else {
-- wpa_printf(MSG_ERROR,
-- "Unsupported parameter %s for SET_MU", config);
-- return -1;
-+
-+ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
-+ return os_snprintf(buf, buflen, "OK\n");
-+ else
-+ goto fail;
- }
-
-- if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
-- return os_snprintf(buf, buflen, "OK\n");
-+ if (hapd->iconf->muru_config == NULL)
-+ hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
-+
-+ muru = hapd->iconf->muru_config;
-+ dl = &muru->dl;
-+ ul = &muru->ul;
-+ comm = &muru->comm;
-+
-+ if (os_strncmp(config, "update", 6) == 0) {
-+ ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
-+
-+ os_free(hapd->iconf->muru_config);
-+ hapd->iconf->muru_config = NULL;
-+
-+ if (ret)
-+ goto fail;
-+ } else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
-+ ul->user_num = (u8)atoi(value);
-+ comm->ppdu_format |= MURU_PPDU_HE_TRIG;
-+ comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
-+ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
-+ } else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
-+ dl->user_num = (u8)atoi(value);
-+ comm->ppdu_format |= MURU_PPDU_HE_MU;
-+ comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
-+ muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
-+ } else if (os_strcmp(config, "dl_comm_bw") == 0) {
-+ dl->bw = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
-+ } else if (os_strcmp(config, "ul_comm_bw") == 0) {
-+ ul->bw = (u8)atoi(value);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
-+ } else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != (dl->user_num * 2))
-+ goto para_fail;
-+ for (i = 0; i < dl->user_num; i++) {
-+ dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+ dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+ dl->usr[i].ru_idx = val[(2 * i) + 1];
-+ }
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
-+ } else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != (ul->user_num * 2))
-+ goto para_fail;
-+ for (i = 0; i < ul->user_num; i++) {
-+ ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+ ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+ ul->usr[i].ru_idx = val[(2 * i) + 1];
-+ }
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
-+ } else if (os_strcmp(config, "dl_user_mcs") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != dl->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ dl->usr[i].mcs = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
-+ } else if (os_strcmp(config, "ul_user_mcs") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != ul->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ ul->usr[i].mcs = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
-+ } else if (os_strcmp(config, "dl_user_cod") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != dl->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ dl->usr[i].ldpc = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
-+ } else if (os_strcmp(config, "ul_user_cod") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != ul->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ ul->usr[i].ldpc = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
-+ } else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ if (cnt != ul->user_num)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ ul->usr[i].nss = (u8) val[i];
-+ os_free(val);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
-+ } else if (os_strcmp(config, "dl_comm_gi") == 0) {
-+ dl->gi = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
-+ } else if (os_strcmp(config, "dl_comm_ltf") == 0) {
-+ dl->ltf = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
-+ } else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
-+ ul->gi_ltf = (u8)atoi(value);
-+ muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
-+ } else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
-+ dl->ack_policy = (u8)atoi(value);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
-+ } else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
-+ MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
-+ cnt = hostapd_parse_argument_helper(value, &val);
-+ if (cnt == -1)
-+ goto fail;
-+ i = pow(2, dl->bw);
-+ if (cnt != i)
-+ goto para_fail;
-+ for (i = 0; i < cnt; i++)
-+ dl->ru[i] = host_to_le16(val[i]);
-+ os_free(val);
-+ muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
- } else {
-- return -1;
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for SET_MU", config);
-+ goto fail;
- }
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+
-+para_fail:
-+ os_free(val);
-+ wpa_printf(MSG_ERROR, "Incorrect input number\n");
-+fail:
-+ return os_snprintf(buf, buflen, "FAIL\n");
- }
-
-
-@@ -4540,8 +4722,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- reply_size);
- } else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
-- reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
-- reply_size);
-+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
- } else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- } else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-@@ -4567,6 +4748,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
-+ // Replace first ':' with a single space ' '
-+ char *pos = buf + 23;
-+
-+ pos = os_strchr(pos, ':');
-+ if (pos)
-+ *pos = ' ';
-+ reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index f03a957b7..7c0d12a3b 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1203,6 +1203,7 @@ struct hostapd_config {
- u8 ibf_enable;
- u8 dfs_detect_mode;
- u8 amsdu;
-+ void *muru_config;
- };
-
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 0aec9e925..721bfa053 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1162,11 +1162,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
- {
- if (!hapd->driver || !hapd->driver->mu_ctrl)
- return 0;
-- return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
-+ return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
- }
-
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 464efbae1..1e7ae7a8d 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -148,7 +148,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 2d7fb6d39..a28466405 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2518,7 +2518,7 @@ dfs_offload:
- if (hostapd_drv_configure_edcca_threshold(hapd,
- hapd->iconf->edcca_threshold) < 0)
- goto fail;
-- if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
-+ if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
- goto fail;
- if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99371bf73..e140de60b 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,8 +199,11 @@ enum mtk_vendor_attr_mu_ctrl {
-
- MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
-- MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
-+ /**
-+ * The above attrs are also used by connac 2. It is best not to modify the
-+ * above data structure.
-+ */
-+ MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-
- /* keep last */
- NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -275,8 +278,163 @@ struct amnt_resp_data {
- };
-
- enum {
-+ MU_CTRL_UPDATE,
- MU_CTRL_ONOFF,
-- MU_CTRL_DL_USER_CNT,
-- MU_CTRL_UL_USER_CNT,
- };
-+
-+struct connac3_muru_comm {
-+ u8 pda_pol;
-+ u8 band;
-+ u8 spe_idx;
-+ u8 proc_type;
-+
-+ le16 mlo_ctrl;
-+ u8 sch_type;
-+ u8 ppdu_format;
-+ u8 ac;
-+ u8 _rsv[3];
-+};
-+
-+struct connac3_muru_dl {
-+ u8 user_num;
-+ u8 tx_mode;
-+ u8 bw;
-+ u8 gi;
-+
-+ u8 ltf;
-+ u8 mcs;
-+ u8 dcm;
-+ u8 cmprs;
-+
-+ le16 ru[16];
-+
-+ u8 c26[2];
-+ u8 ack_policy;
-+ u8 tx_power;
-+
-+ le16 mu_ppdu_duration;
-+ u8 agc_disp_order;
-+ u8 _rsv1;
-+
-+ u8 agc_disp_pol;
-+ u8 agc_disp_ratio;
-+ le16 agc_disp_linkMFG;
-+
-+ le16 prmbl_punc_bmp;
-+ u8 _rsv2[2];
-+
-+ struct {
-+ le16 wlan_idx;
-+ u8 ru_alloc_seg;
-+ u8 ru_idx;
-+ u8 ldpc;
-+ u8 nss;
-+ u8 mcs;
-+ u8 mu_group_idx;
-+ u8 vht_groud_id;
-+ u8 vht_up;
-+ u8 he_start_stream;
-+ u8 he_mu_spatial;
-+ le16 tx_power_alpha;
-+ u8 ack_policy;
-+ u8 ru_allo_ps160;
-+ } usr[16];
-+};
-+
-+struct connac3_muru_ul {
-+ u8 user_num;
-+ u8 tx_mode;
-+
-+ u8 ba_type;
-+ u8 _rsv;
-+
-+ u8 bw;
-+ u8 gi_ltf;
-+ le16 ul_len;
-+
-+ le16 trig_cnt;
-+ u8 pad;
-+ u8 trig_type;
-+
-+ le16 trig_intv;
-+ u8 trig_ta[ETH_ALEN];
-+ le16 ul_ru[16];
-+
-+ u8 c26[2];
-+ le16 agc_disp_linkMFG;
-+
-+ u8 agc_disp_mu_len;
-+ u8 agc_disp_pol;
-+ u8 agc_disp_ratio;
-+ u8 agc_disp_pu_idx;
-+
-+ struct {
-+ le16 wlan_idx;
-+ u8 ru_alloc_seg;
-+ u8 ru_idx;
-+ u8 ldpc;
-+ u8 nss;
-+ u8 mcs;
-+ u8 target_rssi;
-+ le32 trig_pkt_size;
-+ u8 ru_allo_ps160;
-+ u8 _rsv2[3];
-+ } usr[16];
-+};
-+
-+struct connac3_muru_dbg {
-+ /* HE TB RX Debug */
-+ le32 rx_hetb_nonsf_en_bitmap;
-+ le32 rx_hetb_cfg[2];
-+};
-+
-+struct connac3_muru {
-+ le32 cfg_comm;
-+ le32 cfg_dl;
-+ le32 cfg_ul;
-+ le32 cfg_dbg;
-+
-+ struct connac3_muru_comm comm;
-+ struct connac3_muru_dl dl;
-+ struct connac3_muru_ul ul;
-+ struct connac3_muru_dbg dbg;
-+};
-+
-+#define MURU_OFDMA_SCH_TYPE_DL BIT(0)
-+#define MURU_OFDMA_SCH_TYPE_UL BIT(1)
-+#define MURU_PPDU_HE_TRIG BIT(2)
-+#define MURU_PPDU_HE_MU BIT(3)
-+
-+/* Common Config */
-+#define MURU_COMM_PPDU_FMT BIT(0)
-+#define MURU_COMM_BAND BIT(2)
-+#define MURU_COMM_WMM BIT(3)
-+#define MURU_COMM_SPE_IDX BIT(4)
-+#define MURU_COMM_SET (MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
-+ MURU_COMM_WMM | MURU_COMM_SPE_IDX)
-+
-+/* DL Common config */
-+#define MURU_FIXED_DL_BW BIT(0)
-+#define MURU_FIXED_DL_GI BIT(1)
-+#define MURU_FIXED_DL_TONE_PLAN BIT(3)
-+#define MURU_FIXED_DL_TOTAL_USER_CNT BIT(4)
-+#define MURU_FIXED_DL_LTF BIT(5)
-+#define MURU_FIXED_DL_ACK_PLY BIT(9)
-+
-+/* DL Per User Config */
-+#define MURU_FIXED_USER_DL_COD BIT(17)
-+#define MURU_FIXED_USER_DL_MCS BIT(18)
-+#define MURU_FIXED_USER_DL_RU_ALLOC BIT(20)
-+
-+/* UL Common Config */
-+#define MURU_FIXED_UL_TOTAL_USER_CNT BIT(4)
-+#define MURU_FIXED_UL_BW BIT(5)
-+#define MURU_FIXED_UL_GILTF BIT(6)
-+
-+/* UL Per User Config */
-+#define MURU_FIXED_USER_UL_COD BIT(18)
-+#define MURU_FIXED_USER_UL_MCS BIT(19)
-+#define MURU_FIXED_USER_UL_NSS BIT(20)
-+#define MURU_FIXED_USER_UL_RU_ALLOC BIT(21)
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 83d347338..8da93e025 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5125,7 +5125,7 @@ struct wpa_driver_ops {
- * @priv: Private driver interface data
- *
- */
-- int (*mu_ctrl)(void *priv, u8 mode, u8 val);
-+ int (*mu_ctrl)(void *priv, u8 mode, void *config);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 8226bebc4..b3897e61d 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13698,12 +13698,13 @@ fail:
-
-
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
- {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl_msg *msg;
- struct nlattr *data;
-+ struct hostapd_config *cfg = config;
- int ret = -ENOBUFS;
-
- if (!drv->mtk_mu_vendor_cmd_avail) {
-@@ -13720,17 +13721,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
-
- switch (mode) {
- case MU_CTRL_ONOFF:
-- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
-- goto fail;
-+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
-+ goto fail;
- break;
-- case MU_CTRL_UL_USER_CNT:
-- case MU_CTRL_DL_USER_CNT:
-- if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
-- nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
-- goto fail;
-+ case MU_CTRL_UPDATE:
-+ if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-+ sizeof(struct connac3_muru), cfg->muru_config))
-+ goto fail;
- break;
- default:
-- wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+ wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
- ret = -EINVAL;
- goto fail;
- }
-@@ -13738,9 +13738,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- nla_nest_end(msg, data);
-
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-- if(ret){
-+ if (ret)
- wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
-- }
- return ret;
-
- fail:
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch
deleted file mode 100644
index 3801eb3..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 1468c2bb212faf091cb4a0c0ba1394722eb2c94e Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 9 Jun 2023 09:03:05 +0800
-Subject: [PATCH 26/54] mtk: hostapd: Add HE capabilities check
-
-Add HE capabilities check.
-Since "HE capabilities" check has been removed by driver,
-add the support for "HE capabilities" check in hostapd.
----
- src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
- 1 file changed, 26 insertions(+)
-
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index 9d2272522..9a36bcb10 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -709,6 +709,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
- #ifdef CONFIG_IEEE80211AX
- static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
- {
-+ struct hostapd_hw_modes *mode = iface->current_mode;
-+ struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
-+ struct hostapd_config *conf = iface->conf;
-+
-+#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap) \
-+ do { \
-+ if (cfg_cap && !(hw_cap[phy_idx] & field)) { \
-+ wpa_printf(MSG_ERROR, "Driver does not support configured" \
-+ " HE capability [%s]", #field); \
-+ return 0; \
-+ } \
-+ } while (0)
-+
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
-+ HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
-+ conf->he_phy_capab.he_ldpc);
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
-+ HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
-+ conf->he_phy_capab.he_su_beamformer);
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
-+ HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
-+ conf->he_phy_capab.he_su_beamformee);
-+ HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
-+ HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
-+ conf->he_phy_capab.he_mu_beamformer);
-+
- return 1;
- }
- #endif /* CONFIG_IEEE80211AX */
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
deleted file mode 100644
index 6a98b5d..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 163efaffce1344a6c2611a2a75d863eb88b17137 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:44:15 +0800
-Subject: [PATCH 27/54] mtk: hostapd: Fix background channel overlapping
- operating channel issue
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 3b1df6dc6..6f7635436 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -812,6 +812,20 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
- }
-
-
-+static void dfs_check_background_overlapped(struct hostapd_iface *iface)
-+{
-+ int width = hostapd_get_oper_chwidth(iface->conf);
-+
-+ if (!dfs_use_radar_background(iface))
-+ return;
-+
-+ if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
-+ width, iface->radar_background.centr_freq_seg0_idx,
-+ iface->radar_background.centr_freq_seg1_idx))
-+ iface->radar_background.channel = -1;
-+}
-+
-+
- static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
- int start_chan_idx, int n_chans)
- {
-@@ -1132,6 +1146,8 @@ static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
- &oper_centr_freq_seg1_idx,
- &channel_type);
- if (!channel ||
-+ channel->chan == iface->conf->channel ||
-+ channel->chan == iface->radar_background.channel ||
- hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
- channel->freq, channel->chan,
- iface->conf->ieee80211n,
-@@ -1366,6 +1382,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
- hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
- oper_centr_freq_seg1_idx);
- err = 0;
-+ dfs_check_background_overlapped(iface);
-
- hostapd_setup_interface_complete(iface, err);
- return err;
-@@ -1493,6 +1510,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
- hostapd_set_oper_centr_freq_seg1_idx(
- iface->conf, oper_centr_freq_seg1_idx);
-
-+ dfs_check_background_overlapped(iface);
- hostapd_disable_iface(iface);
- hostapd_enable_iface(iface);
- return 0;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
deleted file mode 100644
index 84ca877..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From fdbb7f5123ea8f3c5c568b4f5b311e33aa396e50 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:47:20 +0800
-Subject: [PATCH 28/54] mtk: hostapd: Fix hostapd_dfs_start_cac log
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 6f7635436..95119a391 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1655,9 +1655,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- /* TODO: How to check CAC time for ETSI weather channels? */
- iface->dfs_cac_ms = 60000;
- wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
-- "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
-+ "freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
- "seg1=%d cac_time=%ds%s",
-- freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
-+ freq, (freq - 5000) / 5, chan_offset,
-+ channel_width_to_string(chan_width),
-+ (cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
- iface->dfs_cac_ms / 1000,
- hostapd_dfs_is_background_event(iface, freq) ?
- " (background)" : "");
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
deleted file mode 100644
index 57b8060..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From a08f87c88e8abd539dc75b7fcb15fef786d5a61d Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 13 Jul 2023 13:14:26 +0800
-Subject: [PATCH 29/54] mtk: hostapd: Check the bridge after ioctl SIOCBRADDIF
- failed
-
-If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
-already be bridged by others, and linux_br_add_if should not indicate an
-error in the case.
-
-This patch checks whether the interface is correctly brigded when ioctl
-returns EBUSY.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/drivers/linux_ioctl.c | 16 ++++++++++++++--
- 1 file changed, 14 insertions(+), 2 deletions(-)
-
-diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
-index 7edb9df2e..b8c1af2e3 100644
---- a/src/drivers/linux_ioctl.c
-+++ b/src/drivers/linux_ioctl.c
-@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
- int linux_br_add_if(int sock, const char *brname, const char *ifname)
- {
- struct ifreq ifr;
-- int ifindex;
-+ int ifindex, ret;
-+ char in_br[IFNAMSIZ];
-
- ifindex = if_nametoindex(ifname);
- if (ifindex == 0)
-@@ -164,8 +165,19 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
-
- wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
- "%s: %s", ifname, brname, strerror(errno));
-+
-+ /* If ioctl returns -EBUSY when adding interface into bridge,
-+ * the interface might already be added by netifd, so here we
-+ * check whether the interface is currently on the right
-+ * bridge. */
-+ if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
-+ os_strcmp(in_br, brname) == 0)
-+ ret = 0;
-+ else
-+ ret = -1;
-+
- errno = saved_errno;
-- return -1;
-+ return ret;
- }
-
- return 0;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
deleted file mode 100644
index 28761c5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 8e8590fe62c5b1c9daceb7588798a96b95df6039 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 14 Jul 2023 17:19:13 +0800
-Subject: [PATCH 30/54] mtk: hostapd: Update parameter_set_count in MU EDCA IE
-
-without this patch, MU EDCA Parameter update count not equal to
-WMM Parameter set count.
----
- src/ap/ieee802_11_he.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index 9407dd6e5..353e8156c 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -316,6 +316,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
- edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
- os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
-
-+ if (hapd->conf->wmm_enabled)
-+ edca->he_qos_info = hapd->parameter_set_count % 0xf;
-+
- wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
- pos, sizeof(*edca));
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
deleted file mode 100644
index 0ad3c2d..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From be61f9b1bd2f3c7bcd935968d8709565f6b231b5 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Mon, 24 Jul 2023 11:30:27 +0800
-Subject: [PATCH 31/54] mtk: hostapd: add extension IE list for non-inherit IE
- in mbssid
-
-Certain clients do not scan all non tx profiles due to absence of
-element ID extension list which is mandatory field in non inheritance
-IE. Non inheritance Element ID is followed by extension element ID.
-Length is expected to be mentioned. Currently we do not support any
-extension element and hence filling length as 0.
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 9 +++++++--
- 1 file changed, 7 insertions(+), 2 deletions(-)
- mode change 100644 => 100755 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100644
-new mode 100755
-index 110ad8c2e..e05a06b09
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7659,7 +7659,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
- else if (hapd->conf->xrates_supported)
- ie_count++;
- if (ie_count)
-- nontx_profile_len += 4 + ie_count;
-+ nontx_profile_len += 5 + ie_count;
-
- if (len + nontx_profile_len > 255)
- break;
-@@ -7800,11 +7800,16 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
- non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
- if (ie_count) {
- *eid++ = WLAN_EID_EXTENSION;
-- *eid++ = 2 + ie_count;
-+ *eid++ = 3 + ie_count;
- *eid++ = WLAN_EID_EXT_NON_INHERITANCE;
- *eid++ = ie_count;
- os_memcpy(eid, non_inherit_ie, ie_count);
- eid += ie_count;
-+ /* Element ID extension list is mandatory part of non inheritance IE.
-+ * It has a length field followed by extension IEs. Currently no
-+ * extension IEs are supported so filling length as 0.
-+ */
-+ *eid++ = 0;
- }
-
- *eid_len_pos = (eid - eid_len_pos) - 1;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch
deleted file mode 100644
index 15c97a8..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From fea4788692076f9c0701ecec68a85198a70d05c0 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Mon, 7 Aug 2023 15:27:27 +0800
-Subject: [PATCH 32/54] mtk: hostapd: Fix rnr ie length when no need to report
- bss
-
-Fix rnr ie length when no need to report bss. If we don't have content in
-TBTT then don't change the length of the ie (*size_offset).
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- src/ap/ieee802_11.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index e05a06b09..7b6aabbff 100755
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7511,8 +7511,10 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- }
-
- start = i;
-- *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
-- *size_offset = (eid - size_offset) - 1;
-+ if (tbtt_count != 0) {
-+ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
-+ *size_offset = (eid - size_offset) - 1;
-+ }
- }
-
- if (tbtt_count == 0)
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
deleted file mode 100644
index ebc4408..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 765215ac465b90e20c52dd48d51859d849834c80 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 8 Aug 2023 19:21:41 +0800
-Subject: [PATCH 33/54] mtk: hostapd: add back ht vht cap missing field before
- dfs channel fallback
-
-hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
-on the bandwidth of switched channel.
-For example, vht bw 160 support field would be cleared if we switch to
-non bw 160 channel.
-This design works fine with NON-DFS channel switch.
-However, for those DFS channels who require CAC, channel switch command
-calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
-This is simply restarting the interface not CHANNEL SWITCHING, so
-hostapd will not receive any ch_switch event from kernel.
-Therefore, the cleared field in vht_capab will not be set back to 1,
-even if we channel switch to dfs channel bw 160.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/hostapd.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index a28466405..ec7677f6a 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4112,6 +4112,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
- break;
- }
-
-+ if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
-+ freq_params->bandwidth > 20)
-+ iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
-+ if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
-+ freq_params->bandwidth == 160)
-+ iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
-+
- iface->freq = freq_params->freq;
- iface->conf->channel = freq_params->channel;
- iface->conf->secondary_channel = freq_params->sec_channel_offset;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch
deleted file mode 100644
index 6afdc0b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 9a82dbf6c16899eeb6b98178cc6e341f164acfa6 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 23 Aug 2023 17:44:50 +0800
-Subject: [PATCH 34/54] mtk: hostapd: update op_class when AP channel switching
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/drv_callbacks.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index e7f1f19ce..f749b33dc 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1041,7 +1041,7 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- {
- #ifdef NEED_AP_MLME
- int channel, chwidth, is_dfs0, is_dfs;
-- u8 seg0_idx = 0, seg1_idx = 0;
-+ u8 seg0_idx = 0, seg1_idx = 0, op_class, chan_no;
- size_t i;
-
- hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-@@ -1169,6 +1169,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- hapd->iconf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
-
- hapd->iconf->secondary_channel = offset;
-+ ieee80211_freq_to_channel_ext(freq, offset, chwidth,
-+ &op_class, &chan_no);
-+ hapd->iconf->op_class = op_class;
- hostapd_set_oper_chwidth(hapd->iconf, chwidth);
- hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
- hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
deleted file mode 100644
index 5d8edfd..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From aa65ab579fe3c032034b2ca641a91aa62285b59f Mon Sep 17 00:00:00 2001
-From: mtk23510 <rudra.shahi@mediatek.com>
-Date: Fri, 26 May 2023 14:52:35 +0800
-Subject: [PATCH 35/54] mtk: hostapd: Add support for gtk rekeying in hostapd
- cli
-
-Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
----
- hostapd/hostapd_cli.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 6d763f327..363a6bb93 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1256,6 +1256,15 @@ static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
- }
-
-
-+#ifdef CONFIG_TESTING_OPTIONS
-+static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return wpa_ctrl_command(ctrl, "REKEY_GTK");
-+}
-+#endif
-+
-+
- static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
- char cmd[256];
-@@ -1761,6 +1770,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "= disable hostapd on current interface" },
- { "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
- "= update Beacon frame contents\n"},
-+#ifdef CONFIG_TESTING_OPTIONS
-+ { "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
-+ "= rekey gtk\n"},
-+#endif
- { "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
- "= drop all ERP keys"},
- { "log_level", hostapd_cli_cmd_log_level, NULL,
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
deleted file mode 100644
index 7fb99bd..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From a8d74e867652c6feda7332ee2cb40611635179f0 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 11 Jul 2023 14:17:43 +0800
-Subject: [PATCH 36/54] mtk: hostapd: Set WMM and TX queue parameters for
- wpa_supplicant
-
-Since most of the time, wpa_supplicant will be used to setup an STA
-interface, it's default WMM and TX queue parameters should be set for
-STA.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/config.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index 2652c8a97..cd4025884 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -4673,19 +4673,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
- const struct hostapd_wmm_ac_params ac_bk =
- { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
- const struct hostapd_wmm_ac_params ac_be =
-- { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
-+ { aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
- const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
-- { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
-+ { aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
- const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
-- { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
-+ { aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
- const struct hostapd_tx_queue_params txq_bk =
- { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- const struct hostapd_tx_queue_params txq_be =
-- { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
-+ { 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- const struct hostapd_tx_queue_params txq_vi =
-- { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
-+ { 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
- const struct hostapd_tx_queue_params txq_vo =
-- { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
-+ { 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
- (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
-
- #undef ecw2cw
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
deleted file mode 100644
index aab9689..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From 7a02e5965652229833be92a9bd8f675e2015144a Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 7 Jul 2023 17:16:11 +0800
-Subject: [PATCH 37/54] mtk: hostapd: Set STA TX queue parameters configuration
- after association
-
-This patch adds the way for wpa_supplicant to set driver's TX queue
-parameters.
-Since STA parses and apply TX queue parameters from AP beacon's WMM IE
-during association, wpa_supplicant set driver's TX queue parameters
-after the association.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/driver_i.h | 12 ++++++++++++
- wpa_supplicant/events.c | 16 ++++++++++++++++
- 2 files changed, 28 insertions(+)
-
-diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
-index dcf576487..36ff854ad 100644
---- a/wpa_supplicant/driver_i.h
-+++ b/wpa_supplicant/driver_i.h
-@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
- return 0;
- }
-
-+static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
-+ int q, int aifs, int cw_min,
-+ int cw_max, int burst_time)
-+{
-+ int link_id = -1;
-+ if (wpa_s->driver->set_tx_queue_params)
-+ return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
-+ aifs, cw_min, cw_max,
-+ burst_time, link_id);
-+ return 0;
-+}
-+
- static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
- const u8 *data, size_t data_len, int noack,
- unsigned int freq, unsigned int wait)
-diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
-index 3b8596d6f..f5ac62eda 100644
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -3559,6 +3559,20 @@ out:
- return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
- }
-
-+static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
-+ struct hostapd_tx_queue_params *p;
-+
-+ for (int i = 0; i < NUM_TX_QUEUES; i++){
-+ p = &wpa_s->conf->tx_queue[i];
-+ if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
-+ p->cwmin, p->cwmax,
-+ p->burst)) {
-+ wpa_printf(MSG_DEBUG, "Failed to set TX queue "
-+ "parameters for queue %d.", i);
-+ /* Continue anyway */
-+ }
-+ }
-+}
-
- static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
-@@ -3886,6 +3900,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
-
- if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
- wpa_supplicant_set_4addr_mode(wpa_s);
-+
-+ wpa_supplicant_tx_queue_params(wpa_s);
- }
-
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
deleted file mode 100644
index 23ca582..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 695e63f81f817c302e4594e44b49e1f7a8c7969c Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 1 Sep 2023 15:31:24 +0800
-Subject: [PATCH 38/54] mtk: hostapd: avoid color switch when beacon is not set
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/hostapd.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index ec7677f6a..2b563a572 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4250,7 +4250,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
- {
- struct os_reltime now;
-
-- if (hapd->cca_in_progress)
-+ if (hapd->cca_in_progress || !hapd->beacon_set_done)
- return;
-
- if (os_get_reltime(&now))
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
deleted file mode 100644
index 0a5a04f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 4fe45b586ebb86cfbdd61ab91d5ebef110ce74aa Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Wed, 13 Sep 2023 19:29:51 +0800
-Subject: [PATCH 39/54] mtk: hostapd: 6g bss connect do not consider ht
- operation
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
- mode change 100755 => 100644 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100755
-new mode 100644
-index 7b6aabbff..38fce3e82
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5451,7 +5451,8 @@ static void handle_assoc(struct hostapd_data *hapd,
- ieee802_11_set_beacons(hapd->iface);
- }
-
-- update_ht_state(hapd, sta);
-+ if (!is_6ghz_op_class(hapd->iconf->op_class))
-+ update_ht_state(hapd, sta);
-
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch
deleted file mode 100644
index 7c3853f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-From 49b5d37f77fa48f215b3abc9c3ce74c26bbabefc Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 4 Oct 2023 11:12:52 +0800
-Subject: [PATCH 40/54] mtk: hostapd: avoid unnecessary beacon update for 6 GHz
- co-location
-
-There are two reasons to update beacon for 6 GHz co-location:
-1. 6 GHz out-of-band discovery
-2. MLD operational parameters update
-
-BSS load update is unrelated with the above two reasons, and therefore is
-not a case to update beacon for 6 GHz co-location.
-Moreover, updating beacon for 6 GHz co-location when BSS load update
-makes hostapd set beacon too frequently in a multiple BSSes case.
-
-Besides, it is also not necessary to update beacon for 6 GHz BSS when
-setting 2/5 GHz beacon. (i.e., no need for 2/5 GHz co-location)
-
-This patch adds an new function to update beacon only for current BSS,
-and uses the function duriong BSS load update.
-Also it changes the condition check to make beacon update only for 6 GHz
-co-location.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
-Signed-off-by: Money Wang <money.wang@mediatek.com>
----
- src/ap/beacon.c | 6 ++++++
- src/ap/beacon.h | 1 +
- src/ap/bss_load.c | 2 +-
- 3 files changed, 8 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index d160675cb..b74396a8d 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2216,6 +2216,12 @@ fail:
- }
-
-
-+void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
-+{
-+ __ieee802_11_set_beacon(hapd);
-+}
-+
-+
- int ieee802_11_set_beacon(struct hostapd_data *hapd)
- {
- struct hostapd_iface *iface = hapd->iface;
-diff --git a/src/ap/beacon.h b/src/ap/beacon.h
-index c320825f3..b32b2a7d0 100644
---- a/src/ap/beacon.h
-+++ b/src/ap/beacon.h
-@@ -15,6 +15,7 @@ struct ieee80211_mgmt;
- void handle_probe_req(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len,
- int ssi_signal);
-+void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd);
- int ieee802_11_set_beacon(struct hostapd_data *hapd);
- int ieee802_11_set_beacons(struct hostapd_iface *iface);
- int ieee802_11_update_beacons(struct hostapd_iface *iface);
-diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
-index 725d3cd34..e9baafc96 100644
---- a/src/ap/bss_load.c
-+++ b/src/ap/bss_load.c
-@@ -55,7 +55,7 @@ static void update_channel_utilization(void *eloop_data, void *user_data)
- return;
- }
-
-- ieee802_11_set_beacon(hapd);
-+ ieee802_11_set_beacon_per_bss_only(hapd);
-
- if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
- return;
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch
deleted file mode 100644
index d1e24d5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch
+++ /dev/null
@@ -1,168 +0,0 @@
-From 5daee94e29d5d7a3db5b8c8f03b15aa4a914f85f Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 24 Aug 2023 10:04:15 +0800
-Subject: [PATCH 41/54] mtk: hostapd: refactor the flow to create Wide
- Bandwidth Channel Switch IE
-
-This patch changes the flow to create Wide Bandwidth Channel Switch IE:
-1. 2 GHz: Wide Bandwidth Channel Switch IE should not present.
-2. 5 GHz: fill the subfields according to VHT operation.
-3. 6 GHz: fill the subfields according to VHT operation and HE operation
- in HE mode and EHT mode, respectively.
- This is because the definition of the subfields of Wide Bandwidth
- Channel Switch IE is ambiguous:
- 1. 802.11ac: the definition of subfields follows VHT operation
- (IEEE80211-2020 9.4.2.160)
- 2. 802.11ax: the definition of subfields is not specified
- 3. 802.11be: the definition of subfields follows VHT operation in 5
- GHz and HE operation in 6 GHz (IEEE P802.11be D3.2 9.4.2.159)
-
-To support 320 MHz
- channel switch, set width to 4
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ieee802_11.c | 99 ++++++++++++++++++++++++++++++++++-----------
- 1 file changed, 76 insertions(+), 23 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 38fce3e82..d46c5a42b 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7083,57 +7083,110 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
-
- u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
- {
-- u8 bw, chan1, chan2 = 0;
-- int freq1;
-+ u8 new_bw_field, ccfs0_chan, ccfs1_chan = 0;
-+ int ccfs0_freq = 0, ccfs1_freq = 0;
-+ int control_freq, center_freq1, center_freq2, bandwidth;
-+ int base_freq, offset;
-+ bool is_6ghz, use_he_oper;
-
- if (!hapd->cs_freq_params.channel ||
-+ hapd->cs_freq_params.bandwidth == 20 ||
- (!hapd->cs_freq_params.vht_enabled &&
- !hapd->cs_freq_params.he_enabled &&
- !hapd->cs_freq_params.eht_enabled))
- return eid;
-
-- /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80, 4: 320 */
-- switch (hapd->cs_freq_params.bandwidth) {
-+ control_freq = hapd->cs_freq_params.freq;
-+ center_freq1 = hapd->cs_freq_params.center_freq1;
-+ center_freq2 = hapd->cs_freq_params.center_freq2;
-+ bandwidth = hapd->cs_freq_params.bandwidth;
-+
-+ /* center_freq2 is used if and only if bandwidth is
-+ * 80+80 MHz and phy mode is not EHT
-+ */
-+ if (center_freq2 &&
-+ (bandwidth != 80 || hapd->cs_freq_params.eht_enabled))
-+ return eid;
-+
-+ is_6ghz = is_6ghz_freq(control_freq);
-+ use_he_oper = is_6ghz && hapd->cs_freq_params.eht_enabled;
-+ base_freq = is_6ghz ? 5955 : 5180;
-+
-+ /* About the subfields of the Wide Bandwidth Channel Switch IE,
-+ * IEEE802.11-2020 9.4.2.160 specifies that the subfields New
-+ * Channel Width, New Channel Center Frequency Segment 0 and New
-+ * Channel Center Frequency Segment 1 have the same definition as
-+ * they are in the VHT operation information field.
-+ * However, the standard does not specify the definition of these
-+ * subfields when it comes to HE phy-mode in 6 GHz.
-+ * And in IEEE P802.11be D3.2 9.4.2.159, it specifies that the
-+ * defition should follow VHT operation in 5 GHz, and follow HE
-+ * oepration in 6 GHz.
-+ * Problem happens here for some HE STAs in 6 GHz, they might still
-+ * use VHT operation to parse these subfields.
-+ *
-+ * Here we follow the new Standard to build the IE, meanwhile we have
-+ * a workaround for HE mode in 6 GHz.
-+ *
-+ * 5 GHz: VHT operation
-+ * HE mode in 6 GHz: VHT operation
-+ * EHT mode in 6 GHz: HE operation
-+ */
-+ ccfs0_freq = center_freq1;
-+ ccfs1_freq = center_freq2;
-+ switch (bandwidth) {
- case 40:
-- bw = 0;
-+ new_bw_field = use_he_oper ? 1 : 0;
- break;
- case 80:
-- /* check if it's 80+80 */
-- if (!hapd->cs_freq_params.center_freq2)
-- bw = 1;
-+ if (ccfs1_freq)
-+ new_bw_field = use_he_oper ? 3 : 1;
- else
-- bw = 3;
-+ new_bw_field = use_he_oper ? 2 : 1;
- break;
- case 160:
-- bw = 2;
-+ new_bw_field = use_he_oper ? 3 : 1;
-+
-+ /* ccfs0 is primary 80 MHz
-+ * ccfs1 is center frequency
-+ */
-+ offset = (control_freq - base_freq) / 20;
-+ ccfs0_freq = control_freq + 30 - (offset & 3) * 20;
-+ ccfs1_freq = center_freq1;
- break;
- case 320:
-- bw = 4;
-+ /* TODO switch to bandwidth 320 MHz should be
-+ * indicated by Bandwidth indication IE.
-+ */
-+ new_bw_field = 4;
-+
-+ /* ccfs0 is primary 160 MHz
-+ * ccfs1 is center frequency
-+ */
-+ offset = (control_freq - base_freq) / 20;
-+ ccfs0_freq = control_freq + 70 - (offset & 7) * 20;
-+ ccfs1_freq = center_freq1;
- break;
- default:
-- /* not valid VHT bandwidth or not in CSA */
-+ /* not a valid VHT/HE bandwidth or not in CSA */
- return eid;
- }
-
-- freq1 = hapd->cs_freq_params.center_freq1 ?
-- hapd->cs_freq_params.center_freq1 :
-- hapd->cs_freq_params.freq;
-- if (ieee80211_freq_to_chan(freq1, &chan1) !=
-- HOSTAPD_MODE_IEEE80211A)
-+ if (ieee80211_freq_to_chan(ccfs0_freq, &ccfs0_chan) !=
-+ HOSTAPD_MODE_IEEE80211A)
- return eid;
-
-- if (hapd->cs_freq_params.center_freq2 &&
-- ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
-- &chan2) != HOSTAPD_MODE_IEEE80211A)
-+ if (ccfs1_freq && ieee80211_freq_to_chan(ccfs1_freq, &ccfs1_chan) !=
-+ HOSTAPD_MODE_IEEE80211A)
- return eid;
-
- *eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
- *eid++ = 5; /* Length of Channel Switch Wrapper */
- *eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
- *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
-- *eid++ = bw; /* New Channel Width */
-- *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
-- *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
-+ *eid++ = new_bw_field; /* New Channel Width */
-+ *eid++ = ccfs0_chan; /* New Channel Center Frequency Segment 0 */
-+ *eid++ = ccfs1_chan; /* New Channel Center Frequency Segment 1 */
-
- return eid;
- }
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
deleted file mode 100644
index 6508476..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From 5e51c981aa0540864803b5f5b68e715a8d98a338 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Sun, 8 Oct 2023 11:50:06 +0800
-Subject: [PATCH 42/54] mtk: hostapd: Add ACS chanlist info in get_config
-
-This patch is used to add ACS chanlist info displaying
-for upper layer application obtaining.
-
-Command format:
-hostapd_cli -i phy0-ap0 get_config
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 59 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ae61cf625..aba22e1e6 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1048,6 +1048,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- {
- int ret;
- char *pos, *end;
-+ int i;
-
- pos = buf;
- end = buf + buflen;
-@@ -1227,6 +1228,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- pos += ret;
- }
-
-+ /* dump chanlist */
-+ if (hapd->iface->conf->acs_ch_list.num > 0) {
-+ ret = os_snprintf(pos, end - pos, "chanlist=");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
-+ if (i > 0) {
-+ ret = os_snprintf(pos, end - pos, ", ");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "%d-%d",
-+ hapd->iface->conf->acs_ch_list.range[i].min,
-+ hapd->iface->conf->acs_ch_list.range[i].max);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "\n");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ /* dump freqlist */
-+ if (hapd->iface->conf->acs_freq_list.num > 0) {
-+ ret = os_snprintf(pos, end - pos, "freqlist=");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+
-+ for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
-+ if (i > 0) {
-+ ret = os_snprintf(pos, end - pos, ", ");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "%d-%d",
-+ hapd->iface->conf->acs_freq_list.range[i].min,
-+ hapd->iface->conf->acs_freq_list.range[i].max);
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
-+ ret = os_snprintf(pos, end - pos, "\n");
-+ if (os_snprintf_error(end - pos, ret))
-+ return pos - buf;
-+ pos += ret;
-+ }
-+
- return pos - buf;
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
deleted file mode 100644
index aae2359..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From c21e12bb63b9c3906143ffb368297e3b6a155eb3 Mon Sep 17 00:00:00 2001
-From: mtk25255 <rohit.kamat@mediatek.com>
-Date: Thu, 12 Oct 2023 14:29:23 +0800
-Subject: [PATCH 43/54] mtk: hostapd: Fix RSNXE Interop issue with STA
-
----
- src/ap/ieee802_11.c | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index d46c5a42b..1db66a783 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5181,6 +5181,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- int delay_assoc = 0;
- #endif /* CONFIG_FILS */
- int omit_rsnxe = 0;
-+ bool sae_pk = false;
-
- if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
- sizeof(mgmt->u.assoc_req))) {
-@@ -5399,7 +5400,17 @@ static void handle_assoc(struct hostapd_data *hapd,
- if (resp != WLAN_STATUS_SUCCESS)
- goto fail;
- omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
--
-+#ifdef CONFIG_SAE_PK
-+ sae_pk = hostapd_sae_pk_in_use(hapd->conf);
-+#endif /* CONFIG_SAE_PK */
-+ if (omit_rsnxe) {
-+ if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
-+ (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-+ hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
-+ wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
-+ omit_rsnxe = 0;
-+ }
-+ }
- if (hostapd_get_aid(hapd, sta) < 0) {
- hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "No room for more AIDs");
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
deleted file mode 100644
index 2816120..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From a56c6b0ea29d278bd7ec67e45c298eac8526a055 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Wed, 1 Nov 2023 19:58:05 +0800
-Subject: [PATCH 44/54] mtk: hostapd: 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.
-
-Solution:
-In the hostapd_switch_channel_fallback, need to disable acs if channel
-is valid.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/hostapd.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 2b563a572..f01f607f3 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4121,6 +4121,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
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0045-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0045-mtk-hostapd-update-eht-operation-element.patch
deleted file mode 100644
index 5b1c065..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0045-mtk-hostapd-update-eht-operation-element.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 9d81aac8e0c2a6e1aa968581a9c77cec7468cd60 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 10 May 2023 13:11:34 +0800
-Subject: [PATCH 45/54] mtk: hostapd: update eht operation element
-
----
- src/ap/ieee802_11_eht.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
-index 9a07f7501..f132e1d9c 100644
---- a/src/ap/ieee802_11_eht.c
-+++ b/src/ap/ieee802_11_eht.c
-@@ -215,9 +215,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
-
- /* TODO: Fill in appropriate EHT-MCS max Nss information */
- oper->basic_eht_mcs_nss_set[0] = 0x11;
-- oper->basic_eht_mcs_nss_set[1] = 0x00;
-- oper->basic_eht_mcs_nss_set[2] = 0x00;
-- oper->basic_eht_mcs_nss_set[3] = 0x00;
-+ oper->basic_eht_mcs_nss_set[1] = 0x11;
-+ oper->basic_eht_mcs_nss_set[2] = 0x11;
-+ oper->basic_eht_mcs_nss_set[3] = 0x11;
-
- if (is_6ghz_op_class(conf->op_class))
- chwidth = op_class_to_ch_width(conf->op_class);
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
deleted file mode 100644
index be86061..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From c73597bd17673ac6f33a314458cce14009a15fb0 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 30 Aug 2023 04:23:37 +0800
-Subject: [PATCH 46/54] mtk: hostapd: ucode: add support for ucode to parse
- BW320MHz info
-
----
- src/utils/ucode.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 29c753c32..4b6ed3a94 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- case 2:
- chanwidth = CONF_OPER_CHWIDTH_160MHZ;
- break;
-+ case 9:
-+ width = 3;
-+ chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+ break;
- default:
- return NULL;
- }
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
deleted file mode 100644
index c508d77..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-From 9238758c9fb0efb9b979eacef7d09914332186af Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 11 Sep 2023 10:16:35 +0800
-Subject: [PATCH 47/54] mtk: hostapd: synchronize bandwidth in AP/STA support
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c | 41 +++++++++++++++++++--
- src/utils/ucode.c | 12 +++++--
- wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
- 3 files changed, 117 insertions(+), 18 deletions(-)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 16d1b5153..98b2a3bf2 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -489,6 +489,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
- int i;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
-+ iface->phy, hostapd_state_text(iface->state));
-+
- if (!iface)
- return NULL;
-
-@@ -515,6 +518,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- uint64_t intval;
- int i;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
-+ iface->phy, hostapd_state_text(iface->state));
-+
- if (!iface)
- return NULL;
-
-@@ -537,7 +543,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- UPDATE_VAL(op_class, "op_class");
- UPDATE_VAL(hw_mode, "hw_mode");
- UPDATE_VAL(channel, "channel");
-- UPDATE_VAL(secondary_channel, "sec_channel");
-+
-+ intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
-+ if (!errno) {
-+ conf->secondary_channel = intval;
-+ changed = true;
-+ }
-+
- if (!changed &&
- (iface->bss[0]->beacon_set_done ||
- iface->state == HAPD_IFACE_DFS))
-@@ -583,6 +595,18 @@ out:
- return ucv_boolean_new(true);
- }
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
-+ wpa_printf(MSG_INFO, " * channel: %d\n", conf->channel);
-+ wpa_printf(MSG_INFO, " * op_class: %d\n", conf->op_class);
-+ wpa_printf(MSG_INFO, " * secondary channel: %d\n",
-+ conf->secondary_channel);
-+ wpa_printf(MSG_INFO, " * seg0: %d\n",
-+ hostapd_get_oper_centr_freq_seg0_idx(conf));
-+ wpa_printf(MSG_INFO, " * seg1: %d\n",
-+ hostapd_get_oper_centr_freq_seg0_idx(conf));
-+ wpa_printf(MSG_INFO, " * oper_chwidth: %d\n",
-+ hostapd_get_oper_chwidth(conf));
-+
- for (i = 0; i < iface->num_bss; i++) {
- struct hostapd_data *hapd = iface->bss[i];
- int ret;
-@@ -617,6 +641,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- uint64_t intval;
- int i, ret = 0;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
- if (!iface || ucv_type(info) != UC_OBJECT)
- return NULL;
-
-@@ -636,7 +661,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- if (errno)
- intval = hostapd_get_oper_chwidth(conf);
- if (intval)
-- csa.freq_params.bandwidth = 40 << intval;
-+ csa.freq_params.bandwidth = 40 <<
-+ (intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
- else
- csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
-
-@@ -647,6 +673,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
- csa.freq_params.center_freq2 = intval;
-
-+ wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
-+ wpa_printf(MSG_INFO, " * freq is %d\n", csa.freq_params.freq);
-+ wpa_printf(MSG_INFO, " * bandwidth is %d\n",
-+ csa.freq_params.bandwidth);
-+ wpa_printf(MSG_INFO, " * sec_chan_offset is %d\n",
-+ csa.freq_params.sec_channel_offset);
-+ wpa_printf(MSG_INFO, " * center_freq1 is %d\n",
-+ csa.freq_params.center_freq1);
-+ wpa_printf(MSG_INFO, " * center_freq2 is %d\n",
-+ csa.freq_params.center_freq2);
-+
- for (i = 0; i < iface->num_bss; i++)
- ret = hostapd_switch_channel(iface->bss[i], &csa);
-
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 4b6ed3a94..6f82382f3 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- uc_value_t *freq = uc_fn_arg(0);
- uc_value_t *sec = uc_fn_arg(1);
- int width = ucv_uint64_get(uc_fn_arg(2));
-+ int bw320_offset = 1;
- int freq_val, center_idx, center_ofs;
- enum oper_chan_width chanwidth;
- enum hostapd_hw_mode hw_mode;
-@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- case 9:
- width = 3;
- chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+
-+ /* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
-+ bw320_offset = ucv_uint64_get(uc_fn_arg(3));
- break;
- default:
- return NULL;
-@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
- ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
- ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
-+ ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
-
-- if (!sec_channel)
-+ if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
-+ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
-+ ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
- return ret;
-+ }
-
- if (freq_val >= 5900)
-- center_ofs = 0;
-+ center_ofs = 32 * (1 - bw320_offset);
- else if (freq_val >= 5745)
- center_ofs = 20;
- else
-diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-index 397f85bde..542ca25c9 100644
---- a/wpa_supplicant/ucode.c
-+++ b/wpa_supplicant/ucode.c
-@@ -7,6 +7,8 @@
- #include "wps_supplicant.h"
- #include "bss.h"
- #include "ucode.h"
-+#include "driver_i.h"
-+#include "common/ieee802_11_common.h"
-
- static struct wpa_global *wpa_global;
- static uc_resource_type_t *global_type, *iface_type;
-@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- {
- const char *state;
- uc_value_t *val;
-+ enum oper_chan_width ch_width;
-+ int center_freq1, bw320_offset = 1;
-
- if (event != EVENT_CH_SWITCH_STARTED)
- return;
-@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- uc_value_push(ucv_get(val));
-
- if (event == EVENT_CH_SWITCH_STARTED) {
-+ center_freq1 = data->ch_switch.cf1;
-+
-+ switch (data->ch_switch.ch_width) {
-+ case CHAN_WIDTH_80:
-+ ch_width = CONF_OPER_CHWIDTH_80MHZ;
-+ break;
-+ case CHAN_WIDTH_80P80:
-+ ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
-+ break;
-+ case CHAN_WIDTH_160:
-+ ch_width = CONF_OPER_CHWIDTH_160MHZ;
-+ break;
-+ case CHAN_WIDTH_320:
-+ ch_width = CONF_OPER_CHWIDTH_320MHZ;
-+ break;
-+ case CHAN_WIDTH_20_NOHT:
-+ case CHAN_WIDTH_20:
-+ case CHAN_WIDTH_40:
-+ default:
-+ ch_width = CONF_OPER_CHWIDTH_USE_HT;
-+ break;
-+ }
-+
-+ /* Check bandwidth 320 MHz-2 */
-+ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+ (center_freq1 == 6265) || center_freq1 == 6585 ||
-+ center_freq1 == 6905)
-+ bw320_offset = 2;
-+
- ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
- ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
- ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
-- ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
-+ ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
- ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
-+ ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
-+ ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
- }
-
- ucv_put(wpa_ucode_call(4));
-@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
- struct wpa_bss *bss;
- uc_value_t *ret, *val;
-+ struct wpa_channel_info ci;
-+ u8 op_class, channel;
-+ enum oper_chan_width ch_width;
-+ int center_freq1, bw320_offset = 1, is_24ghz;
-+ enum hostapd_hw_mode hw_mode;
-
- if (!wpa_s)
- return NULL;
-@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- bss = wpa_s->current_bss;
- if (bss) {
- int sec_chan = 0;
-- const u8 *ie;
--
-- ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
-- if (ie && ie[1] >= 2) {
-- const struct ieee80211_ht_operation *ht_oper;
-- int sec;
--
-- ht_oper = (const void *) (ie + 2);
-- sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-- if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-- sec_chan = 1;
-- else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-- sec_chan = -1;
-+
-+ hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
-+ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
-+ hw_mode == HOSTAPD_MODE_IEEE80211B;
-+
-+ wpa_drv_channel_info(wpa_s, &ci);
-+ center_freq1 = ci.center_frq1;
-+
-+ if (bss->freq != center_freq1) {
-+ if (is_24ghz)
-+ sec_chan = (bss->freq < center_freq1) ? 1 : -1;
-+ else
-+ sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
-+ }
-+
-+ if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
-+ sec_chan, &op_class, &channel))
-+ return NULL;
-+
-+ ch_width = op_class_to_ch_width(op_class);
-+ if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+ (center_freq1 == 6265) || center_freq1 == 6585 ||
-+ center_freq1 == 6905) {
-+ /* Bandwidth 320 MHz-2 */
-+ bw320_offset = 2;
- }
-
- ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
- ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
-+ ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
-+ ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
- }
-
- #ifdef CONFIG_MESH
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch
deleted file mode 100644
index ab4ff5b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch
+++ /dev/null
@@ -1,339 +0,0 @@
-From 8590ccd17827862e02b9f40ccca29a03dc3e0ab6 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:25:01 +0800
-Subject: [PATCH 48/54] mtk: hostapd: Add support for updating background
- channel by driver
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 107 ++++++++++++++++++++++++++++-
- src/ap/dfs.h | 3 +
- src/ap/drv_callbacks.c | 22 ++++++
- src/ap/hostapd.h | 5 ++
- src/drivers/driver.h | 12 ++++
- src/drivers/driver_nl80211_event.c | 6 ++
- src/drivers/nl80211_copy.h | 6 ++
- 7 files changed, 160 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 95119a391..008596bb2 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -814,11 +814,14 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
-
- static void dfs_check_background_overlapped(struct hostapd_iface *iface)
- {
-- int width = hostapd_get_oper_chwidth(iface->conf);
-+ int width = iface->radar_background.new_chwidth;
-
- if (!dfs_use_radar_background(iface))
- return;
-
-+ if (!width)
-+ width = hostapd_get_oper_chwidth(iface->conf);
-+
- if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
- width, iface->radar_background.centr_freq_seg0_idx,
- iface->radar_background.centr_freq_seg1_idx))
-@@ -983,6 +986,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- iface->radar_background.temp_ch = 1;
- return 1;
- } else if (dfs_use_radar_background(iface)) {
-+ /*
-+ * AP is going to perform CAC, so reset temp_ch to 0,
-+ * when dedicated rx has already started CAC.
-+ */
-+ if (iface->radar_background.cac_started) {
-+ iface->radar_background.temp_ch = 0;
-+ return 0;
-+ }
-+
- if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
- channel_type = DFS_ANY_CHANNEL;
-
-@@ -1116,6 +1128,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
- * ch_switch_notify event is received */
- wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
-
-+ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
-+
- return 0;
- }
-
-@@ -1167,6 +1181,9 @@ static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
- iface->radar_background.secondary_channel = sec;
- iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
- iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+ /* if main channel do not require dfs, then set temp_ch = 1 */
-+ if (!hostapd_is_dfs_required(iface))
-+ iface->radar_background.temp_ch = 1;
-
- wpa_printf(MSG_DEBUG,
- "%s: setting background chain to chan %d (%d MHz)",
-@@ -1189,6 +1206,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
- int ret;
-
-+ if (iface->radar_background.new_chwidth) {
-+ hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
-+ iface->radar_background.new_chwidth = 0;
-+ }
- ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
- iface->radar_background.freq,
- iface->radar_background.secondary_channel,
-@@ -1211,6 +1232,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- }
-
-
-+static void
-+hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
-+{
-+ struct hostapd_hw_modes *mode = iface->current_mode;
-+ struct hostapd_channel_data *chan;
-+ int i, channel, width = channel_width_to_int(chan_width);
-+
-+ if (iface->conf->channel - iface->radar_background.channel == width / 5)
-+ channel = iface->radar_background.channel;
-+ else if (iface->radar_background.channel - iface->conf->channel == width / 5)
-+ channel = iface->conf->channel;
-+ else
-+ return;
-+
-+ for (i = 0; i < mode->num_channels; i++) {
-+ chan = &mode->channels[i];
-+ if (chan->chan == channel)
-+ break;
-+ }
-+
-+ if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
-+ return;
-+
-+ switch (chan_width) {
-+ case CHAN_WIDTH_20_NOHT:
-+ case CHAN_WIDTH_20:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+ break;
-+ case CHAN_WIDTH_40:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+ break;
-+ case CHAN_WIDTH_80:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+ break;
-+ default:
-+ return;
-+ }
-+
-+ iface->radar_background.freq = channel * 5 + 5000;
-+ iface->radar_background.channel = channel;
-+ iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
-+ iface->radar_background.secondary_channel = 1;
-+ iface->radar_background.expand_ch = 0;
-+}
-+
-+
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- int ht_enabled, int chan_offset, int chan_width,
- int cf1, int cf2)
-@@ -1244,6 +1311,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- return 0;
-
- iface->radar_background.temp_ch = 0;
-+
-+ if (iface->radar_background.expand_ch)
-+ hostapd_dfs_background_expand(iface, chan_width);
-+
- return hostapd_dfs_start_channel_switch_background(iface);
- }
-
-@@ -1274,6 +1345,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- }
- } else if (hostapd_dfs_is_background_event(iface, freq)) {
- iface->radar_background.cac_started = 0;
-+ iface->radar_background.temp_ch = 0;
-+ iface->radar_background.expand_ch = 0;
- hostpad_dfs_update_background_chain(iface);
- }
-
-@@ -1406,6 +1479,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
- return 0;
-
-+ iface->radar_background.temp_ch = 0;
-+ iface->radar_background.expand_ch = 0;
-+
- /* Check if CSA in progress */
- if (hostapd_csa_in_progress(iface))
- return 0;
-@@ -1640,6 +1716,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- }
-
-
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, bool expand)
-+{
-+ switch (chan_width) {
-+ case CHAN_WIDTH_80:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+ break;
-+ case CHAN_WIDTH_160:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+ break;
-+ default:
-+ iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+ break;
-+ };
-+
-+ iface->radar_background.freq = freq;
-+ iface->radar_background.channel = (freq - 5000) / 5;
-+ iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
-+ iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
-+ if (expand) {
-+ iface->radar_background.temp_ch = 1;
-+ iface->radar_background.expand_ch = 1;
-+ }
-+
-+ return 0;
-+}
-+
-+
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- int ht_enabled, int chan_offset, int chan_width,
- int cf1, int cf2)
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 25ba29ca1..a1a2be5ec 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- int ht_enabled,
- int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+ int ht_enabled, int chan_offset, int chan_width,
-+ int cf1, int cf2, bool expand);
- int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
- int ht_enabled, int chan_offset, int chan_width,
- int cf1, int cf2, u32 state);
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index f749b33dc..12419c6d4 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2089,6 +2089,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- radar->cf1, radar->cf2);
- }
-
-+
-+static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
-+ struct dfs_event *radar, bool expand)
-+{
-+ wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
-+ expand ? "expand" : "update", radar->freq);
-+ hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
-+ radar->chan_offset, radar->chan_width,
-+ radar->cf1, radar->cf2, expand);
-+}
-+
-+
- static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
- struct dfs_event *radar)
- {
-@@ -2428,6 +2440,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- break;
- hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- break;
-+ case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
-+ break;
-+ case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
-+ if (!data)
-+ break;
-+ hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
-+ break;
- case EVENT_DFS_STA_CAC_SKIPPED:
- if (!data)
- break;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 1849f38d8..ea8d7254a 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -602,6 +602,11 @@ struct hostapd_iface {
- unsigned int temp_ch:1;
- /* CAC started on radar offchain */
- unsigned int cac_started:1;
-+ /* Main chain should expand its width according to the
-+ * current offchain channel after CAC detection on radar offchain.
-+ */
-+ unsigned int expand_ch:1;
-+ int new_chwidth;
- } radar_background;
-
- u16 hw_flags;
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 8da93e025..b0a6e3514 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5845,6 +5845,18 @@ enum wpa_event_type {
- * The channel in the notification is now marked as usable.
- */
- EVENT_DFS_STA_CAC_EXPIRED,
-+
-+ /**
-+ * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
-+ * channel has been updated.
-+ */
-+ EVENT_DFS_BACKGROUND_CHAN_UPDATE,
-+
-+ /**
-+ * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
-+ * channel has been updated and operating channel should expand its width.
-+ */
-+ EVENT_DFS_BACKGROUND_CHAN_EXPAND,
- };
-
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 63d44017c..c1a65eb4e 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2514,6 +2514,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- case NL80211_RADAR_CAC_STARTED:
- wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- break;
-+ case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
-+ break;
-+ case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
-+ wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
-+ break;
- case NL80211_RADAR_STA_CAC_SKIPPED:
- wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
- break;
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index 225864b94..9b0a8171a 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6643,6 +6643,10 @@ enum nl80211_smps_mode {
- * applicable for ETSI dfs domain where pre-CAC is valid for ever.
- * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
- * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
-+ * driver.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
-+ * driver and required to expand main operating channel.
- * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
- * when receiving CSA/assoc resp
- * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-@@ -6655,6 +6659,8 @@ enum nl80211_radar_event {
- NL80211_RADAR_NOP_FINISHED,
- NL80211_RADAR_PRE_CAC_EXPIRED,
- NL80211_RADAR_CAC_STARTED,
-+ NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
-+ NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
- NL80211_RADAR_STA_CAC_SKIPPED,
- NL80211_RADAR_STA_CAC_EXPIRED,
- };
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
deleted file mode 100644
index 623e3fc..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
+++ /dev/null
@@ -1,277 +0,0 @@
-From e878e546ee5d1e86f174b2713b7a46fb05b6a7b9 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 2 Aug 2023 19:00:34 +0800
-Subject: [PATCH 49/54] mtk: hostapd: add zwdfs mode ctrl for eagle efem hwits
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- hostapd/ctrl_iface.c | 30 +++++++++++++++++++++++++++
- src/ap/ap_config.h | 6 ++++++
- src/ap/ap_drv_ops.c | 14 +++++++++++++
- src/ap/dfs.c | 6 ++++++
- src/common/mtk_vendor.h | 12 +++++++++++
- src/drivers/driver.h | 7 +++++++
- src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +++
- 10 files changed, 115 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 9e3dbb24a..a75199345 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3183,6 +3183,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- conf->acs_exclude_6ghz_non_psc = atoi(pos);
- } else if (os_strcmp(buf, "enable_background_radar") == 0) {
- conf->enable_background_radar = atoi(pos);
-+ } else if (os_strcmp(buf, "background_radar_mode") == 0) {
-+ conf->background_radar_mode = atoi(pos);
- } else if (os_strcmp(buf, "min_tx_power") == 0) {
- int val = atoi(pos);
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index aba22e1e6..f7c7a09f3 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4221,6 +4221,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
- return pos - buf;
- }
-
-+static int
-+hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
-+ char *buf, size_t buflen)
-+{
-+ struct hostapd_iface *iface = hapd->iface;
-+ char *pos, *param;
-+
-+ param = os_strchr(cmd, ' ');
-+ if (!param)
-+ return -1;
-+ *param++ = '\0';
-+
-+ pos = os_strstr(param, "mode=");
-+ if (!pos)
-+ return -1;
-+
-+ if (os_strncmp(pos + 5, "cert", 4) == 0)
-+ iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
-+ else if (os_strncmp(pos + 5, "normal", 6) == 0)
-+ iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
-+
-+ if (hostapd_drv_background_radar_mode(hapd) < 0)
-+ return -1;
-+
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4815,6 +4842,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- if (pos)
- *pos = ' ';
- reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
-+ } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
-+ reply, reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 7c0d12a3b..b6f05e7af 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1025,6 +1025,7 @@ struct hostapd_config {
- bool hw_mode_set;
- int acs_exclude_6ghz_non_psc;
- int enable_background_radar;
-+ int background_radar_mode;
- enum {
- LONG_PREAMBLE = 0,
- SHORT_PREAMBLE = 1
-@@ -1218,6 +1219,11 @@ enum three_wire_mode {
- NUM_THREE_WIRE_MODE - 1
- };
-
-+enum background_radar_mode {
-+ BACKGROUND_RADAR_NORMAL_MODE,
-+ BACKGROUND_RADAR_CERT_MODE,
-+};
-+
- enum dfs_mode {
- DFS_DETECT_MODE_DISABLE,
- DFS_DETECT_MODE_AP_ENABLE,
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 721bfa053..5b93ea647 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1257,3 +1257,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
- return 0;
- return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
- }
-+
-+int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->background_radar_mode ||
-+ !(hapd->iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND) ||
-+ !hapd->iface->conf->enable_background_radar)
-+ return 0;
-+ if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
-+ wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
-+ return 0;
-+ }
-+ return hapd->driver->background_radar_mode(hapd->drv_priv,
-+ hapd->iconf->background_radar_mode);
-+}
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 008596bb2..2564168bb 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -983,6 +983,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- if (res < 0)
- return res;
-
-+ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+ return -1;
-+
- iface->radar_background.temp_ch = 1;
- return 1;
- } else if (dfs_use_radar_background(iface)) {
-@@ -1023,6 +1026,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- iface->radar_background.secondary_channel = sec;
- iface->radar_background.centr_freq_seg0_idx = cf1;
- iface->radar_background.centr_freq_seg1_idx = cf2;
-+
-+ if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+ return -1;
- }
-
- return 0;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e140de60b..5bc1e0444 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
-+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -244,6 +245,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
- NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
- };
-
-+enum mtk_vendor_attr_background_radar_ctrl {
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
-+ MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index b0a6e3514..0c1b2e452 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5204,6 +5204,13 @@ struct wpa_driver_ops {
- * @amnt_dump_buf: Buffer to print
- */
- int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
-+
-+ /**
-+ * background_radar_mode - set background radar mode
-+ * @priv: Private driver interface data
-+ * @background_radar_mode: background radar mode
-+ */
-+ int (*background_radar_mode)(void *priv, u8 background_radar_mode);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index b3897e61d..3cbf8610a 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14743,6 +14743,39 @@ fail:
- return -ENOBUFS;
- }
-
-+static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ /* Prepare nl80211 cmd */
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_background_radar_vendor_cmd_avail) {
-+ wpa_printf(MSG_INFO,
-+ "nl80211: Driver does not support setting background radar mode");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+ if (ret) {
-+ wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
-+ ret, strerror(-ret));
-+ }
-+ return ret;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14915,4 +14948,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .ap_trigtype = nl80211_ap_trigtype,
- .amnt_set = nl80211_amnt_set,
- .amnt_dump = nl80211_amnt_dump,
-+ .background_radar_mode = nl80211_background_radar_mode,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 0d85adfee..74ee9b191 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_bss_color_vendor_cmd_avail:1;
- unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- unsigned int mtk_amnt_vendor_cmd_avail:1;
-+ unsigned int mtk_background_radar_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 4bd15f348..74c3e0d86 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1137,6 +1137,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- drv->mtk_rfeatures_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
-+ drv->mtk_background_radar_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
deleted file mode 100644
index fc9a02d..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
+++ /dev/null
@@ -1,374 +0,0 @@
-From d0f78e1b2efd0d0842c98a8733b06a7b59a9bd07 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 21 Sep 2023 10:29:46 +0800
-Subject: [PATCH 50/54] mtk: hostapd: add support enable/disable preamble
- puncture from mtk vendor command
-
-This commit supports two ways to enable/disable preamble puncture
-feature.
-
-1. Add new hostapd configuration "pp_mode". The possible value could be
-1 to 3. When the value is 0, it means that the firmware will turn off
-the pp algorithm. When the value is 1, it means that the firmware will
-enable the pp algorithm, allowing the algorithm to determine whether pp
-could be applied on each txcmd. When the value is 2, it means that pp
-feature is manually configured by the user. Please noted that for
-current implementation, the default configuration is 0.
-
-2. $ hostapd_cli -i <intf_name> raw set_pp mode val
-The argument "val" could be 0 for PP feature disabled or 1 to configure
-PP feature as auto mode.
-
-This commit also let user check whether pp feature is enabled by
-hostapd_cli command. The usage shows as below:
-$ hostapd_cli -i <intf_name> raw get_pp mode
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c | 11 ++++++
- hostapd/ctrl_iface.c | 59 +++++++++++++++++++++++++++++++
- src/ap/ap_config.c | 1 +
- src/ap/ap_config.h | 7 ++++
- src/ap/ap_drv_ops.c | 9 +++++
- src/ap/ap_drv_ops.h | 1 +
- src/ap/hostapd.c | 2 ++
- src/common/mtk_vendor.h | 12 +++++++
- src/drivers/driver.h | 6 ++++
- src/drivers/driver_nl80211.c | 49 +++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 ++
- 12 files changed, 161 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index a75199345..278f6b347 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4801,6 +4801,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- conf->eht_phy_capab.mu_beamformer = atoi(pos);
- } else if (os_strcmp(buf, "punct_bitmap") == 0) {
- conf->punct_bitmap = atoi(pos);
-+ conf->pp_mode = PP_MANUAL_MODE;
- } else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
- int val = atoi(pos);
-
-@@ -4876,6 +4877,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- return 1;
- }
- conf->amsdu = val;
-+ } else if (os_strcmp(buf, "pp_mode") == 0) {
-+ int val = atoi(pos);
-+
-+ if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
-+ val < PP_DISABLE || val > PP_MANUAL_MODE) {
-+ wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
-+ line);
-+ return 1;
-+ }
-+ conf->pp_mode = (u8) val;
- } else {
- wpa_printf(MSG_ERROR,
- "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index f7c7a09f3..64bab0228 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4248,6 +4248,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
- return os_snprintf(buf, buflen, "OK\n");
- }
-
-+static int
-+hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+ size_t buflen)
-+{
-+ char *pos, *config, *value;
-+
-+ config = cmd;
-+ pos = os_strchr(config, ' ');
-+ if (pos == NULL)
-+ return -1;
-+ *pos++ = '\0';
-+
-+ if (pos == NULL)
-+ return -1;
-+ value = pos;
-+
-+ if (os_strcmp(config, "mode") == 0) {
-+ int val = atoi(value);
-+
-+ if (val < PP_DISABLE || val > PP_AUTO_MODE) {
-+ wpa_printf(MSG_ERROR, "Invalid value for set_pp");
-+ return -1;
-+ }
-+ hapd->iconf->pp_mode = (u8) val;
-+ if (hostapd_drv_pp_mode_set(hapd) != 0)
-+ return -1;
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for set_pp", config);
-+ return -1;
-+ }
-+ return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+ size_t buflen)
-+{
-+ char *pos, *end;
-+
-+ pos = buf;
-+ end = buf + buflen;
-+
-+ if (os_strcmp(cmd, "mode") == 0) {
-+ return os_snprintf(pos, end - pos, "pp_mode: %d\n",
-+ hapd->iconf->pp_mode);
-+ } else {
-+ wpa_printf(MSG_ERROR,
-+ "Unsupported parameter %s for get_pp", cmd);
-+ return -1;
-+ }
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4834,6 +4887,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "set_pp", 6) == 0) {
-+ reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
-+ reply_size);
-+ } else if (os_strncmp(buf, "get_pp", 6) == 0) {
-+ reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
-+ reply_size);
- } else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
- // Replace first ':' with a single space ' '
- char *pos = buf + 23;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 223db56eb..d8dd5495a 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -302,6 +302,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- conf->ibf_enable = IBF_DEFAULT_ENABLE;
- conf->amsdu = 1;
-+ conf->pp_mode = PP_DISABLE;
-
- return conf;
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index b6f05e7af..9e39e8285 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1205,6 +1205,7 @@ struct hostapd_config {
- u8 dfs_detect_mode;
- u8 amsdu;
- void *muru_config;
-+ u8 pp_mode;
- };
-
- enum three_wire_mode {
-@@ -1257,6 +1258,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
- EDCCA_CTRL_NUM,
- };
-
-+enum pp_mode {
-+ PP_DISABLE = 0,
-+ PP_AUTO_MODE,
-+ PP_MANUAL_MODE,
-+};
-+
- #define EDCCA_DEFAULT_COMPENSATION -6
- #define EDCCA_MIN_COMPENSATION -126
- #define EDCCA_MAX_COMPENSATION 126
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 5b93ea647..d0d8279e2 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1271,3 +1271,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
- return hapd->driver->background_radar_mode(hapd->drv_priv,
- hapd->iconf->background_radar_mode);
- }
-+
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
-+{
-+ if (!hapd->driver || !hapd->driver->pp_mode_set ||
-+ hapd->iconf->pp_mode > PP_AUTO_MODE)
-+ return 0;
-+ return hapd->driver->pp_mode_set(hapd->drv_priv,
-+ hapd->iconf->pp_mode);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 1e7ae7a8d..e4c2827bf 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -163,6 +163,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
-
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
-
- #include "drivers/driver.h"
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f01f607f3..2ffa3d4ea 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2526,6 +2526,8 @@ dfs_offload:
- goto fail;
- if (hostapd_drv_amsdu_ctrl(hapd) < 0)
- goto fail;
-+ if (hostapd_drv_pp_mode_set(hapd) < 0)
-+ goto fail;
-
- wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 5bc1e0444..6275c141d 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
-+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
- NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
- };
-
-+enum mtk_vendor_attr_pp_ctrl {
-+ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_PP_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-+ MTK_VENDOR_ATTR_PP_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 0c1b2e452..fb463aaac 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5211,6 +5211,12 @@ struct wpa_driver_ops {
- * @background_radar_mode: background radar mode
- */
- int (*background_radar_mode)(void *priv, u8 background_radar_mode);
-+ /**
-+ * pp_mode_set - Set preamble puncture operation mode
-+ * @priv: Private driver interface data
-+ * @pp_mode: Value is defined in enum pp_mode
-+ */
-+ int (*pp_mode_set)(void *priv, const u8 pp_mode);
- };
-
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3cbf8610a..cbe793cc6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -151,6 +151,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
- [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
- };
-
-+static struct nla_policy
-+pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
-+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- struct nl_sock *handle;
-@@ -14776,6 +14781,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
- return ret;
- }
-
-+static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_pp_vendor_cmd_avail) {
-+ wpa_printf(MSG_DEBUG,
-+ "nl80211: Driver does not support setting preamble puncture");
-+ return 0;
-+ }
-+
-+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+ if (!msg)
-+ goto fail;
-+
-+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
-+ goto fail;
-+
-+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+ if (!data)
-+ goto fail;
-+
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
-+
-+ nla_nest_end(msg, data);
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
-+ ret, strerror(-ret));
-+
-+ return ret;
-+
-+fail:
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .name = "nl80211",
- .desc = "Linux nl80211/cfg80211",
-@@ -14949,4 +14997,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .amnt_set = nl80211_amnt_set,
- .amnt_dump = nl80211_amnt_dump,
- .background_radar_mode = nl80211_background_radar_mode,
-+ .pp_mode_set = nl80211_pp_mode_set,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 74ee9b191..1bba5b14e 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -211,6 +211,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- unsigned int mtk_amnt_vendor_cmd_avail:1;
- unsigned int mtk_background_radar_vendor_cmd_avail:1;
-+ unsigned int mtk_pp_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 74c3e0d86..0e044b03c 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1140,6 +1140,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
- drv->mtk_background_radar_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
-+ drv->mtk_pp_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
deleted file mode 100644
index 5f7489e..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 1ca50d17101a4812a424ed5eb6ae10d54635b055 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 22 Nov 2023 21:41:34 +0800
-Subject: [PATCH 51/54] mtk: hostapd: add no_beacon vendor command for cert
-
-Add the vendor command to disable/enable beacon
-
-[Usage]
-hostapd_cli -i <interface> no_beacon <value>
- <value>
- 0: enable beacon
- 1: disable beacon
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- hostapd/ctrl_iface.c | 21 +++++++++++++++++++
- hostapd/hostapd_cli.c | 7 +++++++
- src/ap/ap_drv_ops.c | 8 ++++++++
- src/ap/ap_drv_ops.h | 1 +
- src/common/mtk_vendor.h | 12 +++++++++++
- src/drivers/driver.h | 7 +++++++
- src/drivers/driver_nl80211.c | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h | 1 +
- src/drivers/driver_nl80211_capa.c | 3 +++
- 9 files changed, 94 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 64bab0228..7406aef38 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4301,6 +4301,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- }
- }
-
-+static int
-+hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
-+ char *buf, size_t buflen)
-+{
-+ int disable_beacon = atoi(value);
-+
-+ if (disable_beacon < 0) {
-+ wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
-+ return -1;
-+ }
-+
-+ if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
-+ return os_snprintf(buf, buflen, "OK\n");
-+ else
-+ return -1;
-+
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- char *buf, char *reply,
- int reply_size,
-@@ -4904,6 +4922,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- } else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
- reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
- reply, reply_size);
-+ } else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
-+ reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
-+ reply_size);
- } else {
- os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 363a6bb93..d5b6d59f3 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1422,6 +1422,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
- return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
- }
-
-+static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
-+ char *argv[])
-+{
-+ return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
-+}
-
- #ifdef CONFIG_DPP
-
-@@ -1799,6 +1804,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- "<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
- { "get_mu", hostapd_cli_cmd_get_mu, NULL,
- " = show mu onoff value in 0-15 bitmap"},
-+ { "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
-+ "<value> 0: Enable beacon, 1: Disable beacon"},
- #ifdef CONFIG_DPP
- { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
- "report a scanned DPP URI from a QR Code" },
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index d0d8279e2..073c05de8 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1280,3 +1280,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
- return hapd->driver->pp_mode_set(hapd->drv_priv,
- hapd->iconf->pp_mode);
- }
-+
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
-+{
-+ if (!hapd->driver || !hapd->driver->beacon_ctrl)
-+ return 0;
-+ return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
-+}
-+
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index e4c2827bf..6b9e454de 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -164,6 +164,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
-
- #include "drivers/driver.h"
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6275c141d..5531802b8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
- MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
-+ MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
- };
-
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -268,6 +269,17 @@ enum mtk_vendor_attr_pp_ctrl {
- NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
- };
-
-+enum mtk_vendor_attr_beacon_ctrl {
-+ MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
-+
-+ MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
-+
-+ /* keep last */
-+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
-+ MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
-+ NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index fb463aaac..40f6150d7 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5128,6 +5128,13 @@ struct wpa_driver_ops {
- int (*mu_ctrl)(void *priv, u8 mode, void *config);
- int (*mu_dump)(void *priv, u8 *mu_onoff);
-
-+ /**
-+ * beacon_ctrl - ctrl on off for beacon
-+ * @priv: Private driver interface data
-+ *
-+ */
-+ int (*beacon_ctrl)(void *priv, u8 beacon_mode);
-+
- /**
- * three_wire_ctrl - set three_wire_ctrl mode
- * @priv: Private driver interface data
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index cbe793cc6..9a10a93f0 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13827,6 +13827,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-
- return ret;
- }
-+static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
-+{
-+ struct i802_bss *bss = priv;
-+ struct wpa_driver_nl80211_data *drv = bss->drv;
-+ struct nl_msg *msg;
-+ struct nlattr *data;
-+ int ret;
-+
-+ if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
-+ wpa_printf(MSG_ERROR,
-+ "nl80211: Driver does not support setting beacon control");
-+ return 0;
-+ }
-+
-+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
-+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+ nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
-+ nlmsg_free(msg);
-+ return -ENOBUFS;
-+ }
-+
-+ nla_nest_end(msg, data);
-+
-+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-+
-+ if (ret)
-+ wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
-+
-+ return ret;
-+}
-+
- #endif /* CONFIG_IEEE80211AX */
-
-
-@@ -14972,6 +15005,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- .set_4addr_mode = nl80211_set_4addr_mode,
- .mu_ctrl = nl80211_mu_ctrl,
- .mu_dump = nl80211_mu_dump,
-+ .beacon_ctrl = nl80211_beacon_ctrl,
- #ifdef CONFIG_DPP
- .dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 1bba5b14e..300c0d11a 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
- unsigned int mtk_amnt_vendor_cmd_avail:1;
- unsigned int mtk_background_radar_vendor_cmd_avail:1;
- unsigned int mtk_pp_vendor_cmd_avail:1;
-+ unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
-
- u64 vendor_scan_cookie;
- u64 remain_on_chan_cookie;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 0e044b03c..2f844053d 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1143,6 +1143,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
- drv->mtk_pp_vendor_cmd_avail = 1;
- break;
-+ case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
-+ drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
-+ break;
- }
- }
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
deleted file mode 100644
index f5b5c24..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
+++ /dev/null
@@ -1,379 +0,0 @@
-From 1f6c4857cb55c8ba81a50f3fe0a1465efee10513 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Thu, 28 Sep 2023 18:03:08 +0800
-Subject: [PATCH 52/54] mtk: hostapd: ACS: Add EHT320 and HT40- support, fix
- issue
-
-1. Add 6G EHT320 support;
-2. Add 2.4G HT40- support;
-3. Fix issue: selected best channel is out of channels;
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/acs.c | 191 +++++++++++++++++++++++++++++++++------------------
- 1 file changed, 124 insertions(+), 67 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index af3140542..e4871921f 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -245,6 +245,7 @@ enum bw_type {
- ACS_BW40,
- ACS_BW80,
- ACS_BW160,
-+ ACS_BW320,
- };
-
- struct bw_item {
-@@ -286,10 +287,16 @@ static const struct bw_item bw_160[] = {
- { 6435, 6575, 111 }, { 6595, 6735, 143 },
- { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
- };
-+static const struct bw_item bw_320[] = {
-+ { 5955, 6255, 31 }, { 6115, 6415, 63 }, { 6275, 6575, 95 },
-+ { 6435, 6735, 127 }, { 6595, 6895, 159 }, { 6755, 7055, 191 },
-+ { -1, -1, -1 }
-+};
- static const struct bw_item *bw_desc[] = {
- [ACS_BW40] = bw_40,
- [ACS_BW80] = bw_80,
- [ACS_BW160] = bw_160,
-+ [ACS_BW320] = bw_320,
- };
-
-
-@@ -775,10 +782,19 @@ 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;
-@@ -786,6 +802,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- bool update_best = 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
-@@ -817,7 +836,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 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);
-@@ -838,7 +857,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
-- (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
-+ (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+ iface->conf->ieee80211be)) {
- if (hostapd_get_oper_chwidth(iface->conf) ==
- CONF_OPER_CHWIDTH_80MHZ &&
- !acs_usable_bw_chan(chan, ACS_BW80)) {
-@@ -856,63 +876,86 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- chan->chan);
- continue;
- }
-- }
-
-- factor = 0;
-- if (acs_usable_chan(chan))
-- factor = chan->interference_factor;
-- total_weight = 1;
--
-- 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)) {
-+ if (iface->conf->ieee80211be &&
-+ hostapd_get_oper_chwidth(iface->conf) ==
-+ CONF_OPER_CHWIDTH_320MHZ &&
-+ !acs_usable_bw_chan(chan, ACS_BW320)) {
- wpa_printf(MSG_DEBUG,
-- "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
-- chan->chan, adj_chan->chan, bw);
-- break;
-+ "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth",
-+ chan->chan);
-+ continue;
- }
-+ }
-
-- if (acs_usable_chan(adj_chan)) {
-- factor += adj_chan->interference_factor;
-+ factor = 0;
-+ total_weight = 0;
-+
-+ 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 (!is_in_chanlist(iface, adj_chan) || !is_in_freqlist(iface, adj_chan))
-+ 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;
-- }
-+ 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;
-+ }
-+
-+ update_best = true;
-+ if (acs_usable_chan(adj_chan)) {
-+ factor += adj_chan->interference_factor;
-+ total_weight += 1;
-+ } else {
-+ update_best = false;
-+ }
-
-- /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
-- * channel interference factor. */
-- if (is_24ghz_mode(mode->mode)) {
-+ /* find the best channel in this segment */
-+ if (update_best &&
-+ adj_chan->interference_factor < best->interference_factor)
-+ best = adj_chan;
-+ }
-+
-+ if (j != n_chans) {
-+ 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 * sec_ch_factor * 20));
-+ 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 * sec_ch_factor * 20) - 5);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -920,7 +963,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- adj_chan = acs_find_chan(iface, chan->freq +
-- (j * 20) - 10);
-+ (j * sec_ch_factor * 20) - 10);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_NEXT_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -928,7 +971,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- adj_chan = acs_find_chan(iface, chan->freq +
-- (j * 20) + 5);
-+ (j * sec_ch_factor * 20) + 5);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -936,7 +979,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- }
-
- adj_chan = acs_find_chan(iface, chan->freq +
-- (j * 20) + 10);
-+ (j * sec_ch_factor * 20) + 10);
- if (adj_chan && acs_usable_chan(adj_chan)) {
- factor += ACS_NEXT_ADJ_WEIGHT *
- adj_chan->interference_factor;
-@@ -945,7 +988,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) {
-@@ -964,11 +1008,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);
- }
-@@ -1021,19 +1065,12 @@ 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;
-
-- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+ iface->conf->ieee80211be) {
- switch (hostapd_get_oper_chwidth(iface->conf)) {
- case CONF_OPER_CHWIDTH_80MHZ:
- n_chans = 4;
-@@ -1043,6 +1080,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- break;
- default:
- break;
-+ /* 320 is supported only in 6GHz 11be mode */
- }
- }
-
-@@ -1063,7 +1101,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);
-
- #ifdef CONFIG_IEEE80211BE
-@@ -1078,6 +1116,21 @@ bw_selected:
- return rand_chan;
- }
-
-+static int acs_get_center_freq_320mhz(int channel)
-+{
-+ if (channel >= 1 && channel <= 45)
-+ return 31;
-+ else if (channel >= 49 && channel <= 77)
-+ return 63;
-+ else if (channel >= 81 && channel <= 109)
-+ return 95;
-+ else if (channel >= 113 && channel <= 141)
-+ return 127;
-+ else if (channel >= 145 && channel <= 173)
-+ return 159;
-+ else
-+ return 191;
-+}
-
- static void acs_adjust_secondary(struct hostapd_iface *iface)
- {
-@@ -1105,7 +1158,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- {
- int center;
-
-- wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
-+ wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
-
- switch (hostapd_get_oper_chwidth(iface->conf)) {
- case CONF_OPER_CHWIDTH_USE_HT:
-@@ -1121,6 +1174,9 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- case CONF_OPER_CHWIDTH_80MHZ:
- center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
- break;
-+ case CONF_OPER_CHWIDTH_320MHZ:
-+ center = acs_get_center_freq_320mhz(iface->conf->channel);
-+ break;
- case CONF_OPER_CHWIDTH_160MHZ:
- center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
- break;
-@@ -1128,7 +1184,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- /* TODO: How can this be calculated? Adjust
- * acs_find_ideal_chan() */
- wpa_printf(MSG_INFO,
-- "ACS: Only VHT20/40/80/160 is supported now");
-+ "ACS: Only VHT20/40/80/160 EHT320 is supported now");
- return;
- }
-
-@@ -1191,7 +1247,8 @@ static void acs_study(struct hostapd_iface *iface)
- iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
-
-- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+ iface->conf->ieee80211be) {
- acs_adjust_secondary(iface);
- acs_adjust_center_freq(iface);
- }
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch
deleted file mode 100644
index 77677d0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch
+++ /dev/null
@@ -1,190 +0,0 @@
-From cb9841c4361d5c1d236b7d257e2d513ecc1c7c91 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 17 Oct 2023 11:11:40 +0800
-Subject: [PATCH 53/54] mtk: hostapd: add eht_bw320_offset configuration option
-
-This patch introduces a new configuration option, "eht_bw320_offset",
-which enables devices to specify a preferred channelization for 320 MHz
-BSSs when using automatic channel selection (ACS).
-This option is only applicable when the channel is not already decided
-and the bandwidth is set to 320 MHz.
-
-The value and meaning of the option:
-0: auto-detected by hostapd
-1: 320 MHz-1
-2: 320 MHz-2
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- hostapd/config_file.c | 3 +++
- hostapd/hostapd.conf | 8 ++++++++
- src/ap/ap_config.c | 6 ++++++
- src/ap/ap_config.h | 37 +++++++++++++++++++++++++++++++++++++
- src/ap/ctrl_iface_ap.c | 11 +++++++++++
- src/ap/drv_callbacks.c | 2 ++
- 6 files changed, 67 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 278f6b347..721685baf 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4822,6 +4822,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- line);
- return 1;
- }
-+ } else if (os_strcmp(buf, "eht_bw320_offset") == 0) {
-+ u8 val = atoi(pos);
-+ conf->eht_bw320_offset = val;
- #endif /* CONFIG_IEEE80211BE */
- } else if (os_strcmp(buf, "edcca_threshold") == 0) {
- if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index f16e3b08d..290504317 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -1032,6 +1032,14 @@ wmm_ac_vo_acm=0
- #eht_oper_chwidth (see vht_oper_chwidth)
- #eht_oper_centr_freq_seg0_idx
-
-+#eht_bw320_offset: For automatic channel selection (ACS) to indicate a prefered
-+# 320 MHz channelization in EHT mode.
-+# If the channel is decided or the bandwidth is not 320 MHz, this option is meaningless.
-+# 0 = auto-detect by hostapd
-+# 1 = 320 MHz-1
-+# 2 = 320 MHz-2
-+#eht_bw320_offset=0
-+
- # Disabled subchannel bitmap (16 bits) as per IEEE P802.11be/3.0,
- # Figure 9-1002c (EHT Operation Information field format). Each bit corresponds
- # to a 20 MHz channel, the lowest bit corresponds to the lowest frequency. A
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index d8dd5495a..3fb98d08f 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -304,6 +304,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- conf->amsdu = 1;
- conf->pp_mode = PP_DISABLE;
-
-+ hostapd_set_and_check_bw320_offset(conf, 0);
- return conf;
- }
-
-@@ -1515,6 +1516,7 @@ static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
- int hostapd_config_check(struct hostapd_config *conf, int full_config)
- {
- size_t i;
-+ u8 bw320_offset = 0;
-
- if (full_config && is_6ghz_op_class(conf->op_class) &&
- !conf->hw_mode_set) {
-@@ -1566,6 +1568,8 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
- "Cannot set ieee80211be without ieee80211ax");
- return -1;
- }
-+
-+ bw320_offset = conf->eht_bw320_offset;
- #endif /* CONFIG_IEEE80211BE */
-
- if (full_config && conf->mbssid && !conf->ieee80211ax) {
-@@ -1574,6 +1578,8 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
- return -1;
- }
-
-+ hostapd_set_and_check_bw320_offset(conf, bw320_offset);
-+
- for (i = 0; i < conf->num_bss; i++) {
- if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
- return -1;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 9e39e8285..3e0505594 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1184,6 +1184,7 @@ struct hostapd_config {
- struct eht_phy_capabilities_info eht_phy_capab;
- u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
- u8 punct_acs_threshold;
-+ u8 eht_bw320_offset;
- #endif /* CONFIG_IEEE80211BE */
-
- /* EHT enable/disable config from CHAN_SWITCH */
-@@ -1355,6 +1356,42 @@ hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
- conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
- }
-
-+static inline u8
-+hostapd_get_bw320_offset(struct hostapd_config *conf)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
-+ hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ)
-+ return conf->eht_bw320_offset;
-+#endif /* CONFIG_IEEE80211BE */
-+ return 0;
-+}
-+
-+static inline void
-+hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
-+ u8 bw320_offset)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
-+ hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ) {
-+ if (conf->channel) {
-+ /* If the channel is set, then calculate bw320_offset
-+ * by center frequency segment 0.
-+ */
-+ u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
-+ conf->eht_bw320_offset = (seg0 - 31) % 64 ? 2 : 1;
-+ } else {
-+ /* If the channel is not set, bw320_offset indicates
-+ * prefered offset of 320 MHz.
-+ */
-+ conf->eht_bw320_offset = bw320_offset;
-+ }
-+ } else {
-+ conf->eht_bw320_offset = 0;
-+ }
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
- #define IBF_DEFAULT_ENABLE 0
-
- int hostapd_mac_comp(const void *a, const void *b);
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 7bdefb4cf..e686fb8b7 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -831,6 +831,17 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
- if (os_snprintf_error(buflen - len, ret))
- return len;
- len += ret;
-+
-+ if (is_6ghz_op_class(iface->conf->op_class) &&
-+ hostapd_get_oper_chwidth(iface->conf) ==
-+ CONF_OPER_CHWIDTH_320MHZ) {
-+ ret = os_snprintf(buf + len, buflen - len,
-+ "eht_bw320_offset=%d\n",
-+ iface->conf->eht_bw320_offset);
-+ if (os_snprintf_error(buflen - len, ret))
-+ return len;
-+ len += ret;
-+ }
- }
- #endif /* CONFIG_IEEE80211BE */
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 12419c6d4..b0d9420e8 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1175,6 +1175,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- hostapd_set_oper_chwidth(hapd->iconf, chwidth);
- hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
- hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
-+ /* Auto-detect new bw320_offset */
-+ hostapd_set_and_check_bw320_offset(hapd->iconf, 0);
- #ifdef CONFIG_IEEE80211BE
- hapd->iconf->punct_bitmap = punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
deleted file mode 100644
index fab7706..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From 44d5b6d691a45b71d2e8896f3302b4b946ae4d33 Mon Sep 17 00:00:00 2001
-From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
-Date: Wed, 13 Dec 2023 18:13:01 +0530
-Subject: [PATCH 54/54] mtk: hostapd: WPS added change to configure AP PIN lock
- timout
-
-added config paramter ap_pin_lockout_time to configure
-AP PIN timeout from hosatpd.conf
-
-
-Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- src/ap/ap_config.h | 1 +
- src/ap/wps_hostapd.c | 9 ++++++---
- 3 files changed, 9 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 721685baf..9dd86e037 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3709,6 +3709,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- bss->wps_independent = atoi(pos);
- } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
- bss->ap_setup_locked = atoi(pos);
-+ } else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
-+ bss->ap_pin_lockout_time = atoi(pos);
- } else if (os_strcmp(buf, "uuid") == 0) {
- if (uuid_str2bin(pos, bss->uuid)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 3e0505594..c03fa11b6 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -491,6 +491,7 @@ struct hostapd_bss_config {
- #ifdef CONFIG_WPS
- int wps_independent;
- int ap_setup_locked;
-+ unsigned int ap_pin_lockout_time;
- u8 uuid[16];
- char *wps_pin_requests;
- char *device_name;
-diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
-index 0c351af5f..ea2608917 100644
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
- eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
- wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
- } else if (!hapd->conf->ap_setup_locked) {
-- if (hapd->ap_pin_lockout_time == 0)
-- hapd->ap_pin_lockout_time = 60;
-- else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
-+ if (hapd->ap_pin_lockout_time == 0) {
-+ if (hapd->conf->ap_pin_lockout_time)
-+ hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
-+ else
-+ hapd->ap_pin_lockout_time = 60;
-+ } else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
- (hapd->ap_pin_failures % 3) == 0)
- hapd->ap_pin_lockout_time *= 2;
-
---
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
index 325e353..e7987dd 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
@@ -1,120 +1,106 @@
#patch patches (come from openwrt/lede/target/linux/mediatek)
SRC_URI_append = " \
- file://001-wolfssl-init-RNG-with-ECC-key.patch \
- file://010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch \
- file://011-mesh-use-deterministic-channel-on-channel-switch.patch \
- file://021-fix-sta-add-after-previous-connection.patch \
- file://022-hostapd-fix-use-of-uninitialized-stack-variables.patch \
- file://023-ndisc_snoop-call-dl_list_del-before-freeing-ipv6-add.patch \
- file://030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch \
- file://040-mesh-allow-processing-authentication-frames-in-block.patch \
- file://050-build_fix.patch \
- file://110-mbedtls-TLS-crypto-option-initial-port.patch \
- file://120-mbedtls-fips186_2_prf.patch \
- file://130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch \
- file://135-mbedtls-fix-owe-association.patch \
- file://140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch \
- file://150-add-NULL-checks-encountered-during-tests-hwsim.patch \
- file://160-dpp_pkex-EC-point-mul-w-value-prime.patch \
- file://170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch \
- file://180-driver_nl80211-fix-setting-QoS-map-on-secondary-BSSs.patch \
- file://181-driver_nl80211-update-drv-ifindex-on-removing-the-fi.patch \
- file://182-nl80211-move-nl80211_put_freq_params-call-outside-of.patch \
- file://183-hostapd-cancel-channel_list_update_timeout-in-hostap.patch \
- file://200-multicall.patch \
- file://300-noscan.patch \
- file://301-mesh-noscan.patch \
- file://310-rescan_immediately.patch \
- file://320-optional_rfkill.patch \
- file://330-nl80211_fix_set_freq.patch \
- file://341-mesh-ctrl-iface-channel-switch.patch \
- file://350-nl80211_del_beacon_bss.patch \
- file://380-disable_ctrl_iface_mib.patch \
- file://381-hostapd_cli_UNKNOWN-COMMAND.patch \
- file://390-wpa_ie_cap_workaround.patch \
- file://400-wps_single_auth_enc_type.patch \
- file://410-limit_debug_messages.patch \
- file://420-indicate-features.patch \
- file://430-hostapd_cli_ifdef.patch \
- file://431-wpa_cli_ifdef.patch \
- file://460-wpa_supplicant-add-new-config-params-to-be-used-with.patch \
- file://463-add-mcast_rate-to-11s.patch \
- file://464-fix-mesh-obss-check.patch \
- file://465-hostapd-config-support-random-BSS-color.patch \
- file://470-survey_data_fallback.patch \
- file://500-lto-jobserver-support.patch \
- file://590-rrm-wnm-statistics.patch \
- file://599-wpa_supplicant-fix-warnings.patch \
- file://600-ubus_support.patch \
- file://601-ucode_support.patch \
- file://610-hostapd_cli_ujail_permission.patch \
- file://701-reload_config_inline.patch \
- file://710-vlan_no_bridge.patch \
- file://711-wds_bridge_force.patch \
- file://720-iface_max_num_sta.patch \
- file://730-ft_iface.patch \
- file://740-snoop_iface.patch \
- file://750-qos_map_set_without_interworking.patch \
- file://751-qos_map_ignore_when_unsupported.patch \
- file://760-dynamic_own_ip.patch \
- file://761-shared_das_port.patch \
- file://770-radius_server.patch \
- file://990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch \
- file://991-Fix-OpenWrt-13156.patch \
- file://992-nl80211-add-extra-ies-only-if-allowed-by-driver.patch \
- file://993-2023-10-28-ACS-Fix-typo-in-bw_40-frequency-array.patch \
- file://mtk-0001-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
- file://mtk-0002-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch \
- file://mtk-0003-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
- file://mtk-0004-mtk-hostapd-Add-mtk_vendor.h.patch \
- file://mtk-0005-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
- file://mtk-0006-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
- file://mtk-0007-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
- file://mtk-0008-mtk-hostapd-Add-hostapd-iBF-control.patch \
- file://mtk-0009-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
- file://mtk-0010-mtk-hostapd-Add-DFS-detection-mode.patch \
- file://mtk-0011-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
- file://mtk-0012-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
- file://mtk-0013-mtk-hostapd-Add-he_ldpc-configuration.patch \
- file://mtk-0014-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch \
- file://mtk-0015-mtk-hostapd-6G-band-does-not-require-DFS.patch \
- file://mtk-0016-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
- file://mtk-0017-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
- file://mtk-0018-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
- file://mtk-0019-mtk-hostapd-Add-available-color-bitmap.patch \
- file://mtk-0020-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
- file://mtk-0021-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
- file://mtk-0022-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
- file://mtk-0023-mtk-hostapd-Fix-setting-wrong-seg0-index-for-5G-cent.patch \
- file://mtk-0024-mtk-hostapd-Add-muru-user-number-debug-command.patch \
- file://mtk-0025-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
- file://mtk-0026-mtk-hostapd-Add-HE-capabilities-check.patch \
- file://mtk-0027-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
- file://mtk-0028-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
- file://mtk-0029-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
- file://mtk-0030-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
- file://mtk-0031-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
- file://mtk-0032-mtk-hostapd-Fix-rnr-ie-length-when-no-need-to-report.patch \
- file://mtk-0033-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
- file://mtk-0034-mtk-hostapd-update-op_class-when-AP-channel-switchin.patch \
- file://mtk-0035-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
- file://mtk-0036-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
- file://mtk-0037-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
- file://mtk-0038-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
- file://mtk-0039-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
- file://mtk-0040-mtk-hostapd-avoid-unnecessary-beacon-update-for-6-GH.patch \
- file://mtk-0041-mtk-hostapd-refactor-the-flow-to-create-Wide-Bandwid.patch \
- file://mtk-0042-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
- file://mtk-0043-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
- file://mtk-0044-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
- file://mtk-0045-mtk-hostapd-update-eht-operation-element.patch \
- file://mtk-0046-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch \
- file://mtk-0047-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
- file://mtk-0048-mtk-hostapd-Add-support-for-updating-background-chan.patch \
- file://mtk-0049-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch \
- file://mtk-0050-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
- file://mtk-0051-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
- file://mtk-0052-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch \
- file://mtk-0053-mtk-hostapd-add-eht_bw320_offset-configuration-optio.patch \
- file://mtk-0054-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
+ file://0001-hostapd-MLO-fix-for_each_mld_link-macro.patch \
+ file://0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch \
+ file://0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch \
+ file://0004-hostapd-MLO-send-link_id-on-sta_deauth.patch \
+ file://0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch \
+ file://0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch \
+ file://0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch \
+ file://0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch \
+ file://0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch \
+ file://0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch \
+ file://0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch \
+ file://0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch \
+ file://0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch \
+ file://0014-hostapd-MLO-update-all-partner-s-link-beacon.patch \
+ file://0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch \
+ file://0016-hostapd-MLO-Enhance-wpa-state-machine.patch \
+ file://0017-hostapd-MLO-add-support-for-MLO-rekey.patch \
+ file://0018-hostapd-MLO-send-link-id-during-flushing-stations.patch \
+ file://0019-hostapd-MLO-display-link-details-in-status-command.patch \
+ file://0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch \
+ file://0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch \
+ file://0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch \
+ file://0023-backport-hostapd-afcd-add-AFC-daemon-support.patch \
+ file://0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch \
+ file://0025-backport-hostapd-ap-add-AFC-client-support.patch \
+ file://0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch \
+ file://0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch \
+ file://0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch \
+ file://0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
+ file://0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch \
+ file://0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
+ file://0032-mtk-hostapd-Add-mtk_vendor.h.patch \
+ file://0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
+ file://0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
+ file://0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
+ file://0036-mtk-hostapd-Add-hostapd-iBF-control.patch \
+ file://0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
+ file://0038-mtk-hostapd-Add-DFS-detection-mode.patch \
+ file://0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
+ file://0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
+ file://0041-mtk-hostapd-Add-he_ldpc-configuration.patch \
+ file://0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch \
+ file://0043-mtk-hostapd-6G-band-does-not-require-DFS.patch \
+ file://0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
+ file://0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
+ file://0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
+ file://0047-mtk-hostapd-Add-available-color-bitmap.patch \
+ file://0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
+ file://0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
+ file://0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
+ file://0051-mtk-hostapd-Add-muru-user-number-debug-command.patch \
+ file://0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
+ file://0053-mtk-hostapd-Add-HE-capabilities-check.patch \
+ file://0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
+ file://0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
+ file://0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
+ file://0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
+ file://0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
+ file://0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
+ file://0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
+ file://0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
+ file://0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
+ file://0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
+ file://0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
+ file://0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
+ file://0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
+ file://0067-mtk-hostapd-update-eht-operation-element.patch \
+ file://0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch \
+ file://0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
+ file://0070-mtk-hostapd-Add-support-for-updating-background-chan.patch \
+ file://0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch \
+ file://0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
+ file://0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
+ file://0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
+ file://0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch \
+ file://0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
+ file://0077-Revert-ACS-upstream-changes.patch \
+ file://0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch \
+ file://0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch \
+ file://0080-mtk-hostapd-fix-mld_assoc_link_id.patch \
+ file://0081-mtk-wpa_s-correctly-get-assoc-frequency.patch \
+ file://0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch \
+ file://0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch \
+ file://0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch \
+ file://0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch \
+ file://0086-mtk-hostapd-add-mld_primary-option.patch \
+ file://0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch \
+ file://0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch \
+ file://0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch \
+ file://0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch \
+ file://0091-mtk-wifi-hostapd-add-wds-mlo-support.patch \
+ file://0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch \
+ file://0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch \
+ file://0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch \
+ file://0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch \
+ file://0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch \
+ file://0098-mtk-hostapd-add-connac3-csi-control-interface.patch \
+ file://0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch \
+ file://0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch \
+ file://0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch \
+ file://0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch \
+ file://0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch \
+ file://0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch \
"
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.c
index 66eba99..8689494 100644
--- a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.c
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.c
@@ -774,7 +774,8 @@
mode ? &mode->he_capab[IEEE80211_MODE_AP] :
NULL,
mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
- NULL);
+ NULL,
+ hostapd_get_punct_bitmap(hapd));
for (i = 0; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.h b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.h
index b0f7c44..22767d6 100644
--- a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.h
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.h
@@ -8,6 +8,8 @@
#ifndef __HOSTAPD_UBUS_H
#define __HOSTAPD_UBUS_H
+#include "sta_info.h"
+
enum hostapd_ubus_event_type {
HOSTAPD_UBUS_PROBE_REQ,
HOSTAPD_UBUS_AUTH_REQ,
@@ -27,6 +29,7 @@
struct hostapd_data;
struct hapd_interfaces;
struct rrm_measurement_beacon_report;
+struct sta_info;
#ifdef UBUS_SUPPORT
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.c
index 16d1b51..d344190 100644
--- a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.c
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.c
@@ -51,7 +51,7 @@
int i;
list = ucv_array_new(vm);
- for (i = 0; i < iface->num_bss; i++) {
+ for (i = 0; iface->bss && i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
diff --git a/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb b/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
index db56247..6be4eca 100644
--- a/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
+++ b/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
@@ -10,7 +10,7 @@
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-${PV}:"
-SRCREV ?= "e5ccbfc69ecf297590341ae8b461edba9d8e964c"
+SRCREV ?= "07c9f183ea744ac04585fb6dd10220c75a5e2e74"
SRC_URI = "git://w1.fi/hostap.git;protocol=https;branch=main \
file://wpa-supplicant.sh \
file://wpa_supplicant.conf \
@@ -33,14 +33,6 @@
EXTRA_OEMAKE = "'LIBDIR=${libdir}' 'INCDIR=${includedir}' 'BINDIR=${sbindir}'"
-do_unpack_append() {
- bb.build.exec_func('do_copy_openwrt_src', d)
-}
-
-do_copy_openwrt_src() {
- cp -Rfp ${WORKDIR}/src-${PV}/* ${S}/
-}
-
do_filogic_patches() {
cd ${S}
if [ ! -e patch_applied ]; then