diff --git a/recipes-kernel/mac80211/mac80211/0001-backport-of-build-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0001-backport-of-build-patches-from-openwrt.patch index 0818f2b..836b0b9 100644 --- a/recipes-kernel/mac80211/mac80211/0001-backport-of-build-patches-from-openwrt.patch +++ b/recipes-kernel/mac80211/mac80211/0001-backport-of-build-patches-from-openwrt.patch @@ -1,40 +1,38 @@ -From 7e308692b72697003d08b900059579585b621b70 Mon Sep 17 00:00:00 2001 +From 929cbc1b2c140cee08e5f2657314356e0fa4fa18 Mon Sep 17 00:00:00 2001 From: Patrick Walther -Date: Mon, 22 Mar 2021 16:28:20 +0100 -Subject: [PATCH] 0001 backport of build patches from openwrt +Date: Wed, 14 Sep 2022 14:24:53 +0200 +Subject: [PATCH] backport of build patches fromopenwrt --- - Kconfig.local | 111 -------------------- - Kconfig.sources | 3 - - Makefile | 116 +++++++++++---------- - Makefile.kernel | 2 - - Makefile.real | 9 +- - backport-include/linux/kconfig.h | 5 + - backport-include/linux/rfkill.h | 6 ++ - compat/main.c | 25 ----- - drivers/net/wireless/broadcom/b43/Kconfig | 12 +-- - drivers/net/wireless/broadcom/b43/main.c | 4 +- - drivers/net/wireless/broadcom/b43legacy/Kconfig | 8 +- - drivers/net/wireless/broadcom/b43legacy/main.c | 4 +- - drivers/net/wireless/broadcom/brcm80211/Kconfig | 2 +- - .../wireless/broadcom/brcm80211/brcmsmac/Makefile | 2 +- - .../net/wireless/broadcom/brcm80211/brcmsmac/led.h | 2 +- - drivers/net/wireless/intel/ipw2x00/ipw2200.c | 18 ++-- - kconf/Makefile | 4 +- - kconf/conf.c | 30 +----- - kconf/confdata.c | 4 +- - local-symbols | 37 ------- - net/wireless/Kconfig | 8 +- - 21 files changed, 114 insertions(+), 298 deletions(-) + Kconfig.local | 111 ----------------- + Kconfig.sources | 3 - + Makefile | 116 +++++++++--------- + Makefile.kernel | 2 - + Makefile.real | 9 +- + compat/main.c | 25 ---- + 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 +- + .../broadcom/brcm80211/brcmsmac/Makefile | 2 +- + .../broadcom/brcm80211/brcmsmac/led.h | 2 +- + drivers/net/wireless/intel/ipw2x00/ipw2200.c | 18 +-- + kconf/Makefile | 4 +- + kconf/conf.c | 30 +---- + kconf/confdata.c | 4 +- + local-symbols | 37 ------ + net/wireless/Kconfig | 8 +- + 19 files changed, 103 insertions(+), 298 deletions(-) diff --git a/Kconfig.local b/Kconfig.local -index a3935cc..dfd68d6 100644 +index 19a25e3..3a694ea 100644 --- a/Kconfig.local +++ b/Kconfig.local -@@ -1315,117 +1315,6 @@ config BACKPORTED_USB_NET_CH9200 - config BACKPORTED_USB_NET_AQC111 +@@ -1357,117 +1357,6 @@ config BACKPORTED_USB_NET_AQC111 + config BACKPORTED_USB_RTL8153_ECM tristate - default USB_NET_AQC111 + default USB_RTL8153_ECM -config BACKPORTED_SSB_POSSIBLE - tristate - default SSB_POSSIBLE @@ -150,10 +148,10 @@ index a3935cc..dfd68d6 100644 tristate default USB_ACM diff --git a/Kconfig.sources b/Kconfig.sources -index 5fb4478..347f1c5 100644 +index 2ea4d8a..0aa62a3 100644 --- a/Kconfig.sources +++ b/Kconfig.sources -@@ -7,9 +7,6 @@ source "$BACKPORT_DIR/net/mac80211/Kconfig" +@@ -10,9 +10,6 @@ source "$BACKPORT_DIR/drivers/soc/qcom/Kconfig" source "$BACKPORT_DIR/drivers/net/wireless/Kconfig" source "$BACKPORT_DIR/drivers/net/usb/Kconfig" @@ -164,7 +162,7 @@ index 5fb4478..347f1c5 100644 source "$BACKPORT_DIR/drivers/staging/Kconfig" diff --git a/Makefile b/Makefile -index ee7df4b..79e5fab 100644 +index 77c2670..c431b71 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ @@ -226,8 +224,8 @@ index ee7df4b..79e5fab 100644 - echo "" ;\ - done \ - ) > Kconfig.kernel ;\ -- kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion | \ -- sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d') ;\ +- 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./')" ;\ @@ -283,8 +281,8 @@ index ee7df4b..79e5fab 100644 + @echo " done." + +Kconfig.versions: Kconfig.kernel -+ @kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion | \ -+ sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d') ;\ ++ @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./')" ;\ @@ -314,12 +312,12 @@ index ee7df4b..79e5fab 100644 .PHONY: defconfig-help diff --git a/Makefile.kernel b/Makefile.kernel -index 62baa94..4212940 100644 +index 11ce417..2de87f5 100644 --- a/Makefile.kernel +++ b/Makefile.kernel -@@ -40,8 +40,6 @@ obj-y += compat/ - obj-$(CPTCFG_CFG80211) += net/wireless/ - obj-$(CPTCFG_MAC80211) += net/mac80211/ +@@ -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/ @@ -353,58 +351,11 @@ index 6550802..d0a8c20 100644 @echo " done." .PHONY: modules -diff --git a/backport-include/linux/kconfig.h b/backport-include/linux/kconfig.h -index d1faad9..a0bc8c4 100644 ---- a/backport-include/linux/kconfig.h -+++ b/backport-include/linux/kconfig.h -@@ -5,6 +5,8 @@ - #include_next - #endif - -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0) -+ - #ifndef __ARG_PLACEHOLDER_1 - #define __ARG_PLACEHOLDER_1 0, - #define config_enabled(cfg) _config_enabled(cfg) -@@ -16,6 +18,7 @@ - * 3.1 - 3.3 had a broken version of this, so undef - * (they didn't have __ARG_PLACEHOLDER_1) - */ -+ - #undef IS_ENABLED - #define IS_ENABLED(option) \ - (config_enabled(option) || config_enabled(option##_MODULE)) -@@ -31,6 +34,8 @@ - #undef IS_BUILTIN - #define IS_BUILTIN(option) config_enabled(option) - -+#endif -+ - #ifndef IS_REACHABLE - /* - * IS_REACHABLE(CONFIG_FOO) evaluates to 1 if the currently compiled -diff --git a/backport-include/linux/rfkill.h b/backport-include/linux/rfkill.h -index 99015af..0193f96 100644 ---- a/backport-include/linux/rfkill.h -+++ b/backport-include/linux/rfkill.h -@@ -2,6 +2,12 @@ - #define __COMPAT_RFKILL_H - #include - -+#undef CONFIG_RFKILL -+#undef CONFIG_RFKILL_FULL -+#undef CONFIG_RFKILL_LEDS -+#undef CONFIG_RFKILL_MODULE -+#undef CONFIG_RFKILL_FULL_MODULE -+ - #if LINUX_VERSION_IS_GEQ(3,10,0) - #include_next - #else diff --git a/compat/main.c b/compat/main.c -index 0bf0420..740bf09 100644 +index 2540e75..c812f7b 100644 --- a/compat/main.c +++ b/compat/main.c -@@ -20,31 +20,6 @@ MODULE_LICENSE("GPL"); +@@ -19,31 +19,6 @@ MODULE_LICENSE("GPL"); #error "You need a CPTCFG_VERSION" #endif @@ -437,7 +388,7 @@ index 0bf0420..740bf09 100644 { } diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig -index 33ecdf1..ffdf951 100644 +index 2e196b5..84cbe38 100644 --- a/drivers/net/wireless/broadcom/b43/Kconfig +++ b/drivers/net/wireless/broadcom/b43/Kconfig @@ -63,21 +63,21 @@ endchoice @@ -483,7 +434,7 @@ index 33ecdf1..ffdf951 100644 config B43_PHY_G diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c -index 1f2c46f..2ef3263 100644 +index 836766e..f378356 100644 --- 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_dev(struct b43_wldev *dev) @@ -537,7 +488,7 @@ index 6ba7eb7..b924f63 100644 # LED support diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c -index e3be1fe..503611f 100644 +index d098c41..0f0fac4 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) @@ -559,7 +510,7 @@ index e3be1fe..503611f 100644 #endif gpiodev = bus->chipco.dev ? : pcidev; diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig -index 053e361..ae090a4 100644 +index c5e6a50..64f6d2f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -8,7 +8,7 @@ config BRCMSMAC @@ -572,7 +523,7 @@ index 053e361..ae090a4 100644 select LEDS_CLASS if BCMA_DRIVER_GPIO select BRCMUTIL diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile -index a1a5259..cb2fb8d 100644 +index 50239f6..e90b34e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile @@ -42,6 +42,6 @@ brcmsmac-y := \ @@ -717,13 +668,13 @@ index df26c7b..1038c30 100644 * 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 a59d23e..d5a2bfd 100644 +index 0aaef6d..354f8eb 100644 --- a/local-symbols +++ b/local-symbols -@@ -437,43 +437,6 @@ USB_SIERRA_NET= - USB_VL600= +@@ -451,43 +451,6 @@ USB_VL600= USB_NET_CH9200= USB_NET_AQC111= + USB_RTL8153_ECM= -SSB_POSSIBLE= -SSB= -SSB_SPROM= @@ -765,7 +716,7 @@ index a59d23e..d5a2bfd 100644 USB_PRINTER= USB_WDM= diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig -index 2e6b1fc..1edfb29 100644 +index f3cb47e..5f173c8 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -188,7 +188,7 @@ config CFG80211_WEXT_EXPORT diff --git a/recipes-kernel/mac80211/mac80211/0002-backport-of-subsys-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0002-backport-of-subsys-patches-from-openwrt.patch index 82c51f5..8237145 100644 --- a/recipes-kernel/mac80211/mac80211/0002-backport-of-subsys-patches-from-openwrt.patch +++ b/recipes-kernel/mac80211/mac80211/0002-backport-of-subsys-patches-from-openwrt.patch @@ -1,487 +1,301 @@ -From 02ae47316099feb8f3634fc87005b064a1cd7379 Mon Sep 17 00:00:00 2001 +From af9497b64852209f0f7980eaeede4cae9a741942 Mon Sep 17 00:00:00 2001 From: Patrick Walther -Date: Mon, 22 Mar 2021 16:34:34 +0100 -Subject: [PATCH] 0002 backport of subsys patches from openwrt +Date: Wed, 14 Sep 2022 14:25:55 +0200 +Subject: [PATCH] backport of subsys patches from openwrt --- - include/net/cfg80211.h | 6 +- - include/net/fq.h | 11 +- - include/net/fq_impl.h | 171 ++-- - include/net/mac80211.h | 27 + - include/uapi/linux/nl80211.h | 11 + - net/mac80211/Kconfig | 3 - - net/mac80211/Makefile | 5 +- - net/mac80211/aead_api.c | 112 --- - net/mac80211/aead_api.h | 23 - - net/mac80211/aes_ccm.c | 144 ++++ - net/mac80211/aes_ccm.h | 46 +- - net/mac80211/aes_cmac.c | 148 +++- - net/mac80211/aes_cmac.h | 11 +- - net/mac80211/aes_gcm.c | 109 +++ - net/mac80211/aes_gcm.h | 30 +- - net/mac80211/aes_gmac.h | 22 +- - net/mac80211/cfg.c | 17 +- - net/mac80211/debugfs.c | 1 + - net/mac80211/debugfs_sta.c | 1 + - net/mac80211/driver-ops.h | 16 + - net/mac80211/fils_aead.c | 2 +- - net/mac80211/fils_aead.h | 2 +- - net/mac80211/ieee80211_i.h | 6 +- - net/mac80211/iface.c | 17 +- - net/mac80211/key.h | 4 +- - net/mac80211/main.c | 29 +- - net/mac80211/mlme.c | 72 +- - net/mac80211/rc80211_minstrel.c | 574 ------------- - net/mac80211/rc80211_minstrel.h | 184 ----- - net/mac80211/rc80211_minstrel_debugfs.c | 172 ---- - net/mac80211/rc80211_minstrel_ht.c | 1223 ++++++++++++++++------------ - net/mac80211/rc80211_minstrel_ht.h | 137 +++- - net/mac80211/rc80211_minstrel_ht_debugfs.c | 79 +- - net/mac80211/rx.c | 260 +++--- - net/mac80211/sta_info.c | 1 + - net/mac80211/sta_info.h | 2 + - net/mac80211/trace.h | 41 +- - net/mac80211/tx.c | 24 +- - net/mac80211/wpa.c | 43 +- - net/wireless/core.c | 15 - - net/wireless/mlme.c | 26 +- - net/wireless/nl80211.c | 38 +- - net/wireless/nl80211.h | 8 +- - net/wireless/sysfs.c | 27 +- - net/wireless/trace.h | 12 +- - 45 files changed, 1856 insertions(+), 2056 deletions(-) - delete mode 100644 net/mac80211/aead_api.c - delete mode 100644 net/mac80211/aead_api.h - create mode 100644 net/mac80211/aes_ccm.c - create mode 100644 net/mac80211/aes_gcm.c - delete mode 100644 net/mac80211/rc80211_minstrel.c - delete mode 100644 net/mac80211/rc80211_minstrel.h - delete mode 100644 net/mac80211/rc80211_minstrel_debugfs.c + drivers/net/wireless/mac80211_hwsim.c | 27 +- + include/net/cfg80211.h | 89 +++- + include/net/mac80211.h | 47 +- + include/uapi/linux/nl80211.h | 100 +++- + net/mac80211/cfg.c | 231 +++++++-- + net/mac80211/debugfs.c | 102 ++-- + net/mac80211/debugfs_netdev.c | 32 +- + net/mac80211/debugfs_sta.c | 24 +- + net/mac80211/driver-ops.h | 24 + + net/mac80211/ieee80211_i.h | 210 ++------- + net/mac80211/iface.c | 99 +++- + net/mac80211/main.c | 39 +- + net/mac80211/rc80211_minstrel_ht.c | 75 +-- + net/mac80211/rc80211_minstrel_ht.h | 2 +- + net/mac80211/rx.c | 53 ++- + net/mac80211/sta_info.c | 79 ++-- + net/mac80211/sta_info.h | 12 +- + net/mac80211/status.c | 19 - + net/mac80211/trace.h | 9 + + net/mac80211/tx.c | 644 ++++++++++++-------------- + net/mac80211/util.c | 3 + + net/wireless/chan.c | 16 + + net/wireless/core.c | 23 +- + net/wireless/core.h | 16 + + net/wireless/mlme.c | 153 +++++- + net/wireless/nl80211.c | 260 ++++++++++- + net/wireless/rdev-ops.h | 17 + + net/wireless/sysfs.c | 27 +- + net/wireless/trace.h | 30 +- + 30 files changed, 1581 insertions(+), 907 deletions(-) + delete mode 100644 backport-include/linux/of_net.h +diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c +index bfc2b1f..79a00c2 100644 +--- a/drivers/net/wireless/mac80211_hwsim.c ++++ b/drivers/net/wireless/mac80211_hwsim.c +@@ -3004,15 +3004,19 @@ static void mac80211_hwsim_he_capab(struct ieee80211_supported_band *sband) + { + u16 n_iftype_data; + +- if (sband->band == NL80211_BAND_2GHZ) { ++ switch (sband->band) { ++ case NL80211_BAND_2GHZ: + n_iftype_data = ARRAY_SIZE(he_capa_2ghz); + sband->iftype_data = + (struct ieee80211_sband_iftype_data *)he_capa_2ghz; +- } else if (sband->band == NL80211_BAND_5GHZ) { ++ break; ++ case NL80211_BAND_5GHZ: ++ case NL80211_BAND_6GHZ: + n_iftype_data = ARRAY_SIZE(he_capa_5ghz); + sband->iftype_data = + (struct ieee80211_sband_iftype_data *)he_capa_5ghz; +- } else { ++ break; ++ default: + return; + } + +@@ -3302,6 +3306,12 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, + sband->vht_cap.vht_mcs.tx_mcs_map = + sband->vht_cap.vht_mcs.rx_mcs_map; + break; ++ case NL80211_BAND_6GHZ: ++ sband->channels = data->channels_6ghz; ++ sband->n_channels = ARRAY_SIZE(hwsim_channels_6ghz); ++ sband->bitrates = data->rates + 4; ++ sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; ++ break; + case NL80211_BAND_S1GHZ: + memcpy(&sband->s1g_cap, &hwsim_s1g_cap, + sizeof(sband->s1g_cap)); +@@ -3312,6 +3322,13 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, + continue; + } + ++ mac80211_hwsim_he_capab(sband); ++ ++ hw->wiphy->bands[band] = sband; ++ ++ if (band == NL80211_BAND_6GHZ) ++ continue; ++ + sband->ht_cap.ht_supported = true; + sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | +@@ -3325,10 +3342,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, + sband->ht_cap.mcs.rx_mask[0] = 0xff; + sband->ht_cap.mcs.rx_mask[1] = 0xff; + sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +- +- mac80211_hwsim_he_capab(sband); +- +- hw->wiphy->bands[band] = sband; + } + + /* By default all radios belong to the first group */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h -index 695bb73..449df09 100644 +index 30c9ebd..ab83553 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h -@@ -3736,6 +3736,7 @@ struct mgmt_frame_regs { +@@ -1045,6 +1045,36 @@ struct cfg80211_crypto_settings { + enum nl80211_sae_pwe_mechanism sae_pwe; + }; + ++/** ++ * struct cfg80211_mbssid_config - AP settings for multi bssid ++ * ++ * @tx_wdev: pointer to the transmitted interface in the MBSSID set ++ * @index: index of this AP in the multi bssid group. ++ * @ema: set to true if the beacons should be sent out in EMA mode. ++ */ ++struct cfg80211_mbssid_config { ++ struct wireless_dev *tx_wdev; ++ u8 index; ++ bool ema; ++}; ++ ++/** ++ * struct cfg80211_mbssid_elems - Multiple BSSID elements ++ * ++ * @cnt: Number of elements in array %elems. ++ * ++ * @elem: Array of multiple BSSID element(s) to be added into Beacon frames. ++ * @elem.data: Data for multiple BSSID elements. ++ * @elem.len: Length of data. ++ */ ++struct cfg80211_mbssid_elems { ++ u8 cnt; ++ struct { ++ const u8 *data; ++ size_t len; ++ } elem[]; ++}; ++ + /** + * struct cfg80211_beacon_data - beacon data + * @head: head portion of beacon (before TIM IE) +@@ -1063,6 +1093,7 @@ struct cfg80211_crypto_settings { + * @assocresp_ies_len: length of assocresp_ies in octets + * @probe_resp_len: length of probe response template (@probe_resp) + * @probe_resp: probe response template (AP mode only) ++ * @mbssid_ies: multiple BSSID elements + * @ftm_responder: enable FTM responder functionality; -1 for no change + * (which also implies no change in LCI/civic location data) + * @lci: Measurement Report element content, starting with Measurement Token +@@ -1080,6 +1111,7 @@ struct cfg80211_beacon_data { + const u8 *probe_resp; + const u8 *lci; + const u8 *civicloc; ++ struct cfg80211_mbssid_elems *mbssid_ies; + s8 ftm_responder; + + size_t head_len, tail_len; +@@ -1194,6 +1226,7 @@ enum cfg80211_ap_settings_flags { + * @he_oper: HE operation IE (or %NULL if HE isn't enabled) + * @fils_discovery: FILS discovery transmission parameters + * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters ++ * @mbssid_config: AP settings for multiple bssid + */ + struct cfg80211_ap_settings { + struct cfg80211_chan_def chandef; +@@ -1226,6 +1259,7 @@ struct cfg80211_ap_settings { + struct cfg80211_he_bss_color he_bss_color; + struct cfg80211_fils_discovery fils_discovery; + struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp; ++ struct cfg80211_mbssid_config mbssid_config; + }; + + /** +@@ -3835,6 +3869,7 @@ struct mgmt_frame_regs { * (as advertised by the nl80211 feature flag.) * @get_tx_power: store the current TX power into the dbm variable; * return 0 if successful + * @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary * - * @set_wds_peer: set the WDS peer for a WDS interface + * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting + * functions to adjust rfkill hw state +@@ -4023,6 +4058,15 @@ struct mgmt_frame_regs { + * @set_sar_specs: Update the SAR (TX power) settings. * -@@ -4058,6 +4059,7 @@ struct cfg80211_ops { + * @color_change: Initiate a color change. ++ * ++ * @set_radar_background: Configure dedicated offchannel chain available for ++ * radar/CAC detection on some hw. This chain can't be used to transmit ++ * or receive frames and it is bounded to a running wdev. ++ * Background radar/CAC detection allows to avoid the CAC downtime ++ * switching to a different channel during CAC detection on the selected ++ * radar channel. ++ * The caller is expected to set chandef pointer to NULL in order to ++ * disable background CAC/radar detection. + */ + struct cfg80211_ops { + int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); +@@ -4159,6 +4203,7 @@ struct cfg80211_ops { enum nl80211_tx_power_setting type, int mbm); int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, int *dbm); + int (*set_antenna_gain)(struct wiphy *wiphy, int dbi); - int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr); -@@ -6410,13 +6412,15 @@ void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss); - * @dev: network device - * @buf: 802.11 frame (header + body) - * @len: length of the frame data -+ * @reconnect: immediate reconnect is desired (include the nl80211 attribute) - * - * This function is called whenever deauthentication has been processed in - * station mode. This includes both received deauthentication frames and - * locally generated ones. This function may sleep. The caller must hold the - * corresponding wdev's mutex. - */ --void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len); -+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len, -+ bool reconnect); + void (*rfkill_poll)(struct wiphy *wiphy); - /** - * cfg80211_rx_unprot_mlme_mgmt - notification of unprotected mlme mgmt frame -diff --git a/include/net/fq.h b/include/net/fq.h -index e39f3f8..2eccbbd 100644 ---- a/include/net/fq.h -+++ b/include/net/fq.h -@@ -19,8 +19,6 @@ struct fq_tin; - * @flowchain: can be linked to fq_tin's new_flows or old_flows. Used for DRR++ - * (deficit round robin) based round robin queuing similar to the one - * found in net/sched/sch_fq_codel.c -- * @backlogchain: can be linked to other fq_flow and fq. Used to keep track of -- * fat flows and efficient head-dropping if packet limit is reached - * @queue: sk_buff queue to hold packets - * @backlog: number of bytes pending in the queue. The number of packets can be - * found in @queue.qlen -@@ -29,7 +27,6 @@ struct fq_tin; - struct fq_flow { - struct fq_tin *tin; - struct list_head flowchain; -- struct list_head backlogchain; - struct sk_buff_head queue; - u32 backlog; - int deficit; -@@ -47,6 +44,8 @@ struct fq_flow { - struct fq_tin { - struct list_head new_flows; - struct list_head old_flows; -+ struct list_head tin_list; -+ struct fq_flow default_flow; - u32 backlog_bytes; - u32 backlog_packets; - u32 overlimit; -@@ -59,14 +58,14 @@ struct fq_tin { - /** - * struct fq - main container for fair queuing purposes - * -- * @backlogs: linked to fq_flows. Used to maintain fat flows for efficient -- * head-dropping when @backlog reaches @limit - * @limit: max number of packets that can be queued across all flows - * @backlog: number of packets queued across all flows - */ - struct fq { - struct fq_flow *flows; -- struct list_head backlogs; -+ unsigned long *flows_bitmap; -+ -+ struct list_head tin_backlog; - spinlock_t lock; - u32 flows_cnt; - u32 limit; -diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h -index e73d74d..a5f67a2 100644 ---- a/include/net/fq_impl.h -+++ b/include/net/fq_impl.h -@@ -11,35 +11,37 @@ - - /* functions that are embedded into includer */ - --static void fq_adjust_removal(struct fq *fq, -- struct fq_flow *flow, -- struct sk_buff *skb) -+ -+static void -+__fq_adjust_removal(struct fq *fq, struct fq_flow *flow, unsigned int packets, -+ unsigned int bytes, unsigned int truesize) - { - struct fq_tin *tin = flow->tin; -+ int idx; - -- tin->backlog_bytes -= skb->len; -- tin->backlog_packets--; -- flow->backlog -= skb->len; -- fq->backlog--; -- fq->memory_usage -= skb->truesize; --} -+ tin->backlog_bytes -= bytes; -+ tin->backlog_packets -= packets; -+ flow->backlog -= bytes; -+ fq->backlog -= packets; -+ fq->memory_usage -= truesize; - --static void fq_rejigger_backlog(struct fq *fq, struct fq_flow *flow) --{ -- struct fq_flow *i; -+ if (flow->backlog) -+ return; - -- if (flow->backlog == 0) { -- list_del_init(&flow->backlogchain); -- } else { -- i = flow; -+ if (flow == &tin->default_flow) { -+ list_del_init(&tin->tin_list); -+ return; -+ } - -- list_for_each_entry_continue(i, &fq->backlogs, backlogchain) -- if (i->backlog < flow->backlog) -- break; -+ idx = flow - fq->flows; -+ __clear_bit(idx, fq->flows_bitmap); -+} - -- list_move_tail(&flow->backlogchain, -- &i->backlogchain); -- } -+static void fq_adjust_removal(struct fq *fq, -+ struct fq_flow *flow, -+ struct sk_buff *skb) -+{ -+ __fq_adjust_removal(fq, flow, 1, skb->len, skb->truesize); - } - - static struct sk_buff *fq_flow_dequeue(struct fq *fq, -@@ -54,11 +56,37 @@ static struct sk_buff *fq_flow_dequeue(struct fq *fq, - return NULL; - - fq_adjust_removal(fq, flow, skb); -- fq_rejigger_backlog(fq, flow); - - return skb; - } - -+static int fq_flow_drop(struct fq *fq, struct fq_flow *flow, -+ fq_skb_free_t free_func) -+{ -+ unsigned int packets = 0, bytes = 0, truesize = 0; -+ struct fq_tin *tin = flow->tin; -+ struct sk_buff *skb; -+ int pending; -+ -+ lockdep_assert_held(&fq->lock); -+ -+ pending = min_t(int, 32, skb_queue_len(&flow->queue) / 2); -+ do { -+ skb = __skb_dequeue(&flow->queue); -+ if (!skb) -+ break; -+ -+ packets++; -+ bytes += skb->len; -+ truesize += skb->truesize; -+ free_func(fq, tin, flow, skb); -+ } while (packets < pending); -+ -+ __fq_adjust_removal(fq, flow, packets, bytes, truesize); -+ -+ return packets; -+} -+ - static struct sk_buff *fq_tin_dequeue(struct fq *fq, - struct fq_tin *tin, - fq_tin_dequeue_t dequeue_func) -@@ -115,8 +143,7 @@ static u32 fq_flow_idx(struct fq *fq, struct sk_buff *skb) - - static struct fq_flow *fq_flow_classify(struct fq *fq, - struct fq_tin *tin, u32 idx, -- struct sk_buff *skb, -- fq_flow_get_default_t get_default_func) -+ struct sk_buff *skb) - { - struct fq_flow *flow; - -@@ -124,7 +151,7 @@ static struct fq_flow *fq_flow_classify(struct fq *fq, - - flow = &fq->flows[idx]; - if (flow->tin && flow->tin != tin) { -- flow = get_default_func(fq, tin, idx, skb); -+ flow = &tin->default_flow; - tin->collisions++; - fq->collisions++; - } -@@ -135,36 +162,56 @@ static struct fq_flow *fq_flow_classify(struct fq *fq, - return flow; - } - --static void fq_recalc_backlog(struct fq *fq, -- struct fq_tin *tin, -- struct fq_flow *flow) -+static struct fq_flow *fq_find_fattest_flow(struct fq *fq) - { -- struct fq_flow *i; -+ struct fq_tin *tin; -+ struct fq_flow *flow = NULL; -+ u32 len = 0; -+ int i; - -- if (list_empty(&flow->backlogchain)) -- list_add_tail(&flow->backlogchain, &fq->backlogs); -+ for_each_set_bit(i, fq->flows_bitmap, fq->flows_cnt) { -+ struct fq_flow *cur = &fq->flows[i]; -+ unsigned int cur_len; - -- i = flow; -- list_for_each_entry_continue_reverse(i, &fq->backlogs, -- backlogchain) -- if (i->backlog > flow->backlog) -- break; -+ cur_len = cur->backlog; -+ if (cur_len <= len) -+ continue; -+ -+ flow = cur; -+ len = cur_len; -+ } -+ -+ list_for_each_entry(tin, &fq->tin_backlog, tin_list) { -+ unsigned int cur_len = tin->default_flow.backlog; - -- list_move(&flow->backlogchain, &i->backlogchain); -+ if (cur_len <= len) -+ continue; -+ -+ flow = &tin->default_flow; -+ len = cur_len; -+ } -+ -+ return flow; - } - - static void fq_tin_enqueue(struct fq *fq, - struct fq_tin *tin, u32 idx, - struct sk_buff *skb, -- fq_skb_free_t free_func, -- fq_flow_get_default_t get_default_func) -+ fq_skb_free_t free_func) - { - struct fq_flow *flow; - bool oom; - - lockdep_assert_held(&fq->lock); - -- flow = fq_flow_classify(fq, tin, idx, skb, get_default_func); -+ flow = fq_flow_classify(fq, tin, idx, skb); -+ -+ if (!flow->backlog) { -+ if (flow != &tin->default_flow) -+ __set_bit(idx, fq->flows_bitmap); -+ else if (list_empty(&tin->tin_list)) -+ list_add(&tin->tin_list, &fq->tin_backlog); -+ } - - flow->tin = tin; - flow->backlog += skb->len; -@@ -173,8 +220,6 @@ static void fq_tin_enqueue(struct fq *fq, - fq->memory_usage += skb->truesize; - fq->backlog++; - -- fq_recalc_backlog(fq, tin, flow); -- - if (list_empty(&flow->flowchain)) { - flow->deficit = fq->quantum; - list_add_tail(&flow->flowchain, -@@ -184,18 +229,13 @@ static void fq_tin_enqueue(struct fq *fq, - __skb_queue_tail(&flow->queue, skb); - oom = (fq->memory_usage > fq->memory_limit); - while (fq->backlog > fq->limit || oom) { -- flow = list_first_entry_or_null(&fq->backlogs, -- struct fq_flow, -- backlogchain); -+ flow = fq_find_fattest_flow(fq); - if (!flow) - return; - -- skb = fq_flow_dequeue(fq, flow); -- if (!skb) -+ if (!fq_flow_drop(fq, flow, free_func)) - return; - -- free_func(fq, flow->tin, flow, skb); -- - flow->tin->overlimit++; - fq->overlimit++; - if (oom) { -@@ -224,8 +264,6 @@ static void fq_flow_filter(struct fq *fq, - fq_adjust_removal(fq, flow, skb); - free_func(fq, tin, flow, skb); - } -- -- fq_rejigger_backlog(fq, flow); - } - - static void fq_tin_filter(struct fq *fq, -@@ -248,16 +286,18 @@ static void fq_flow_reset(struct fq *fq, - struct fq_flow *flow, - fq_skb_free_t free_func) - { -+ struct fq_tin *tin = flow->tin; - struct sk_buff *skb; - - while ((skb = fq_flow_dequeue(fq, flow))) -- free_func(fq, flow->tin, flow, skb); -+ free_func(fq, tin, flow, skb); - -- if (!list_empty(&flow->flowchain)) -+ if (!list_empty(&flow->flowchain)) { - list_del_init(&flow->flowchain); -- -- if (!list_empty(&flow->backlogchain)) -- list_del_init(&flow->backlogchain); -+ if (list_empty(&tin->new_flows) && -+ list_empty(&tin->old_flows)) -+ list_del_init(&tin->tin_list); -+ } - - flow->tin = NULL; - -@@ -283,6 +323,7 @@ static void fq_tin_reset(struct fq *fq, - fq_flow_reset(fq, flow, free_func); - } - -+ WARN_ON_ONCE(!list_empty(&tin->tin_list)); - WARN_ON_ONCE(tin->backlog_bytes); - WARN_ON_ONCE(tin->backlog_packets); - } -@@ -290,7 +331,6 @@ static void fq_tin_reset(struct fq *fq, - static void fq_flow_init(struct fq_flow *flow) - { - INIT_LIST_HEAD(&flow->flowchain); -- INIT_LIST_HEAD(&flow->backlogchain); - __skb_queue_head_init(&flow->queue); - } - -@@ -298,6 +338,8 @@ static void fq_tin_init(struct fq_tin *tin) - { - INIT_LIST_HEAD(&tin->new_flows); - INIT_LIST_HEAD(&tin->old_flows); -+ INIT_LIST_HEAD(&tin->tin_list); -+ fq_flow_init(&tin->default_flow); - } - - static int fq_init(struct fq *fq, int flows_cnt) -@@ -305,8 +347,8 @@ static int fq_init(struct fq *fq, int flows_cnt) - int i; - - memset(fq, 0, sizeof(fq[0])); -- INIT_LIST_HEAD(&fq->backlogs); - spin_lock_init(&fq->lock); -+ INIT_LIST_HEAD(&fq->tin_backlog); - fq->flows_cnt = max_t(u32, flows_cnt, 1); - fq->quantum = 300; - fq->limit = 8192; -@@ -316,6 +358,14 @@ static int fq_init(struct fq *fq, int flows_cnt) - if (!fq->flows) - return -ENOMEM; - -+ fq->flows_bitmap = kcalloc(BITS_TO_LONGS(fq->flows_cnt), sizeof(long), -+ GFP_KERNEL); -+ if (!fq->flows_bitmap) { -+ kvfree(fq->flows); -+ fq->flows = NULL; -+ return -ENOMEM; -+ } -+ - for (i = 0; i < fq->flows_cnt; i++) - fq_flow_init(&fq->flows[i]); - -@@ -332,6 +382,9 @@ static void fq_reset(struct fq *fq, - - kvfree(fq->flows); - fq->flows = NULL; -+ -+ kfree(fq->flows_bitmap); -+ fq->flows_bitmap = NULL; - } - - #endif -diff --git a/include/net/mac80211.h b/include/net/mac80211.h -index 9840f6e..9f185ee 100644 ---- a/include/net/mac80211.h -+++ b/include/net/mac80211.h -@@ -1297,6 +1297,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) - * the "0-length PSDU" field included there. The value for it is - * in &struct ieee80211_rx_status. Note that if this value isn't - * known the frame shouldn't be reported. -+ * @RX_FLAG_8023: the frame has an 802.3 header (decap offload performed by -+ * hardware or driver) - */ - enum mac80211_rx_flags { - RX_FLAG_MMIC_ERROR = BIT(0), -@@ -1329,6 +1331,7 @@ enum mac80211_rx_flags { - RX_FLAG_RADIOTAP_HE_MU = BIT(27), - RX_FLAG_RADIOTAP_LSIG = BIT(28), - RX_FLAG_NO_PSDU = BIT(29), -+ RX_FLAG_8023 = BIT(30), +@@ -4353,6 +4398,8 @@ struct cfg80211_ops { + int (*color_change)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_color_change_settings *params); ++ int (*set_radar_background)(struct wiphy *wiphy, ++ struct cfg80211_chan_def *chandef); }; + /* +@@ -4986,6 +5033,13 @@ struct wiphy_iftype_akm_suites { + * %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes + * @sar_capa: SAR control capabilities + * @rfkill: a pointer to the rfkill structure ++ * ++ * @mbssid_max_interfaces: maximum number of interfaces supported by the driver ++ * in a multiple BSSID set. This field must be set to a non-zero value ++ * by the driver to advertise MBSSID support. ++ * @mbssid_max_ema_profile_periodicity: maximum profile periodicity supported by ++ * the driver. Setting this field to a non-zero value indicates that the ++ * driver supports enhanced multi-BSSID advertisements (EMA AP). + */ + struct wiphy { + struct mutex mtx; +@@ -5133,6 +5187,9 @@ struct wiphy { + + struct rfkill *rfkill; + ++ u8 mbssid_max_interfaces; ++ u8 ema_max_profile_periodicity; ++ + char priv[] __aligned(NETDEV_ALIGN); + }; + +@@ -7525,15 +7582,33 @@ void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, + void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp); + /** -@@ -1558,6 +1561,7 @@ enum ieee80211_smps_mode { +- * cfg80211_radar_event - radar detection event ++ * __cfg80211_radar_event - radar detection event + * @wiphy: the wiphy + * @chandef: chandef for the current channel ++ * @offchan: the radar has been detected on the offchannel chain + * @gfp: context flags + * + * This function is called when a radar is detected on the current chanenl. + */ +-void cfg80211_radar_event(struct wiphy *wiphy, +- struct cfg80211_chan_def *chandef, gfp_t gfp); ++void __cfg80211_radar_event(struct wiphy *wiphy, ++ struct cfg80211_chan_def *chandef, ++ bool offchan, gfp_t gfp); ++ ++static inline void ++cfg80211_radar_event(struct wiphy *wiphy, ++ struct cfg80211_chan_def *chandef, ++ gfp_t gfp) ++{ ++ __cfg80211_radar_event(wiphy, chandef, false, gfp); ++} ++ ++static inline void ++cfg80211_background_radar_event(struct wiphy *wiphy, ++ struct cfg80211_chan_def *chandef, ++ gfp_t gfp) ++{ ++ __cfg80211_radar_event(wiphy, chandef, true, gfp); ++} + + /** + * cfg80211_sta_opmode_change_notify - STA's ht/vht operation mode change event +@@ -7564,6 +7639,14 @@ void cfg80211_cac_event(struct net_device *netdev, + const struct cfg80211_chan_def *chandef, + enum nl80211_radar_event event, gfp_t gfp); + ++/** ++ * cfg80211_background_cac_abort - Channel Availability Check offchan abort event ++ * @wiphy: the wiphy ++ * ++ * This function is called by the driver when a Channel Availability Check ++ * (CAC) is aborted by a offchannel dedicated chain. ++ */ ++void cfg80211_background_cac_abort(struct wiphy *wiphy); + + /** + * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying +diff --git a/include/net/mac80211.h b/include/net/mac80211.h +index a331431..c85050f 100644 +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -1566,6 +1566,7 @@ enum ieee80211_smps_mode { * * @power_level: requested transmit power (in dBm), backward compatibility * value only that is set to the minimum of all interfaces @@ -489,7 +303,7 @@ index 9840f6e..9f185ee 100644 * * @chandef: the channel definition to tune to * @radar_enabled: whether radar detection is enabled -@@ -1578,6 +1582,7 @@ enum ieee80211_smps_mode { +@@ -1586,6 +1587,7 @@ enum ieee80211_smps_mode { struct ieee80211_conf { u32 flags; int power_level, dynamic_ps_timeout; @@ -497,79 +311,165 @@ index 9840f6e..9f185ee 100644 u16 listen_interval; u8 ps_dtim_period; -@@ -1650,11 +1655,15 @@ enum ieee80211_vif_flags { - * The driver supports sending frames passed as 802.3 frames by mac80211. - * It must also support sending 802.11 packets for the same interface. - * @IEEE80211_OFFLOAD_ENCAP_4ADDR: support 4-address mode encapsulation offload -+ * @IEEE80211_OFFLOAD_DECAP_ENABLED: rx encapsulation offload is enabled -+ * The driver supports passing received 802.11 frames as 802.3 frames to -+ * mac80211. +@@ -1719,6 +1721,7 @@ enum ieee80211_offload_flags { + * write-protected by sdata_lock and local->mtx so holding either is fine + * for read access. + * @color_change_color: the bss color that will be used after the change. ++ * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled. */ + struct ieee80211_vif { + enum nl80211_iftype type; +@@ -1750,6 +1753,8 @@ struct ieee80211_vif { + bool color_change_active; + u8 color_change_color; - enum ieee80211_offload_flags { - IEEE80211_OFFLOAD_ENCAP_ENABLED = BIT(0), - IEEE80211_OFFLOAD_ENCAP_4ADDR = BIT(1), -+ IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2), ++ struct ieee80211_vif *mbssid_tx_vif; ++ + /* must be last */ + u8 drv_priv[] __aligned(sizeof(void *)); }; - - /** -@@ -2390,6 +2399,9 @@ struct ieee80211_txq { - * @IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD: Hardware supports tx encapsulation - * offload +@@ -2415,6 +2420,9 @@ struct ieee80211_txq { + * usage and 802.11 frames with %RX_FLAG_ONLY_MONITOR set for monitor to + * the stack. * -+ * @IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD: Hardware supports rx decapsulation -+ * offload ++ * @IEEE80211_HW_DETECTS_COLOR_COLLISION: HW/driver has support for BSS color ++ * collision detection and doesn't need it in software. + * * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays */ enum ieee80211_hw_flags { -@@ -2443,6 +2455,7 @@ enum ieee80211_hw_flags { - IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID, - IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT, +@@ -2470,6 +2478,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD, -+ IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD, + IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD, + IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP, ++ IEEE80211_HW_DETECTS_COLOR_COLLISION, /* keep last, obviously */ NUM_IEEE80211_HW_FLAGS -@@ -4196,6 +4209,9 @@ struct ieee80211_ops { - struct ieee80211_vif *vif); - void (*sta_set_4addr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool enabled); -+ void (*sta_set_decap_offload)(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta, bool enabled); +@@ -3937,6 +3946,16 @@ struct ieee80211_prep_tx_info { + * twt structure. + * @twt_teardown_request: Update the hw with TWT teardown request received + * from the peer. ++ * @set_radar_background: Configure dedicated offchannel chain available for ++ * radar/CAC detection on some hw. This chain can't be used to transmit ++ * or receive frames and it is bounded to a running wdev. ++ * Background radar/CAC detection allows to avoid the CAC downtime ++ * switching to a different channel during CAC detection on the selected ++ * radar channel. ++ * The caller is expected to set chandef pointer to NULL in order to ++ * 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 + */ + struct ieee80211_ops { + void (*tx)(struct ieee80211_hw *hw, +@@ -4265,6 +4284,15 @@ struct ieee80211_ops { + struct ieee80211_twt_setup *twt); + void (*twt_teardown_request)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u8 flowid); ++ int (*set_radar_background)(struct ieee80211_hw *hw, ++ struct cfg80211_chan_def *chandef); ++#if LINUX_VERSION_IS_GEQ(5,10,0) ++ int (*net_fill_forward_path)(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ struct net_device_path_ctx *ctx, ++ struct net_device_path *path); ++#endif }; /** -@@ -5885,6 +5901,17 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); - void ieee80211_connection_loss(struct ieee80211_vif *vif); +@@ -4916,12 +4944,14 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); + * @cntdwn_counter_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets + * to countdown counters. This array can contain zero values which + * should be ignored. ++ * @mbssid_off: position of the multiple bssid element + */ + struct ieee80211_mutable_offsets { + u16 tim_offset; + u16 tim_length; + + u16 cntdwn_counter_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM]; ++ u16 mbssid_off; + }; /** -+ * ieee80211_disconnect - request disconnection -+ * -+ * @vif: &struct ieee80211_vif pointer from the add_interface callback. -+ * @reconnect: immediate reconnect is desired -+ * -+ * Request disconnection from the current network and, if enabled, send a -+ * hint to the higher layers that immediate reconnect is desired. -+ */ -+void ieee80211_disconnect(struct ieee80211_vif *vif, bool reconnect); +@@ -6642,6 +6672,9 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac) + { + } + ++void __ieee80211_schedule_txq(struct ieee80211_hw *hw, ++ struct ieee80211_txq *txq, bool force); + -+/** - * ieee80211_resume_disconnect - disconnect from AP after resume + /** + * ieee80211_schedule_txq - schedule a TXQ for transmission * - * @vif: &struct ieee80211_vif pointer from the add_interface callback. +@@ -6654,7 +6687,11 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac) + * The driver may call this function if it has buffered packets for + * this TXQ internally. + */ +-void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq); ++static inline void ++ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq) ++{ ++ __ieee80211_schedule_txq(hw, txq, true); ++} + + /** + * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq() +@@ -6666,8 +6703,12 @@ void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq); + * The driver may set force=true if it has buffered packets for this TXQ + * internally. + */ +-void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq, +- bool force); ++static inline void ++ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq, ++ bool force) ++{ ++ __ieee80211_schedule_txq(hw, txq, force); ++} + + /** + * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h -index 47700a2..0d90183 100644 +index c2efea9..019f065 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h -@@ -2527,6 +2527,13 @@ enum nl80211_commands { - * override mask. Used with NL80211_ATTR_S1G_CAPABILITY in - * NL80211_CMD_ASSOCIATE or NL80211_CMD_CONNECT. +@@ -337,7 +337,10 @@ + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires +- * attribute %NL80211_ATTR_IFINDEX. ++ * attribute %NL80211_ATTR_IFINDEX. If multiple BSSID advertisements are ++ * enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS, ++ * and if this command is used for the transmitting interface, then all ++ * the non-transmitting interfaces are deleted as well. * -+ * @NL80211_ATTR_RECONNECT_REQUESTED: flag attribute, used with deauth and -+ * disassoc events to indicate that an immediate reconnect to the AP -+ * is desired. + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. +@@ -2593,6 +2596,28 @@ enum nl80211_commands { + * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE + * information for the time while performing a color switch. + * ++ * @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID ++ * advertisements (MBSSID) parameters in AP mode. ++ * Kernel uses this attribute to indicate the driver's support for MBSSID ++ * and enhanced multi-BSSID advertisements (EMA AP) to the userspace. ++ * Userspace should use this attribute to configure per interface MBSSID ++ * parameters. ++ * See &enum nl80211_mbssid_config_attributes for details. ++ * ++ * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements. ++ * Mandatory parameter for the transmitting interface to enable MBSSID. ++ * Optional for the non-transmitting interfaces. ++ * ++ * @NL80211_ATTR_RADAR_BACKGROUND: Configure dedicated offchannel chain ++ * available for radar/CAC detection on some hw. This chain can't be used ++ * to transmit or receive frames and it is bounded to a running wdev. ++ * Background radar/CAC detection allows to avoid the CAC downtime ++ * switching on a different channel during CAC detection on the selected ++ * radar channel. + * + * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce + * transmit power to stay within regulatory limits. u32, dBi. @@ -577,836 +477,249 @@ index 47700a2..0d90183 100644 * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use -@@ -3016,6 +3023,10 @@ enum nl80211_attrs { - NL80211_ATTR_S1G_CAPABILITY, - NL80211_ATTR_S1G_CAPABILITY_MASK, +@@ -3096,6 +3121,13 @@ enum nl80211_attrs { + NL80211_ATTR_COLOR_CHANGE_COLOR, + NL80211_ATTR_COLOR_CHANGE_ELEMS, -+ NL80211_ATTR_RECONNECT_REQUESTED, ++ NL80211_ATTR_MBSSID_CONFIG, ++ NL80211_ATTR_MBSSID_ELEMS, ++ ++ NL80211_ATTR_RADAR_BACKGROUND, + + NL80211_ATTR_WIPHY_ANTENNA_GAIN, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, -diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig -index 329467a..0e07a82 100644 ---- a/net/mac80211/Kconfig -+++ b/net/mac80211/Kconfig -@@ -6,9 +6,6 @@ config MAC80211 - depends on CRYPTO - select BPAUTO_CRYPTO_LIB_ARC4 - depends on CRYPTO_AES -- depends on CRYPTO_CCM -- depends on CRYPTO_GCM -- depends on CRYPTO_CMAC - depends on CRC32 - help - This option enables the hardware independent IEEE 802.11 -diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile -index 8a8f265..6a2ce73 100644 ---- a/net/mac80211/Makefile -+++ b/net/mac80211/Makefile -@@ -7,7 +7,6 @@ mac80211-y := \ - driver-ops.o \ - sta_info.o \ - wep.o \ -- aead_api.o \ - wpa.o \ - scan.o offchannel.o \ - ht.o agg-tx.o agg-rx.o \ -@@ -19,8 +18,8 @@ mac80211-y := \ - rate.o \ - michael.o \ - tkip.o \ -+ aes_ccm.o \ - aes_cmac.o \ -- aes_gmac.o \ - fils_aead.o \ - cfg.o \ - ethtool.o \ -@@ -56,11 +55,9 @@ mac80211-$(CONFIG_PM) += pm.o - CFLAGS_trace.o := -I$(src) - - rc80211_minstrel-y := \ -- rc80211_minstrel.o \ - rc80211_minstrel_ht.o - - rc80211_minstrel-$(CPTCFG_MAC80211_DEBUGFS) += \ -- rc80211_minstrel_debugfs.o \ - rc80211_minstrel_ht_debugfs.o - - mac80211-$(CPTCFG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) -diff --git a/net/mac80211/aead_api.c b/net/mac80211/aead_api.c -deleted file mode 100644 -index d7b3d90..0000000 ---- a/net/mac80211/aead_api.c -+++ /dev/null -@@ -1,112 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright 2003-2004, Instant802 Networks, Inc. -- * Copyright 2005-2006, Devicescape Software, Inc. -- * Copyright 2014-2015, Qualcomm Atheros, Inc. -- * -- * Rewrite: Copyright (C) 2013 Linaro Ltd -- */ -- --#include --#include --#include --#include --#include -- --#include "aead_api.h" -- --int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, -- u8 *data, size_t data_len, u8 *mic) --{ -- size_t mic_len = crypto_aead_authsize(tfm); -- struct scatterlist sg[3]; -- struct aead_request *aead_req; -- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -- u8 *__aad; -- -- aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC); -- if (!aead_req) -- return -ENOMEM; -- -- __aad = (u8 *)aead_req + reqsize; -- memcpy(__aad, aad, aad_len); -- -- sg_init_table(sg, 3); -- sg_set_buf(&sg[0], __aad, aad_len); -- sg_set_buf(&sg[1], data, data_len); -- sg_set_buf(&sg[2], mic, mic_len); -- -- aead_request_set_tfm(aead_req, tfm); -- aead_request_set_crypt(aead_req, sg, sg, data_len, b_0); -- aead_request_set_ad(aead_req, sg[0].length); -- -- crypto_aead_encrypt(aead_req); -- kfree_sensitive(aead_req); -- -- return 0; --} -- --int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, -- u8 *data, size_t data_len, u8 *mic) --{ -- size_t mic_len = crypto_aead_authsize(tfm); -- struct scatterlist sg[3]; -- struct aead_request *aead_req; -- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -- u8 *__aad; -- int err; -- -- if (data_len == 0) -- return -EINVAL; -- -- aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC); -- if (!aead_req) -- return -ENOMEM; -- -- __aad = (u8 *)aead_req + reqsize; -- memcpy(__aad, aad, aad_len); -- -- sg_init_table(sg, 3); -- sg_set_buf(&sg[0], __aad, aad_len); -- sg_set_buf(&sg[1], data, data_len); -- sg_set_buf(&sg[2], mic, mic_len); -- -- aead_request_set_tfm(aead_req, tfm); -- aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0); -- aead_request_set_ad(aead_req, sg[0].length); -- -- err = crypto_aead_decrypt(aead_req); -- kfree_sensitive(aead_req); -- -- return err; --} -- --struct crypto_aead * --aead_key_setup_encrypt(const char *alg, const u8 key[], -- size_t key_len, size_t mic_len) --{ -- struct crypto_aead *tfm; -- int err; -- -- tfm = crypto_alloc_aead(alg, 0, CRYPTO_ALG_ASYNC); -- if (IS_ERR(tfm)) -- return tfm; -- -- err = crypto_aead_setkey(tfm, key, key_len); -- if (err) -- goto free_aead; -- err = crypto_aead_setauthsize(tfm, mic_len); -- if (err) -- goto free_aead; -- -- return tfm; -- --free_aead: -- crypto_free_aead(tfm); -- return ERR_PTR(err); --} -- --void aead_key_free(struct crypto_aead *tfm) --{ -- crypto_free_aead(tfm); --} -diff --git a/net/mac80211/aead_api.h b/net/mac80211/aead_api.h -deleted file mode 100644 -index 7d463b8..0000000 ---- a/net/mac80211/aead_api.h -+++ /dev/null -@@ -1,23 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ -- --#ifndef _AEAD_API_H --#define _AEAD_API_H -- --#include --#include -- --struct crypto_aead * --aead_key_setup_encrypt(const char *alg, const u8 key[], -- size_t key_len, size_t mic_len); -- --int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, -- size_t aad_len, u8 *data, -- size_t data_len, u8 *mic); -- --int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, -- size_t aad_len, u8 *data, -- size_t data_len, u8 *mic); -- --void aead_key_free(struct crypto_aead *tfm); -- --#endif /* _AEAD_API_H */ -diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c -new file mode 100644 -index 0000000..4f60182 ---- /dev/null -+++ b/net/mac80211/aes_ccm.c -@@ -0,0 +1,144 @@ -+/* -+ * Copyright 2003-2004, Instant802 Networks, Inc. -+ * Copyright 2005-2006, Devicescape Software, Inc. +@@ -5995,6 +6027,14 @@ enum nl80211_feature_flags { + * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision + * detection and change announcemnts. + * ++ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports ++ * FILS encryption and decryption for (Re)Association Request and Response ++ * frames. Userspace has to share FILS AAD details to the driver by using ++ * @NL80211_CMD_SET_FILS_AAD. + * -+ * Rewrite: Copyright (C) 2013 Linaro Ltd ++ * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC ++ * detection. + * -+ * 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. + * @NUM_NL80211_EXT_FEATURES: number of extended features. + * @MAX_NL80211_EXT_FEATURES: highest extended feature index. + */ +@@ -6060,6 +6100,8 @@ enum nl80211_ext_feature_index { + NL80211_EXT_FEATURE_SECURE_RTT, + NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, + NL80211_EXT_FEATURE_BSS_COLOR, ++ NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD, ++ NL80211_EXT_FEATURE_RADAR_BACKGROUND, + + /* add new features before the definition below */ + NUM_NL80211_EXT_FEATURES, +@@ -7349,4 +7391,60 @@ enum nl80211_sar_specs_attrs { + NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1, + }; + ++/** ++ * enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced ++ * multi-BSSID advertisements (EMA) in AP mode. ++ * Kernel uses some of these attributes to advertise driver's support for ++ * MBSSID and EMA. ++ * Remaining attributes should be used by the userspace to configure the ++ * features. ++ * ++ * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid ++ * ++ * @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise ++ * the maximum number of MBSSID interfaces supported by the driver. ++ * Driver should indicate MBSSID support by setting ++ * wiphy->mbssid_max_interfaces to a value more than or equal to 2. ++ * ++ * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel ++ * to advertise the maximum profile periodicity supported by the driver ++ * if EMA is enabled. Driver should indicate EMA support to the userspace ++ * by setting wiphy->mbssid_max_ema_profile_periodicity to ++ * a non-zero value. ++ * ++ * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of ++ * this BSS (u8) in the multiple BSSID set. ++ * Value must be set to 0 for the transmitting interface and non-zero for ++ * all non-transmitting interfaces. The userspace will be responsible ++ * for using unique indices for the interfaces. ++ * Range: 0 to wiphy->mbssid_max_interfaces-1. ++ * ++ * @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for ++ * a non-transmitted profile which provides the interface index (u32) of ++ * the transmitted profile. The value must match one of the interface ++ * indices advertised by the kernel. Optional if the interface being set up ++ * is the transmitting one, however, if provided then the value must match ++ * the interface index of the same. ++ * ++ * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature. ++ * Setting this flag is permitted only if the driver advertises EMA support ++ * by setting wiphy->mbssid_max_ema_profile_periodicity to non-zero. ++ * ++ * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal ++ * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute + */ ++enum nl80211_mbssid_config_attributes { ++ __NL80211_MBSSID_CONFIG_ATTR_INVALID, + -+#include -+#include -+#include -+#include -+#include ++ NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES, ++ NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY, ++ NL80211_MBSSID_CONFIG_ATTR_INDEX, ++ NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX, ++ NL80211_MBSSID_CONFIG_ATTR_EMA, + -+#include -+#include "key.h" -+#include "aes_ccm.h" ++ /* keep last */ ++ __NL80211_MBSSID_CONFIG_ATTR_LAST, ++ NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1, ++}; + -+static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, u8 *s_0, -+ u8 *a, u8 *b) -+{ -+ int i; -+ -+ crypto_cipher_encrypt_one(tfm, b, b_0); -+ -+ /* Extra Authenticate-only data (always two AES blocks) */ -+ for (i = 0; i < AES_BLOCK_SIZE; i++) -+ aad[i] ^= b[i]; -+ crypto_cipher_encrypt_one(tfm, b, aad); -+ -+ aad += AES_BLOCK_SIZE; -+ -+ for (i = 0; i < AES_BLOCK_SIZE; i++) -+ aad[i] ^= b[i]; -+ crypto_cipher_encrypt_one(tfm, a, aad); -+ -+ /* Mask out bits from auth-only-b_0 */ -+ b_0[0] &= 0x07; -+ -+ /* S_0 is used to encrypt T (= MIC) */ -+ b_0[14] = 0; -+ b_0[15] = 0; -+ crypto_cipher_encrypt_one(tfm, s_0, b_0); -+} -+ -+ -+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len) -+{ -+ int i, j, last_len, num_blocks; -+ u8 b[AES_BLOCK_SIZE]; -+ u8 s_0[AES_BLOCK_SIZE]; -+ u8 e[AES_BLOCK_SIZE]; -+ u8 *pos, *cpos; -+ -+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); -+ last_len = data_len % AES_BLOCK_SIZE; -+ aes_ccm_prepare(tfm, b_0, aad, s_0, b, b); -+ -+ /* Process payload blocks */ -+ pos = data; -+ cpos = data; -+ for (j = 1; j <= num_blocks; j++) { -+ int blen = (j == num_blocks && last_len) ? -+ last_len : AES_BLOCK_SIZE; -+ -+ /* Authentication followed by encryption */ -+ for (i = 0; i < blen; i++) -+ b[i] ^= pos[i]; -+ crypto_cipher_encrypt_one(tfm, b, b); -+ -+ b_0[14] = (j >> 8) & 0xff; -+ b_0[15] = j & 0xff; -+ crypto_cipher_encrypt_one(tfm, e, b_0); -+ for (i = 0; i < blen; i++) -+ *cpos++ = *pos++ ^ e[i]; -+ } -+ -+ for (i = 0; i < mic_len; i++) -+ mic[i] = b[i] ^ s_0[i]; -+} -+ -+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len) -+{ -+ int i, j, last_len, num_blocks; -+ u8 *pos, *cpos; -+ u8 a[AES_BLOCK_SIZE]; -+ u8 b[AES_BLOCK_SIZE]; -+ u8 s_0[AES_BLOCK_SIZE]; -+ -+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); -+ last_len = data_len % AES_BLOCK_SIZE; -+ aes_ccm_prepare(tfm, b_0, aad, s_0, a, b); -+ -+ /* Process payload blocks */ -+ cpos = data; -+ pos = data; -+ for (j = 1; j <= num_blocks; j++) { -+ int blen = (j == num_blocks && last_len) ? -+ last_len : AES_BLOCK_SIZE; -+ -+ /* Decryption followed by authentication */ -+ b_0[14] = (j >> 8) & 0xff; -+ b_0[15] = j & 0xff; -+ crypto_cipher_encrypt_one(tfm, b, b_0); -+ for (i = 0; i < blen; i++) { -+ *pos = *cpos++ ^ b[i]; -+ a[i] ^= *pos++; -+ } -+ crypto_cipher_encrypt_one(tfm, a, a); -+ } -+ -+ for (i = 0; i < mic_len; i++) { -+ if ((mic[i] ^ s_0[i]) != a[i]) -+ return -1; -+ } -+ -+ return 0; -+} -+ -+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], -+ size_t key_len, -+ size_t mic_len) -+{ -+ struct crypto_cipher *tfm; -+ -+ tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); -+ if (!IS_ERR(tfm)) -+ crypto_cipher_setkey(tfm, key, key_len); -+ -+ return tfm; -+} -+ -+ -+void ieee80211_aes_key_free(struct crypto_cipher *tfm) -+{ -+ crypto_free_cipher(tfm); -+} -diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h -index 9625619..7e99852 100644 ---- a/net/mac80211/aes_ccm.h -+++ b/net/mac80211/aes_ccm.h -@@ -7,39 +7,17 @@ - #ifndef AES_CCM_H - #define AES_CCM_H - --#include "aead_api.h" -- --#define CCM_AAD_LEN 32 -- --static inline struct crypto_aead * --ieee80211_aes_key_setup_encrypt(const u8 key[], size_t key_len, size_t mic_len) --{ -- return aead_key_setup_encrypt("ccm(aes)", key, key_len, mic_len); --} -- --static inline int --ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, -- u8 *b_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) --{ -- return aead_encrypt(tfm, b_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); --} -- --static inline int --ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, -- u8 *b_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) --{ -- return aead_decrypt(tfm, b_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); --} -- --static inline void ieee80211_aes_key_free(struct crypto_aead *tfm) --{ -- return aead_key_free(tfm); --} -+#include -+ -+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], -+ size_t key_len, -+ size_t mic_len); -+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len); -+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len); -+void ieee80211_aes_key_free(struct crypto_cipher *tfm); - - #endif /* AES_CCM_H */ -diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c -index b31f102..f06086c 100644 ---- a/net/mac80211/aes_cmac.c -+++ b/net/mac80211/aes_cmac.c -@@ -19,67 +19,151 @@ - #define CMAC_TLEN_256 16 /* CMAC TLen = 128 bits (16 octets) */ - #define AAD_LEN 20 - --static const u8 zero[CMAC_TLEN_256]; - --void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad, -+void gf_mulx(u8 *pad) -+{ -+ int i, carry; -+ -+ carry = pad[0] & 0x80; -+ for (i = 0; i < AES_BLOCK_SIZE - 1; i++) -+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); -+ pad[AES_BLOCK_SIZE - 1] <<= 1; -+ if (carry) -+ pad[AES_BLOCK_SIZE - 1] ^= 0x87; -+} -+ -+void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, -+ const u8 *addr[], const size_t *len, u8 *mac, -+ size_t mac_len) -+{ -+ u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; -+ const u8 *pos, *end; -+ size_t i, e, left, total_len; -+ -+ memset(cbc, 0, AES_BLOCK_SIZE); -+ -+ total_len = 0; -+ for (e = 0; e < num_elem; e++) -+ total_len += len[e]; -+ left = total_len; -+ -+ e = 0; -+ pos = addr[0]; -+ end = pos + len[0]; -+ -+ while (left >= AES_BLOCK_SIZE) { -+ for (i = 0; i < AES_BLOCK_SIZE; i++) { -+ cbc[i] ^= *pos++; -+ if (pos >= end) { -+ e++; -+ pos = addr[e]; -+ end = pos + len[e]; -+ } -+ } -+ if (left > AES_BLOCK_SIZE) -+ crypto_cipher_encrypt_one(tfm, cbc, cbc); -+ left -= AES_BLOCK_SIZE; -+ } -+ -+ memset(pad, 0, AES_BLOCK_SIZE); -+ crypto_cipher_encrypt_one(tfm, pad, pad); -+ gf_mulx(pad); -+ -+ if (left || total_len == 0) { -+ for (i = 0; i < left; i++) { -+ cbc[i] ^= *pos++; -+ if (pos >= end) { -+ e++; -+ pos = addr[e]; -+ end = pos + len[e]; -+ } -+ } -+ cbc[left] ^= 0x80; -+ gf_mulx(pad); -+ } -+ -+ for (i = 0; i < AES_BLOCK_SIZE; i++) -+ pad[i] ^= cbc[i]; -+ crypto_cipher_encrypt_one(tfm, pad, pad); -+ memcpy(mac, pad, mac_len); -+} -+ -+ -+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic) - { -- SHASH_DESC_ON_STACK(desc, tfm); -- u8 out[AES_BLOCK_SIZE]; -+ const u8 *addr[4]; -+ size_t len[4]; -+ u8 zero[CMAC_TLEN]; - const __le16 *fc; - -- desc->tfm = tfm; -- -- crypto_shash_init(desc); -- crypto_shash_update(desc, aad, AAD_LEN); -+ memset(zero, 0, CMAC_TLEN); -+ addr[0] = aad; -+ len[0] = AAD_LEN; - fc = (const __le16 *)aad; - if (ieee80211_is_beacon(*fc)) { - /* mask Timestamp field to zero */ -- crypto_shash_update(desc, zero, 8); -- crypto_shash_update(desc, data + 8, data_len - 8 - CMAC_TLEN); -+ addr[1] = zero; -+ len[1] = 8; -+ addr[2] = data + 8; -+ len[2] = data_len - 8 - CMAC_TLEN; -+ addr[3] = zero; -+ len[3] = CMAC_TLEN; -+ aes_cmac_vector(tfm, 4, addr, len, mic, CMAC_TLEN); - } else { -- crypto_shash_update(desc, data, data_len - CMAC_TLEN); -+ addr[1] = data; -+ len[1] = data_len - CMAC_TLEN; -+ addr[2] = zero; -+ len[2] = CMAC_TLEN; -+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN); - } -- crypto_shash_finup(desc, zero, CMAC_TLEN, out); -- -- memcpy(mic, out, CMAC_TLEN); - } - --void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad, -+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic) - { -- SHASH_DESC_ON_STACK(desc, tfm); -+ const u8 *addr[4]; -+ size_t len[4]; -+ u8 zero[CMAC_TLEN_256]; - const __le16 *fc; - -- desc->tfm = tfm; -- -- crypto_shash_init(desc); -- crypto_shash_update(desc, aad, AAD_LEN); -+ memset(zero, 0, CMAC_TLEN_256); -+ addr[0] = aad; -+ len[0] = AAD_LEN; -+ addr[1] = data; - fc = (const __le16 *)aad; - if (ieee80211_is_beacon(*fc)) { - /* mask Timestamp field to zero */ -- crypto_shash_update(desc, zero, 8); -- crypto_shash_update(desc, data + 8, -- data_len - 8 - CMAC_TLEN_256); -+ addr[1] = zero; -+ len[1] = 8; -+ addr[2] = data + 8; -+ len[2] = data_len - 8 - CMAC_TLEN_256; -+ addr[3] = zero; -+ len[3] = CMAC_TLEN_256; -+ aes_cmac_vector(tfm, 4, addr, len, mic, CMAC_TLEN_256); - } else { -- crypto_shash_update(desc, data, data_len - CMAC_TLEN_256); -+ addr[1] = data; -+ len[1] = data_len - CMAC_TLEN_256; -+ addr[2] = zero; -+ len[2] = CMAC_TLEN_256; -+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256); - } -- crypto_shash_finup(desc, zero, CMAC_TLEN_256, mic); - } - --struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[], -- size_t key_len) -+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], -+ size_t key_len) - { -- struct crypto_shash *tfm; -+ struct crypto_cipher *tfm; - -- tfm = crypto_alloc_shash("cmac(aes)", 0, 0); -+ tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); - if (!IS_ERR(tfm)) -- crypto_shash_setkey(tfm, key, key_len); -+ crypto_cipher_setkey(tfm, key, key_len); - - return tfm; - } - --void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm) -+ -+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm) - { -- crypto_free_shash(tfm); -+ crypto_free_cipher(tfm); - } -diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h -index 7681744..430616c 100644 ---- a/net/mac80211/aes_cmac.h -+++ b/net/mac80211/aes_cmac.h -@@ -7,14 +7,13 @@ - #define AES_CMAC_H - - #include --#include - --struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[], -- size_t key_len); --void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad, -+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], -+ size_t key_len); -+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic); --void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad, -+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic); --void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm); -+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm); - - #endif /* AES_CMAC_H */ -diff --git a/net/mac80211/aes_gcm.c b/net/mac80211/aes_gcm.c -new file mode 100644 -index 0000000..8a4397c ---- /dev/null -+++ b/net/mac80211/aes_gcm.c -@@ -0,0 +1,109 @@ -+/* -+ * Copyright 2014-2015, Qualcomm Atheros, Inc. -+ * -+ * 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. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include "key.h" -+#include "aes_gcm.h" -+ -+int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) -+{ -+ struct scatterlist sg[3]; -+ struct aead_request *aead_req; -+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -+ u8 *__aad; -+ -+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC); -+ if (!aead_req) -+ return -ENOMEM; -+ -+ __aad = (u8 *)aead_req + reqsize; -+ memcpy(__aad, aad, GCM_AAD_LEN); -+ -+ sg_init_table(sg, 3); -+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad)); -+ sg_set_buf(&sg[1], data, data_len); -+ sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN); -+ -+ aead_request_set_tfm(aead_req, tfm); -+ aead_request_set_crypt(aead_req, sg, sg, data_len, j_0); -+ aead_request_set_ad(aead_req, sg[0].length); -+ -+ crypto_aead_encrypt(aead_req); -+ kzfree(aead_req); -+ return 0; -+} -+ -+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) -+{ -+ struct scatterlist sg[3]; -+ struct aead_request *aead_req; -+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -+ u8 *__aad; -+ int err; -+ -+ if (data_len == 0) -+ return -EINVAL; -+ -+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC); -+ if (!aead_req) -+ return -ENOMEM; -+ -+ __aad = (u8 *)aead_req + reqsize; -+ memcpy(__aad, aad, GCM_AAD_LEN); -+ -+ sg_init_table(sg, 3); -+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad)); -+ sg_set_buf(&sg[1], data, data_len); -+ sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN); -+ -+ aead_request_set_tfm(aead_req, tfm); -+ aead_request_set_crypt(aead_req, sg, sg, -+ data_len + IEEE80211_GCMP_MIC_LEN, j_0); -+ aead_request_set_ad(aead_req, sg[0].length); -+ -+ err = crypto_aead_decrypt(aead_req); -+ kzfree(aead_req); -+ -+ return err; -+} -+ -+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], -+ size_t key_len) -+{ -+ struct crypto_aead *tfm; -+ int err; -+ -+ tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC); -+ if (IS_ERR(tfm)) -+ return tfm; -+ -+ err = crypto_aead_setkey(tfm, key, key_len); -+ if (err) -+ goto free_aead; -+ err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN); -+ if (err) -+ goto free_aead; -+ -+ return tfm; -+ -+free_aead: -+ crypto_free_aead(tfm); -+ return ERR_PTR(err); -+} -+ -+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) -+{ -+ crypto_free_aead(tfm); -+} -diff --git a/net/mac80211/aes_gcm.h b/net/mac80211/aes_gcm.h -index b14093b..7c3d78d 100644 ---- a/net/mac80211/aes_gcm.h -+++ b/net/mac80211/aes_gcm.h -@@ -6,38 +6,30 @@ - #ifndef AES_GCM_H - #define AES_GCM_H - --#include "aead_api.h" -+#include - --#define GCM_AAD_LEN 32 -- --static inline int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, -- u8 *j_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) -+static inline void -+ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) - { -- return aead_encrypt(tfm, j_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); - } - --static inline int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, -- u8 *j_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) -+static inline int -+ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) - { -- return aead_decrypt(tfm, j_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); -+ return -EOPNOTSUPP; - } - - static inline struct crypto_aead * - ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len) - { -- return aead_key_setup_encrypt("gcm(aes)", key, -- key_len, IEEE80211_GCMP_MIC_LEN); -+ return NULL; - } - --static inline void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) -+static inline void -+ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) - { -- return aead_key_free(tfm); - } - - #endif /* AES_GCM_H */ -diff --git a/net/mac80211/aes_gmac.h b/net/mac80211/aes_gmac.h -index c739356..f0e649f 100644 ---- a/net/mac80211/aes_gmac.h -+++ b/net/mac80211/aes_gmac.h -@@ -12,10 +12,22 @@ - #define GMAC_MIC_LEN 16 - #define GMAC_NONCE_LEN 12 - --struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[], -- size_t key_len); --int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, -- const u8 *data, size_t data_len, u8 *mic); --void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm); -+static inline struct crypto_aead * -+ieee80211_aes_gmac_key_setup(const u8 key[], size_t key_len) -+{ -+ return NULL; -+} -+ -+static inline int -+ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, -+ const u8 *data, size_t data_len, u8 *mic) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static inline void -+ieee80211_aes_gmac_key_free(struct crypto_aead *tfm) -+{ -+} - - #endif /* AES_GMAC_H */ + #endif /* __LINUX_NL80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c -index 97ea630..272b433 100644 +index 1de989c..abe7318 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c -@@ -1288,7 +1288,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) +@@ -112,6 +112,36 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata, + return 0; + } + ++static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata, ++ struct cfg80211_mbssid_config params) ++{ ++ struct ieee80211_sub_if_data *tx_sdata; ++ ++ sdata->vif.mbssid_tx_vif = NULL; ++ sdata->vif.bss_conf.bssid_index = 0; ++ sdata->vif.bss_conf.nontransmitted = false; ++ sdata->vif.bss_conf.ema_ap = false; ++ ++ if (sdata->vif.type != NL80211_IFTYPE_AP || !params.tx_wdev) ++ return -EINVAL; ++ ++ tx_sdata = IEEE80211_WDEV_TO_SUB_IF(params.tx_wdev); ++ if (!tx_sdata) ++ return -EINVAL; ++ ++ if (tx_sdata == sdata) { ++ sdata->vif.mbssid_tx_vif = &sdata->vif; ++ } else { ++ sdata->vif.mbssid_tx_vif = &tx_sdata->vif; ++ sdata->vif.bss_conf.nontransmitted = true; ++ sdata->vif.bss_conf.bssid_index = params.index; ++ } ++ if (params.ema) ++ sdata->vif.bss_conf.ema_ap = true; ++ ++ return 0; ++} ++ + static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, +@@ -959,11 +989,29 @@ static int ieee80211_set_ftm_responder_params( + return 0; + } + ++static int ++ieee80211_copy_mbssid_beacon(u8 *pos, struct cfg80211_mbssid_elems *dst, ++ struct cfg80211_mbssid_elems *src) ++{ ++ int i, offset = 0; ++ ++ for (i = 0; i < src->cnt; i++) { ++ memcpy(pos + offset, src->elem[i].data, src->elem[i].len); ++ dst->elem[i].len = src->elem[i].len; ++ dst->elem[i].data = pos + offset; ++ offset += dst->elem[i].len; ++ } ++ dst->cnt = src->cnt; ++ ++ return offset; ++} ++ + static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_beacon_data *params, + const struct ieee80211_csa_settings *csa, + const struct ieee80211_color_change_settings *cca) + { ++ struct cfg80211_mbssid_elems *mbssid = NULL; + struct beacon_data *new, *old; + int new_head_len, new_tail_len; + int size, err; +@@ -991,6 +1039,17 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + + size = sizeof(*new) + new_head_len + new_tail_len; + ++ /* new or old multiple BSSID elements? */ ++ if (params->mbssid_ies) { ++ mbssid = params->mbssid_ies; ++ size += struct_size(new->mbssid_ies, elem, mbssid->cnt); ++ size += ieee80211_get_mbssid_beacon_len(mbssid); ++ } else if (old && old->mbssid_ies) { ++ mbssid = old->mbssid_ies; ++ size += struct_size(new->mbssid_ies, elem, mbssid->cnt); ++ size += ieee80211_get_mbssid_beacon_len(mbssid); ++ } ++ + new = kzalloc(size, GFP_KERNEL); + if (!new) + return -ENOMEM; +@@ -999,12 +1058,23 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + + /* + * pointers go into the block we allocated, +- * memory is | beacon_data | head | tail | ++ * memory is | beacon_data | head | tail | mbssid_ies + */ + new->head = ((u8 *) new) + sizeof(*new); + new->tail = new->head + new_head_len; + new->head_len = new_head_len; + new->tail_len = new_tail_len; ++ /* copy in optional mbssid_ies */ ++ if (mbssid) { ++ u8 *pos = new->tail + new->tail_len; ++ ++ new->mbssid_ies = (void *)pos; ++ pos += struct_size(new->mbssid_ies, elem, mbssid->cnt); ++ ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies, mbssid); ++ /* update bssid_indicator */ ++ sdata->vif.bss_conf.bssid_indicator = ++ ilog2(__roundup_pow_of_two(mbssid->cnt + 1)); ++ } + + if (csa) { + new->cntdwn_current_counter = csa->count; +@@ -1107,6 +1177,14 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, + changed |= BSS_CHANGED_HE_BSS_COLOR; + } + ++ if (sdata->vif.type == NL80211_IFTYPE_AP && ++ params->mbssid_config.tx_wdev) { ++ err = ieee80211_set_ap_mbssid_options(sdata, ++ params->mbssid_config); ++ if (err) ++ return err; ++ } ++ + mutex_lock(&local->mtx); + err = ieee80211_vif_use_channel(sdata, ¶ms->chandef, + IEEE80211_CHANCTX_SHARED); +@@ -1294,8 +1372,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) + + mutex_unlock(&local->mtx); + +- kfree(sdata->u.ap.next_beacon); +- sdata->u.ap.next_beacon = NULL; ++ if (sdata->u.ap.next_beacon) { ++ kfree(sdata->u.ap.next_beacon->mbssid_ies); ++ kfree(sdata->u.ap.next_beacon); ++ sdata->u.ap.next_beacon = NULL; ++ } + + /* turn off carrier for this interface and dependent VLANs */ + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +@@ -1319,7 +1400,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) sdata->vif.bss_conf.ftmr_params = NULL; __sta_info_flush(sdata, true); @@ -1414,7 +727,56 @@ index 97ea630..272b433 100644 sdata->vif.bss_conf.enable_beacon = false; sdata->beacon_rate_set = false; -@@ -2443,7 +2442,7 @@ static int ieee80211_scan(struct wiphy *wiphy, +@@ -1474,38 +1554,6 @@ static void sta_apply_mesh_params(struct ieee80211_local *local, + #endif + } + +-static void sta_apply_airtime_params(struct ieee80211_local *local, +- struct sta_info *sta, +- struct station_parameters *params) +-{ +- u8 ac; +- +- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +- struct airtime_sched_info *air_sched = &local->airtime[ac]; +- struct airtime_info *air_info = &sta->airtime[ac]; +- struct txq_info *txqi; +- u8 tid; +- +- spin_lock_bh(&air_sched->lock); +- for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) { +- if (air_info->weight == params->airtime_weight || +- !sta->sta.txq[tid] || +- ac != ieee80211_ac_from_tid(tid)) +- continue; +- +- airtime_weight_set(air_info, params->airtime_weight); +- +- txqi = to_txq_info(sta->sta.txq[tid]); +- if (RB_EMPTY_NODE(&txqi->schedule_order)) +- continue; +- +- ieee80211_update_airtime_weight(local, air_sched, +- 0, true); +- } +- spin_unlock_bh(&air_sched->lock); +- } +-} +- + static int sta_apply_parameters(struct ieee80211_local *local, + struct sta_info *sta, + struct station_parameters *params) +@@ -1693,8 +1741,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, + sta_apply_mesh_params(local, sta, params); + + if (params->airtime_weight) +- sta_apply_airtime_params(local, sta, params); +- ++ sta->airtime_weight = params->airtime_weight; + + /* set the STA state after all sta info from usermode has been set */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) || +@@ -2498,7 +2545,7 @@ static int ieee80211_scan(struct wiphy *wiphy, * the frames sent while scanning on other channel will be * lost) */ @@ -1423,7 +785,7 @@ index 97ea630..272b433 100644 (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || !(req->flags & NL80211_SCAN_FLAG_AP))) return -EOPNOTSUPP; -@@ -2708,6 +2707,19 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, +@@ -2765,6 +2812,19 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, return 0; } @@ -1440,113 +802,640 @@ index 97ea630..272b433 100644 + return 0; +} + - static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr) + static void ieee80211_rfkill_poll(struct wiphy *wiphy) { -@@ -4138,6 +4150,7 @@ const struct cfg80211_ops mac80211_config_ops = { + struct ieee80211_local *local = wiphy_priv(wiphy); +@@ -3089,12 +3149,24 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) + + len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len + + beacon->proberesp_ies_len + beacon->assocresp_ies_len + +- beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len; ++ beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len + ++ ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies); + + new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL); + if (!new_beacon) + return NULL; + ++ if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) { ++ new_beacon->mbssid_ies = ++ kzalloc(struct_size(new_beacon->mbssid_ies, ++ elem, beacon->mbssid_ies->cnt), ++ GFP_KERNEL); ++ if (!new_beacon->mbssid_ies) { ++ kfree(new_beacon); ++ return NULL; ++ } ++ } ++ + pos = (u8 *)(new_beacon + 1); + if (beacon->head_len) { + new_beacon->head_len = beacon->head_len; +@@ -3132,6 +3204,10 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) + memcpy(pos, beacon->probe_resp, beacon->probe_resp_len); + pos += beacon->probe_resp_len; + } ++ if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) ++ pos += ieee80211_copy_mbssid_beacon(pos, ++ new_beacon->mbssid_ies, ++ beacon->mbssid_ies); + + /* might copy -1, meaning no changes requested */ + new_beacon->ftm_responder = beacon->ftm_responder; +@@ -3154,9 +3230,31 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) + void ieee80211_csa_finish(struct ieee80211_vif *vif) + { + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); ++ struct ieee80211_local *local = sdata->local; + +- ieee80211_queue_work(&sdata->local->hw, +- &sdata->csa_finalize_work); ++ rcu_read_lock(); ++ ++ if (vif->mbssid_tx_vif == vif) { ++ /* Trigger ieee80211_csa_finish() on the non-transmitting ++ * interfaces when channel switch is received on ++ * transmitting interface ++ */ ++ struct ieee80211_sub_if_data *iter; ++ ++ list_for_each_entry_rcu(iter, &local->interfaces, list) { ++ if (!ieee80211_sdata_running(iter)) ++ continue; ++ ++ if (iter == sdata || iter->vif.mbssid_tx_vif != vif) ++ continue; ++ ++ ieee80211_queue_work(&iter->local->hw, ++ &iter->csa_finalize_work); ++ } ++ } ++ ieee80211_queue_work(&local->hw, &sdata->csa_finalize_work); ++ ++ rcu_read_unlock(); + } + EXPORT_SYMBOL(ieee80211_csa_finish); + +@@ -3169,8 +3267,11 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, + case NL80211_IFTYPE_AP: + err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon, + NULL, NULL); +- kfree(sdata->u.ap.next_beacon); +- sdata->u.ap.next_beacon = NULL; ++ if (sdata->u.ap.next_beacon) { ++ kfree(sdata->u.ap.next_beacon->mbssid_ies); ++ kfree(sdata->u.ap.next_beacon); ++ sdata->u.ap.next_beacon = NULL; ++ } + + if (err < 0) + return err; +@@ -3325,8 +3426,12 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, + if ((params->n_counter_offsets_beacon > + IEEE80211_MAX_CNTDWN_COUNTERS_NUM) || + (params->n_counter_offsets_presp > +- IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) ++ IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) { ++ kfree(sdata->u.ap.next_beacon->mbssid_ies); ++ kfree(sdata->u.ap.next_beacon); ++ sdata->u.ap.next_beacon = NULL; + return -EINVAL; ++ } + + csa.counter_offsets_beacon = params->counter_offsets_beacon; + csa.counter_offsets_presp = params->counter_offsets_presp; +@@ -3336,7 +3441,9 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, + + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa, NULL); + if (err < 0) { ++ kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); ++ sdata->u.ap.next_beacon = NULL; + return err; + } + *changed |= err; +@@ -3426,8 +3533,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, + static void ieee80211_color_change_abort(struct ieee80211_sub_if_data *sdata) + { + sdata->vif.color_change_active = false; +- kfree(sdata->u.ap.next_beacon); +- sdata->u.ap.next_beacon = NULL; ++ if (sdata->u.ap.next_beacon) { ++ kfree(sdata->u.ap.next_beacon->mbssid_ies); ++ kfree(sdata->u.ap.next_beacon); ++ sdata->u.ap.next_beacon = NULL; ++ } + + cfg80211_color_change_aborted_notify(sdata->dev); + } +@@ -4165,8 +4275,11 @@ ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata, + + ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon, + NULL, NULL); +- kfree(sdata->u.ap.next_beacon); +- sdata->u.ap.next_beacon = NULL; ++ if (sdata->u.ap.next_beacon) { ++ kfree(sdata->u.ap.next_beacon->mbssid_ies); ++ kfree(sdata->u.ap.next_beacon); ++ sdata->u.ap.next_beacon = NULL; ++ } + + if (ret < 0) + return ret; +@@ -4209,7 +4322,11 @@ ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata, + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_color_change, + NULL, &color_change); + if (err < 0) { +- kfree(sdata->u.ap.next_beacon); ++ if (sdata->u.ap.next_beacon) { ++ kfree(sdata->u.ap.next_beacon->mbssid_ies); ++ kfree(sdata->u.ap.next_beacon); ++ sdata->u.ap.next_beacon = NULL; ++ } + return err; + } + *changed |= err; +@@ -4345,6 +4462,18 @@ out: + return err; + } + ++static int ++ieee80211_set_radar_background(struct wiphy *wiphy, ++ struct cfg80211_chan_def *chandef) ++{ ++ struct ieee80211_local *local = wiphy_priv(wiphy); ++ ++ if (!local->ops->set_radar_background) ++ return -EOPNOTSUPP; ++ ++ return local->ops->set_radar_background(&local->hw, chandef); ++} ++ + const struct cfg80211_ops mac80211_config_ops = { + .add_virtual_intf = ieee80211_add_iface, + .del_virtual_intf = ieee80211_del_iface, +@@ -4400,6 +4529,7 @@ const struct cfg80211_ops mac80211_config_ops = { .set_wiphy_params = ieee80211_set_wiphy_params, .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, + .set_antenna_gain = ieee80211_set_antenna_gain, - .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) + CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) +@@ -4449,4 +4579,5 @@ const struct cfg80211_ops mac80211_config_ops = { + .reset_tid_config = ieee80211_reset_tid_config, + .set_sar_specs = ieee80211_set_sar_specs, + .color_change = ieee80211_color_change, ++ .set_radar_background = ieee80211_set_radar_background, + }; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c -index 2cd52fc..44305e7 100644 +index a50f925..46f6c82 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c -@@ -405,6 +405,7 @@ static const char *hw_flag_names[] = { - FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), - FLAG(AMPDU_KEYBORDER_SUPPORT), +@@ -201,6 +201,36 @@ static const struct file_operations airtime_flags_ops = { + .llseek = default_llseek, + }; + ++static ssize_t aql_pending_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ieee80211_local *local = file->private_data; ++ char buf[400]; ++ int len = 0; ++ ++ len = scnprintf(buf, sizeof(buf), ++ "AC AQL pending\n" ++ "VO %u us\n" ++ "VI %u us\n" ++ "BE %u us\n" ++ "BK %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_total_pending_airtime)); ++ return simple_read_from_buffer(user_buf, count, ppos, ++ buf, len); ++} ++ ++static const struct file_operations aql_pending_ops = { ++ .read = aql_pending_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ + static ssize_t aql_txq_limit_read(struct file *file, + char __user *user_buf, + size_t count, +@@ -216,14 +246,14 @@ static ssize_t aql_txq_limit_read(struct file *file, + "VI %u %u\n" + "BE %u %u\n" + "BK %u %u\n", +- local->airtime[IEEE80211_AC_VO].aql_txq_limit_low, +- local->airtime[IEEE80211_AC_VO].aql_txq_limit_high, +- local->airtime[IEEE80211_AC_VI].aql_txq_limit_low, +- local->airtime[IEEE80211_AC_VI].aql_txq_limit_high, +- local->airtime[IEEE80211_AC_BE].aql_txq_limit_low, +- local->airtime[IEEE80211_AC_BE].aql_txq_limit_high, +- local->airtime[IEEE80211_AC_BK].aql_txq_limit_low, +- local->airtime[IEEE80211_AC_BK].aql_txq_limit_high); ++ local->aql_txq_limit_low[IEEE80211_AC_VO], ++ local->aql_txq_limit_high[IEEE80211_AC_VO], ++ local->aql_txq_limit_low[IEEE80211_AC_VI], ++ local->aql_txq_limit_high[IEEE80211_AC_VI], ++ 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]); + return simple_read_from_buffer(user_buf, count, ppos, + buf, len); + } +@@ -255,11 +285,11 @@ static ssize_t aql_txq_limit_write(struct file *file, + if (ac >= IEEE80211_NUM_ACS) + return -EINVAL; + +- q_limit_low_old = local->airtime[ac].aql_txq_limit_low; +- q_limit_high_old = local->airtime[ac].aql_txq_limit_high; ++ q_limit_low_old = local->aql_txq_limit_low[ac]; ++ q_limit_high_old = local->aql_txq_limit_high[ac]; + +- local->airtime[ac].aql_txq_limit_low = q_limit_low; +- local->airtime[ac].aql_txq_limit_high = q_limit_high; ++ local->aql_txq_limit_low[ac] = q_limit_low; ++ local->aql_txq_limit_high[ac] = q_limit_high; + + mutex_lock(&local->sta_mtx); + list_for_each_entry(sta, &local->sta_list, list) { +@@ -382,46 +412,6 @@ static const struct file_operations force_tx_status_ops = { + .llseek = default_llseek, + }; + +-static ssize_t airtime_read(struct file *file, +- char __user *user_buf, +- size_t count, +- loff_t *ppos) +-{ +- struct ieee80211_local *local = file->private_data; +- char buf[200]; +- u64 v_t[IEEE80211_NUM_ACS]; +- u64 wt[IEEE80211_NUM_ACS]; +- int len = 0, ac; +- +- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +- spin_lock_bh(&local->airtime[ac].lock); +- v_t[ac] = local->airtime[ac].v_t; +- wt[ac] = local->airtime[ac].weight_sum; +- spin_unlock_bh(&local->airtime[ac].lock); +- } +- len = scnprintf(buf, sizeof(buf), +- "\tVO VI BE BK\n" +- "Virt-t\t%-10llu %-10llu %-10llu %-10llu\n" +- "Weight\t%-10llu %-10llu %-10llu %-10llu\n", +- v_t[0], +- v_t[1], +- v_t[2], +- v_t[3], +- wt[0], +- wt[1], +- wt[2], +- wt[3]); +- +- return simple_read_from_buffer(user_buf, count, ppos, +- buf, len); +-} +- +-static const struct file_operations airtime_ops = { +- .read = airtime_read, +- .open = simple_open, +- .llseek = default_llseek, +-}; +- + #ifdef CONFIG_PM + static ssize_t reset_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +@@ -504,6 +494,7 @@ static const char *hw_flag_names[] = { FLAG(SUPPORTS_TX_ENCAP_OFFLOAD), -+ FLAG(SUPPORTS_RX_DECAP_OFFLOAD), + FLAG(SUPPORTS_RX_DECAP_OFFLOAD), + FLAG(SUPPORTS_CONC_MON_RX_DECAP), ++ FLAG(DETECTS_COLOR_COLLISION), #undef FLAG }; +@@ -668,15 +659,12 @@ void debugfs_hw_add(struct ieee80211_local *local) + DEBUGFS_ADD(hw_conf); + DEBUGFS_ADD_MODE(force_tx_status, 0600); + DEBUGFS_ADD_MODE(aql_enable, 0600); ++ DEBUGFS_ADD(aql_pending); + + if (local->ops->wake_tx_queue) + DEBUGFS_ADD_MODE(aqm, 0600); + +- if (wiphy_ext_feature_isset(local->hw.wiphy, +- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) { +- DEBUGFS_ADD_MODE(airtime, 0600); +- DEBUGFS_ADD_MODE(airtime_flags, 0600); +- } ++ DEBUGFS_ADD_MODE(airtime_flags, 0600); + + DEBUGFS_ADD(aql_txq_limit); + debugfs_create_u32("aql_threshold", 0600, +diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c +index ac5e3ec..88e6e72 100644 +--- a/net/mac80211/debugfs_netdev.c ++++ b/net/mac80211/debugfs_netdev.c +@@ -512,34 +512,6 @@ static ssize_t ieee80211_if_fmt_aqm( + } + IEEE80211_IF_FILE_R(aqm); + +-static ssize_t ieee80211_if_fmt_airtime( +- const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +-{ +- struct ieee80211_local *local = sdata->local; +- struct ieee80211_txq *txq = sdata->vif.txq; +- struct airtime_info *air_info; +- int len; +- +- if (!txq) +- return 0; +- +- spin_lock_bh(&local->airtime[txq->ac].lock); +- air_info = to_airtime_info(txq); +- len = scnprintf(buf, +- buflen, +- "RX: %llu us\nTX: %llu us\nWeight: %u\n" +- "Virt-T: %lld us\n", +- air_info->rx_airtime, +- air_info->tx_airtime, +- air_info->weight, +- air_info->v_t); +- spin_unlock_bh(&local->airtime[txq->ac].lock); +- +- return len; +-} +- +-IEEE80211_IF_FILE_R(airtime); +- + IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX); + + /* IBSS attributes */ +@@ -685,10 +657,8 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata) + + if (sdata->local->ops->wake_tx_queue && + sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && +- sdata->vif.type != NL80211_IFTYPE_NAN) { ++ sdata->vif.type != NL80211_IFTYPE_NAN) + DEBUGFS_ADD(aqm); +- DEBUGFS_ADD(airtime); +- } + } + + static void add_sta_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c -index 829dcad..2e4392e 100644 +index 8be28cf..afac8d4 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c -@@ -79,6 +79,7 @@ static const char * const sta_flag_names[] = { - FLAG(MPSP_RECIPIENT), - FLAG(PS_DELIVER), - FLAG(USES_ENCRYPTION), -+ FLAG(DECAP_OFFLOAD), - #undef FLAG - }; +@@ -202,7 +202,7 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, + size_t bufsz = 400; + char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf; + u64 rx_airtime = 0, tx_airtime = 0; +- u64 v_t[IEEE80211_NUM_ACS]; ++ s32 deficit[IEEE80211_NUM_ACS]; + ssize_t rv; + int ac; + +@@ -210,18 +210,18 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, + return -ENOMEM; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +- spin_lock_bh(&local->airtime[ac].lock); ++ spin_lock_bh(&local->active_txq_lock[ac]); + rx_airtime += sta->airtime[ac].rx_airtime; + tx_airtime += sta->airtime[ac].tx_airtime; +- v_t[ac] = sta->airtime[ac].v_t; +- spin_unlock_bh(&local->airtime[ac].lock); ++ deficit[ac] = sta->airtime[ac].deficit; ++ spin_unlock_bh(&local->active_txq_lock[ac]); + } + + p += scnprintf(p, bufsz + buf - p, + "RX: %llu us\nTX: %llu us\nWeight: %u\n" +- "Virt-T: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n", +- rx_airtime, tx_airtime, sta->airtime[0].weight, +- v_t[0], v_t[1], v_t[2], v_t[3]); ++ "Deficit: VO: %d us VI: %d us BE: %d us BK: %d us\n", ++ rx_airtime, tx_airtime, sta->airtime_weight, ++ deficit[0], deficit[1], deficit[2], deficit[3]); + + rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); + kfree(buf); +@@ -236,11 +236,11 @@ static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf, + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +- spin_lock_bh(&local->airtime[ac].lock); ++ spin_lock_bh(&local->active_txq_lock[ac]); + sta->airtime[ac].rx_airtime = 0; + sta->airtime[ac].tx_airtime = 0; +- sta->airtime[ac].v_t = 0; +- spin_unlock_bh(&local->airtime[ac].lock); ++ sta->airtime[ac].deficit = sta->airtime_weight; ++ spin_unlock_bh(&local->active_txq_lock[ac]); + } + + return count; +@@ -263,10 +263,10 @@ static ssize_t sta_aql_read(struct file *file, char __user *userbuf, + return -ENOMEM; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +- spin_lock_bh(&local->airtime[ac].lock); ++ spin_lock_bh(&local->active_txq_lock[ac]); + q_limit_l[ac] = sta->airtime[ac].aql_limit_low; + q_limit_h[ac] = sta->airtime[ac].aql_limit_high; +- spin_unlock_bh(&local->airtime[ac].lock); ++ spin_unlock_bh(&local->active_txq_lock[ac]); + q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending); + } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h -index 37dbabd..a1ef2b9 100644 +index 912c75d..2df420b 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h -@@ -1413,4 +1413,20 @@ static inline void drv_sta_set_4addr(struct ieee80211_local *local, +@@ -1486,4 +1486,28 @@ static inline void drv_twt_teardown_request(struct ieee80211_local *local, trace_drv_return_void(local); } -+static inline void drv_sta_set_decap_offload(struct ieee80211_local *local, -+ struct ieee80211_sub_if_data *sdata, -+ struct ieee80211_sta *sta, -+ bool enabled) ++#if LINUX_VERSION_IS_GEQ(5,10,0) ++static inline int drv_net_fill_forward_path(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, ++ struct ieee80211_sta *sta, ++ struct net_device_path_ctx *ctx, ++ struct net_device_path *path) +{ ++ int ret = -EOPNOTSUPP; ++ + sdata = get_bss_sdata(sdata); + if (!check_sdata_in_driver(sdata)) -+ return; ++ return -EIO; + -+ trace_drv_sta_set_decap_offload(local, sdata, sta, enabled); -+ if (local->ops->sta_set_decap_offload) -+ local->ops->sta_set_decap_offload(&local->hw, &sdata->vif, sta, -+ enabled); -+ trace_drv_return_void(local); ++ trace_drv_net_fill_forward_path(local, sdata, sta); ++ if (local->ops->net_fill_forward_path) ++ ret = local->ops->net_fill_forward_path(&local->hw, ++ &sdata->vif, sta, ++ ctx, path); ++ trace_drv_return_int(local, ret); ++ ++ return ret; +} ++#endif + #endif /* __MAC80211_DRIVER_OPS */ -diff --git a/net/mac80211/fils_aead.c b/net/mac80211/fils_aead.c -index 87e34f6..744664c 100644 ---- a/net/mac80211/fils_aead.c -+++ b/net/mac80211/fils_aead.c -@@ -1,4 +1,4 @@ --#if LINUX_VERSION_IS_GEQ(4,3,0) -+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */ - // SPDX-License-Identifier: GPL-2.0-only - /* - * FILS AEAD for (Re)Association Request/Response frames -diff --git a/net/mac80211/fils_aead.h b/net/mac80211/fils_aead.h -index 017bd7a..a6b59d7 100644 ---- a/net/mac80211/fils_aead.h -+++ b/net/mac80211/fils_aead.h -@@ -7,7 +7,7 @@ - #ifndef FILS_AEAD_H - #define FILS_AEAD_H - --#if LINUX_VERSION_IS_GEQ(4,3,0) -+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */ - int fils_encrypt_assoc_req(struct sk_buff *skb, - struct ieee80211_mgd_assoc_data *assoc_data); - int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h -index da88464..d588fb5 100644 +index 5e9b0af..0d697af 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h -@@ -461,7 +461,9 @@ struct ieee80211_if_managed { - unsigned long probe_timeout; - int probe_send_count; - bool nullfunc_failed; -- bool connection_loss; -+ u8 connection_loss:1, -+ driver_disconnect:1, -+ reconnect:1; +@@ -83,6 +83,15 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS]; - struct cfg80211_bss *associated; - struct ieee80211_mgd_auth_data *auth_data; -@@ -855,7 +857,6 @@ enum txq_info_flags { + #define IEEE80211_MAX_NAN_INSTANCE_ID 255 + ++ ++/* ++ * Keep a station's queues on the active list for deficit accounting purposes ++ * if it was active or queued during the last 100ms ++ */ ++#define AIRTIME_ACTIVE_DURATION (HZ / 10) ++ ++#define AIRTIME_QUANTUM_SHIFT 3 ++ + struct ieee80211_bss { + u32 device_ts_beacon, device_ts_presp; + +@@ -261,6 +270,7 @@ struct beacon_data { + struct ieee80211_meshconf_ie *meshconf; + u16 cntdwn_counter_offsets[IEEE80211_MAX_CNTDWN_COUNTERS_NUM]; + u8 cntdwn_current_counter; ++ struct cfg80211_mbssid_elems *mbssid_ies; + struct rcu_head rcu_head; + }; + +@@ -862,16 +872,20 @@ enum txq_info_flags { + * @def_flow: used as a fallback flow when a packet destined to @tin hashes to + * a fq_flow which is already owned by a different tin + * @def_cvars: codel vars for @def_flow +- * @schedule_order: used with ieee80211_local->active_txqs + * @frags: used to keep fragments created after dequeue ++ * @schedule_order: used with ieee80211_local->active_txqs ++ * @schedule_round: counter to prevent infinite loops on TXQ scheduling */ struct txq_info { struct fq_tin tin; -- struct fq_flow def_flow; struct codel_vars def_cvars; struct codel_stats cstats; +- struct rb_node schedule_order; ++ ++ u16 schedule_round; ++ struct list_head schedule_order; + struct sk_buff_head frags; -@@ -1402,6 +1403,7 @@ struct ieee80211_local { ++ + unsigned long flags; + + /* keep last! */ +@@ -948,8 +962,6 @@ struct ieee80211_sub_if_data { + struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; + struct mac80211_qos_map __rcu *qos_map; + +- struct airtime_info airtime[IEEE80211_NUM_ACS]; +- + struct work_struct csa_finalize_work; + bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ + struct cfg80211_chan_def csa_chandef; +@@ -1083,6 +1095,20 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif) + return shift; + } + ++static inline int ++ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems) ++{ ++ int i, len = 0; ++ ++ if (!elems) ++ return 0; ++ ++ for (i = 0; i < elems->cnt; i++) ++ len += elems->elem[i].len; ++ ++ return len; ++} ++ + enum { + IEEE80211_RX_MSG = 1, + IEEE80211_TX_STATUS_MSG = 2, +@@ -1170,44 +1196,6 @@ enum mac80211_scan_state { + SCAN_ABORT, + }; + +-/** +- * struct airtime_sched_info - state used for airtime scheduling and AQL +- * +- * @lock: spinlock that protects all the fields in this struct +- * @active_txqs: rbtree of currently backlogged queues, sorted by virtual time +- * @schedule_pos: the current position maintained while a driver walks the tree +- * with ieee80211_next_txq() +- * @active_list: list of struct airtime_info structs that were active within +- * the last AIRTIME_ACTIVE_DURATION (100 ms), used to compute +- * weight_sum +- * @last_weight_update: used for rate limiting walking active_list +- * @last_schedule_time: tracks the last time a transmission was scheduled; used +- * for catching up v_t if no stations are eligible for +- * transmission. +- * @v_t: global virtual time; queues with v_t < this are eligible for +- * transmission +- * @weight_sum: total sum of all active stations used for dividing airtime +- * @weight_sum_reciprocal: reciprocal of weight_sum (to avoid divisions in fast +- * path - see comment above +- * IEEE80211_RECIPROCAL_DIVISOR_64) +- * @aql_txq_limit_low: AQL limit when total outstanding airtime +- * is < IEEE80211_AQL_THRESHOLD +- * @aql_txq_limit_high: AQL limit when total outstanding airtime +- * is > IEEE80211_AQL_THRESHOLD +- */ +-struct airtime_sched_info { +- spinlock_t lock; +- struct rb_root_cached active_txqs; +- struct rb_node *schedule_pos; +- struct list_head active_list; +- u64 last_weight_update; +- u64 last_schedule_activity; +- u64 v_t; +- u64 weight_sum; +- u64 weight_sum_reciprocal; +- u32 aql_txq_limit_low; +- u32 aql_txq_limit_high; +-}; + DECLARE_STATIC_KEY_FALSE(aql_disable); + + struct ieee80211_local { +@@ -1221,10 +1209,16 @@ struct ieee80211_local { + struct codel_params cparams; + + /* protects active_txqs and txqi->schedule_order */ +- struct airtime_sched_info airtime[IEEE80211_NUM_ACS]; ++ spinlock_t active_txq_lock[IEEE80211_NUM_ACS]; ++ struct list_head active_txqs[IEEE80211_NUM_ACS]; ++ u16 schedule_round[IEEE80211_NUM_ACS]; ++ + u16 airtime_flags; ++ 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_ac_pending_airtime[IEEE80211_NUM_ACS]; + + const struct ieee80211_ops *ops; + +@@ -1454,6 +1448,7 @@ struct ieee80211_local { int dynamic_ps_forced_timeout; int user_power_level; /* in dBm, for all interfaces */ @@ -1554,80 +1443,306 @@ index da88464..d588fb5 100644 enum ieee80211_smps_mode smps_mode; +@@ -1490,7 +1485,7 @@ struct ieee80211_local { + }; + + static inline struct ieee80211_sub_if_data * +-IEEE80211_DEV_TO_SUB_IF(struct net_device *dev) ++IEEE80211_DEV_TO_SUB_IF(const struct net_device *dev) + { + return netdev_priv(dev); + } +@@ -1639,125 +1634,6 @@ static inline bool txq_has_queue(struct ieee80211_txq *txq) + return !(skb_queue_empty(&txqi->frags) && !txqi->tin.backlog_packets); + } + +-static inline struct airtime_info *to_airtime_info(struct ieee80211_txq *txq) +-{ +- struct ieee80211_sub_if_data *sdata; +- struct sta_info *sta; +- +- if (txq->sta) { +- sta = container_of(txq->sta, struct sta_info, sta); +- return &sta->airtime[txq->ac]; +- } +- +- sdata = vif_to_sdata(txq->vif); +- return &sdata->airtime[txq->ac]; +-} +- +-/* To avoid divisions in the fast path, we keep pre-computed reciprocals for +- * airtime weight calculations. There are two different weights to keep track +- * of: The per-station weight and the sum of weights per phy. +- * +- * For the per-station weights (kept in airtime_info below), we use 32-bit +- * reciprocals with a devisor of 2^19. This lets us keep the multiplications and +- * divisions for the station weights as 32-bit operations at the cost of a bit +- * of rounding error for high weights; but the choice of divisor keeps rounding +- * errors <10% for weights <2^15, assuming no more than 8ms of airtime is +- * reported at a time. +- * +- * For the per-phy sum of weights the values can get higher, so we use 64-bit +- * operations for those with a 32-bit divisor, which should avoid any +- * significant rounding errors. +- */ +-#define IEEE80211_RECIPROCAL_DIVISOR_64 0x100000000ULL +-#define IEEE80211_RECIPROCAL_SHIFT_64 32 +-#define IEEE80211_RECIPROCAL_DIVISOR_32 0x80000U +-#define IEEE80211_RECIPROCAL_SHIFT_32 19 +- +-static inline void airtime_weight_set(struct airtime_info *air_info, u16 weight) +-{ +- if (air_info->weight == weight) +- return; +- +- air_info->weight = weight; +- if (weight) { +- air_info->weight_reciprocal = +- IEEE80211_RECIPROCAL_DIVISOR_32 / weight; +- } else { +- air_info->weight_reciprocal = 0; +- } +-} +- +-static inline void airtime_weight_sum_set(struct airtime_sched_info *air_sched, +- int weight_sum) +-{ +- if (air_sched->weight_sum == weight_sum) +- return; +- +- air_sched->weight_sum = weight_sum; +- if (air_sched->weight_sum) { +- air_sched->weight_sum_reciprocal = IEEE80211_RECIPROCAL_DIVISOR_64; +- do_div(air_sched->weight_sum_reciprocal, air_sched->weight_sum); +- } else { +- air_sched->weight_sum_reciprocal = 0; +- } +-} +- +-/* A problem when trying to enforce airtime fairness is that we want to divide +- * the airtime between the currently *active* stations. However, basing this on +- * the instantaneous queue state of stations doesn't work, as queues tend to +- * oscillate very quickly between empty and occupied, leading to the scheduler +- * thinking only a single station is active when deciding whether to allow +- * transmission (and thus not throttling correctly). +- * +- * To fix this we use a timer-based notion of activity: a station is considered +- * active if it has been scheduled within the last 100 ms; we keep a separate +- * list of all the stations considered active in this manner, and lazily update +- * the total weight of active stations from this list (filtering the stations in +- * the list by their 'last active' time). +- * +- * We add one additional safeguard to guard against stations that manage to get +- * scheduled every 100 ms but don't transmit a lot of data, and thus don't use +- * up any airtime. Such stations would be able to get priority for an extended +- * period of time if they do start transmitting at full capacity again, and so +- * we add an explicit maximum for how far behind a station is allowed to fall in +- * the virtual airtime domain. This limit is set to a relatively high value of +- * 20 ms because the main mechanism for catching up idle stations is the active +- * state as described above; i.e., the hard limit should only be hit in +- * pathological cases. +- */ +-#define AIRTIME_ACTIVE_DURATION (100 * NSEC_PER_MSEC) +-#define AIRTIME_MAX_BEHIND 20000 /* 20 ms */ +- +-static inline bool airtime_is_active(struct airtime_info *air_info, u64 now) +-{ +- return air_info->last_scheduled >= now - AIRTIME_ACTIVE_DURATION; +-} +- +-static inline void airtime_set_active(struct airtime_sched_info *air_sched, +- struct airtime_info *air_info, u64 now) +-{ +- air_info->last_scheduled = now; +- air_sched->last_schedule_activity = now; +- list_move_tail(&air_info->list, &air_sched->active_list); +-} +- +-static inline bool airtime_catchup_v_t(struct airtime_sched_info *air_sched, +- u64 v_t, u64 now) +-{ +- air_sched->v_t = v_t; +- return true; +-} +- +-static inline void init_airtime_info(struct airtime_info *air_info, +- struct airtime_sched_info *air_sched) +-{ +- atomic_set(&air_info->aql_tx_pending, 0); +- air_info->aql_limit_low = air_sched->aql_txq_limit_low; +- air_info->aql_limit_high = air_sched->aql_txq_limit_high; +- airtime_weight_set(air_info, IEEE80211_DEFAULT_AIRTIME_WEIGHT); +- INIT_LIST_HEAD(&air_info->list); +-} +- + static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) + { + return ether_addr_equal(raddr, addr) || +@@ -2003,14 +1879,6 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, + u64 *cookie); + int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev, + const u8 *buf, size_t len); +-void ieee80211_resort_txq(struct ieee80211_hw *hw, +- struct ieee80211_txq *txq); +-void ieee80211_unschedule_txq(struct ieee80211_hw *hw, +- struct ieee80211_txq *txq, +- bool purge); +-void ieee80211_update_airtime_weight(struct ieee80211_local *local, +- struct airtime_sched_info *air_sched, +- u64 now, bool force); + + /* HT */ + void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c -index 68665bc..bf17fa9 100644 +index b6dc214..b777921 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c -@@ -839,7 +839,7 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = { +@@ -378,6 +378,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do + struct cfg80211_nan_func *func; + + clear_bit(SDATA_STATE_RUNNING, &sdata->state); ++ synchronize_rcu(); /* flush _ieee80211_wake_txqs() */ + + cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata; + if (cancel_scan) +@@ -632,17 +633,46 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do + ieee80211_add_virtual_monitor(local); + } + ++static void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata) ++{ ++ struct ieee80211_sub_if_data *tx_sdata, *non_tx_sdata, *tmp_sdata; ++ struct ieee80211_vif *tx_vif = sdata->vif.mbssid_tx_vif; ++ ++ if (!tx_vif) ++ return; ++ ++ tx_sdata = vif_to_sdata(tx_vif); ++ sdata->vif.mbssid_tx_vif = NULL; ++ ++ list_for_each_entry_safe(non_tx_sdata, tmp_sdata, ++ &tx_sdata->local->interfaces, list) { ++ if (non_tx_sdata != sdata && non_tx_sdata != tx_sdata && ++ non_tx_sdata->vif.mbssid_tx_vif == tx_vif && ++ ieee80211_sdata_running(non_tx_sdata)) { ++ non_tx_sdata->vif.mbssid_tx_vif = NULL; ++ dev_close(non_tx_sdata->wdev.netdev); ++ } ++ } ++ ++ if (sdata != tx_sdata && ieee80211_sdata_running(tx_sdata)) { ++ tx_sdata->vif.mbssid_tx_vif = NULL; ++ dev_close(tx_sdata->wdev.netdev); ++ } ++} ++ + static int ieee80211_stop(struct net_device *dev) + { + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +- /* close all dependent VLAN interfaces before locking wiphy */ ++ /* close dependent VLAN and MBSSID interfaces before locking wiphy */ + if (sdata->vif.type == NL80211_IFTYPE_AP) { + struct ieee80211_sub_if_data *vlan, *tmpsdata; + + list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, + u.vlan.list) + dev_close(vlan->dev); ++ ++ ieee80211_stop_mbssid(sdata); + } + + wiphy_lock(sdata->local->hw.wiphy); +@@ -822,6 +852,66 @@ static const struct net_device_ops ieee80211_monitorif_ops = { }; --static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype) -+static bool ieee80211_iftype_supports_hdr_offload(enum nl80211_iftype iftype) - { - switch (iftype) { - /* P2P GO and client are mapped to AP/STATION types */ -@@ -859,7 +859,7 @@ static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdat - flags = sdata->vif.offload_flags; - - if (ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) && -- ieee80211_iftype_supports_encap_offload(sdata->vif.type)) { -+ ieee80211_iftype_supports_hdr_offload(sdata->vif.type)) { - flags |= IEEE80211_OFFLOAD_ENCAP_ENABLED; - - if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) && -@@ -872,10 +872,21 @@ static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdat - flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; - } - -+ if (ieee80211_hw_check(&local->hw, SUPPORTS_RX_DECAP_OFFLOAD) && -+ ieee80211_iftype_supports_hdr_offload(sdata->vif.type)) { -+ flags |= IEEE80211_OFFLOAD_DECAP_ENABLED; ++#if LINUX_VERSION_IS_GEQ(5,10,0) ++static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx, ++ struct net_device_path *path) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_local *local; ++ struct sta_info *sta; ++ int ret = -ENOENT; + -+ if (local->monitors) -+ flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED; -+ } else { -+ flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED; ++ sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev); ++ local = sdata->local; ++ ++ if (!local->ops->net_fill_forward_path) ++ return -EOPNOTSUPP; ++ ++ rcu_read_lock(); ++ switch (sdata->vif.type) { ++ case NL80211_IFTYPE_AP_VLAN: ++ sta = rcu_dereference(sdata->u.vlan.sta); ++ if (sta) ++ break; ++ if (sdata->wdev.use_4addr) ++ goto out; ++ if (is_multicast_ether_addr(ctx->daddr)) ++ goto out; ++ sta = sta_info_get_bss(sdata, ctx->daddr); ++ break; ++ case NL80211_IFTYPE_AP: ++ if (is_multicast_ether_addr(ctx->daddr)) ++ goto out; ++ sta = sta_info_get(sdata, ctx->daddr); ++ break; ++ case NL80211_IFTYPE_STATION: ++ if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { ++ sta = sta_info_get(sdata, ctx->daddr); ++ if (sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { ++ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) ++ goto out; ++ ++ break; ++ } ++ } ++ ++ sta = sta_info_get(sdata, sdata->u.mgd.bssid); ++ break; ++ default: ++ goto out; + } + - if (sdata->vif.offload_flags == flags) - return false; ++ if (!sta) ++ goto out; ++ ++ ret = drv_net_fill_forward_path(local, sdata, &sta->sta, ctx, path); ++out: ++ rcu_read_unlock(); ++ ++ return ret; ++} ++#endif ++ + static const struct net_device_ops ieee80211_dataif_8023_ops = { + #if LINUX_VERSION_IS_LESS(4,10,0) + .ndo_change_mtu = __change_mtu, +@@ -839,7 +929,9 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = { + #else + .ndo_get_stats64 = bp_ieee80211_get_stats64, + #endif +- ++#if LINUX_VERSION_IS_GEQ(5,10,0) ++ .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path, ++#endif + }; - sdata->vif.offload_flags = flags; -+ ieee80211_check_fast_rx_iface(sdata); - return true; - } - -@@ -893,7 +904,7 @@ static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata) + static bool ieee80211_iftype_supports_hdr_offload(enum nl80211_iftype iftype) +@@ -2099,9 +2191,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, + } } - if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) || -- !ieee80211_iftype_supports_encap_offload(bss->vif.type)) -+ !ieee80211_iftype_supports_hdr_offload(bss->vif.type)) - return; +- for (i = 0; i < IEEE80211_NUM_ACS; i++) +- init_airtime_info(&sdata->airtime[i], &local->airtime[i]); +- + ieee80211_set_default_queues(sdata); - enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED; -diff --git a/net/mac80211/key.h b/net/mac80211/key.h -index 00f3d1b..81d3370 100644 ---- a/net/mac80211/key.h -+++ b/net/mac80211/key.h -@@ -89,12 +89,12 @@ struct ieee80211_key { - * Management frames. - */ - u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; -- struct crypto_aead *tfm; -+ struct crypto_cipher *tfm; - u32 replays; /* dot11RSNAStatsCCMPReplays */ - } ccmp; - struct { - u8 rx_pn[IEEE80211_CMAC_PN_LEN]; -- struct crypto_shash *tfm; -+ struct crypto_cipher *tfm; - u32 replays; /* dot11RSNAStatsCMACReplays */ - u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ - } aes_cmac; + sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; diff --git a/net/mac80211/main.c b/net/mac80211/main.c -index bca1a02..44076fb 100644 +index 397d289..09e5bf1 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -96,7 +96,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) @@ -1652,7 +1767,7 @@ index bca1a02..44076fb 100644 if (local->hw.conf.power_level != power) { changed |= IEEE80211_CONF_CHANGE_POWER; local->hw.conf.power_level = power; -@@ -321,7 +327,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) +@@ -337,7 +343,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_restart_hw); @@ -1661,7 +1776,7 @@ index bca1a02..44076fb 100644 static int ieee80211_ifa_changed(struct notifier_block *nb, unsigned long data, void *arg) { -@@ -380,7 +386,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, +@@ -396,7 +402,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, } #endif @@ -1670,16 +1785,7 @@ index bca1a02..44076fb 100644 static int ieee80211_ifa6_changed(struct notifier_block *nb, unsigned long data, void *arg) { -@@ -591,7 +597,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, - NL80211_FEATURE_MAC_ON_CREATE | - NL80211_FEATURE_USERSPACE_MPM | - NL80211_FEATURE_FULL_AP_CLIENT_STATE; --#if LINUX_VERSION_IS_GEQ(4,3,0) -+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */ - wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_STA); - #endif - wiphy_ext_feature_set(wiphy, -@@ -665,6 +671,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, +@@ -679,6 +685,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, IEEE80211_RADIOTAP_MCS_HAVE_BW; local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI | IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; @@ -1687,8 +1793,28 @@ index bca1a02..44076fb 100644 local->hw.uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; local->hw.uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; local->hw.max_mtu = IEEE80211_MAX_DATA_LEN; -@@ -1301,14 +1308,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) +@@ -707,14 +714,12 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, + spin_lock_init(&local->queue_stop_reason_lock); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { +- struct airtime_sched_info *air_sched = &local->airtime[i]; +- +- air_sched->active_txqs = RB_ROOT_CACHED; +- INIT_LIST_HEAD(&air_sched->active_list); +- spin_lock_init(&air_sched->lock); +- air_sched->aql_txq_limit_low = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L; +- air_sched->aql_txq_limit_high = ++ INIT_LIST_HEAD(&local->active_txqs[i]); ++ spin_lock_init(&local->active_txq_lock[i]); ++ local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L; ++ local->aql_txq_limit_high[i] = + IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H; ++ atomic_set(&local->aql_ac_pending_airtime[i], 0); + } + + local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX; +@@ -1321,14 +1326,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) + wiphy_unlock(hw->wiphy); rtnl_unlock(); -#ifdef CONFIG_INET @@ -1704,7 +1830,7 @@ index bca1a02..44076fb 100644 local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; result = register_inet6addr_notifier(&local->ifa6_notifier); if (result) -@@ -1317,13 +1324,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) +@@ -1337,13 +1342,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) return 0; @@ -1721,7 +1847,7 @@ index bca1a02..44076fb 100644 fail_ifa: #endif wiphy_unregister(local->hw.wiphy); -@@ -1351,10 +1358,10 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) +@@ -1371,10 +1376,10 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) tasklet_kill(&local->tx_pending_tasklet); tasklet_kill(&local->tasklet); @@ -1734,1524 +1860,38 @@ index bca1a02..44076fb 100644 unregister_inet6addr_notifier(&local->ifa6_notifier); #endif -diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c -index 6adfcb9..1c9cc65 100644 ---- a/net/mac80211/mlme.c -+++ b/net/mac80211/mlme.c -@@ -2720,7 +2720,7 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get); - - static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, - const u8 *buf, size_t len, bool tx, -- u16 reason) -+ u16 reason, bool reconnect) - { - struct ieee80211_event event = { - .type = MLME_EVENT, -@@ -2729,7 +2729,7 @@ static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, - }; - - if (tx) -- cfg80211_tx_mlme_mgmt(sdata->dev, buf, len); -+ cfg80211_tx_mlme_mgmt(sdata->dev, buf, len, reconnect); - else - cfg80211_rx_mlme_mgmt(sdata->dev, buf, len); - -@@ -2751,13 +2751,18 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) - - tx = !sdata->csa_block_tx; - -- /* AP is probably out of range (or not reachable for another reason) so -- * remove the bss struct for that AP. -- */ -- cfg80211_unlink_bss(local->hw.wiphy, ifmgd->associated); -+ if (!ifmgd->driver_disconnect) { -+ /* -+ * AP is probably out of range (or not reachable for another -+ * reason) so remove the bss struct for that AP. -+ */ -+ cfg80211_unlink_bss(local->hw.wiphy, ifmgd->associated); -+ } - - ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, -- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, -+ ifmgd->driver_disconnect ? -+ WLAN_REASON_DEAUTH_LEAVING : -+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - tx, frame_buf); - mutex_lock(&local->mtx); - sdata->vif.csa_active = false; -@@ -2770,7 +2775,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) - mutex_unlock(&local->mtx); - - ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx, -- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); -+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, -+ ifmgd->reconnect); -+ ifmgd->reconnect = false; - - sdata_unlock(sdata); - } -@@ -2789,6 +2796,13 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work) - sdata_info(sdata, "Connection to AP %pM lost\n", - ifmgd->bssid); - __ieee80211_disconnect(sdata); -+ ifmgd->connection_loss = false; -+ } else if (ifmgd->driver_disconnect) { -+ sdata_info(sdata, -+ "Driver requested disconnection from AP %pM\n", -+ ifmgd->bssid); -+ __ieee80211_disconnect(sdata); -+ ifmgd->driver_disconnect = false; - } else { - ieee80211_mgd_probe_ap(sdata, true); - } -@@ -2827,6 +2841,21 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif) - } - EXPORT_SYMBOL(ieee80211_connection_loss); - -+void ieee80211_disconnect(struct ieee80211_vif *vif, bool reconnect) -+{ -+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); -+ struct ieee80211_hw *hw = &sdata->local->hw; -+ -+ trace_api_disconnect(sdata, reconnect); -+ -+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) -+ return; -+ -+ sdata->u.mgd.driver_disconnect = true; -+ sdata->u.mgd.reconnect = reconnect; -+ ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); -+} -+EXPORT_SYMBOL(ieee80211_disconnect); - - static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, - bool assoc) -@@ -3130,7 +3159,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, - ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - - ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, -- reason_code); -+ reason_code, false); - return; - } - -@@ -3179,7 +3208,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, - - ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - -- ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code); -+ ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code, -+ false); - } - - static void ieee80211_get_rates(struct ieee80211_supported_band *sband, -@@ -4199,7 +4229,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, - true, deauth_buf); - ieee80211_report_disconnect(sdata, deauth_buf, - sizeof(deauth_buf), true, -- WLAN_REASON_DEAUTH_LEAVING); -+ WLAN_REASON_DEAUTH_LEAVING, -+ false); - return; - } - -@@ -4344,7 +4375,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, - tx, frame_buf); - - ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, -- reason); -+ reason, false); - } - - static int ieee80211_auth(struct ieee80211_sub_if_data *sdata) -@@ -4716,7 +4747,8 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) - if (ifmgd->auth_data) - ieee80211_destroy_auth_data(sdata, false); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, -- IEEE80211_DEAUTH_FRAME_LEN); -+ IEEE80211_DEAUTH_FRAME_LEN, -+ false); - } - - /* This is a bit of a hack - we should find a better and more generic -@@ -5430,7 +5462,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, - - ieee80211_report_disconnect(sdata, frame_buf, - sizeof(frame_buf), true, -- WLAN_REASON_UNSPECIFIED); -+ WLAN_REASON_UNSPECIFIED, -+ false); - } - - sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); -@@ -5502,7 +5535,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, - - ieee80211_report_disconnect(sdata, frame_buf, - sizeof(frame_buf), true, -- WLAN_REASON_UNSPECIFIED); -+ WLAN_REASON_UNSPECIFIED, -+ false); - } - - if (ifmgd->auth_data && !ifmgd->auth_data->done) { -@@ -5801,7 +5835,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - ieee80211_destroy_auth_data(sdata, false); - ieee80211_report_disconnect(sdata, frame_buf, - sizeof(frame_buf), true, -- req->reason_code); -+ req->reason_code, false); - - return 0; - } -@@ -5821,7 +5855,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - ieee80211_destroy_assoc_data(sdata, false, true); - ieee80211_report_disconnect(sdata, frame_buf, - sizeof(frame_buf), true, -- req->reason_code); -+ req->reason_code, false); - return 0; - } - -@@ -5836,7 +5870,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - req->reason_code, tx, frame_buf); - ieee80211_report_disconnect(sdata, frame_buf, - sizeof(frame_buf), true, -- req->reason_code); -+ req->reason_code, false); - return 0; - } - -@@ -5869,7 +5903,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - frame_buf); - - ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, -- req->reason_code); -+ req->reason_code, false); - - return 0; - } -diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c -deleted file mode 100644 -index 4e96d6d..0000000 ---- a/net/mac80211/rc80211_minstrel.c -+++ /dev/null -@@ -1,574 +0,0 @@ --/* -- * Copyright (C) 2008 Felix Fietkau -- * -- * 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. -- * -- * Based on minstrel.c: -- * Copyright (C) 2005-2007 Derek Smithies -- * Sponsored by Indranet Technologies Ltd -- * -- * Based on sample.c: -- * Copyright (c) 2005 John Bicket -- * All rights reserved. -- * -- * Redistribution and use in source and binary forms, with or without -- * modification, are permitted provided that the following conditions -- * are met: -- * 1. Redistributions of source code must retain the above copyright -- * notice, this list of conditions and the following disclaimer, -- * without modification. -- * 2. Redistributions in binary form must reproduce at minimum a disclaimer -- * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any -- * redistribution must be conditioned upon including a substantially -- * similar Disclaimer requirement for further binary redistribution. -- * 3. Neither the names of the above-listed copyright holders nor the names -- * of any contributors may be used to endorse or promote products derived -- * from this software without specific prior written permission. -- * -- * Alternatively, this software may be distributed under the terms of the -- * GNU General Public License ("GPL") version 2 as published by the Free -- * Software Foundation. -- * -- * NO WARRANTY -- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY -- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -- * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, -- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -- * THE POSSIBILITY OF SUCH DAMAGES. -- */ --#include --#include --#include --#include --#include --#include --#include --#include --#include "rate.h" --#include "rc80211_minstrel.h" -- --#define SAMPLE_TBL(_mi, _idx, _col) \ -- _mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col] -- --/* convert mac80211 rate index to local array index */ --static inline int --rix_to_ndx(struct minstrel_sta_info *mi, int rix) --{ -- int i = rix; -- for (i = rix; i >= 0; i--) -- if (mi->r[i].rix == rix) -- break; -- return i; --} -- --/* return current EMWA throughput */ --int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_avg) --{ -- int usecs; -- -- usecs = mr->perfect_tx_time; -- if (!usecs) -- usecs = 1000000; -- -- /* reset thr. below 10% success */ -- if (mr->stats.prob_avg < MINSTREL_FRAC(10, 100)) -- return 0; -- -- if (prob_avg > MINSTREL_FRAC(90, 100)) -- return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs)); -- else -- return MINSTREL_TRUNC(100000 * (prob_avg / usecs)); --} -- --/* find & sort topmost throughput rates */ --static inline void --minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) --{ -- int j; -- struct minstrel_rate_stats *tmp_mrs; -- struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats; -- -- for (j = MAX_THR_RATES; j > 0; --j) { -- tmp_mrs = &mi->r[tp_list[j - 1]].stats; -- if (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_avg) <= -- minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_avg)) -- break; -- } -- -- if (j < MAX_THR_RATES - 1) -- memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); -- if (j < MAX_THR_RATES) -- tp_list[j] = i; --} -- --static void --minstrel_set_rate(struct minstrel_sta_info *mi, struct ieee80211_sta_rates *ratetbl, -- int offset, int idx) --{ -- struct minstrel_rate *r = &mi->r[idx]; -- -- ratetbl->rate[offset].idx = r->rix; -- ratetbl->rate[offset].count = r->adjusted_retry_count; -- ratetbl->rate[offset].count_cts = r->retry_count_cts; -- ratetbl->rate[offset].count_rts = r->stats.retry_count_rtscts; --} -- --static void --minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi) --{ -- struct ieee80211_sta_rates *ratetbl; -- int i = 0; -- -- ratetbl = kzalloc(sizeof(*ratetbl), GFP_ATOMIC); -- if (!ratetbl) -- return; -- -- /* Start with max_tp_rate */ -- minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[0]); -- -- if (mp->hw->max_rates >= 3) { -- /* At least 3 tx rates supported, use max_tp_rate2 next */ -- minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[1]); -- } -- -- if (mp->hw->max_rates >= 2) { -- /* At least 2 tx rates supported, use max_prob_rate next */ -- minstrel_set_rate(mi, ratetbl, i++, mi->max_prob_rate); -- } -- -- /* Use lowest rate last */ -- ratetbl->rate[i].idx = mi->lowest_rix; -- ratetbl->rate[i].count = mp->max_retry; -- ratetbl->rate[i].count_cts = mp->max_retry; -- ratetbl->rate[i].count_rts = mp->max_retry; -- -- rate_control_set_rates(mp->hw, mi->sta, ratetbl); --} -- --/* --* Recalculate statistics and counters of a given rate --*/ --void --minstrel_calc_rate_stats(struct minstrel_priv *mp, -- struct minstrel_rate_stats *mrs) --{ -- unsigned int cur_prob; -- -- if (unlikely(mrs->attempts > 0)) { -- mrs->sample_skipped = 0; -- cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); -- if (mp->new_avg) { -- minstrel_filter_avg_add(&mrs->prob_avg, -- &mrs->prob_avg_1, cur_prob); -- } else if (unlikely(!mrs->att_hist)) { -- mrs->prob_avg = cur_prob; -- } else { -- /*update exponential weighted moving avarage */ -- mrs->prob_avg = minstrel_ewma(mrs->prob_avg, -- cur_prob, -- EWMA_LEVEL); -- } -- mrs->att_hist += mrs->attempts; -- mrs->succ_hist += mrs->success; -- } else { -- mrs->sample_skipped++; -- } -- -- mrs->last_success = mrs->success; -- mrs->last_attempts = mrs->attempts; -- mrs->success = 0; -- mrs->attempts = 0; --} -- --static void --minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) --{ -- u8 tmp_tp_rate[MAX_THR_RATES]; -- u8 tmp_prob_rate = 0; -- int i, tmp_cur_tp, tmp_prob_tp; -- -- for (i = 0; i < MAX_THR_RATES; i++) -- tmp_tp_rate[i] = 0; -- -- for (i = 0; i < mi->n_rates; i++) { -- struct minstrel_rate *mr = &mi->r[i]; -- struct minstrel_rate_stats *mrs = &mi->r[i].stats; -- struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats; -- -- /* Update statistics of success probability per rate */ -- minstrel_calc_rate_stats(mp, mrs); -- -- /* Sample less often below the 10% chance of success. -- * Sample less often above the 95% chance of success. */ -- if (mrs->prob_avg > MINSTREL_FRAC(95, 100) || -- mrs->prob_avg < MINSTREL_FRAC(10, 100)) { -- mr->adjusted_retry_count = mrs->retry_count >> 1; -- if (mr->adjusted_retry_count > 2) -- mr->adjusted_retry_count = 2; -- mr->sample_limit = 4; -- } else { -- mr->sample_limit = -1; -- mr->adjusted_retry_count = mrs->retry_count; -- } -- if (!mr->adjusted_retry_count) -- mr->adjusted_retry_count = 2; -- -- minstrel_sort_best_tp_rates(mi, i, tmp_tp_rate); -- -- /* To determine the most robust rate (max_prob_rate) used at -- * 3rd mmr stage we distinct between two cases: -- * (1) if any success probabilitiy >= 95%, out of those rates -- * choose the maximum throughput rate as max_prob_rate -- * (2) if all success probabilities < 95%, the rate with -- * highest success probability is chosen as max_prob_rate */ -- if (mrs->prob_avg >= MINSTREL_FRAC(95, 100)) { -- tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_avg); -- tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate], -- tmp_mrs->prob_avg); -- if (tmp_cur_tp >= tmp_prob_tp) -- tmp_prob_rate = i; -- } else { -- if (mrs->prob_avg >= tmp_mrs->prob_avg) -- tmp_prob_rate = i; -- } -- } -- -- /* Assign the new rate set */ -- memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate)); -- mi->max_prob_rate = tmp_prob_rate; -- --#ifdef CPTCFG_MAC80211_DEBUGFS -- /* use fixed index if set */ -- if (mp->fixed_rate_idx != -1) { -- mi->max_tp_rate[0] = mp->fixed_rate_idx; -- mi->max_tp_rate[1] = mp->fixed_rate_idx; -- mi->max_prob_rate = mp->fixed_rate_idx; -- } --#endif -- -- /* Reset update timer */ -- mi->last_stats_update = jiffies; -- -- minstrel_update_rates(mp, mi); --} -- --static void --minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, -- void *priv_sta, struct ieee80211_tx_status *st) --{ -- struct ieee80211_tx_info *info = st->info; -- struct minstrel_priv *mp = priv; -- struct minstrel_sta_info *mi = priv_sta; -- struct ieee80211_tx_rate *ar = info->status.rates; -- int i, ndx; -- int success; -- -- success = !!(info->flags & IEEE80211_TX_STAT_ACK); -- -- for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { -- if (ar[i].idx < 0 || !ar[i].count) -- break; -- -- ndx = rix_to_ndx(mi, ar[i].idx); -- if (ndx < 0) -- continue; -- -- mi->r[ndx].stats.attempts += ar[i].count; -- -- if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) -- mi->r[ndx].stats.success += success; -- } -- -- if (time_after(jiffies, mi->last_stats_update + -- mp->update_interval / (mp->new_avg ? 2 : 1))) -- minstrel_update_stats(mp, mi); --} -- -- --static inline unsigned int --minstrel_get_retry_count(struct minstrel_rate *mr, -- struct ieee80211_tx_info *info) --{ -- u8 retry = mr->adjusted_retry_count; -- -- if (info->control.use_rts) -- retry = max_t(u8, 2, min(mr->stats.retry_count_rtscts, retry)); -- else if (info->control.use_cts_prot) -- retry = max_t(u8, 2, min(mr->retry_count_cts, retry)); -- return retry; --} -- -- --static int --minstrel_get_next_sample(struct minstrel_sta_info *mi) --{ -- unsigned int sample_ndx; -- sample_ndx = SAMPLE_TBL(mi, mi->sample_row, mi->sample_column); -- mi->sample_row++; -- if ((int) mi->sample_row >= mi->n_rates) { -- mi->sample_row = 0; -- mi->sample_column++; -- if (mi->sample_column >= SAMPLE_COLUMNS) -- mi->sample_column = 0; -- } -- return sample_ndx; --} -- --static void --minstrel_get_rate(void *priv, struct ieee80211_sta *sta, -- void *priv_sta, struct ieee80211_tx_rate_control *txrc) --{ -- struct sk_buff *skb = txrc->skb; -- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -- struct minstrel_sta_info *mi = priv_sta; -- struct minstrel_priv *mp = priv; -- struct ieee80211_tx_rate *rate = &info->control.rates[0]; -- struct minstrel_rate *msr, *mr; -- unsigned int ndx; -- bool mrr_capable; -- bool prev_sample; -- int delta; -- int sampling_ratio; -- -- /* check multi-rate-retry capabilities & adjust lookaround_rate */ -- mrr_capable = mp->has_mrr && -- !txrc->rts && -- !txrc->bss_conf->use_cts_prot; -- if (mrr_capable) -- sampling_ratio = mp->lookaround_rate_mrr; -- else -- sampling_ratio = mp->lookaround_rate; -- -- /* increase sum packet counter */ -- mi->total_packets++; -- --#ifdef CPTCFG_MAC80211_DEBUGFS -- if (mp->fixed_rate_idx != -1) -- return; --#endif -- -- /* Don't use EAPOL frames for sampling on non-mrr hw */ -- if (mp->hw->max_rates == 1 && -- (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) -- return; -- -- delta = (mi->total_packets * sampling_ratio / 100) - -- mi->sample_packets; -- -- /* delta < 0: no sampling required */ -- prev_sample = mi->prev_sample; -- mi->prev_sample = false; -- if (delta < 0 || (!mrr_capable && prev_sample)) -- return; -- -- if (mi->total_packets >= 10000) { -- mi->sample_packets = 0; -- mi->total_packets = 0; -- } else if (delta > mi->n_rates * 2) { -- /* With multi-rate retry, not every planned sample -- * attempt actually gets used, due to the way the retry -- * chain is set up - [max_tp,sample,prob,lowest] for -- * sample_rate < max_tp. -- * -- * If there's too much sampling backlog and the link -- * starts getting worse, minstrel would start bursting -- * out lots of sampling frames, which would result -- * in a large throughput loss. */ -- mi->sample_packets += (delta - mi->n_rates * 2); -- } -- -- /* get next random rate sample */ -- ndx = minstrel_get_next_sample(mi); -- msr = &mi->r[ndx]; -- mr = &mi->r[mi->max_tp_rate[0]]; -- -- /* Decide if direct ( 1st mrr stage) or indirect (2nd mrr stage) -- * rate sampling method should be used. -- * Respect such rates that are not sampled for 20 interations. -- */ -- if (msr->perfect_tx_time < mr->perfect_tx_time || -- msr->stats.sample_skipped >= 20) { -- if (!msr->sample_limit) -- return; -- -- mi->sample_packets++; -- if (msr->sample_limit > 0) -- msr->sample_limit--; -- } -- -- /* If we're not using MRR and the sampling rate already -- * has a probability of >95%, we shouldn't be attempting -- * to use it, as this only wastes precious airtime */ -- if (!mrr_capable && -- (mi->r[ndx].stats.prob_avg > MINSTREL_FRAC(95, 100))) -- return; -- -- mi->prev_sample = true; -- -- rate->idx = mi->r[ndx].rix; -- rate->count = minstrel_get_retry_count(&mi->r[ndx], info); -- info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; --} -- -- --static void --calc_rate_durations(enum nl80211_band band, -- struct minstrel_rate *d, -- struct ieee80211_rate *rate, -- struct cfg80211_chan_def *chandef) --{ -- int erp = !!(rate->flags & IEEE80211_RATE_ERP_G); -- int shift = ieee80211_chandef_get_shift(chandef); -- -- d->perfect_tx_time = ieee80211_frame_duration(band, 1200, -- DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1, -- shift); -- d->ack_time = ieee80211_frame_duration(band, 10, -- DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1, -- shift); --} -- --static void --init_sample_table(struct minstrel_sta_info *mi) --{ -- unsigned int i, col, new_idx; -- u8 rnd[8]; -- -- mi->sample_column = 0; -- mi->sample_row = 0; -- memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates); -- -- for (col = 0; col < SAMPLE_COLUMNS; col++) { -- prandom_bytes(rnd, sizeof(rnd)); -- for (i = 0; i < mi->n_rates; i++) { -- new_idx = (i + rnd[i & 7]) % mi->n_rates; -- while (SAMPLE_TBL(mi, new_idx, col) != 0xff) -- new_idx = (new_idx + 1) % mi->n_rates; -- -- SAMPLE_TBL(mi, new_idx, col) = i; -- } -- } --} -- --static void --minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, -- struct cfg80211_chan_def *chandef, -- struct ieee80211_sta *sta, void *priv_sta) --{ -- struct minstrel_sta_info *mi = priv_sta; -- struct minstrel_priv *mp = priv; -- struct ieee80211_rate *ctl_rate; -- unsigned int i, n = 0; -- unsigned int t_slot = 9; /* FIXME: get real slot time */ -- u32 rate_flags; -- -- mi->sta = sta; -- mi->lowest_rix = rate_lowest_index(sband, sta); -- ctl_rate = &sband->bitrates[mi->lowest_rix]; -- mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10, -- ctl_rate->bitrate, -- !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1, -- ieee80211_chandef_get_shift(chandef)); -- -- rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); -- memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate)); -- mi->max_prob_rate = 0; -- -- for (i = 0; i < sband->n_bitrates; i++) { -- struct minstrel_rate *mr = &mi->r[n]; -- struct minstrel_rate_stats *mrs = &mi->r[n].stats; -- unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; -- unsigned int tx_time_single; -- unsigned int cw = mp->cw_min; -- int shift; -- -- if (!rate_supported(sta, sband->band, i)) -- continue; -- if ((rate_flags & sband->bitrates[i].flags) != rate_flags) -- continue; -- -- n++; -- memset(mr, 0, sizeof(*mr)); -- memset(mrs, 0, sizeof(*mrs)); -- -- mr->rix = i; -- shift = ieee80211_chandef_get_shift(chandef); -- mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate, -- (1 << shift) * 5); -- calc_rate_durations(sband->band, mr, &sband->bitrates[i], -- chandef); -- -- /* calculate maximum number of retransmissions before -- * fallback (based on maximum segment size) */ -- mr->sample_limit = -1; -- mrs->retry_count = 1; -- mr->retry_count_cts = 1; -- mrs->retry_count_rtscts = 1; -- tx_time = mr->perfect_tx_time + mi->sp_ack_dur; -- do { -- /* add one retransmission */ -- tx_time_single = mr->ack_time + mr->perfect_tx_time; -- -- /* contention window */ -- tx_time_single += (t_slot * cw) >> 1; -- cw = min((cw << 1) | 1, mp->cw_max); -- -- tx_time += tx_time_single; -- tx_time_cts += tx_time_single + mi->sp_ack_dur; -- tx_time_rtscts += tx_time_single + 2 * mi->sp_ack_dur; -- if ((tx_time_cts < mp->segment_size) && -- (mr->retry_count_cts < mp->max_retry)) -- mr->retry_count_cts++; -- if ((tx_time_rtscts < mp->segment_size) && -- (mrs->retry_count_rtscts < mp->max_retry)) -- mrs->retry_count_rtscts++; -- } while ((tx_time < mp->segment_size) && -- (++mr->stats.retry_count < mp->max_retry)); -- mr->adjusted_retry_count = mrs->retry_count; -- if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)) -- mr->retry_count_cts = mrs->retry_count; -- } -- -- for (i = n; i < sband->n_bitrates; i++) { -- struct minstrel_rate *mr = &mi->r[i]; -- mr->rix = -1; -- } -- -- mi->n_rates = n; -- mi->last_stats_update = jiffies; -- -- init_sample_table(mi); -- minstrel_update_rates(mp, mi); --} -- --static u32 minstrel_get_expected_throughput(void *priv_sta) --{ -- struct minstrel_sta_info *mi = priv_sta; -- struct minstrel_rate_stats *tmp_mrs; -- int idx = mi->max_tp_rate[0]; -- int tmp_cur_tp; -- -- /* convert pkt per sec in kbps (1200 is the average pkt size used for -- * computing cur_tp -- */ -- tmp_mrs = &mi->r[idx].stats; -- tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_avg) * 10; -- tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024; -- -- return tmp_cur_tp; --} -- --const struct rate_control_ops mac80211_minstrel = { -- .tx_status_ext = minstrel_tx_status, -- .get_rate = minstrel_get_rate, -- .rate_init = minstrel_rate_init, -- .get_expected_throughput = minstrel_get_expected_throughput, --}; -diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h -deleted file mode 100644 -index 302d090..0000000 ---- a/net/mac80211/rc80211_minstrel.h -+++ /dev/null -@@ -1,184 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2008 Felix Fietkau -- */ -- --#ifndef __RC_MINSTREL_H --#define __RC_MINSTREL_H -- --#define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */ --#define EWMA_DIV 128 --#define SAMPLE_COLUMNS 10 /* number of columns in sample table */ -- --/* scaled fraction values */ --#define MINSTREL_SCALE 12 --#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) --#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) -- --/* number of highest throughput rates to consider*/ --#define MAX_THR_RATES 4 -- --/* -- * Coefficients for moving average with noise filter (period=16), -- * scaled by 10 bits -- * -- * a1 = exp(-pi * sqrt(2) / period) -- * coeff2 = 2 * a1 * cos(sqrt(2) * 2 * pi / period) -- * coeff3 = -sqr(a1) -- * coeff1 = 1 - coeff2 - coeff3 -- */ --#define MINSTREL_AVG_COEFF1 (MINSTREL_FRAC(1, 1) - \ -- MINSTREL_AVG_COEFF2 - \ -- MINSTREL_AVG_COEFF3) --#define MINSTREL_AVG_COEFF2 0x00001499 --#define MINSTREL_AVG_COEFF3 -0x0000092e -- --/* -- * Perform EWMA (Exponentially Weighted Moving Average) calculation -- */ --static inline int --minstrel_ewma(int old, int new, int weight) --{ -- int diff, incr; -- -- diff = new - old; -- incr = (EWMA_DIV - weight) * diff / EWMA_DIV; -- -- return old + incr; --} -- --static inline int minstrel_filter_avg_add(u16 *prev_1, u16 *prev_2, s32 in) --{ -- s32 out_1 = *prev_1; -- s32 out_2 = *prev_2; -- s32 val; -- -- if (!in) -- in += 1; -- -- if (!out_1) { -- val = out_1 = in; -- goto out; -- } -- -- val = MINSTREL_AVG_COEFF1 * in; -- val += MINSTREL_AVG_COEFF2 * out_1; -- val += MINSTREL_AVG_COEFF3 * out_2; -- val >>= MINSTREL_SCALE; -- -- if (val > 1 << MINSTREL_SCALE) -- val = 1 << MINSTREL_SCALE; -- if (val < 0) -- val = 1; -- --out: -- *prev_2 = out_1; -- *prev_1 = val; -- -- return val; --} -- --struct minstrel_rate_stats { -- /* current / last sampling period attempts/success counters */ -- u16 attempts, last_attempts; -- u16 success, last_success; -- -- /* total attempts/success counters */ -- u32 att_hist, succ_hist; -- -- /* prob_avg - moving average of prob */ -- u16 prob_avg; -- u16 prob_avg_1; -- -- /* maximum retry counts */ -- u8 retry_count; -- u8 retry_count_rtscts; -- -- u8 sample_skipped; -- bool retry_updated; --}; -- --struct minstrel_rate { -- int bitrate; -- -- s8 rix; -- u8 retry_count_cts; -- u8 adjusted_retry_count; -- -- unsigned int perfect_tx_time; -- unsigned int ack_time; -- -- int sample_limit; -- -- struct minstrel_rate_stats stats; --}; -- --struct minstrel_sta_info { -- struct ieee80211_sta *sta; -- -- unsigned long last_stats_update; -- unsigned int sp_ack_dur; -- unsigned int rate_avg; -- -- unsigned int lowest_rix; -- -- u8 max_tp_rate[MAX_THR_RATES]; -- u8 max_prob_rate; -- unsigned int total_packets; -- unsigned int sample_packets; -- -- unsigned int sample_row; -- unsigned int sample_column; -- -- int n_rates; -- struct minstrel_rate *r; -- bool prev_sample; -- -- /* sampling table */ -- u8 *sample_table; --}; -- --struct minstrel_priv { -- struct ieee80211_hw *hw; -- bool has_mrr; -- bool new_avg; -- u32 sample_switch; -- unsigned int cw_min; -- unsigned int cw_max; -- unsigned int max_retry; -- unsigned int segment_size; -- unsigned int update_interval; -- unsigned int lookaround_rate; -- unsigned int lookaround_rate_mrr; -- -- u8 cck_rates[4]; -- --#ifdef CPTCFG_MAC80211_DEBUGFS -- /* -- * enable fixed rate processing per RC -- * - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx -- * - write -1 to enable RC processing again -- * - setting will be applied on next update -- */ -- u32 fixed_rate_idx; --#endif --}; -- --struct minstrel_debugfs_info { -- size_t len; -- char buf[]; --}; -- --extern const struct rate_control_ops mac80211_minstrel; --void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); -- --/* Recalculate success probabilities and counters for a given rate using EWMA */ --void minstrel_calc_rate_stats(struct minstrel_priv *mp, -- struct minstrel_rate_stats *mrs); --int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_avg); -- --/* debugfs */ --int minstrel_stats_open(struct inode *inode, struct file *file); --int minstrel_stats_csv_open(struct inode *inode, struct file *file); -- --#endif -diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c -deleted file mode 100644 -index 9b8e0da..0000000 ---- a/net/mac80211/rc80211_minstrel_debugfs.c -+++ /dev/null -@@ -1,172 +0,0 @@ --/* -- * Copyright (C) 2008 Felix Fietkau -- * -- * 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. -- * -- * Based on minstrel.c: -- * Copyright (C) 2005-2007 Derek Smithies -- * Sponsored by Indranet Technologies Ltd -- * -- * Based on sample.c: -- * Copyright (c) 2005 John Bicket -- * All rights reserved. -- * -- * Redistribution and use in source and binary forms, with or without -- * modification, are permitted provided that the following conditions -- * are met: -- * 1. Redistributions of source code must retain the above copyright -- * notice, this list of conditions and the following disclaimer, -- * without modification. -- * 2. Redistributions in binary form must reproduce at minimum a disclaimer -- * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any -- * redistribution must be conditioned upon including a substantially -- * similar Disclaimer requirement for further binary redistribution. -- * 3. Neither the names of the above-listed copyright holders nor the names -- * of any contributors may be used to endorse or promote products derived -- * from this software without specific prior written permission. -- * -- * Alternatively, this software may be distributed under the terms of the -- * GNU General Public License ("GPL") version 2 as published by the Free -- * Software Foundation. -- * -- * NO WARRANTY -- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY -- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -- * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, -- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -- * THE POSSIBILITY OF SUCH DAMAGES. -- */ --#include --#include --#include --#include --#include --#include --#include --#include --#include "rc80211_minstrel.h" -- --int --minstrel_stats_open(struct inode *inode, struct file *file) --{ -- struct minstrel_sta_info *mi = inode->i_private; -- struct minstrel_debugfs_info *ms; -- unsigned int i, tp_max, tp_avg, eprob; -- char *p; -- -- ms = kmalloc(2048, GFP_KERNEL); -- if (!ms) -- return -ENOMEM; -- -- file->private_data = ms; -- p = ms->buf; -- p += sprintf(p, "\n"); -- p += sprintf(p, -- "best __________rate_________ ____statistics___ ____last_____ ______sum-of________\n"); -- p += sprintf(p, -- "rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts]\n"); -- -- for (i = 0; i < mi->n_rates; i++) { -- struct minstrel_rate *mr = &mi->r[i]; -- struct minstrel_rate_stats *mrs = &mi->r[i].stats; -- -- *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' '; -- *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' '; -- *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' '; -- *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' '; -- *(p++) = (i == mi->max_prob_rate) ? 'P' : ' '; -- -- p += sprintf(p, " %3u%s ", mr->bitrate / 2, -- (mr->bitrate & 1 ? ".5" : " ")); -- p += sprintf(p, "%3u ", i); -- p += sprintf(p, "%6u ", mr->perfect_tx_time); -- -- tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); -- tp_avg = minstrel_get_tp_avg(mr, mrs->prob_avg); -- eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000); -- -- p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u" -- " %3u %3u %-3u " -- "%9llu %-9llu\n", -- tp_max / 10, tp_max % 10, -- tp_avg / 10, tp_avg % 10, -- eprob / 10, eprob % 10, -- mrs->retry_count, -- mrs->last_success, -- mrs->last_attempts, -- (unsigned long long)mrs->succ_hist, -- (unsigned long long)mrs->att_hist); -- } -- p += sprintf(p, "\nTotal packet count:: ideal %d " -- "lookaround %d\n\n", -- mi->total_packets - mi->sample_packets, -- mi->sample_packets); -- ms->len = p - ms->buf; -- -- WARN_ON(ms->len + sizeof(*ms) > 2048); -- -- return 0; --} -- --int --minstrel_stats_csv_open(struct inode *inode, struct file *file) --{ -- struct minstrel_sta_info *mi = inode->i_private; -- struct minstrel_debugfs_info *ms; -- unsigned int i, tp_max, tp_avg, eprob; -- char *p; -- -- ms = kmalloc(2048, GFP_KERNEL); -- if (!ms) -- return -ENOMEM; -- -- file->private_data = ms; -- p = ms->buf; -- -- for (i = 0; i < mi->n_rates; i++) { -- struct minstrel_rate *mr = &mi->r[i]; -- struct minstrel_rate_stats *mrs = &mi->r[i].stats; -- -- p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : "")); -- p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : "")); -- p += sprintf(p, "%s" ,((i == mi->max_tp_rate[2]) ? "C" : "")); -- p += sprintf(p, "%s" ,((i == mi->max_tp_rate[3]) ? "D" : "")); -- p += sprintf(p, "%s" ,((i == mi->max_prob_rate) ? "P" : "")); -- -- p += sprintf(p, ",%u%s", mr->bitrate / 2, -- (mr->bitrate & 1 ? ".5," : ",")); -- p += sprintf(p, "%u,", i); -- p += sprintf(p, "%u,",mr->perfect_tx_time); -- -- tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); -- tp_avg = minstrel_get_tp_avg(mr, mrs->prob_avg); -- eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000); -- -- p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u," -- "%llu,%llu,%d,%d\n", -- tp_max / 10, tp_max % 10, -- tp_avg / 10, tp_avg % 10, -- eprob / 10, eprob % 10, -- mrs->retry_count, -- mrs->last_success, -- mrs->last_attempts, -- (unsigned long long)mrs->succ_hist, -- (unsigned long long)mrs->att_hist, -- mi->total_packets - mi->sample_packets, -- mi->sample_packets); -- -- } -- ms->len = p - ms->buf; -- -- WARN_ON(ms->len + sizeof(*ms) > 2048); -- -- return 0; --} diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c -index 53d7c74..af4358e 100644 +index 0e6133c..61ff2ff 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c -@@ -13,7 +13,6 @@ - #include - #include "rate.h" - #include "sta_info.h" --#include "rc80211_minstrel.h" - #include "rc80211_minstrel_ht.h" - - #define AVG_AMPDU_SIZE 16 -@@ -136,20 +135,16 @@ - __VHT_GROUP(_streams, _sgi, _bw, \ - VHT_GROUP_SHIFT(_streams, _sgi, _bw)) - --#define CCK_DURATION(_bitrate, _short, _len) \ -+#define CCK_DURATION(_bitrate, _short) \ - (1000 * (10 /* SIFS */ + \ - (_short ? 72 + 24 : 144 + 48) + \ -- (8 * (_len + 4) * 10) / (_bitrate))) -- --#define CCK_ACK_DURATION(_bitrate, _short) \ -- (CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) + \ -- CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE)) -+ (8 * (AVG_PKT_SIZE + 4) * 10) / (_bitrate))) - - #define CCK_DURATION_LIST(_short, _s) \ -- CCK_ACK_DURATION(10, _short) >> _s, \ -- CCK_ACK_DURATION(20, _short) >> _s, \ -- CCK_ACK_DURATION(55, _short) >> _s, \ -- CCK_ACK_DURATION(110, _short) >> _s -+ CCK_DURATION(10, _short) >> _s, \ -+ CCK_DURATION(20, _short) >> _s, \ -+ CCK_DURATION(55, _short) >> _s, \ -+ CCK_DURATION(110, _short) >> _s - - #define __CCK_GROUP(_s) \ - [MINSTREL_CCK_GROUP] = { \ -@@ -163,10 +158,42 @@ - } - - #define CCK_GROUP_SHIFT \ -- GROUP_SHIFT(CCK_ACK_DURATION(10, false)) -+ GROUP_SHIFT(CCK_DURATION(10, false)) - - #define CCK_GROUP __CCK_GROUP(CCK_GROUP_SHIFT) - -+#define OFDM_DURATION(_bitrate) \ -+ (1000 * (16 /* SIFS + signal ext */ + \ -+ 16 /* T_PREAMBLE */ + \ -+ 4 /* T_SIGNAL */ + \ -+ 4 * (((16 + 80 * (AVG_PKT_SIZE + 4) + 6) / \ -+ ((_bitrate) * 4))))) -+ -+#define OFDM_DURATION_LIST(_s) \ -+ OFDM_DURATION(60) >> _s, \ -+ OFDM_DURATION(90) >> _s, \ -+ OFDM_DURATION(120) >> _s, \ -+ OFDM_DURATION(180) >> _s, \ -+ OFDM_DURATION(240) >> _s, \ -+ OFDM_DURATION(360) >> _s, \ -+ OFDM_DURATION(480) >> _s, \ -+ OFDM_DURATION(540) >> _s -+ -+#define __OFDM_GROUP(_s) \ -+ [MINSTREL_OFDM_GROUP] = { \ -+ .streams = 1, \ -+ .flags = 0, \ -+ .shift = _s, \ -+ .duration = { \ -+ OFDM_DURATION_LIST(_s), \ -+ } \ -+ } -+ -+#define OFDM_GROUP_SHIFT \ -+ GROUP_SHIFT(OFDM_DURATION(60)) -+ -+#define OFDM_GROUP __OFDM_GROUP(OFDM_GROUP_SHIFT) -+ - - static bool minstrel_vht_only = true; - module_param(minstrel_vht_only, bool, 0644); -@@ -203,6 +230,7 @@ const struct mcs_group minstrel_mcs_groups[] = { - MCS_GROUP(4, 1, BW_40), - - CCK_GROUP, -+ OFDM_GROUP, - - VHT_GROUP(1, 0, BW_20), - VHT_GROUP(2, 0, BW_20), -@@ -235,7 +263,17 @@ const struct mcs_group minstrel_mcs_groups[] = { - VHT_GROUP(4, 1, BW_80), - }; - -+const s16 minstrel_cck_bitrates[4] = { 10, 20, 55, 110 }; -+const s16 minstrel_ofdm_bitrates[8] = { 60, 90, 120, 180, 240, 360, 480, 540 }; - static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; -+static const u8 minstrel_sample_seq[] = { -+ MINSTREL_SAMPLE_TYPE_INC, -+ MINSTREL_SAMPLE_TYPE_JUMP, -+ MINSTREL_SAMPLE_TYPE_INC, -+ MINSTREL_SAMPLE_TYPE_JUMP, -+ MINSTREL_SAMPLE_TYPE_INC, -+ MINSTREL_SAMPLE_TYPE_SLOW, -+}; - - static void - minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); -@@ -279,6 +317,13 @@ minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map) - return 0x3ff & ~mask; - } - -+static bool -+minstrel_ht_is_legacy_group(int group) -+{ -+ return group == MINSTREL_CCK_GROUP || -+ group == MINSTREL_OFDM_GROUP; -+} -+ - /* - * Look up an MCS group index based on mac80211 rate information - */ -@@ -308,37 +353,74 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - if (rate->flags & IEEE80211_TX_RC_MCS) { - group = minstrel_ht_get_group_idx(rate); - idx = rate->idx % 8; -- } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { -+ goto out; -+ } -+ -+ if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { - group = minstrel_vht_get_group_idx(rate); - idx = ieee80211_rate_get_vht_mcs(rate); -- } else { -- group = MINSTREL_CCK_GROUP; -+ goto out; -+ } - -- for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++) -- if (rate->idx == mp->cck_rates[idx]) -- break; -+ group = MINSTREL_CCK_GROUP; -+ for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++) { -+ if (rate->idx != mp->cck_rates[idx]) -+ continue; - - /* short preamble */ - if ((mi->supported[group] & BIT(idx + 4)) && - (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)) -- idx += 4; -+ idx += 4; -+ goto out; - } -+ -+ group = MINSTREL_OFDM_GROUP; -+ for (idx = 0; idx < ARRAY_SIZE(mp->ofdm_rates[0]); idx++) -+ if (rate->idx == mp->ofdm_rates[mi->band][idx]) -+ goto out; -+ -+ idx = 0; -+out: - return &mi->groups[group].rates[idx]; - } - - static inline struct minstrel_rate_stats * - minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) - { -- return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; -+ return &mi->groups[MI_RATE_GROUP(index)].rates[MI_RATE_IDX(index)]; -+} -+ -+static inline int minstrel_get_duration(int index) -+{ -+ const struct mcs_group *group = &minstrel_mcs_groups[MI_RATE_GROUP(index)]; -+ unsigned int duration = group->duration[MI_RATE_IDX(index)]; -+ -+ return duration << group->shift; - } - - static unsigned int - minstrel_ht_avg_ampdu_len(struct minstrel_ht_sta *mi) - { -- if (!mi->avg_ampdu_len) -- return AVG_AMPDU_SIZE; -+ int duration; -+ -+ if (mi->avg_ampdu_len) -+ return MINSTREL_TRUNC(mi->avg_ampdu_len); -+ -+ if (minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_tp_rate[0]))) -+ return 1; -+ -+ duration = minstrel_get_duration(mi->max_tp_rate[0]); - -- return MINSTREL_TRUNC(mi->avg_ampdu_len); -+ if (duration > 400 * 1000) -+ return 2; -+ -+ if (duration > 250 * 1000) -+ return 4; -+ -+ if (duration > 150 * 1000) -+ return 8; -+ -+ return 16; - } - - /* -@@ -349,15 +431,19 @@ int - minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, - int prob_avg) - { -- unsigned int nsecs = 0; -+ unsigned int nsecs = 0, overhead = mi->overhead; -+ unsigned int ampdu_len = 1; - - /* do not account throughput if sucess prob is below 10% */ - if (prob_avg < MINSTREL_FRAC(10, 100)) - return 0; - -- if (group != MINSTREL_CCK_GROUP) -- nsecs = 1000 * mi->overhead / minstrel_ht_avg_ampdu_len(mi); -+ if (minstrel_ht_is_legacy_group(group)) -+ overhead = mi->overhead_legacy; -+ else -+ ampdu_len = minstrel_ht_avg_ampdu_len(mi); - -+ nsecs = 1000 * overhead / ampdu_len; - nsecs += minstrel_mcs_groups[group].duration[rate] << - minstrel_mcs_groups[group].shift; - -@@ -367,10 +453,9 @@ minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, - * (prob is scaled - see MINSTREL_FRAC above) - */ - if (prob_avg > MINSTREL_FRAC(90, 100)) -- return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000) -- / nsecs)); -- else -- return MINSTREL_TRUNC(100000 * ((prob_avg * 1000) / nsecs)); -+ prob_avg = MINSTREL_FRAC(90, 100); -+ -+ return MINSTREL_TRUNC(100 * ((prob_avg * 1000000) / nsecs)); - } - - /* -@@ -388,14 +473,14 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, - int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; - int j = MAX_THR_RATES; - -- cur_group = index / MCS_GROUP_RATES; -- cur_idx = index % MCS_GROUP_RATES; -+ cur_group = MI_RATE_GROUP(index); -+ cur_idx = MI_RATE_IDX(index); - cur_prob = mi->groups[cur_group].rates[cur_idx].prob_avg; - cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, cur_prob); - - do { -- tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; -- tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; -+ tmp_group = MI_RATE_GROUP(tp_list[j - 1]); -+ tmp_idx = MI_RATE_IDX(tp_list[j - 1]); - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; - tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, - tmp_prob); -@@ -417,41 +502,53 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, - * Find and set the topmost probability rate per sta and per group - */ - static void --minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) -+minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index) - { - struct minstrel_mcs_group_data *mg; - struct minstrel_rate_stats *mrs; - int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; -- int max_tp_group, cur_tp_avg, cur_group, cur_idx; -+ int max_tp_group, max_tp_idx, max_tp_prob; -+ int cur_tp_avg, cur_group, cur_idx; +@@ -514,6 +514,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; - -- cur_group = index / MCS_GROUP_RATES; -- cur_idx = index % MCS_GROUP_RATES; -- mg = &mi->groups[index / MCS_GROUP_RATES]; -- mrs = &mg->rates[index % MCS_GROUP_RATES]; ++ + min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]), + minstrel_get_duration(mi->max_tp_rate[1])); - -- tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; -- tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; ++ + /* 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); -+ mg = &mi->groups[cur_group]; -+ mrs = &mg->rates[cur_idx]; -+ -+ tmp_group = MI_RATE_GROUP(*dest); -+ tmp_idx = MI_RATE_IDX(*dest); - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; - tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); - /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from - * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */ -- max_tp_group = mi->max_tp_rate[0] / MCS_GROUP_RATES; -- if((index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) && -- (max_tp_group != MINSTREL_CCK_GROUP)) -+ max_tp_group = MI_RATE_GROUP(mi->max_tp_rate[0]); -+ max_tp_idx = MI_RATE_IDX(mi->max_tp_rate[0]); -+ max_tp_prob = mi->groups[max_tp_group].rates[max_tp_idx].prob_avg; -+ -+ if (minstrel_ht_is_legacy_group(MI_RATE_GROUP(index)) && -+ !minstrel_ht_is_legacy_group(max_tp_group)) + cur_group = MI_RATE_GROUP(index); + cur_idx = MI_RATE_IDX(index); +@@ -535,11 +543,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; -- max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES; -- max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; -+ max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate); -+ max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate); +- /* 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; - - if (mrs->prob_avg > MINSTREL_FRAC(75, 100)) { - cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, - mrs->prob_avg); - if (cur_tp_avg > tmp_tp_avg) -- mi->max_prob_rate = index; -+ *dest = index; - - max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group, - max_gpr_idx, -@@ -460,7 +557,7 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) - mg->max_group_prob_rate = index; - } else { - if (mrs->prob_avg > tmp_prob) -- mi->max_prob_rate = index; -+ *dest = index; - if (mrs->prob_avg > max_gpr_prob) - mg->max_group_prob_rate = index; - } -@@ -476,188 +573,384 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) - static void - minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, - u16 tmp_mcs_tp_rate[MAX_THR_RATES], -- u16 tmp_cck_tp_rate[MAX_THR_RATES]) -+ u16 tmp_legacy_tp_rate[MAX_THR_RATES]) - { - unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob; - int i; - -- tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES; -- tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES; -+ tmp_group = MI_RATE_GROUP(tmp_legacy_tp_rate[0]); -+ tmp_idx = MI_RATE_IDX(tmp_legacy_tp_rate[0]); - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; - tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); - -- tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES; -- tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES; -+ tmp_group = MI_RATE_GROUP(tmp_mcs_tp_rate[0]); -+ tmp_idx = MI_RATE_IDX(tmp_mcs_tp_rate[0]); - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; - tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); - - if (tmp_cck_tp > tmp_mcs_tp) { - for(i = 0; i < MAX_THR_RATES; i++) { -- minstrel_ht_sort_best_tp_rates(mi, tmp_cck_tp_rate[i], -+ minstrel_ht_sort_best_tp_rates(mi, tmp_legacy_tp_rate[i], - tmp_mcs_tp_rate); - } - } +@@ -597,40 +600,6 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, } @@ -3261,27 +1901,22 @@ index 53d7c74..af4358e 100644 - */ -static inline void -minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) -+static u16 -+__minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi, -+ enum minstrel_sample_type type) - { +-{ - struct minstrel_mcs_group_data *mg; - int tmp_max_streams, group, tmp_idx, tmp_prob; - int tmp_tp = 0; -+ u16 *rates = mi->sample[type].sample_rates; -+ u16 cur; -+ int i; - -- tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / -- MCS_GROUP_RATES].streams; +- +- if (!mi->sta->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) -+ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { -+ if (!rates[i]) - continue; - -- tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; +- 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) && @@ -3291,668 +1926,32 @@ index 53d7c74..af4358e 100644 - tmp_idx, - tmp_prob); - } -+ cur = rates[i]; -+ rates[i] = 0; -+ return cur; - } -+ -+ return 0; - } +- } +-} +- + static u16 + __minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi, + enum minstrel_sample_type type) +@@ -703,7 +672,8 @@ minstrel_ht_calc_rate_stats(struct minstrel_priv *mp, + unsigned int cur_prob; - static inline int --minstrel_get_duration(int index) -+minstrel_ewma(int old, int new, int weight) - { -- const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; -- unsigned int duration = group->duration[index % MCS_GROUP_RATES]; -- return duration << group->shift; -+ int diff, incr; -+ -+ diff = new - old; -+ incr = (EWMA_DIV - weight) * diff / EWMA_DIV; -+ -+ return old + incr; - } - --static bool --minstrel_ht_probe_group(struct minstrel_ht_sta *mi, const struct mcs_group *tp_group, -- int tp_idx, const struct mcs_group *group) -+static inline int minstrel_filter_avg_add(u16 *prev_1, u16 *prev_2, s32 in) - { -- if (group->bw < tp_group->bw) -- return false; -+ s32 out_1 = *prev_1; -+ s32 out_2 = *prev_2; -+ s32 val; - -- if (group->streams == tp_group->streams) -- return true; -+ if (!in) -+ in += 1; - -- if (tp_idx < 4 && group->streams == tp_group->streams - 1) -- return true; -+ if (!out_1) { -+ val = out_1 = in; -+ goto out; -+ } - -- return group->streams == tp_group->streams + 1; -+ val = MINSTREL_AVG_COEFF1 * in; -+ val += MINSTREL_AVG_COEFF2 * out_1; -+ val += MINSTREL_AVG_COEFF3 * out_2; -+ val >>= MINSTREL_SCALE; -+ -+ if (val > 1 << MINSTREL_SCALE) -+ val = 1 << MINSTREL_SCALE; -+ if (val < 0) -+ val = 1; -+ -+out: -+ *prev_2 = out_1; -+ *prev_1 = val; -+ -+ return val; - } - -+/* -+* Recalculate statistics and counters of a given rate -+*/ - static void --minstrel_ht_find_probe_rates(struct minstrel_ht_sta *mi, u16 *rates, int *n_rates, -- bool faster_rate) -+minstrel_ht_calc_rate_stats(struct minstrel_priv *mp, -+ struct minstrel_rate_stats *mrs) - { -- const struct mcs_group *group, *tp_group; -- int i, g, max_dur; -- int tp_idx; -+ unsigned int cur_prob; -+ -+ if (unlikely(mrs->attempts > 0)) { + 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; -+ mrs->succ_hist += mrs->success; -+ } + minstrel_filter_avg_add(&mrs->prob_avg, + &mrs->prob_avg_1, cur_prob); + mrs->att_hist += mrs->attempts; +@@ -1109,8 +1079,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) -- tp_group = &minstrel_mcs_groups[mi->max_tp_rate[0] / MCS_GROUP_RATES]; -- tp_idx = mi->max_tp_rate[0] % MCS_GROUP_RATES; -+ mrs->last_success = mrs->success; -+ mrs->last_attempts = mrs->attempts; -+ mrs->success = 0; -+ mrs->attempts = 0; -+} - -- max_dur = minstrel_get_duration(mi->max_tp_rate[0]); -- if (faster_rate) -- max_dur -= max_dur / 16; -+static bool -+minstrel_ht_find_sample_rate(struct minstrel_ht_sta *mi, int type, int idx) -+{ -+ int i; - -- for (g = 0; g < MINSTREL_GROUPS_NB; g++) { -- u16 supported = mi->supported[g]; -+ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { -+ u16 cur = mi->sample[type].sample_rates[i]; - -- if (!supported) -- continue; -+ if (cur == idx) -+ return true; - -- group = &minstrel_mcs_groups[g]; -- if (!minstrel_ht_probe_group(mi, tp_group, tp_idx, group)) -- continue; -+ if (!cur) -+ break; -+ } - -- for (i = 0; supported; supported >>= 1, i++) { -- int idx; -+ return false; -+} - -- if (!(supported & 1)) -- continue; -+static int -+minstrel_ht_move_sample_rates(struct minstrel_ht_sta *mi, int type, -+ u32 fast_rate_dur, u32 slow_rate_dur) -+{ -+ u16 *rates = mi->sample[type].sample_rates; -+ int i, j; - -- if ((group->duration[i] << group->shift) > max_dur) -- continue; -+ for (i = 0, j = 0; i < MINSTREL_SAMPLE_RATES; i++) { -+ u32 duration; -+ bool valid = false; -+ u16 cur; - -- idx = g * MCS_GROUP_RATES + i; -- if (idx == mi->max_tp_rate[0]) -- continue; -+ cur = rates[i]; -+ if (!cur) -+ continue; - -- rates[(*n_rates)++] = idx; -+ duration = minstrel_get_duration(cur); -+ switch (type) { -+ case MINSTREL_SAMPLE_TYPE_SLOW: -+ valid = duration > fast_rate_dur && -+ duration < slow_rate_dur; -+ break; -+ case MINSTREL_SAMPLE_TYPE_INC: -+ case MINSTREL_SAMPLE_TYPE_JUMP: -+ valid = duration < fast_rate_dur; -+ break; -+ default: -+ valid = false; - break; - } -+ -+ if (!valid) { -+ rates[i] = 0; -+ continue; -+ } -+ -+ if (i == j) -+ continue; -+ -+ rates[j++] = cur; -+ rates[i] = 0; - } -+ -+ return j; - } - --static void --minstrel_ht_rate_sample_switch(struct minstrel_priv *mp, -- struct minstrel_ht_sta *mi) -+static int -+minstrel_ht_group_min_rate_offset(struct minstrel_ht_sta *mi, int group, -+ u32 max_duration) - { -- struct minstrel_rate_stats *mrs; -- u16 rates[MINSTREL_GROUPS_NB]; -- int n_rates = 0; -- int probe_rate = 0; -- bool faster_rate; -+ u16 supported = mi->supported[group]; - int i; -- u8 random; - -- /* -- * Use rate switching instead of probing packets for devices with -- * little control over retry fallback behavior -- */ -- if (mp->hw->max_rates > 1) -- return; -+ for (i = 0; i < MCS_GROUP_RATES && supported; i++, supported >>= 1) { -+ if (!(supported & BIT(0))) -+ continue; - -- /* -- * If the current EWMA prob is >75%, look for a rate that's 6.25% -- * faster than the max tp rate. -- * If that fails, look again for a rate that is at least as fast -- */ -- mrs = minstrel_get_ratestats(mi, mi->max_tp_rate[0]); -- faster_rate = mrs->prob_avg > MINSTREL_FRAC(75, 100); -- minstrel_ht_find_probe_rates(mi, rates, &n_rates, faster_rate); -- if (!n_rates && faster_rate) -- minstrel_ht_find_probe_rates(mi, rates, &n_rates, false); -- -- /* If no suitable rate was found, try to pick the next one in the group */ -- if (!n_rates) { -- int g_idx = mi->max_tp_rate[0] / MCS_GROUP_RATES; -- u16 supported = mi->supported[g_idx]; -- -- supported >>= mi->max_tp_rate[0] % MCS_GROUP_RATES; -- for (i = 0; supported; supported >>= 1, i++) { -- if (!(supported & 1)) -- continue; -+ if (minstrel_get_duration(MI_RATE(group, i)) >= max_duration) -+ continue; -+ -+ return i; -+ } - -- probe_rate = mi->max_tp_rate[0] + i; -+ return -1; -+} -+ -+/* -+ * Incremental update rates: -+ * Flip through groups and pick the first group rate that is faster than the -+ * highest currently selected rate -+ */ -+static u16 -+minstrel_ht_next_inc_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur) -+{ -+ struct minstrel_mcs_group_data *mg; -+ u8 type = MINSTREL_SAMPLE_TYPE_INC; -+ int i, index = 0; -+ u8 group; -+ -+ group = mi->sample[type].sample_group; -+ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { -+ group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); -+ mg = &mi->groups[group]; -+ -+ index = minstrel_ht_group_min_rate_offset(mi, group, -+ fast_rate_dur); -+ if (index < 0) -+ continue; -+ -+ index = MI_RATE(group, index & 0xf); -+ if (!minstrel_ht_find_sample_rate(mi, type, index)) - goto out; -+ } -+ index = 0; -+ -+out: -+ mi->sample[type].sample_group = group; -+ -+ return index; -+} -+ -+static int -+minstrel_ht_next_group_sample_rate(struct minstrel_ht_sta *mi, int group, -+ u16 supported, int offset) -+{ -+ struct minstrel_mcs_group_data *mg = &mi->groups[group]; -+ u16 idx; -+ int i; -+ -+ for (i = 0; i < MCS_GROUP_RATES; i++) { -+ idx = sample_table[mg->column][mg->index]; -+ if (++mg->index >= MCS_GROUP_RATES) { -+ mg->index = 0; -+ if (++mg->column >= ARRAY_SIZE(sample_table)) -+ mg->column = 0; - } - -- return; -+ if (idx < offset) -+ continue; -+ -+ if (!(supported & BIT(idx))) -+ continue; -+ -+ return MI_RATE(group, idx); -+ } -+ -+ return -1; -+} -+ -+/* -+ * Jump rates: -+ * Sample random rates, use those that are faster than the highest -+ * currently selected rate. Rates between the fastest and the slowest -+ * get sorted into the slow sample bucket, but only if it has room -+ */ -+static u16 -+minstrel_ht_next_jump_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur, -+ u32 slow_rate_dur, int *slow_rate_ofs) -+{ -+ struct minstrel_mcs_group_data *mg; -+ struct minstrel_rate_stats *mrs; -+ u32 max_duration = slow_rate_dur; -+ int i, index, offset; -+ u16 *slow_rates; -+ u16 supported; -+ u32 duration; -+ u8 group; -+ -+ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) -+ max_duration = fast_rate_dur; -+ -+ slow_rates = mi->sample[MINSTREL_SAMPLE_TYPE_SLOW].sample_rates; -+ group = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group; -+ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { -+ u8 type; -+ -+ group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); -+ mg = &mi->groups[group]; -+ -+ supported = mi->supported[group]; -+ if (!supported) -+ continue; -+ -+ offset = minstrel_ht_group_min_rate_offset(mi, group, -+ max_duration); -+ if (offset < 0) -+ continue; -+ -+ index = minstrel_ht_next_group_sample_rate(mi, group, supported, -+ offset); -+ if (index < 0) -+ continue; -+ -+ duration = minstrel_get_duration(index); -+ if (duration < fast_rate_dur) -+ type = MINSTREL_SAMPLE_TYPE_JUMP; -+ else -+ type = MINSTREL_SAMPLE_TYPE_SLOW; -+ -+ if (minstrel_ht_find_sample_rate(mi, type, index)) -+ continue; -+ -+ if (type == MINSTREL_SAMPLE_TYPE_JUMP) -+ goto found; -+ -+ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) -+ continue; -+ -+ if (duration >= slow_rate_dur) -+ continue; -+ -+ /* skip slow rates with high success probability */ -+ mrs = minstrel_get_ratestats(mi, index); -+ if (mrs->prob_avg > MINSTREL_FRAC(95, 100)) -+ continue; -+ -+ slow_rates[(*slow_rate_ofs)++] = index; -+ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) -+ max_duration = fast_rate_dur; - } -+ index = 0; -+ -+found: -+ mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group = group; -+ -+ return index; -+} - -- i = 0; -- if (n_rates > 1) { -- random = prandom_u32(); -- i = random % n_rates; -+static void -+minstrel_ht_refill_sample_rates(struct minstrel_ht_sta *mi) -+{ -+ u32 prob_dur = minstrel_get_duration(mi->max_prob_rate); -+ u32 tp_dur = minstrel_get_duration(mi->max_tp_rate[0]); -+ u32 tp2_dur = minstrel_get_duration(mi->max_tp_rate[1]); -+ u32 fast_rate_dur = min(min(tp_dur, tp2_dur), prob_dur); -+ u32 slow_rate_dur = max(max(tp_dur, tp2_dur), prob_dur); -+ u16 *rates; -+ int i, j; -+ -+ rates = mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates; -+ i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_INC, -+ fast_rate_dur, slow_rate_dur); -+ while (i < MINSTREL_SAMPLE_RATES) { -+ rates[i] = minstrel_ht_next_inc_rate(mi, tp_dur); -+ if (!rates[i]) -+ break; -+ -+ i++; - } -- probe_rate = rates[i]; - --out: -- mi->sample_rate = probe_rate; -- mi->sample_mode = MINSTREL_SAMPLE_ACTIVE; -+ rates = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_rates; -+ i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_JUMP, -+ fast_rate_dur, slow_rate_dur); -+ j = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_SLOW, -+ fast_rate_dur, slow_rate_dur); -+ while (i < MINSTREL_SAMPLE_RATES) { -+ rates[i] = minstrel_ht_next_jump_rate(mi, fast_rate_dur, -+ slow_rate_dur, &j); -+ if (!rates[i]) -+ break; -+ -+ i++; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(mi->sample); i++) -+ memcpy(mi->sample[i].cur_sample_rates, mi->sample[i].sample_rates, -+ sizeof(mi->sample[i].cur_sample_rates)); - } - -+ - /* - * Update rate statistics and select new primary rates - * -@@ -668,26 +961,15 @@ out: - * higher throughput rates, even if the probablity is a bit lower - */ - static void --minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -- bool sample) -+minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) - { - struct minstrel_mcs_group_data *mg; - struct minstrel_rate_stats *mrs; - int group, i, j, cur_prob; - u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; -- u16 tmp_cck_tp_rate[MAX_THR_RATES], index; -- -- mi->sample_mode = MINSTREL_SAMPLE_IDLE; -- -- if (sample) { -- mi->total_packets_cur = mi->total_packets - -- mi->total_packets_last; -- mi->total_packets_last = mi->total_packets; -- } -- if (!mp->sample_switch) -- sample = false; -- if (mi->total_packets_cur < SAMPLE_SWITCH_THR && mp->sample_switch != 1) -- sample = false; -+ u16 tmp_legacy_tp_rate[MAX_THR_RATES], tmp_max_prob_rate; -+ u16 index; -+ bool ht_supported = mi->sta->ht_cap.ht_supported; - - if (mi->ampdu_packets > 0) { - if (!ieee80211_hw_check(mp->hw, TX_STATUS_NO_AMPDU_LEN)) -@@ -700,65 +982,72 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - mi->ampdu_packets = 0; - } - -- mi->sample_slow = 0; -- mi->sample_count = 0; -- -- memset(tmp_mcs_tp_rate, 0, sizeof(tmp_mcs_tp_rate)); -- memset(tmp_cck_tp_rate, 0, sizeof(tmp_cck_tp_rate)); - if (mi->supported[MINSTREL_CCK_GROUP]) -- for (j = 0; j < ARRAY_SIZE(tmp_cck_tp_rate); j++) -- tmp_cck_tp_rate[j] = MINSTREL_CCK_GROUP * MCS_GROUP_RATES; -+ group = MINSTREL_CCK_GROUP; -+ else if (mi->supported[MINSTREL_OFDM_GROUP]) -+ group = MINSTREL_OFDM_GROUP; -+ else -+ group = 0; -+ -+ index = MI_RATE(group, 0); -+ for (j = 0; j < ARRAY_SIZE(tmp_legacy_tp_rate); j++) -+ tmp_legacy_tp_rate[j] = index; - - if (mi->supported[MINSTREL_VHT_GROUP_0]) -- index = MINSTREL_VHT_GROUP_0 * MCS_GROUP_RATES; -+ group = MINSTREL_VHT_GROUP_0; -+ else if (ht_supported) -+ group = MINSTREL_HT_GROUP_0; -+ else if (mi->supported[MINSTREL_CCK_GROUP]) -+ group = MINSTREL_CCK_GROUP; - else -- index = MINSTREL_HT_GROUP_0 * MCS_GROUP_RATES; -+ group = MINSTREL_OFDM_GROUP; - -+ index = MI_RATE(group, 0); -+ tmp_max_prob_rate = index; - for (j = 0; j < ARRAY_SIZE(tmp_mcs_tp_rate); j++) - tmp_mcs_tp_rate[j] = index; - - /* Find best rate sets within all MCS groups*/ - for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { -+ u16 *tp_rate = tmp_mcs_tp_rate; -+ u16 last_prob = 0; - - mg = &mi->groups[group]; - if (!mi->supported[group]) - continue; - -- mi->sample_count++; -- - /* (re)Initialize group rate indexes */ - for(j = 0; j < MAX_THR_RATES; j++) -- tmp_group_tp_rate[j] = MCS_GROUP_RATES * group; -+ tmp_group_tp_rate[j] = MI_RATE(group, 0); - -- for (i = 0; i < MCS_GROUP_RATES; i++) { -+ if (group == MINSTREL_CCK_GROUP && ht_supported) -+ tp_rate = tmp_legacy_tp_rate; -+ -+ for (i = MCS_GROUP_RATES - 1; i >= 0; i--) { - if (!(mi->supported[group] & BIT(i))) - continue; - -- index = MCS_GROUP_RATES * group + i; -+ index = MI_RATE(group, i); - - mrs = &mg->rates[i]; - mrs->retry_updated = false; -- minstrel_calc_rate_stats(mp, mrs); -+ minstrel_ht_calc_rate_stats(mp, mrs); -+ -+ if (mrs->att_hist) -+ last_prob = max(last_prob, mrs->prob_avg); -+ else -+ mrs->prob_avg = max(last_prob, mrs->prob_avg); - cur_prob = mrs->prob_avg; - - if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0) - continue; - - /* Find max throughput rate set */ -- if (group != MINSTREL_CCK_GROUP) { -- minstrel_ht_sort_best_tp_rates(mi, index, -- tmp_mcs_tp_rate); -- } else if (group == MINSTREL_CCK_GROUP) { -- minstrel_ht_sort_best_tp_rates(mi, index, -- tmp_cck_tp_rate); -- } -+ minstrel_ht_sort_best_tp_rates(mi, index, tp_rate); - - /* Find max throughput rate set within a group */ - minstrel_ht_sort_best_tp_rates(mi, index, - tmp_group_tp_rate); -- -- /* Find max probability rate per group and global */ -- minstrel_ht_set_best_prob_rate(mi, index); - } - - memcpy(mg->max_group_tp_rate, tmp_group_tp_rate, -@@ -766,19 +1055,32 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - } - - /* Assign new rate set per sta */ -- minstrel_ht_assign_best_tp_rates(mi, tmp_mcs_tp_rate, tmp_cck_tp_rate); -+ minstrel_ht_assign_best_tp_rates(mi, tmp_mcs_tp_rate, -+ tmp_legacy_tp_rate); - memcpy(mi->max_tp_rate, tmp_mcs_tp_rate, sizeof(mi->max_tp_rate)); + mi->max_prob_rate = tmp_max_prob_rate; - /* Try to increase robustness of max_prob_rate*/ - minstrel_ht_prob_rate_reduce_streams(mi); -+ for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { -+ if (!mi->supported[group]) -+ continue; - -- /* try to sample all available rates during each interval */ -- mi->sample_count *= 8; -- if (mp->new_avg) -- mi->sample_count /= 2; -+ mg = &mi->groups[group]; -+ mg->max_group_prob_rate = MI_RATE(group, 0); -+ -+ for (i = 0; i < MCS_GROUP_RATES; i++) { -+ if (!(mi->supported[group] & BIT(i))) -+ continue; -+ -+ index = MI_RATE(group, i); -+ -+ /* Find max probability rate per group and global */ -+ minstrel_ht_set_best_prob_rate(mi, &tmp_max_prob_rate, -+ index); -+ } -+ } -+ -+ mi->max_prob_rate = tmp_max_prob_rate; - -- if (sample) -- minstrel_ht_rate_sample_switch(mp, mi); -+ minstrel_ht_refill_sample_rates(mi); + minstrel_ht_refill_sample_rates(mi); #ifdef CPTCFG_MAC80211_DEBUGFS - /* use fixed index if set */ -@@ -786,17 +1088,20 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - for (i = 0; i < 4; i++) - mi->max_tp_rate[i] = mp->fixed_rate_idx; - mi->max_prob_rate = mp->fixed_rate_idx; -- mi->sample_mode = MINSTREL_SAMPLE_IDLE; - } - #endif - - /* Reset update timer */ - mi->last_stats_update = jiffies; -+ mi->sample_time = jiffies; - } - - static bool --minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate) -+minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -+ struct ieee80211_tx_rate *rate) - { -+ int i; -+ - if (rate->idx < 0) - return false; - -@@ -807,40 +1112,23 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat - rate->flags & IEEE80211_TX_RC_VHT_MCS) - return true; - -- return rate->idx == mp->cck_rates[0] || -- rate->idx == mp->cck_rates[1] || -- rate->idx == mp->cck_rates[2] || -- rate->idx == mp->cck_rates[3]; --} -+ for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) -+ if (rate->idx == mp->cck_rates[i]) -+ return true; - --static void --minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi) --{ -- struct minstrel_mcs_group_data *mg; -+ for (i = 0; i < ARRAY_SIZE(mp->ofdm_rates[0]); i++) -+ if (rate->idx == mp->ofdm_rates[mi->band][i]) -+ return true; - -- for (;;) { -- mi->sample_group++; -- mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); -- mg = &mi->groups[mi->sample_group]; -- -- if (!mi->supported[mi->sample_group]) -- continue; -- -- if (++mg->index >= MCS_GROUP_RATES) { -- mg->index = 0; -- if (++mg->column >= ARRAY_SIZE(sample_table)) -- mg->column = 0; -- } -- break; -- } -+ return false; +@@ -1155,7 +1123,7 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, } static void @@ -3961,12 +1960,7 @@ index 53d7c74..af4358e 100644 { int group, orig_group; -- orig_group = group = *idx / MCS_GROUP_RATES; -+ orig_group = group = MI_RATE_GROUP(*idx); - while (group > 0) { - group--; - -@@ -851,11 +1139,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary) +@@ -1170,11 +1138,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary) minstrel_mcs_groups[orig_group].streams) continue; @@ -3979,103 +1973,16 @@ index 53d7c74..af4358e 100644 } } -@@ -887,21 +1171,14 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, - void *priv_sta, struct ieee80211_tx_status *st) - { +@@ -1185,7 +1149,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_tx_info *info = st->info; -- struct minstrel_ht_sta_priv *msp = priv_sta; -- struct minstrel_ht_sta *mi = &msp->ht; -+ struct minstrel_ht_sta *mi = priv_sta; + struct minstrel_ht_sta *mi = priv_sta; struct ieee80211_tx_rate *ar = info->status.rates; -- struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL; +- struct minstrel_rate_stats *rate, *rate2; + struct minstrel_rate_stats *rate; struct minstrel_priv *mp = priv; -- u32 update_interval = mp->update_interval / 2; -+ u32 update_interval = mp->update_interval; + u32 update_interval = mp->update_interval; bool last, update = false; -- bool sample_status = false; - int i; - -- if (!msp->is_ht) -- return mac80211_minstrel.tx_status_ext(priv, sband, -- &msp->legacy, st); -- -- - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) -@@ -913,87 +1190,49 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, - info->status.ampdu_len = 1; - } - -- mi->ampdu_packets++; -- mi->ampdu_len += info->status.ampdu_len; -- -- if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) { -- int avg_ampdu_len = minstrel_ht_avg_ampdu_len(mi); -- -- mi->sample_wait = 16 + 2 * avg_ampdu_len; -- mi->sample_tries = 1; -- mi->sample_count--; -+ /* wraparound */ -+ if (mi->total_packets >= ~0 - info->status.ampdu_len) { -+ mi->total_packets = 0; -+ mi->sample_packets = 0; - } - -+ mi->total_packets += info->status.ampdu_len; - if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) - mi->sample_packets += info->status.ampdu_len; - -- if (mi->sample_mode != MINSTREL_SAMPLE_IDLE) -- rate_sample = minstrel_get_ratestats(mi, mi->sample_rate); -+ mi->ampdu_packets++; -+ mi->ampdu_len += info->status.ampdu_len; - -- last = !minstrel_ht_txstat_valid(mp, &ar[0]); -+ last = !minstrel_ht_txstat_valid(mp, mi, &ar[0]); - for (i = 0; !last; i++) { - last = (i == IEEE80211_TX_MAX_RATES - 1) || -- !minstrel_ht_txstat_valid(mp, &ar[i + 1]); -+ !minstrel_ht_txstat_valid(mp, mi, &ar[i + 1]); - - rate = minstrel_ht_get_stats(mp, mi, &ar[i]); -- if (rate == rate_sample) -- sample_status = true; -- - if (last) - rate->success += info->status.ampdu_ack_len; - - rate->attempts += ar[i].count * info->status.ampdu_len; - } - -- switch (mi->sample_mode) { -- case MINSTREL_SAMPLE_IDLE: -- if (mp->new_avg && -- (mp->hw->max_rates > 1 || -- mi->total_packets_cur < SAMPLE_SWITCH_THR)) -- update_interval /= 2; -- break; -- -- case MINSTREL_SAMPLE_ACTIVE: -- if (!sample_status) -- break; -- -- mi->sample_mode = MINSTREL_SAMPLE_PENDING; -- update = true; -- break; -- -- case MINSTREL_SAMPLE_PENDING: -- if (sample_status) -- break; -- -- update = true; -- minstrel_ht_update_stats(mp, mi, false); -- break; -- } -- -- - if (mp->hw->max_rates > 1) { +@@ -1235,18 +1199,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. @@ -4098,1352 +2005,107 @@ index 53d7c74..af4358e 100644 update = true; } } - - if (time_after(jiffies, mi->last_stats_update + update_interval)) { - update = true; -- minstrel_ht_update_stats(mp, mi, true); -+ minstrel_ht_update_stats(mp, mi); - } - - if (update) -@@ -1031,7 +1270,10 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - ctime += (t_slot * cw) >> 1; - cw = min((cw << 1) | 1, mp->cw_max); - -- if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) { -+ if (minstrel_ht_is_legacy_group(MI_RATE_GROUP(index))) { -+ overhead = mi->overhead_legacy; -+ overhead_rtscts = mi->overhead_legacy_rtscts; -+ } else { - overhead = mi->overhead; - overhead_rtscts = mi->overhead_rtscts; - } -@@ -1061,7 +1303,8 @@ static void - minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - struct ieee80211_sta_rates *ratetbl, int offset, int index) - { -- const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; -+ int group_idx = MI_RATE_GROUP(index); -+ const struct mcs_group *group = &minstrel_mcs_groups[group_idx]; - struct minstrel_rate_stats *mrs; - u8 idx; - u16 flags = group->flags; -@@ -1080,13 +1323,17 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts; - } - -- if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) -+ index = MI_RATE_IDX(index); -+ if (group_idx == MINSTREL_CCK_GROUP) - idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; -+ else if (group_idx == MINSTREL_OFDM_GROUP) -+ idx = mp->ofdm_rates[mi->band][index % -+ ARRAY_SIZE(mp->ofdm_rates[0])]; - else if (flags & IEEE80211_TX_RC_VHT_MCS) - idx = ((group->streams - 1) << 4) | -- ((index % MCS_GROUP_RATES) & 0xF); -+ (index & 0xF); - else -- idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8; -+ idx = index + (group->streams - 1) * 8; - - /* enable RTS/CTS if needed: - * - if station is in dynamic SMPS (and streams > 1) -@@ -1106,17 +1353,17 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - static inline int - minstrel_ht_get_prob_avg(struct minstrel_ht_sta *mi, int rate) - { -- int group = rate / MCS_GROUP_RATES; -- rate %= MCS_GROUP_RATES; -+ int group = MI_RATE_GROUP(rate); -+ rate = MI_RATE_IDX(rate); - return mi->groups[group].rates[rate].prob_avg; - } - - static int - minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi) - { -- int group = mi->max_prob_rate / MCS_GROUP_RATES; -+ int group = MI_RATE_GROUP(mi->max_prob_rate); - const struct mcs_group *g = &minstrel_mcs_groups[group]; -- int rate = mi->max_prob_rate % MCS_GROUP_RATES; -+ int rate = MI_RATE_IDX(mi->max_prob_rate); - unsigned int duration; - - /* Disable A-MSDU if max_prob_rate is bad */ -@@ -1164,18 +1411,14 @@ static void - minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) - { - struct ieee80211_sta_rates *rates; -- u16 first_rate = mi->max_tp_rate[0]; - int i = 0; - -- if (mi->sample_mode == MINSTREL_SAMPLE_ACTIVE) -- first_rate = mi->sample_rate; -- - rates = kzalloc(sizeof(*rates), GFP_ATOMIC); - if (!rates) - return; - - /* Start with max_tp_rate[0] */ -- minstrel_ht_set_rate(mp, mi, rates, i++, first_rate); -+ minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[0]); - - if (mp->hw->max_rates >= 3) { - /* At least 3 tx rates supported, use max_tp_rate[1] next */ -@@ -1191,102 +1434,20 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) - rate_control_set_rates(mp->hw, mi->sta, rates); - } - --static int --minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) -+static u16 -+minstrel_ht_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) - { -- struct minstrel_rate_stats *mrs; -- struct minstrel_mcs_group_data *mg; -- unsigned int sample_dur, sample_group, cur_max_tp_streams; -- int tp_rate1, tp_rate2; -- int sample_idx = 0; -- -- if (mp->hw->max_rates == 1 && mp->sample_switch && -- (mi->total_packets_cur >= SAMPLE_SWITCH_THR || -- mp->sample_switch == 1)) -- return -1; -- -- if (mi->sample_wait > 0) { -- mi->sample_wait--; -- return -1; -- } -- -- if (!mi->sample_tries) -- return -1; -- -- sample_group = mi->sample_group; -- mg = &mi->groups[sample_group]; -- sample_idx = sample_table[mg->column][mg->index]; -- minstrel_set_next_sample_idx(mi); -+ u8 seq; - -- if (!(mi->supported[sample_group] & BIT(sample_idx))) -- return -1; -- -- mrs = &mg->rates[sample_idx]; -- sample_idx += sample_group * MCS_GROUP_RATES; -- -- /* Set tp_rate1, tp_rate2 to the highest / second highest max_tp_rate */ -- if (minstrel_get_duration(mi->max_tp_rate[0]) > -- minstrel_get_duration(mi->max_tp_rate[1])) { -- tp_rate1 = mi->max_tp_rate[1]; -- tp_rate2 = mi->max_tp_rate[0]; -+ if (mp->hw->max_rates > 1) { -+ seq = mi->sample_seq; -+ mi->sample_seq = (seq + 1) % ARRAY_SIZE(minstrel_sample_seq); -+ seq = minstrel_sample_seq[seq]; - } else { -- tp_rate1 = mi->max_tp_rate[0]; -- tp_rate2 = mi->max_tp_rate[1]; -+ seq = MINSTREL_SAMPLE_TYPE_INC; - } - -- /* -- * Sampling might add some overhead (RTS, no aggregation) -- * to the frame. Hence, don't use sampling for the highest currently -- * used highest throughput or probability rate. -- */ -- if (sample_idx == mi->max_tp_rate[0] || sample_idx == mi->max_prob_rate) -- return -1; -- -- /* -- * Do not sample if the probability is already higher than 95%, -- * or if the rate is 3 times slower than the current max probability -- * rate, to avoid wasting airtime. -- */ -- sample_dur = minstrel_get_duration(sample_idx); -- if (mrs->prob_avg > MINSTREL_FRAC(95, 100) || -- minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur) -- return -1; -- -- -- /* -- * For devices with no configurable multi-rate retry, skip sampling -- * below the per-group max throughput rate, and only use one sampling -- * attempt per rate -- */ -- if (mp->hw->max_rates == 1 && -- (minstrel_get_duration(mg->max_group_tp_rate[0]) < sample_dur || -- mrs->attempts)) -- return -1; -- -- /* Skip already sampled slow rates */ -- if (sample_dur >= minstrel_get_duration(tp_rate1) && mrs->attempts) -- return -1; -- -- /* -- * Make sure that lower rates get sampled only occasionally, -- * if the link is working perfectly. -- */ -- -- cur_max_tp_streams = minstrel_mcs_groups[tp_rate1 / -- MCS_GROUP_RATES].streams; -- if (sample_dur >= minstrel_get_duration(tp_rate2) && -- (cur_max_tp_streams - 1 < -- minstrel_mcs_groups[sample_group].streams || -- sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { -- if (mrs->sample_skipped < 20) -- return -1; -- -- if (mi->sample_slow++ > 2) -- return -1; -- } -- mi->sample_tries--; -- -- return sample_idx; -+ return __minstrel_ht_get_sample_rate(mi, seq); - } - - static void -@@ -1296,16 +1457,12 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, - const struct mcs_group *sample_group; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); - struct ieee80211_tx_rate *rate = &info->status.rates[0]; -- struct minstrel_ht_sta_priv *msp = priv_sta; -- struct minstrel_ht_sta *mi = &msp->ht; -+ struct minstrel_ht_sta *mi = priv_sta; - struct minstrel_priv *mp = priv; -- int sample_idx; -- -- if (!msp->is_ht) -- return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); -+ u16 sample_idx; - - if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && -- mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) -+ !minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_prob_rate))) - minstrel_aggr_check(sta, txrc->skb); - - info->flags |= mi->tx_flags; -@@ -1318,23 +1475,18 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, - /* Don't use EAPOL frames for sampling on non-mrr hw */ - if (mp->hw->max_rates == 1 && - (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) -- sample_idx = -1; -- else -- sample_idx = minstrel_get_sample_rate(mp, mi); -- -- mi->total_packets++; -+ return; - -- /* wraparound */ -- if (mi->total_packets == ~0) { -- mi->total_packets = 0; -- mi->sample_packets = 0; -- } -+ if (time_is_before_jiffies(mi->sample_time)) -+ return; - -- if (sample_idx < 0) -+ mi->sample_time = jiffies + MINSTREL_SAMPLE_INTERVAL; -+ sample_idx = minstrel_ht_get_sample_rate(mp, mi); -+ if (!sample_idx) - return; - -- sample_group = &minstrel_mcs_groups[sample_idx / MCS_GROUP_RATES]; -- sample_idx %= MCS_GROUP_RATES; -+ sample_group = &minstrel_mcs_groups[MI_RATE_GROUP(sample_idx)]; -+ sample_idx = MI_RATE_IDX(sample_idx); - - if (sample_group == &minstrel_mcs_groups[MINSTREL_CCK_GROUP] && - (sample_idx >= 4) != txrc->short_preamble) -@@ -1346,8 +1498,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, - if (sample_group == &minstrel_mcs_groups[MINSTREL_CCK_GROUP]) { - int idx = sample_idx % ARRAY_SIZE(mp->cck_rates); - rate->idx = mp->cck_rates[idx]; -+ } else if (sample_group == &minstrel_mcs_groups[MINSTREL_OFDM_GROUP]) { -+ int idx = sample_idx % ARRAY_SIZE(mp->ofdm_rates[0]); -+ rate->idx = mp->ofdm_rates[mi->band][idx]; - } else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) { -- ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES, -+ ieee80211_rate_set_vht(rate, MI_RATE_IDX(sample_idx), - sample_group->streams); - } else { - rate->idx = sample_idx + (sample_group->streams - 1) * 8; -@@ -1366,44 +1521,59 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - if (sband->band != NL80211_BAND_2GHZ) - return; - -- if (!ieee80211_hw_check(mp->hw, SUPPORTS_HT_CCK_RATES)) -+ if (sta->ht_cap.ht_supported && -+ !ieee80211_hw_check(mp->hw, SUPPORTS_HT_CCK_RATES)) - return; - -- mi->cck_supported = 0; -- mi->cck_supported_short = 0; - for (i = 0; i < 4; i++) { -- if (!rate_supported(sta, sband->band, mp->cck_rates[i])) -+ if (mp->cck_rates[i] == 0xff || -+ !rate_supported(sta, sband->band, mp->cck_rates[i])) - continue; - -- mi->cck_supported |= BIT(i); -+ mi->supported[MINSTREL_CCK_GROUP] |= BIT(i); - if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE) -- mi->cck_supported_short |= BIT(i); -+ mi->supported[MINSTREL_CCK_GROUP] |= BIT(i + 4); - } -+} -+ -+static void -+minstrel_ht_update_ofdm(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -+ struct ieee80211_supported_band *sband, -+ struct ieee80211_sta *sta) -+{ -+ const u8 *rates; -+ int i; - -- mi->supported[MINSTREL_CCK_GROUP] = mi->cck_supported; -+ if (sta->ht_cap.ht_supported) -+ return; -+ -+ rates = mp->ofdm_rates[sband->band]; -+ for (i = 0; i < ARRAY_SIZE(mp->ofdm_rates[0]); i++) { -+ if (rates[i] == 0xff || -+ !rate_supported(sta, sband->band, rates[i])) -+ continue; -+ -+ mi->supported[MINSTREL_OFDM_GROUP] |= BIT(i); -+ } - } - - static void - minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, -- struct ieee80211_sta *sta, void *priv_sta) -+ struct ieee80211_sta *sta, void *priv_sta) - { - struct minstrel_priv *mp = priv; -- struct minstrel_ht_sta_priv *msp = priv_sta; -- struct minstrel_ht_sta *mi = &msp->ht; -+ struct minstrel_ht_sta *mi = priv_sta; - struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; - u16 ht_cap = sta->ht_cap.cap; - struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; -+ const struct ieee80211_rate *ctl_rate; -+ bool ldpc, erp; - int use_vht; - int n_supported = 0; - int ack_dur; - int stbc; - int i; -- bool ldpc; -- -- /* fall back to the old minstrel for legacy stations */ -- if (!sta->ht_cap.ht_supported) -- goto use_legacy; - - BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB); - -@@ -1412,10 +1582,10 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, - else - use_vht = 0; - -- msp->is_ht = true; - memset(mi, 0, sizeof(*mi)); - - mi->sta = sta; -+ mi->band = sband->band; - mi->last_stats_update = jiffies; - - ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0); -@@ -1423,17 +1593,15 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, - mi->overhead += ack_dur; - mi->overhead_rtscts = mi->overhead + 2 * ack_dur; - -- mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); -+ ctl_rate = &sband->bitrates[rate_lowest_index(sband, sta)]; -+ erp = ctl_rate->flags & IEEE80211_RATE_ERP_G; -+ ack_dur = ieee80211_frame_duration(sband->band, 10, -+ ctl_rate->bitrate, erp, 1, -+ ieee80211_chandef_get_shift(chandef)); -+ mi->overhead_legacy = ack_dur; -+ mi->overhead_legacy_rtscts = mi->overhead_legacy + 2 * ack_dur; - -- /* When using MRR, sample more on the first attempt, without delay */ -- if (mp->has_mrr) { -- mi->sample_count = 16; -- mi->sample_wait = 0; -- } else { -- mi->sample_count = 8; -- mi->sample_wait = 8; -- } -- mi->sample_tries = 4; -+ mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); - - if (!use_vht) { - stbc = (ht_cap & IEEE80211_HT_CAP_RX_STBC) >> -@@ -1456,10 +1624,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, - int bw, nss; - - mi->supported[i] = 0; -- if (i == MINSTREL_CCK_GROUP) { -- minstrel_ht_update_cck(mp, mi, sband, sta); -+ if (minstrel_ht_is_legacy_group(i)) - continue; -- } - - if (gflags & IEEE80211_TX_RC_SHORT_GI) { - if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) { -@@ -1520,24 +1686,12 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, - n_supported++; - } - -- if (!n_supported) -- goto use_legacy; -- -- mi->supported[MINSTREL_CCK_GROUP] |= mi->cck_supported_short << 4; -+ minstrel_ht_update_cck(mp, mi, sband, sta); -+ minstrel_ht_update_ofdm(mp, mi, sband, sta); - - /* create an initial rate table with the lowest supported rates */ -- minstrel_ht_update_stats(mp, mi, true); -+ minstrel_ht_update_stats(mp, mi); - minstrel_ht_update_rates(mp, mi); -- -- return; -- --use_legacy: -- msp->is_ht = false; -- memset(&msp->legacy, 0, sizeof(msp->legacy)); -- msp->legacy.r = msp->ratelist; -- msp->legacy.sample_table = msp->sample_table; -- return mac80211_minstrel.rate_init(priv, sband, chandef, sta, -- &msp->legacy); - } - - static void -@@ -1561,7 +1715,7 @@ static void * - minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) - { - struct ieee80211_supported_band *sband; -- struct minstrel_ht_sta_priv *msp; -+ struct minstrel_ht_sta *mi; - struct minstrel_priv *mp = priv; - struct ieee80211_hw *hw = mp->hw; - int max_rates = 0; -@@ -1573,91 +1727,91 @@ minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) - max_rates = sband->n_bitrates; - } - -- msp = kzalloc(sizeof(*msp), gfp); -- if (!msp) -- return NULL; -- -- msp->ratelist = kcalloc(max_rates, sizeof(struct minstrel_rate), gfp); -- if (!msp->ratelist) -- goto error; -- -- msp->sample_table = kmalloc_array(max_rates, SAMPLE_COLUMNS, gfp); -- if (!msp->sample_table) -- goto error1; -- -- return msp; -- --error1: -- kfree(msp->ratelist); --error: -- kfree(msp); -- return NULL; -+ return kzalloc(sizeof(*mi), gfp); - } - - static void - minstrel_ht_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) - { -- struct minstrel_ht_sta_priv *msp = priv_sta; -- -- kfree(msp->sample_table); -- kfree(msp->ratelist); -- kfree(msp); -+ kfree(priv_sta); - } - - static void --minstrel_ht_init_cck_rates(struct minstrel_priv *mp) -+minstrel_ht_fill_rate_array(u8 *dest, struct ieee80211_supported_band *sband, -+ const s16 *bitrates, int n_rates, u32 rate_flags) - { -- static const int bitrates[4] = { 10, 20, 55, 110 }; -- struct ieee80211_supported_band *sband; -- u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); - int i, j; - -- sband = mp->hw->wiphy->bands[NL80211_BAND_2GHZ]; -- if (!sband) -- return; -- - for (i = 0; i < sband->n_bitrates; i++) { - struct ieee80211_rate *rate = &sband->bitrates[i]; - -- if (rate->flags & IEEE80211_RATE_ERP_G) -- continue; -- - if ((rate_flags & sband->bitrates[i].flags) != rate_flags) - continue; - -- for (j = 0; j < ARRAY_SIZE(bitrates); j++) { -+ for (j = 0; j < n_rates; j++) { - if (rate->bitrate != bitrates[j]) - continue; - -- mp->cck_rates[j] = i; -+ dest[j] = i; - break; - } - } - } - -+static void -+minstrel_ht_init_cck_rates(struct minstrel_priv *mp) -+{ -+ static const s16 bitrates[4] = { 10, 20, 55, 110 }; -+ struct ieee80211_supported_band *sband; -+ u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); -+ -+ memset(mp->cck_rates, 0xff, sizeof(mp->cck_rates)); -+ sband = mp->hw->wiphy->bands[NL80211_BAND_2GHZ]; -+ if (!sband) -+ return; -+ -+ BUILD_BUG_ON(ARRAY_SIZE(mp->cck_rates) != ARRAY_SIZE(bitrates)); -+ minstrel_ht_fill_rate_array(mp->cck_rates, sband, -+ minstrel_cck_bitrates, -+ ARRAY_SIZE(minstrel_cck_bitrates), -+ rate_flags); -+} -+ -+static void -+minstrel_ht_init_ofdm_rates(struct minstrel_priv *mp, enum nl80211_band band) -+{ -+ static const s16 bitrates[8] = { 60, 90, 120, 180, 240, 360, 480, 540 }; -+ struct ieee80211_supported_band *sband; -+ u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); -+ -+ memset(mp->ofdm_rates[band], 0xff, sizeof(mp->ofdm_rates[band])); -+ sband = mp->hw->wiphy->bands[band]; -+ if (!sband) -+ return; -+ -+ BUILD_BUG_ON(ARRAY_SIZE(mp->ofdm_rates[band]) != ARRAY_SIZE(bitrates)); -+ minstrel_ht_fill_rate_array(mp->ofdm_rates[band], sband, -+ minstrel_ofdm_bitrates, -+ ARRAY_SIZE(minstrel_ofdm_bitrates), -+ rate_flags); -+} -+ - static void * - minstrel_ht_alloc(struct ieee80211_hw *hw) - { - struct minstrel_priv *mp; -+ int i; - - mp = kzalloc(sizeof(struct minstrel_priv), GFP_ATOMIC); - if (!mp) - return NULL; - -- mp->sample_switch = -1; -- - /* contention window settings - * Just an approximation. Using the per-queue values would complicate - * the calculations and is probably unnecessary */ - mp->cw_min = 15; - mp->cw_max = 1023; - -- /* number of packets (in %) to use for sampling other rates -- * sample less often for non-mrr packets, because the overhead -- * is much higher than with mrr */ -- mp->lookaround_rate = 5; -- mp->lookaround_rate_mrr = 10; -- - /* maximum time that the hw is allowed to stay in one MRR segment */ - mp->segment_size = 6000; - -@@ -1671,10 +1825,11 @@ minstrel_ht_alloc(struct ieee80211_hw *hw) - mp->has_mrr = true; - - mp->hw = hw; -- mp->update_interval = HZ / 10; -- mp->new_avg = true; -+ mp->update_interval = HZ / 20; - - minstrel_ht_init_cck_rates(mp); -+ for (i = 0; i < ARRAY_SIZE(mp->hw->wiphy->bands); i++) -+ minstrel_ht_init_ofdm_rates(mp, i); - - return mp; - } -@@ -1688,10 +1843,6 @@ static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv, - mp->fixed_rate_idx = (u32) -1; - debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir, - &mp->fixed_rate_idx); -- debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir, -- &mp->sample_switch); -- debugfs_create_bool("new_avg", S_IRUGO | S_IWUSR, debugfsdir, -- &mp->new_avg); - } - #endif - -@@ -1703,15 +1854,11 @@ minstrel_ht_free(void *priv) - - static u32 minstrel_ht_get_expected_throughput(void *priv_sta) - { -- struct minstrel_ht_sta_priv *msp = priv_sta; -- struct minstrel_ht_sta *mi = &msp->ht; -+ struct minstrel_ht_sta *mi = priv_sta; - int i, j, prob, tp_avg; - -- if (!msp->is_ht) -- return mac80211_minstrel.get_expected_throughput(priv_sta); -- -- i = mi->max_tp_rate[0] / MCS_GROUP_RATES; -- j = mi->max_tp_rate[0] % MCS_GROUP_RATES; -+ i = MI_RATE_GROUP(mi->max_tp_rate[0]); -+ j = MI_RATE_IDX(mi->max_tp_rate[0]); - prob = mi->groups[i].rates[j].prob_avg; - - /* convert tp_avg from pkt per second in kbps */ diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h -index 53ea3c2..a5b56e5 100644 +index a5b56e5..dad1403 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h -@@ -6,6 +6,35 @@ - #ifndef __RC_MINSTREL_HT_H - #define __RC_MINSTREL_HT_H +@@ -14,7 +14,7 @@ -+#include -+ -+/* number of highest throughput rates to consider*/ -+#define MAX_THR_RATES 4 -+#define SAMPLE_COLUMNS 10 /* number of columns in sample table */ -+ -+/* scaled fraction values */ -+#define MINSTREL_SCALE 12 -+#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] */ -+#define EWMA_DIV 128 -+ -+/* -+ * Coefficients for moving average with noise filter (period=16), -+ * scaled by 10 bits -+ * -+ * a1 = exp(-pi * sqrt(2) / period) -+ * coeff2 = 2 * a1 * cos(sqrt(2) * 2 * pi / period) -+ * coeff3 = -sqr(a1) -+ * coeff1 = 1 - coeff2 - coeff3 -+ */ -+#define MINSTREL_AVG_COEFF1 (MINSTREL_FRAC(1, 1) - \ -+ MINSTREL_AVG_COEFF2 - \ -+ MINSTREL_AVG_COEFF3) -+#define MINSTREL_AVG_COEFF2 0x00001499 -+#define MINSTREL_AVG_COEFF3 -0x0000092e -+ - /* - * The number of streams can be changed to 2 to reduce code - * size and memory footprint. -@@ -18,17 +47,55 @@ - MINSTREL_HT_STREAM_GROUPS) - #define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS * \ - MINSTREL_VHT_STREAM_GROUPS) --#define MINSTREL_CCK_GROUPS_NB 1 -+#define MINSTREL_LEGACY_GROUPS_NB 2 - #define MINSTREL_GROUPS_NB (MINSTREL_HT_GROUPS_NB + \ - MINSTREL_VHT_GROUPS_NB + \ -- MINSTREL_CCK_GROUPS_NB) -+ MINSTREL_LEGACY_GROUPS_NB) + /* 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 MINSTREL_HT_GROUP_0 0 - #define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB) --#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1) -+#define MINSTREL_OFDM_GROUP (MINSTREL_CCK_GROUP + 1) -+#define MINSTREL_VHT_GROUP_0 (MINSTREL_OFDM_GROUP + 1) - - #define MCS_GROUP_RATES 10 - -+#define MI_RATE_IDX_MASK GENMASK(3, 0) -+#define MI_RATE_GROUP_MASK GENMASK(15, 4) -+ -+#define MI_RATE(_group, _idx) \ -+ (FIELD_PREP(MI_RATE_GROUP_MASK, _group) | \ -+ FIELD_PREP(MI_RATE_IDX_MASK, _idx)) -+ -+#define MI_RATE_IDX(_rate) FIELD_GET(MI_RATE_IDX_MASK, _rate) -+#define MI_RATE_GROUP(_rate) FIELD_GET(MI_RATE_GROUP_MASK, _rate) -+ -+#define MINSTREL_SAMPLE_RATES 5 /* rates per sample type */ -+#define MINSTREL_SAMPLE_INTERVAL (HZ / 50) -+ -+struct minstrel_priv { -+ struct ieee80211_hw *hw; -+ bool has_mrr; -+ unsigned int cw_min; -+ unsigned int cw_max; -+ unsigned int max_retry; -+ unsigned int segment_size; -+ unsigned int update_interval; -+ -+ u8 cck_rates[4]; -+ u8 ofdm_rates[NUM_NL80211_BANDS][8]; -+ -+#ifdef CPTCFG_MAC80211_DEBUGFS -+ /* -+ * enable fixed rate processing per RC -+ * - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx -+ * - write -1 to enable RC processing again -+ * - setting will be applied on next update -+ */ -+ u32 fixed_rate_idx; -+#endif -+}; -+ -+ - struct mcs_group { - u16 flags; - u8 streams; -@@ -37,8 +104,36 @@ struct mcs_group { - u16 duration[MCS_GROUP_RATES]; - }; - -+extern const s16 minstrel_cck_bitrates[4]; -+extern const s16 minstrel_ofdm_bitrates[8]; - extern const struct mcs_group minstrel_mcs_groups[]; - -+struct minstrel_rate_stats { -+ /* current / last sampling period attempts/success counters */ -+ u16 attempts, last_attempts; -+ u16 success, last_success; -+ -+ /* total attempts/success counters */ -+ u32 att_hist, succ_hist; -+ -+ /* prob_avg - moving average of prob */ -+ u16 prob_avg; -+ u16 prob_avg_1; -+ -+ /* maximum retry counts */ -+ u8 retry_count; -+ u8 retry_count_rtscts; -+ -+ bool retry_updated; -+}; -+ -+enum minstrel_sample_type { -+ MINSTREL_SAMPLE_TYPE_INC, -+ MINSTREL_SAMPLE_TYPE_JUMP, -+ MINSTREL_SAMPLE_TYPE_SLOW, -+ __MINSTREL_SAMPLE_TYPE_MAX -+}; -+ - struct minstrel_mcs_group_data { - u8 index; - u8 column; -@@ -51,10 +146,10 @@ struct minstrel_mcs_group_data { - struct minstrel_rate_stats rates[MCS_GROUP_RATES]; - }; - --enum minstrel_sample_mode { -- MINSTREL_SAMPLE_IDLE, -- MINSTREL_SAMPLE_ACTIVE, -- MINSTREL_SAMPLE_PENDING, -+struct minstrel_sample_category { -+ u8 sample_group; -+ u16 sample_rates[MINSTREL_SAMPLE_RATES]; -+ u16 cur_sample_rates[MINSTREL_SAMPLE_RATES]; - }; - - struct minstrel_ht_sta { -@@ -77,28 +172,22 @@ struct minstrel_ht_sta { - /* overhead time in usec for each frame */ - unsigned int overhead; - unsigned int overhead_rtscts; -+ unsigned int overhead_legacy; -+ unsigned int overhead_legacy_rtscts; - -- unsigned int total_packets_last; -- unsigned int total_packets_cur; - unsigned int total_packets; - unsigned int sample_packets; - - /* tx flags to add for frames for this sta */ - u32 tx_flags; - -- u8 sample_wait; -- u8 sample_tries; -- u8 sample_count; -- u8 sample_slow; -+ u8 band; - -- enum minstrel_sample_mode sample_mode; -+ u8 sample_seq; - u16 sample_rate; - -- /* current MCS group to be sampled */ -- u8 sample_group; -- -- u8 cck_supported; -- u8 cck_supported_short; -+ unsigned long sample_time; -+ struct minstrel_sample_category sample[__MINSTREL_SAMPLE_TYPE_MAX]; - - /* Bitfield of supported MCS rates of all groups */ - u16 supported[MINSTREL_GROUPS_NB]; -@@ -107,16 +196,6 @@ struct minstrel_ht_sta { - struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB]; - }; - --struct minstrel_ht_sta_priv { -- union { -- struct minstrel_ht_sta ht; -- struct minstrel_sta_info legacy; -- }; -- void *ratelist; -- void *sample_table; -- bool is_ht; --}; -- - void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); - int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, - int prob_avg); -diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c -index bebb719..25b8a67 100644 ---- a/net/mac80211/rc80211_minstrel_ht_debugfs.c -+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c -@@ -9,9 +9,13 @@ - #include - #include - #include --#include "rc80211_minstrel.h" - #include "rc80211_minstrel_ht.h" - -+struct minstrel_debugfs_info { -+ size_t len; -+ char buf[]; -+}; -+ - static ssize_t - minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) - { -@@ -28,6 +32,18 @@ minstrel_stats_release(struct inode *inode, struct file *file) - return 0; - } - -+static bool -+minstrel_ht_is_sample_rate(struct minstrel_ht_sta *mi, int idx) -+{ -+ int type, i; -+ -+ for (type = 0; type < ARRAY_SIZE(mi->sample); type++) -+ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) -+ if (mi->sample[type].cur_sample_rates[i] == idx) -+ return true; -+ return false; -+} -+ - static char * - minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) - { -@@ -52,8 +68,7 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) - - for (j = 0; j < MCS_GROUP_RATES; j++) { - struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; -- static const int bitrates[4] = { 10, 20, 55, 110 }; -- int idx = i * MCS_GROUP_RATES + j; -+ int idx = MI_RATE(i, j); - unsigned int duration; - - if (!(mi->supported[i] & BIT(j))) -@@ -67,6 +82,9 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) - p += sprintf(p, "VHT%c0 ", htmode); - p += sprintf(p, "%cGI ", gimode); - p += sprintf(p, "%d ", mg->streams); -+ } else if (i == MINSTREL_OFDM_GROUP) { -+ p += sprintf(p, "OFDM "); -+ p += sprintf(p, "1 "); - } else { - p += sprintf(p, "CCK "); - p += sprintf(p, "%cP ", j < 4 ? 'L' : 'S'); -@@ -78,13 +96,19 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) - *(p++) = (idx == mi->max_tp_rate[2]) ? 'C' : ' '; - *(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' '; - *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; -+ *(p++) = minstrel_ht_is_sample_rate(mi, idx) ? 'S' : ' '; - - if (gflags & IEEE80211_TX_RC_MCS) { - p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j); - } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { - p += sprintf(p, " MCS%-1u/%1u", j, mg->streams); - } else { -- int r = bitrates[j % 4]; -+ int r; -+ -+ if (i == MINSTREL_OFDM_GROUP) -+ r = minstrel_ofdm_bitrates[j % 8]; -+ else -+ r = minstrel_cck_bitrates[j % 4]; - - p += sprintf(p, " %2u.%1uM", r / 10, r % 10); - } -@@ -120,20 +144,11 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) - static int - minstrel_ht_stats_open(struct inode *inode, struct file *file) - { -- struct minstrel_ht_sta_priv *msp = inode->i_private; -- struct minstrel_ht_sta *mi = &msp->ht; -+ struct minstrel_ht_sta *mi = inode->i_private; - struct minstrel_debugfs_info *ms; - unsigned int i; -- int ret; - char *p; - -- if (!msp->is_ht) { -- inode->i_private = &msp->legacy; -- ret = minstrel_stats_open(inode, file); -- inode->i_private = msp; -- return ret; -- } -- - ms = kmalloc(32768, GFP_KERNEL); - if (!ms) - return -ENOMEM; -@@ -143,9 +158,9 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) - - p += sprintf(p, "\n"); - p += sprintf(p, -- " best ____________rate__________ ____statistics___ _____last____ ______sum-of________\n"); -+ " best ____________rate__________ ____statistics___ _____last____ ______sum-of________\n"); - p += sprintf(p, -- "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts]\n"); -+ "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts]\n"); - - p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); - for (i = 0; i < MINSTREL_CCK_GROUP; i++) -@@ -199,8 +214,7 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) - - for (j = 0; j < MCS_GROUP_RATES; j++) { - struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; -- static const int bitrates[4] = { 10, 20, 55, 110 }; -- int idx = i * MCS_GROUP_RATES + j; -+ int idx = MI_RATE(i, j); - unsigned int duration; - - if (!(mi->supported[i] & BIT(j))) -@@ -214,6 +228,8 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) - p += sprintf(p, "VHT%c0,", htmode); - p += sprintf(p, "%cGI,", gimode); - p += sprintf(p, "%d,", mg->streams); -+ } else if (i == MINSTREL_OFDM_GROUP) { -+ p += sprintf(p, "OFDM,,1,"); - } else { - p += sprintf(p, "CCK,"); - p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S'); -@@ -225,13 +241,20 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) - p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : "")); - p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : "")); - p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : "")); -+ p += sprintf(p, "%s", (minstrel_ht_is_sample_rate(mi, idx) ? "S" : "")); - - if (gflags & IEEE80211_TX_RC_MCS) { - p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j); - } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { - p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams); - } else { -- int r = bitrates[j % 4]; -+ int r; -+ -+ if (i == MINSTREL_OFDM_GROUP) -+ r = minstrel_ofdm_bitrates[j % 8]; -+ else -+ r = minstrel_cck_bitrates[j % 4]; -+ - p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10); - } - -@@ -270,22 +293,12 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) - static int - minstrel_ht_stats_csv_open(struct inode *inode, struct file *file) - { -- struct minstrel_ht_sta_priv *msp = inode->i_private; -- struct minstrel_ht_sta *mi = &msp->ht; -+ struct minstrel_ht_sta *mi = inode->i_private; - struct minstrel_debugfs_info *ms; - unsigned int i; -- int ret; - char *p; - -- if (!msp->is_ht) { -- inode->i_private = &msp->legacy; -- ret = minstrel_stats_csv_open(inode, file); -- inode->i_private = msp; -- return ret; -- } -- - ms = kmalloc(32768, GFP_KERNEL); -- - if (!ms) - return -ENOMEM; - -@@ -316,10 +329,8 @@ static const struct file_operations minstrel_ht_stat_csv_fops = { - void - minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) - { -- struct minstrel_ht_sta_priv *msp = priv_sta; -- -- debugfs_create_file("rc_stats", 0444, dir, msp, -+ debugfs_create_file("rc_stats", 0444, dir, priv_sta, - &minstrel_ht_stat_fops); -- debugfs_create_file("rc_stats_csv", 0444, dir, msp, -+ debugfs_create_file("rc_stats_csv", 0444, dir, priv_sta, - &minstrel_ht_stat_csv_fops); - } + #define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c -index 0c5c63a..adeac74 100644 +index 2f34d3d..584e3a2 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c -@@ -4114,7 +4114,9 @@ void ieee80211_check_fast_rx(struct sta_info *sta) - .vif_type = sdata->vif.type, - .control_port_protocol = sdata->control_port_protocol, - }, *old, *new = NULL; -+ bool set_offload = false; - bool assign = false; -+ bool offload; +@@ -1583,8 +1583,12 @@ static void sta_ps_start(struct sta_info *sta) - /* use sparse to check that we don't return without updating */ - __acquire(check_fast_rx); -@@ -4227,6 +4229,17 @@ void ieee80211_check_fast_rx(struct sta_info *sta) - if (assign) - new = kmemdup(&fastrx, sizeof(fastrx), GFP_KERNEL); + for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { + struct ieee80211_txq *txq = sta->sta.txq[tid]; ++ struct txq_info *txqi = to_txq_info(txq); -+ offload = assign && -+ (sdata->vif.offload_flags & IEEE80211_OFFLOAD_DECAP_ENABLED); -+ -+ if (offload) -+ set_offload = !test_and_set_sta_flag(sta, WLAN_STA_DECAP_OFFLOAD); -+ else -+ set_offload = test_and_clear_sta_flag(sta, WLAN_STA_DECAP_OFFLOAD); -+ -+ if (set_offload) -+ drv_sta_set_decap_offload(local, sdata, &sta->sta, assign); -+ - spin_lock_bh(&sta->lock); - old = rcu_dereference_protected(sta->fast_rx, true); - rcu_assign_pointer(sta->fast_rx, new); -@@ -4273,6 +4286,108 @@ void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata) - mutex_unlock(&local->sta_mtx); +- ieee80211_unschedule_txq(&local->hw, txq, false); ++ spin_lock(&local->active_txq_lock[txq->ac]); ++ if (!list_empty(&txqi->schedule_order)) ++ list_del_init(&txqi->schedule_order); ++ spin_unlock(&local->active_txq_lock[txq->ac]); + + if (txq_has_queue(txq)) + set_bit(tid, &sta->txq_buffered_tids); +@@ -2948,6 +2952,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) + if (!fwd_skb) + goto out; + ++ fwd_skb->dev = sdata->dev; + fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; + fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY); + info = IEEE80211_SKB_CB(fwd_skb); +@@ -3175,6 +3180,49 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, + ieee80211_tx_skb(sdata, skb); } -+static void ieee80211_rx_8023(struct ieee80211_rx_data *rx, -+ struct ieee80211_fast_rx *fast_rx, -+ int orig_len) ++static void ++ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx) +{ -+ struct ieee80211_sta_rx_stats *stats; -+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); -+ struct sta_info *sta = rx->sta; -+ struct sk_buff *skb = rx->skb; -+ void *sa = skb->data + ETH_ALEN; -+ void *da = skb->data; ++ struct ieee80211_mgmt *mgmt = (void *)rx->skb->data; ++ const struct element *ie; ++ size_t baselen; + -+ stats = &sta->rx_stats; -+ if (fast_rx->uses_rss) -+ stats = this_cpu_ptr(sta->pcpu_rx_stats); ++ if (!wiphy_ext_feature_isset(rx->local->hw.wiphy, ++ NL80211_EXT_FEATURE_BSS_COLOR)) ++ return; + -+ /* statistics part of ieee80211_rx_h_sta_process() */ -+ if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { -+ stats->last_signal = status->signal; -+ if (!fast_rx->uses_rss) -+ ewma_signal_add(&sta->rx_stats_avg.signal, -+ -status->signal); -+ } ++ if (ieee80211_hw_check(&rx->local->hw, DETECTS_COLOR_COLLISION)) ++ return; + -+ if (status->chains) { -+ int i; ++ if (rx->sdata->vif.csa_active) ++ return; + -+ stats->chains = status->chains; -+ for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { -+ int signal = status->chain_signal[i]; ++ baselen = mgmt->u.beacon.variable - rx->skb->data; ++ if (baselen > rx->skb->len) ++ return; + -+ if (!(status->chains & BIT(i))) -+ continue; ++ ie = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ++ mgmt->u.beacon.variable, ++ rx->skb->len - baselen); ++ if (ie && ie->datalen >= sizeof(struct ieee80211_he_operation) && ++ ie->datalen >= ieee80211_he_oper_size(ie->data + 1)) { ++ struct ieee80211_bss_conf *bss_conf = &rx->sdata->vif.bss_conf; ++ const struct ieee80211_he_operation *he_oper; ++ u8 color; + -+ stats->chain_signal_last[i] = signal; -+ if (!fast_rx->uses_rss) -+ ewma_signal_add(&sta->rx_stats_avg.chain_signal[i], -+ -signal); -+ } -+ } -+ /* end of statistics */ -+ -+ stats->last_rx = jiffies; -+ stats->last_rate = sta_stats_encode_rate(status); -+ -+ stats->fragments++; -+ stats->packets++; -+ -+ skb->dev = fast_rx->dev; -+ -+ ieee80211_rx_stats(fast_rx->dev, skb->len); -+ -+ /* The seqno index has the same property as needed -+ * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS -+ * for non-QoS-data frames. Here we know it's a data -+ * frame, so count MSDUs. -+ */ -+ u64_stats_update_begin(&stats->syncp); -+ stats->msdu[rx->seqno_idx]++; -+ stats->bytes += orig_len; -+ u64_stats_update_end(&stats->syncp); -+ -+ if (fast_rx->internal_forward) { -+ struct sk_buff *xmit_skb = NULL; -+ if (is_multicast_ether_addr(da)) { -+ xmit_skb = skb_copy(skb, GFP_ATOMIC); -+ } else if (!ether_addr_equal(da, sa) && -+ sta_info_get(rx->sdata, da)) { -+ xmit_skb = skb; -+ skb = NULL; -+ } -+ -+ if (xmit_skb) { -+ /* -+ * Send to wireless media and increase priority by 256 -+ * to keep the received priority instead of -+ * reclassifying the frame (see cfg80211_classify8021d). -+ */ -+ xmit_skb->priority += 256; -+ xmit_skb->protocol = htons(ETH_P_802_3); -+ skb_reset_network_header(xmit_skb); -+ skb_reset_mac_header(xmit_skb); -+ dev_queue_xmit(xmit_skb); -+ } -+ -+ if (!skb) ++ he_oper = (void *)(ie->data + 1); ++ if (le32_get_bits(he_oper->he_oper_params, ++ IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED)) + return; ++ ++ color = le32_get_bits(he_oper->he_oper_params, ++ IEEE80211_HE_OPERATION_BSS_COLOR_MASK); ++ if (color == bss_conf->he_bss_color.color) ++ ieeee80211_obss_color_collision_notify(&rx->sdata->vif, ++ BIT_ULL(color)); + } -+ -+ /* deliver to local stack */ -+ skb->protocol = eth_type_trans(skb, fast_rx->dev); -+ memset(skb->cb, 0, sizeof(skb->cb)); -+ if (rx->list) -+#if LINUX_VERSION_IS_GEQ(4,19,0) -+ list_add_tail(&skb->list, rx->list); -+#else -+ __skb_queue_tail(rx->list, skb); -+#endif -+ else -+ netif_receive_skb(skb); -+ +} + - static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, - struct ieee80211_fast_rx *fast_rx) + static ieee80211_rx_result debug_noinline + ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) { -@@ -4293,9 +4408,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, - } addrs __aligned(2); - struct ieee80211_sta_rx_stats *stats = &sta->rx_stats; +@@ -3200,6 +3248,9 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) + !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { + int sig = 0; -- if (fast_rx->uses_rss) -- stats = this_cpu_ptr(sta->pcpu_rx_stats); -- - /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write - * to a common data structure; drivers can implement that per queue - * but we don't have that information in mac80211 -@@ -4369,32 +4481,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, - pskb_trim(skb, skb->len - fast_rx->icv_len)) - goto drop; - -- /* statistics part of ieee80211_rx_h_sta_process() */ -- if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { -- stats->last_signal = status->signal; -- if (!fast_rx->uses_rss) -- ewma_signal_add(&sta->rx_stats_avg.signal, -- -status->signal); -- } -- -- if (status->chains) { -- int i; -- -- stats->chains = status->chains; -- for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { -- int signal = status->chain_signal[i]; -- -- if (!(status->chains & BIT(i))) -- continue; -- -- stats->chain_signal_last[i] = signal; -- if (!fast_rx->uses_rss) -- ewma_signal_add(&sta->rx_stats_avg.chain_signal[i], -- -signal); -- } -- } -- /* end of statistics */ -- - if (rx->key && !ieee80211_has_protected(hdr->frame_control)) - goto drop; - -@@ -4406,12 +4492,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, - return true; - } - -- stats->last_rx = jiffies; -- stats->last_rate = sta_stats_encode_rate(status); -- -- stats->fragments++; -- stats->packets++; -- - /* do the header conversion - first grab the addresses */ - ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs); - ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs); -@@ -4420,62 +4500,14 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, - /* push the addresses in front */ - memcpy(skb_push(skb, sizeof(addrs)), &addrs, sizeof(addrs)); - -- skb->dev = fast_rx->dev; -- -- ieee80211_rx_stats(fast_rx->dev, skb->len); -- -- /* The seqno index has the same property as needed -- * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS -- * for non-QoS-data frames. Here we know it's a data -- * frame, so count MSDUs. -- */ -- u64_stats_update_begin(&stats->syncp); -- stats->msdu[rx->seqno_idx]++; -- stats->bytes += orig_len; -- u64_stats_update_end(&stats->syncp); -- -- if (fast_rx->internal_forward) { -- struct sk_buff *xmit_skb = NULL; -- if (is_multicast_ether_addr(addrs.da)) { -- xmit_skb = skb_copy(skb, GFP_ATOMIC); -- } else if (!ether_addr_equal(addrs.da, addrs.sa) && -- sta_info_get(rx->sdata, addrs.da)) { -- xmit_skb = skb; -- skb = NULL; -- } -- -- if (xmit_skb) { -- /* -- * Send to wireless media and increase priority by 256 -- * to keep the received priority instead of -- * reclassifying the frame (see cfg80211_classify8021d). -- */ -- xmit_skb->priority += 256; -- xmit_skb->protocol = htons(ETH_P_802_3); -- skb_reset_network_header(xmit_skb); -- skb_reset_mac_header(xmit_skb); -- dev_queue_xmit(xmit_skb); -- } -- -- if (!skb) -- return true; -- } -- -- /* deliver to local stack */ -- skb->protocol = eth_type_trans(skb, fast_rx->dev); -- memset(skb->cb, 0, sizeof(skb->cb)); -- if (rx->list) --#if LINUX_VERSION_IS_GEQ(4,19,0) -- list_add_tail(&skb->list, rx->list); --#else -- __skb_queue_tail(rx->list, skb); --#endif -- else -- netif_receive_skb(skb); -+ ieee80211_rx_8023(rx, fast_rx, orig_len); - - return true; - drop: - dev_kfree_skb(skb); -+ if (fast_rx->uses_rss) -+ stats = this_cpu_ptr(sta->pcpu_rx_stats); ++ /* sw bss color collision detection */ ++ ieee80211_rx_check_bss_color_collision(rx); + - stats->dropped++; - return true; - } -@@ -4529,6 +4561,47 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, - return true; - } - -+static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw, -+ struct ieee80211_sta *pubsta, -+ struct sk_buff *skb, -+#if LINUX_VERSION_IS_GEQ(4,19,0) -+ struct list_head *list) -+#else -+ struct sk_buff_head *list) -+#endif -+{ -+ struct ieee80211_local *local = hw_to_local(hw); -+ struct ieee80211_fast_rx *fast_rx; -+ struct ieee80211_rx_data rx; -+ -+ memset(&rx, 0, sizeof(rx)); -+ rx.skb = skb; -+ rx.local = local; -+ rx.list = list; -+ -+ I802_DEBUG_INC(local->dot11ReceivedFragmentCount); -+ -+ /* drop frame if too short for header */ -+ if (skb->len < sizeof(struct ethhdr)) -+ goto drop; -+ -+ if (!pubsta) -+ goto drop; -+ -+ rx.sta = container_of(pubsta, struct sta_info, sta); -+ rx.sdata = rx.sta->sdata; -+ -+ fast_rx = rcu_dereference(rx.sta->fast_rx); -+ if (!fast_rx) -+ goto drop; -+ -+ ieee80211_rx_8023(&rx, fast_rx, skb->len); -+ return; -+ -+drop: -+ dev_kfree_skb(skb); -+} -+ - /* - * This is the actual Rx frames handler. as it belongs to Rx path it must - * be called with rcu_read_lock protection. -@@ -4766,15 +4839,20 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, - * if it was previously present. - * Also, frames with less than 16 bytes are dropped. - */ -- skb = ieee80211_rx_monitor(local, skb, rate); -- if (!skb) -- return; -+ if (!(status->flag & RX_FLAG_8023)) { -+ skb = ieee80211_rx_monitor(local, skb, rate); -+ if (!skb) -+ return; -+ } - - ieee80211_tpt_led_trig_rx(local, - ((struct ieee80211_hdr *)skb->data)->frame_control, - skb->len); - -- __ieee80211_rx_handle_packet(hw, pubsta, skb, list); -+ if (status->flag & RX_FLAG_8023) -+ __ieee80211_rx_handle_8023(hw, pubsta, skb, list); -+ else -+ __ieee80211_rx_handle_packet(hw, pubsta, skb, list); - - return; - drop: + if (ieee80211_hw_check(&rx->local->hw, SIGNAL_DBM) && + !(status->flag & RX_FLAG_NO_SIGNAL_VAL)) + sig = status->signal; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c -index 3058f0a..83c2d15 100644 +index dccaf4b..d67dfdc 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -357,6 +357,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, @@ -5454,304 +2116,1109 @@ index 3058f0a..83c2d15 100644 #ifdef CPTCFG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) { sta->mesh = kzalloc(sizeof(*sta->mesh), gfp); +@@ -425,11 +426,15 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + if (sta_prepare_rate_control(local, sta, gfp)) + goto free_txq; + ++ sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + skb_queue_head_init(&sta->ps_tx_buf[i]); + skb_queue_head_init(&sta->tx_filtered[i]); +- init_airtime_info(&sta->airtime[i], &local->airtime[i]); ++ sta->airtime[i].deficit = sta->airtime_weight; ++ atomic_set(&sta->airtime[i].aql_tx_pending, 0); ++ sta->airtime[i].aql_limit_low = local->aql_txq_limit_low[i]; ++ sta->airtime[i].aql_limit_high = local->aql_txq_limit_high[i]; + } + + for (i = 0; i < IEEE80211_NUM_TIDS; i++) +@@ -1888,59 +1893,29 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, + } + EXPORT_SYMBOL(ieee80211_sta_set_buffered); + +-void ieee80211_register_airtime(struct ieee80211_txq *txq, +- u32 tx_airtime, u32 rx_airtime) ++void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid, ++ u32 tx_airtime, u32 rx_airtime) + { +- struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); +- struct ieee80211_local *local = sdata->local; +- u64 weight_sum, weight_sum_reciprocal; +- struct airtime_sched_info *air_sched; +- struct airtime_info *air_info; ++ struct sta_info *sta = container_of(pubsta, struct sta_info, sta); ++ struct ieee80211_local *local = sta->sdata->local; ++ u8 ac = ieee80211_ac_from_tid(tid); + u32 airtime = 0; ++ u32 diff; + +- air_sched = &local->airtime[txq->ac]; +- air_info = to_airtime_info(txq); +- +- if (local->airtime_flags & AIRTIME_USE_TX) ++ if (sta->local->airtime_flags & AIRTIME_USE_TX) + airtime += tx_airtime; +- if (local->airtime_flags & AIRTIME_USE_RX) ++ if (sta->local->airtime_flags & AIRTIME_USE_RX) + airtime += rx_airtime; + +- /* Weights scale so the unit weight is 256 */ +- airtime <<= 8; +- +- spin_lock_bh(&air_sched->lock); +- +- air_info->tx_airtime += tx_airtime; +- air_info->rx_airtime += rx_airtime; +- +- if (air_sched->weight_sum) { +- weight_sum = air_sched->weight_sum; +- weight_sum_reciprocal = air_sched->weight_sum_reciprocal; +- } else { +- weight_sum = air_info->weight; +- weight_sum_reciprocal = air_info->weight_reciprocal; +- } ++ spin_lock_bh(&local->active_txq_lock[ac]); ++ sta->airtime[ac].tx_airtime += tx_airtime; ++ sta->airtime[ac].rx_airtime += rx_airtime; + +- /* Round the calculation of global vt */ +- air_sched->v_t += (u64)((airtime + (weight_sum >> 1)) * +- weight_sum_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_64; +- air_info->v_t += (u32)((airtime + (air_info->weight >> 1)) * +- air_info->weight_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_32; +- ieee80211_resort_txq(&local->hw, txq); ++ diff = (u32)jiffies - sta->airtime[ac].last_active; ++ if (diff <= AIRTIME_ACTIVE_DURATION) ++ sta->airtime[ac].deficit -= airtime; + +- spin_unlock_bh(&air_sched->lock); +-} +- +-void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid, +- u32 tx_airtime, u32 rx_airtime) +-{ +- struct ieee80211_txq *txq = pubsta->txq[tid]; +- +- if (!txq) +- return; +- +- ieee80211_register_airtime(txq, tx_airtime, rx_airtime); ++ spin_unlock_bh(&local->active_txq_lock[ac]); + } + EXPORT_SYMBOL(ieee80211_sta_register_airtime); + +@@ -1959,6 +1934,7 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, + &sta->airtime[ac].aql_tx_pending); + + atomic_add(tx_airtime, &local->aql_total_pending_airtime); ++ atomic_add(tx_airtime, &local->aql_ac_pending_airtime[ac]); + return; + } + +@@ -1970,14 +1946,17 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, + tx_pending, 0); + } + ++ atomic_sub(tx_airtime, &local->aql_total_pending_airtime); + tx_pending = atomic_sub_return(tx_airtime, +- &local->aql_total_pending_airtime); ++ &local->aql_ac_pending_airtime[ac]); + if (WARN_ONCE(tx_pending < 0, + "Device %s AC %d pending airtime underflow: %u, %u", + wiphy_name(local->hw.wiphy), ac, tx_pending, +- tx_airtime)) +- atomic_cmpxchg(&local->aql_total_pending_airtime, ++ tx_airtime)) { ++ atomic_cmpxchg(&local->aql_ac_pending_airtime[ac], + tx_pending, 0); ++ atomic_sub(tx_pending, &local->aql_total_pending_airtime); ++ } + } + + int sta_info_move_state(struct sta_info *sta, +@@ -2384,7 +2363,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, + } + + if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) { +- sinfo->airtime_weight = sta->airtime[0].weight; ++ sinfo->airtime_weight = sta->airtime_weight; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT); + } + diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h -index 94f4422..df67568 100644 +index 78b0c18..70600a6 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h -@@ -71,6 +71,7 @@ - * until pending frames are delivered - * @WLAN_STA_USES_ENCRYPTION: This station was configured for encryption, - * so drop all packets without a key later. -+ * @WLAN_STA_DECAP_OFFLOAD: This station uses rx decap offload - * - * @NUM_WLAN_STA_FLAGS: number of defined flags - */ -@@ -102,6 +103,7 @@ enum ieee80211_sta_info_flags { - WLAN_STA_MPSP_RECIPIENT, - WLAN_STA_PS_DELIVER, - WLAN_STA_USES_ENCRYPTION, -+ WLAN_STA_DECAP_OFFLOAD, +@@ -135,25 +135,19 @@ enum ieee80211_agg_stop_reason { + #define AIRTIME_USE_TX BIT(0) + #define AIRTIME_USE_RX BIT(1) - NUM_WLAN_STA_FLAGS, +- + struct airtime_info { + u64 rx_airtime; + u64 tx_airtime; +- u64 v_t; +- u64 last_scheduled; +- struct list_head list; ++ u32 last_active; ++ s32 deficit; + atomic_t aql_tx_pending; /* Estimated airtime for frames pending */ + u32 aql_limit_low; + u32 aql_limit_high; +- u32 weight_reciprocal; +- u16 weight; }; + + void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, + struct sta_info *sta, u8 ac, + u16 tx_airtime, bool tx_completed); +-void ieee80211_register_airtime(struct ieee80211_txq *txq, +- u32 tx_airtime, u32 rx_airtime); + + struct sta_info; + +@@ -523,6 +517,7 @@ struct ieee80211_fragment_cache { + * @tid_seq: per-TID sequence numbers for sending to this STA + * @airtime: per-AC struct airtime_info describing airtime statistics for this + * station ++ * @airtime_weight: station weight for airtime fairness calculation purposes + * @ampdu_mlme: A-MPDU state machine state + * @mesh: mesh STA information + * @debugfs_dir: debug filesystem directory dentry +@@ -653,6 +648,7 @@ struct sta_info { + u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; + + struct airtime_info airtime[IEEE80211_NUM_ACS]; ++ u16 airtime_weight; + + /* + * Aggregation information, locked with lock. +diff --git a/net/mac80211/status.c b/net/mac80211/status.c +index ef17d43..97d57cb 100644 +--- a/net/mac80211/status.c ++++ b/net/mac80211/status.c +@@ -983,25 +983,6 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, + if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked) + ieee80211_frame_acked(sta, skb); + +- } else if (wiphy_ext_feature_isset(local->hw.wiphy, +- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) { +- struct ieee80211_sub_if_data *sdata; +- struct ieee80211_txq *txq; +- u32 airtime; +- +- /* Account airtime to multicast queue */ +- sdata = ieee80211_sdata_from_skb(local, skb); +- +- if (sdata && (txq = sdata->vif.txq)) { +- airtime = info->status.tx_time ?: +- ieee80211_calc_expected_tx_airtime(hw, +- &sdata->vif, +- NULL, +- skb->len, +- false); +- +- ieee80211_register_airtime(txq, airtime, 0); +- } + } + + /* SNMP counters diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h -index 8972390..8fcc390 100644 +index 9e8381b..bbda9e9 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h -@@ -2,7 +2,7 @@ - /* - * Portions of this file - * Copyright(c) 2016-2017 Intel Deutschland GmbH --* Copyright (C) 2018 - 2019 Intel Corporation -+* Copyright (C) 2018 - 2020 Intel Corporation - */ - - #if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) -@@ -2086,6 +2086,27 @@ TRACE_EVENT(api_connection_loss, +@@ -2892,6 +2892,15 @@ TRACE_EVENT(drv_twt_teardown_request, ) ); -+TRACE_EVENT(api_disconnect, -+ TP_PROTO(struct ieee80211_sub_if_data *sdata, bool reconnect), -+ -+ TP_ARGS(sdata, reconnect), -+ -+ TP_STRUCT__entry( -+ VIF_ENTRY -+ __field(int, reconnect) -+ ), -+ -+ TP_fast_assign( -+ VIF_ASSIGN; -+ __entry->reconnect = reconnect; -+ ), -+ -+ TP_printk( -+ VIF_PR_FMT " reconnect:%d", -+ VIF_PR_ARG, __entry->reconnect -+ ) -+); -+ - TRACE_EVENT(api_cqm_rssi_notify, - TP_PROTO(struct ieee80211_sub_if_data *sdata, - enum nl80211_cqm_rssi_threshold_event rssi_event, -@@ -2740,7 +2761,7 @@ DEFINE_EVENT(local_sdata_addr_evt, drv_update_vif_offload, - TP_ARGS(local, sdata) - ); - --TRACE_EVENT(drv_sta_set_4addr, -+DECLARE_EVENT_CLASS(sta_flag_evt, - TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - struct ieee80211_sta *sta, bool enabled), -@@ -2767,6 +2788,22 @@ TRACE_EVENT(drv_sta_set_4addr, - ) - ); - -+DEFINE_EVENT(sta_flag_evt, drv_sta_set_4addr, ++#if LINUX_VERSION_IS_GEQ(5,10,0) ++DEFINE_EVENT(sta_event, drv_net_fill_forward_path, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, -+ struct ieee80211_sta *sta, bool enabled), -+ -+ TP_ARGS(local, sdata, sta, enabled) -+); -+ -+DEFINE_EVENT(sta_flag_evt, drv_sta_set_decap_offload, -+ TP_PROTO(struct ieee80211_local *local, -+ struct ieee80211_sub_if_data *sdata, -+ struct ieee80211_sta *sta, bool enabled), -+ -+ TP_ARGS(local, sdata, sta, enabled) ++ struct ieee80211_sta *sta), ++ TP_ARGS(local, sdata, sta) +); ++#endif + #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c -index 32b13b4..e5e8aa1 100644 +index 15888e4..50025ff 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c -@@ -1322,7 +1322,7 @@ static struct sk_buff *codel_dequeue_func(struct codel_vars *cvars, - fq = &local->fq; - - if (cvars == &txqi->def_cvars) -- flow = &txqi->def_flow; -+ flow = &txqi->tin.default_flow; - else - flow = &fq->flows[cvars - local->cvars]; - -@@ -1365,7 +1365,7 @@ static struct sk_buff *fq_tin_dequeue_func(struct fq *fq, - cparams = &local->cparams; - } - -- if (flow == &txqi->def_flow) -+ if (flow == &tin->default_flow) - cvars = &txqi->def_cvars; - else - cvars = &local->cvars[flow - fq->flows]; -@@ -1392,17 +1392,6 @@ static void fq_skb_free_func(struct fq *fq, - ieee80211_free_txskb(&local->hw, skb); - } - --static struct fq_flow *fq_flow_get_default_func(struct fq *fq, -- struct fq_tin *tin, -- int idx, -- struct sk_buff *skb) --{ -- struct txq_info *txqi; -- -- txqi = container_of(tin, struct txq_info, tin); -- return &txqi->def_flow; --} -- - static void ieee80211_txq_enqueue(struct ieee80211_local *local, - struct txq_info *txqi, - struct sk_buff *skb) -@@ -1415,8 +1404,7 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local, - - spin_lock_bh(&fq->lock); - fq_tin_enqueue(fq, tin, flow_idx, skb, -- fq_skb_free_func, -- fq_flow_get_default_func); -+ fq_skb_free_func); - spin_unlock_bh(&fq->lock); - } - -@@ -1459,7 +1447,6 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata, - struct txq_info *txqi, int tid) - { - fq_tin_init(&txqi->tin); -- fq_flow_init(&txqi->def_flow); +@@ -18,7 +18,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -1480,7 +1479,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata, codel_vars_init(&txqi->def_cvars); codel_stats_init(&txqi->cstats); __skb_queue_head_init(&txqi->frags); -@@ -3310,8 +3297,7 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata, - */ +- RB_CLEAR_NODE(&txqi->schedule_order); ++ INIT_LIST_HEAD(&txqi->schedule_order); - tin = &txqi->tin; -- flow = fq_flow_classify(fq, tin, flow_idx, skb, -- fq_flow_get_default_func); -+ flow = fq_flow_classify(fq, tin, flow_idx, skb); - head = skb_peek_tail(&flow->queue); - if (!head || skb_is_gso(head)) - goto out; -@@ -3378,8 +3364,6 @@ out_recalc: - if (head->len != orig_len) { - flow->backlog += head->len - orig_len; - tin->backlog_bytes += head->len - orig_len; -- -- fq_recalc_backlog(fq, tin, flow); - } - out: + txqi->txq.vif = &sdata->vif; + +@@ -1524,7 +1523,9 @@ void ieee80211_txq_purge(struct ieee80211_local *local, + ieee80211_purge_tx_queue(&local->hw, &txqi->frags); spin_unlock_bh(&fq->lock); -diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c -index 91bf32a..690fe47 100644 ---- a/net/mac80211/wpa.c -+++ b/net/mac80211/wpa.c -@@ -311,7 +311,8 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) + +- ieee80211_unschedule_txq(&local->hw, &txqi->txq, true); ++ spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]); ++ list_del_init(&txqi->schedule_order); ++ spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]); } + void ieee80211_txq_set_params(struct ieee80211_local *local) +@@ -3791,7 +3792,7 @@ begin: + encap_out: + IEEE80211_SKB_CB(skb)->control.vif = vif; --static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad) -+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, -+ u16 data_len) +- if (vif && ++ if (tx.sta && + wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) { + bool ampdu = txq->ac != IEEE80211_AC_VO; + u32 airtime; +@@ -3816,262 +3817,147 @@ out: + } + EXPORT_SYMBOL(ieee80211_tx_dequeue); + +-struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) ++static inline s32 ieee80211_sta_deficit(struct sta_info *sta, u8 ac) { - __le16 mask_fc; - int a4_included, mgmt; -@@ -341,14 +342,8 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad) - else - qos_tid = 0; +- struct ieee80211_local *local = hw_to_local(hw); +- struct airtime_sched_info *air_sched; +- u64 now = ktime_get_boottime_ns(); +- struct ieee80211_txq *ret = NULL; +- struct airtime_info *air_info; +- struct txq_info *txqi = NULL; +- struct rb_node *node; +- bool first = false; +- +- air_sched = &local->airtime[ac]; +- spin_lock_bh(&air_sched->lock); +- +- node = air_sched->schedule_pos; +- +-begin: +- if (!node) { +- node = rb_first_cached(&air_sched->active_txqs); +- first = true; +- } else { +- node = rb_next(node); +- } +- +- if (!node) +- goto out; +- +- txqi = container_of(node, struct txq_info, schedule_order); +- air_info = to_airtime_info(&txqi->txq); +- +- if (air_info->v_t > air_sched->v_t && +- (!first || !airtime_catchup_v_t(air_sched, air_info->v_t, now))) +- goto out; +- +- if (!ieee80211_txq_airtime_check(hw, &txqi->txq)) { +- first = false; +- goto begin; +- } ++ struct airtime_info *air_info = &sta->airtime[ac]; -- /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC -- * mode authentication are not allowed to collide, yet both are derived -- * from this vector b_0. We only set L := 1 here to indicate that the -- * data size can be represented in (L+1) bytes. The CCM layer will take -- * care of storing the data length in the top (L+1) bytes and setting -- * and clearing the other bits as is required to derive the two IVs. -- */ -- b_0[0] = 0x1; -+ /* First block, b_0 */ -+ b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ - - /* Nonce: Nonce Flags | A2 | PN - * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) -@@ -356,6 +351,8 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad) - b_0[1] = qos_tid | (mgmt << 4); - memcpy(&b_0[2], hdr->addr2, ETH_ALEN); - memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); -+ /* l(m) */ -+ put_unaligned_be16(data_len, &b_0[14]); - - /* AAD (extra authenticate-only data) / masked 802.11 header - * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ -@@ -412,7 +409,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb, - u8 *pos; - u8 pn[6]; - u64 pn64; -- u8 aad[CCM_AAD_LEN]; -+ u8 aad[2 * AES_BLOCK_SIZE]; - u8 b_0[AES_BLOCK_SIZE]; - - if (info->control.hw_key && -@@ -467,9 +464,11 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb, - return 0; - - pos += IEEE80211_CCMP_HDR_LEN; -- ccmp_special_blocks(skb, pn, b_0, aad); -- return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, -- skb_put(skb, mic_len)); -+ ccmp_special_blocks(skb, pn, b_0, aad, len); -+ ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, -+ skb_put(skb, mic_len), mic_len); -+ -+ return 0; +- air_sched->schedule_pos = node; +- air_sched->last_schedule_activity = now; +- ret = &txqi->txq; +-out: +- spin_unlock_bh(&air_sched->lock); +- return ret; +-} +-EXPORT_SYMBOL(ieee80211_next_txq); +- +-static void __ieee80211_insert_txq(struct rb_root_cached *root, +- struct txq_info *txqi) +-{ +- struct rb_node **new = &root->rb_root.rb_node; +- struct airtime_info *old_air, *new_air; +- struct rb_node *parent = NULL; +- struct txq_info *__txqi; +- bool leftmost = true; +- +- while (*new) { +- parent = *new; +- __txqi = rb_entry(parent, struct txq_info, schedule_order); +- old_air = to_airtime_info(&__txqi->txq); +- new_air = to_airtime_info(&txqi->txq); +- +- if (new_air->v_t <= old_air->v_t) { +- new = &parent->rb_left; +- } else { +- new = &parent->rb_right; +- leftmost = false; +- } +- } +- +- rb_link_node(&txqi->schedule_order, parent, new); +- rb_insert_color_cached(&txqi->schedule_order, root, leftmost); ++ return air_info->deficit - atomic_read(&air_info->aql_tx_pending); } +-void ieee80211_resort_txq(struct ieee80211_hw *hw, +- struct ieee80211_txq *txq) ++static void ++ieee80211_txq_set_active(struct txq_info *txqi) + { +- struct airtime_info *air_info = to_airtime_info(txq); +- struct ieee80211_local *local = hw_to_local(hw); +- struct txq_info *txqi = to_txq_info(txq); +- struct airtime_sched_info *air_sched; +- +- air_sched = &local->airtime[txq->ac]; +- +- lockdep_assert_held(&air_sched->lock); +- +- if (!RB_EMPTY_NODE(&txqi->schedule_order)) { +- struct airtime_info *a_prev = NULL, *a_next = NULL; +- struct txq_info *t_prev, *t_next; +- struct rb_node *n_prev, *n_next; +- +- /* Erasing a node can cause an expensive rebalancing operation, +- * so we check the previous and next nodes first and only remove +- * and re-insert if the current node is not already in the +- * correct position. +- */ +- if ((n_prev = rb_prev(&txqi->schedule_order)) != NULL) { +- t_prev = container_of(n_prev, struct txq_info, +- schedule_order); +- a_prev = to_airtime_info(&t_prev->txq); +- } +- +- if ((n_next = rb_next(&txqi->schedule_order)) != NULL) { +- t_next = container_of(n_next, struct txq_info, +- schedule_order); +- a_next = to_airtime_info(&t_next->txq); +- } +- +- if ((!a_prev || a_prev->v_t <= air_info->v_t) && +- (!a_next || a_next->v_t > air_info->v_t)) +- return; ++ struct sta_info *sta; -@@ -542,13 +541,13 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, - u8 aad[2 * AES_BLOCK_SIZE]; - u8 b_0[AES_BLOCK_SIZE]; - /* hardware didn't decrypt/verify MIC */ -- ccmp_special_blocks(skb, pn, b_0, aad); -+ ccmp_special_blocks(skb, pn, b_0, aad, data_len); +- if (air_sched->schedule_pos == &txqi->schedule_order) +- air_sched->schedule_pos = n_prev; ++ if (!txqi->txq.sta) ++ return; - if (ieee80211_aes_ccm_decrypt( - key->u.ccmp.tfm, b_0, aad, - skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, - data_len, -- skb->data + skb->len - mic_len)) -+ skb->data + skb->len - mic_len, mic_len)) - return RX_DROP_UNUSABLE; +- rb_erase_cached(&txqi->schedule_order, +- &air_sched->active_txqs); +- RB_CLEAR_NODE(&txqi->schedule_order); +- __ieee80211_insert_txq(&air_sched->active_txqs, txqi); +- } ++ sta = container_of(txqi->txq.sta, struct sta_info, sta); ++ sta->airtime[txqi->txq.ac].last_active = (u32)jiffies; + } + +-void ieee80211_update_airtime_weight(struct ieee80211_local *local, +- struct airtime_sched_info *air_sched, +- u64 now, bool force) ++static bool ++ieee80211_txq_keep_active(struct txq_info *txqi) + { +- struct airtime_info *air_info, *tmp; +- u64 weight_sum = 0; ++ struct sta_info *sta; ++ u32 diff; + +- if (unlikely(!now)) +- now = ktime_get_boottime_ns(); ++ if (!txqi->txq.sta) ++ return false; + +- lockdep_assert_held(&air_sched->lock); ++ sta = container_of(txqi->txq.sta, struct sta_info, sta); ++ if (ieee80211_sta_deficit(sta, txqi->txq.ac) >= 0) ++ return false; + +- if (!force && (air_sched->last_weight_update < +- now - AIRTIME_ACTIVE_DURATION)) +- return; ++ diff = (u32)jiffies - sta->airtime[txqi->txq.ac].last_active; + +- list_for_each_entry_safe(air_info, tmp, +- &air_sched->active_list, list) { +- if (airtime_is_active(air_info, now)) +- weight_sum += air_info->weight; +- else +- list_del_init(&air_info->list); +- } +- airtime_weight_sum_set(air_sched, weight_sum); +- air_sched->last_weight_update = now; ++ return diff <= AIRTIME_ACTIVE_DURATION; + } + +-void ieee80211_schedule_txq(struct ieee80211_hw *hw, +- struct ieee80211_txq *txq) +- __acquires(txq_lock) __releases(txq_lock) ++struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) + { + struct ieee80211_local *local = hw_to_local(hw); +- struct txq_info *txqi = to_txq_info(txq); +- struct airtime_sched_info *air_sched; +- u64 now = ktime_get_boottime_ns(); +- struct airtime_info *air_info; +- u8 ac = txq->ac; +- bool was_active; +- +- air_sched = &local->airtime[ac]; +- air_info = to_airtime_info(txq); ++ struct ieee80211_txq *ret = NULL; ++ struct txq_info *txqi = NULL, *head = NULL; ++ bool found_eligible_txq = false; + +- spin_lock_bh(&air_sched->lock); +- was_active = airtime_is_active(air_info, now); +- airtime_set_active(air_sched, air_info, now); ++ spin_lock_bh(&local->active_txq_lock[ac]); + +- if (!RB_EMPTY_NODE(&txqi->schedule_order)) ++ if (!local->schedule_round[ac]) + goto out; + +- /* If the station has been inactive for a while, catch up its v_t so it +- * doesn't get indefinite priority; see comment above the definition of +- * AIRTIME_MAX_BEHIND. +- */ +- if ((!was_active && air_info->v_t < air_sched->v_t) || +- air_info->v_t < air_sched->v_t - AIRTIME_MAX_BEHIND) +- air_info->v_t = air_sched->v_t; ++ begin: ++ txqi = list_first_entry_or_null(&local->active_txqs[ac], ++ struct txq_info, ++ schedule_order); ++ if (!txqi) ++ goto out; + +- ieee80211_update_airtime_weight(local, air_sched, now, !was_active); +- __ieee80211_insert_txq(&air_sched->active_txqs, txqi); ++ if (txqi == head) { ++ if (!found_eligible_txq) ++ goto out; ++ else ++ found_eligible_txq = false; ++ } + +-out: +- spin_unlock_bh(&air_sched->lock); +-} +-EXPORT_SYMBOL(ieee80211_schedule_txq); ++ if (!head) ++ head = txqi; + +-static void __ieee80211_unschedule_txq(struct ieee80211_hw *hw, +- struct ieee80211_txq *txq, +- bool purge) +-{ +- struct ieee80211_local *local = hw_to_local(hw); +- struct txq_info *txqi = to_txq_info(txq); +- struct airtime_sched_info *air_sched; +- struct airtime_info *air_info; ++ 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); + +- air_sched = &local->airtime[txq->ac]; +- air_info = to_airtime_info(&txqi->txq); ++ if (aql_check) ++ found_eligible_txq = true; + +- lockdep_assert_held(&air_sched->lock); ++ if (deficit < 0) ++ sta->airtime[txqi->txq.ac].deficit += ++ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT; + +- if (purge) { +- list_del_init(&air_info->list); +- ieee80211_update_airtime_weight(local, air_sched, 0, true); ++ if (deficit < 0 || !aql_check) { ++ list_move_tail(&txqi->schedule_order, ++ &local->active_txqs[txqi->txq.ac]); ++ goto begin; ++ } + } + +- if (RB_EMPTY_NODE(&txqi->schedule_order)) +- return; +- +- if (air_sched->schedule_pos == &txqi->schedule_order) +- air_sched->schedule_pos = rb_prev(&txqi->schedule_order); ++ if (txqi->schedule_round == local->schedule_round[ac]) ++ goto out; + +- if (!purge) +- airtime_set_active(air_sched, air_info, +- ktime_get_boottime_ns()); ++ list_del_init(&txqi->schedule_order); ++ txqi->schedule_round = local->schedule_round[ac]; ++ ret = &txqi->txq; + +- rb_erase_cached(&txqi->schedule_order, +- &air_sched->active_txqs); +- RB_CLEAR_NODE(&txqi->schedule_order); ++out: ++ spin_unlock_bh(&local->active_txq_lock[ac]); ++ return ret; + } ++EXPORT_SYMBOL(ieee80211_next_txq); + +-void ieee80211_unschedule_txq(struct ieee80211_hw *hw, ++void __ieee80211_schedule_txq(struct ieee80211_hw *hw, + struct ieee80211_txq *txq, +- bool purge) +- __acquires(txq_lock) __releases(txq_lock) +-{ +- struct ieee80211_local *local = hw_to_local(hw); +- +- spin_lock_bh(&local->airtime[txq->ac].lock); +- __ieee80211_unschedule_txq(hw, txq, purge); +- spin_unlock_bh(&local->airtime[txq->ac].lock); +-} +- +-void ieee80211_return_txq(struct ieee80211_hw *hw, +- struct ieee80211_txq *txq, bool force) ++ bool force) + { + struct ieee80211_local *local = hw_to_local(hw); + struct txq_info *txqi = to_txq_info(txq); ++ bool has_queue; ++ ++ spin_lock_bh(&local->active_txq_lock[txq->ac]); ++ ++ has_queue = force || txq_has_queue(txq); ++ if (list_empty(&txqi->schedule_order) && ++ (has_queue || ieee80211_txq_keep_active(txqi))) { ++ /* If airtime accounting is active, always enqueue STAs at the ++ * head of the list to ensure that they only get moved to the ++ * back by the airtime DRR scheduler once they have a negative ++ * deficit. A station that already has a negative deficit will ++ * get immediately moved to the back of the list on the next ++ * call to ieee80211_next_txq(). ++ */ ++ if (txqi->txq.sta && local->airtime_flags && has_queue && ++ wiphy_ext_feature_isset(local->hw.wiphy, ++ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) ++ list_add(&txqi->schedule_order, ++ &local->active_txqs[txq->ac]); ++ else ++ list_add_tail(&txqi->schedule_order, ++ &local->active_txqs[txq->ac]); ++ if (has_queue) ++ ieee80211_txq_set_active(txqi); ++ } + +- spin_lock_bh(&local->airtime[txq->ac].lock); +- +- if (!RB_EMPTY_NODE(&txqi->schedule_order) && !force && +- !txq_has_queue(txq)) +- __ieee80211_unschedule_txq(hw, txq, false); +- +- spin_unlock_bh(&local->airtime[txq->ac].lock); ++ spin_unlock_bh(&local->active_txq_lock[txq->ac]); + } +-EXPORT_SYMBOL(ieee80211_return_txq); ++EXPORT_SYMBOL(__ieee80211_schedule_txq); + + DEFINE_STATIC_KEY_FALSE(aql_disable); + + bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) + { +- struct airtime_info *air_info = to_airtime_info(txq); ++ struct sta_info *sta; + struct ieee80211_local *local = hw_to_local(hw); + + if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) +@@ -4086,74 +3972,109 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, + if (unlikely(txq->tid == IEEE80211_NUM_TIDS)) + return true; + +- if (atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_low) ++ sta = container_of(txq->sta, struct sta_info, sta); ++ if (atomic_read(&sta->airtime[txq->ac].aql_tx_pending) < ++ sta->airtime[txq->ac].aql_limit_low) + return true; + + if (atomic_read(&local->aql_total_pending_airtime) < + local->aql_threshold && +- atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_high) ++ atomic_read(&sta->airtime[txq->ac].aql_tx_pending) < ++ sta->airtime[txq->ac].aql_limit_high) + return true; + + return false; + } + EXPORT_SYMBOL(ieee80211_txq_airtime_check); + ++static bool ++ieee80211_txq_schedule_airtime_check(struct ieee80211_local *local, u8 ac) ++{ ++ unsigned int num_txq = 0; ++ struct txq_info *txq; ++ u32 aql_limit; ++ ++ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) ++ return true; ++ ++ list_for_each_entry(txq, &local->active_txqs[ac], schedule_order) ++ num_txq++; ++ ++ aql_limit = (num_txq - 1) * local->aql_txq_limit_low[ac] / 2 + ++ local->aql_txq_limit_high[ac]; ++ ++ return atomic_read(&local->aql_ac_pending_airtime[ac]) < aql_limit; ++} ++ + bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) + { +- struct txq_info *first_txqi = NULL, *txqi = to_txq_info(txq); + struct ieee80211_local *local = hw_to_local(hw); +- struct airtime_sched_info *air_sched; +- struct airtime_info *air_info; +- struct rb_node *node = NULL; +- bool ret = false; +- u64 now; +- ++ struct txq_info *iter, *tmp, *txqi = to_txq_info(txq); ++ struct sta_info *sta; ++ u8 ac = txq->ac; + +- if (!ieee80211_txq_airtime_check(hw, txq)) +- return false; ++ spin_lock_bh(&local->active_txq_lock[ac]); + +- air_sched = &local->airtime[txq->ac]; +- spin_lock_bh(&air_sched->lock); ++ if (!txqi->txq.sta) ++ goto out; + +- if (RB_EMPTY_NODE(&txqi->schedule_order)) ++ if (list_empty(&txqi->schedule_order)) + goto out; + +- now = ktime_get_boottime_ns(); ++ if (!ieee80211_txq_schedule_airtime_check(local, ac)) ++ goto out; + +- /* Like in ieee80211_next_txq(), make sure the first station in the +- * scheduling order is eligible for transmission to avoid starvation. +- */ +- node = rb_first_cached(&air_sched->active_txqs); +- if (node) { +- first_txqi = container_of(node, struct txq_info, +- schedule_order); +- air_info = to_airtime_info(&first_txqi->txq); ++ list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac], ++ schedule_order) { ++ if (iter == txqi) ++ break; + +- if (air_sched->v_t < air_info->v_t) +- airtime_catchup_v_t(air_sched, air_info->v_t, now); ++ if (!iter->txq.sta) { ++ list_move_tail(&iter->schedule_order, ++ &local->active_txqs[ac]); ++ continue; ++ } ++ sta = container_of(iter->txq.sta, struct sta_info, sta); ++ if (ieee80211_sta_deficit(sta, ac) < 0) ++ sta->airtime[ac].deficit += sta->airtime_weight << ++ AIRTIME_QUANTUM_SHIFT; ++ list_move_tail(&iter->schedule_order, &local->active_txqs[ac]); + } + +- air_info = to_airtime_info(&txqi->txq); +- if (air_info->v_t <= air_sched->v_t) { +- air_sched->last_schedule_activity = now; +- ret = true; +- } ++ sta = container_of(txqi->txq.sta, struct sta_info, sta); ++ if (sta->airtime[ac].deficit >= 0) ++ goto out; ++ ++ 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]); + ++ return false; + out: +- spin_unlock_bh(&air_sched->lock); +- return ret; ++ if (!list_empty(&txqi->schedule_order)) ++ list_del_init(&txqi->schedule_order); ++ spin_unlock_bh(&local->active_txq_lock[ac]); ++ ++ return true; + } + EXPORT_SYMBOL(ieee80211_txq_may_transmit); + + void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac) + { + struct ieee80211_local *local = hw_to_local(hw); +- struct airtime_sched_info *air_sched = &local->airtime[ac]; + +- spin_lock_bh(&air_sched->lock); +- air_sched->schedule_pos = NULL; +- spin_unlock_bh(&air_sched->lock); ++ spin_lock_bh(&local->active_txq_lock[ac]); ++ ++ if (ieee80211_txq_schedule_airtime_check(local, ac)) { ++ local->schedule_round[ac]++; ++ if (!local->schedule_round[ac]) ++ local->schedule_round[ac]++; ++ } else { ++ local->schedule_round[ac] = 0; ++ } ++ ++ spin_unlock_bh(&local->active_txq_lock[ac]); + } + EXPORT_SYMBOL(ieee80211_txq_schedule_start); + +@@ -4987,6 +4908,135 @@ static int ieee80211_beacon_protect(struct sk_buff *skb, + return 0; + } + ++static void ++ieee80211_beacon_get_finish(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_mutable_offsets *offs, ++ struct beacon_data *beacon, ++ struct sk_buff *skb, ++ struct ieee80211_chanctx_conf *chanctx_conf, ++ u16 csa_off_base) ++{ ++ struct ieee80211_local *local = hw_to_local(hw); ++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); ++ struct ieee80211_tx_info *info; ++ enum nl80211_band band; ++ struct ieee80211_tx_rate_control txrc; ++ ++ /* CSA offsets */ ++ if (offs && beacon) { ++ u16 i; ++ ++ for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; i++) { ++ u16 csa_off = beacon->cntdwn_counter_offsets[i]; ++ ++ if (!csa_off) ++ continue; ++ ++ offs->cntdwn_counter_offs[i] = csa_off_base + csa_off; ++ } ++ } ++ ++ band = chanctx_conf->def.chan->band; ++ info = IEEE80211_SKB_CB(skb); ++ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ++ info->flags |= IEEE80211_TX_CTL_NO_ACK; ++ info->band = band; ++ ++ memset(&txrc, 0, sizeof(txrc)); ++ txrc.hw = hw; ++ txrc.sband = local->hw.wiphy->bands[band]; ++ txrc.bss_conf = &sdata->vif.bss_conf; ++ txrc.skb = skb; ++ txrc.reported_rate.idx = -1; ++ if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band]) ++ txrc.rate_idx_mask = sdata->beacon_rateidx_mask[band]; ++ else ++ txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; ++ txrc.bss = true; ++ rate_control_get_rate(sdata, NULL, &txrc); ++ ++ info->control.vif = vif; ++ info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT | ++ IEEE80211_TX_CTL_ASSIGN_SEQ | ++ IEEE80211_TX_CTL_FIRST_FRAGMENT; ++} ++ ++static void ++ieee80211_beacon_add_mbssid(struct sk_buff *skb, struct beacon_data *beacon) ++{ ++ int i; ++ ++ if (!beacon->mbssid_ies) ++ return; ++ ++ for (i = 0; i < beacon->mbssid_ies->cnt; i++) ++ skb_put_data(skb, beacon->mbssid_ies->elem[i].data, ++ beacon->mbssid_ies->elem[i].len); ++} ++ ++static struct sk_buff * ++ieee80211_beacon_get_ap(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_mutable_offsets *offs, ++ bool is_template, ++ struct beacon_data *beacon, ++ struct ieee80211_chanctx_conf *chanctx_conf) ++{ ++ struct ieee80211_local *local = hw_to_local(hw); ++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); ++ struct ieee80211_if_ap *ap = &sdata->u.ap; ++ struct sk_buff *skb = NULL; ++ u16 csa_off_base = 0; ++ int mbssid_len; ++ ++ if (beacon->cntdwn_counter_offsets[0]) { ++ if (!is_template) ++ ieee80211_beacon_update_cntdwn(vif); ++ ++ ieee80211_set_beacon_cntdwn(sdata, beacon); ++ } ++ ++ /* headroom, head length, ++ * tail length, maximum TIM length and multiple BSSID length ++ */ ++ mbssid_len = ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies); ++ skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + ++ beacon->tail_len + 256 + ++ local->hw.extra_beacon_tailroom + mbssid_len); ++ if (!skb) ++ return NULL; ++ ++ skb_reserve(skb, local->tx_headroom); ++ skb_put_data(skb, beacon->head, beacon->head_len); ++ ++ ieee80211_beacon_add_tim(sdata, &ap->ps, skb, is_template); ++ ++ if (offs) { ++ offs->tim_offset = beacon->head_len; ++ offs->tim_length = skb->len - beacon->head_len; ++ offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0]; ++ ++ if (mbssid_len) { ++ ieee80211_beacon_add_mbssid(skb, beacon); ++ offs->mbssid_off = skb->len - mbssid_len; ++ } ++ ++ /* for AP the csa offsets are from tail */ ++ csa_off_base = skb->len; ++ } ++ ++ if (beacon->tail) ++ skb_put_data(skb, beacon->tail, beacon->tail_len); ++ ++ if (ieee80211_beacon_protect(skb, local, sdata) < 0) ++ return NULL; ++ ++ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb, chanctx_conf, ++ csa_off_base); ++ return skb; ++} ++ + static struct sk_buff * + __ieee80211_beacon_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, +@@ -4996,12 +5046,8 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, + struct ieee80211_local *local = hw_to_local(hw); + struct beacon_data *beacon = NULL; + struct sk_buff *skb = NULL; +- struct ieee80211_tx_info *info; + struct ieee80211_sub_if_data *sdata = NULL; +- enum nl80211_band band; +- struct ieee80211_tx_rate_control txrc; + struct ieee80211_chanctx_conf *chanctx_conf; +- int csa_off_base = 0; + + rcu_read_lock(); + +@@ -5018,48 +5064,11 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, + struct ieee80211_if_ap *ap = &sdata->u.ap; + + beacon = rcu_dereference(ap->beacon); +- if (beacon) { +- if (beacon->cntdwn_counter_offsets[0]) { +- if (!is_template) +- ieee80211_beacon_update_cntdwn(vif); +- +- ieee80211_set_beacon_cntdwn(sdata, beacon); +- } +- +- /* +- * headroom, head length, +- * tail length and maximum TIM length +- */ +- skb = dev_alloc_skb(local->tx_headroom + +- beacon->head_len + +- beacon->tail_len + 256 + +- local->hw.extra_beacon_tailroom); +- if (!skb) +- goto out; +- +- skb_reserve(skb, local->tx_headroom); +- skb_put_data(skb, beacon->head, beacon->head_len); +- +- ieee80211_beacon_add_tim(sdata, &ap->ps, skb, +- is_template); +- +- if (offs) { +- offs->tim_offset = beacon->head_len; +- offs->tim_length = skb->len - beacon->head_len; +- offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0]; +- +- /* for AP the csa offsets are from tail */ +- csa_off_base = skb->len; +- } +- +- if (beacon->tail) +- skb_put_data(skb, beacon->tail, +- beacon->tail_len); +- +- if (ieee80211_beacon_protect(skb, local, sdata) < 0) +- goto out; +- } else ++ if (!beacon) + goto out; ++ ++ skb = ieee80211_beacon_get_ap(hw, vif, offs, is_template, ++ beacon, chanctx_conf); + } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct ieee80211_hdr *hdr; +@@ -5085,6 +5094,9 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, + hdr = (struct ieee80211_hdr *) skb->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_BEACON); ++ ++ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb, ++ chanctx_conf, 0); + } else if (ieee80211_vif_is_mesh(&sdata->vif)) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +@@ -5124,51 +5136,13 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, } -@@ -643,7 +642,7 @@ static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) - u8 *pos; - u8 pn[6]; - u64 pn64; -- u8 aad[GCM_AAD_LEN]; -+ u8 aad[2 * AES_BLOCK_SIZE]; - u8 j_0[AES_BLOCK_SIZE]; + skb_put_data(skb, beacon->tail, beacon->tail_len); ++ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb, ++ chanctx_conf, 0); + } else { + WARN_ON(1); + goto out; + } - if (info->control.hw_key && -@@ -700,8 +699,10 @@ static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) +- /* CSA offsets */ +- if (offs && beacon) { +- int i; +- +- for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; i++) { +- u16 csa_off = beacon->cntdwn_counter_offsets[i]; +- +- if (!csa_off) +- continue; +- +- offs->cntdwn_counter_offs[i] = csa_off_base + csa_off; +- } +- } +- +- band = chanctx_conf->def.chan->band; +- +- info = IEEE80211_SKB_CB(skb); +- +- info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +- info->flags |= IEEE80211_TX_CTL_NO_ACK; +- info->band = band; +- +- memset(&txrc, 0, sizeof(txrc)); +- txrc.hw = hw; +- txrc.sband = local->hw.wiphy->bands[band]; +- txrc.bss_conf = &sdata->vif.bss_conf; +- txrc.skb = skb; +- txrc.reported_rate.idx = -1; +- if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band]) +- txrc.rate_idx_mask = sdata->beacon_rateidx_mask[band]; +- else +- txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; +- txrc.bss = true; +- rate_control_get_rate(sdata, NULL, &txrc); +- +- info->control.vif = vif; +- +- info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT | +- IEEE80211_TX_CTL_ASSIGN_SEQ | +- IEEE80211_TX_CTL_FIRST_FRAGMENT; + out: + rcu_read_unlock(); + return skb; +diff --git a/net/mac80211/util.c b/net/mac80211/util.c +index be1911d..d85a39a 100644 +--- a/net/mac80211/util.c ++++ b/net/mac80211/util.c +@@ -301,6 +301,9 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) + local_bh_disable(); + spin_lock(&fq->lock); - pos += IEEE80211_GCMP_HDR_LEN; - gcmp_special_blocks(skb, pn, j_0, aad); -- return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len, -- skb_put(skb, IEEE80211_GCMP_MIC_LEN)); -+ ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len, -+ skb_put(skb, IEEE80211_GCMP_MIC_LEN)); ++ if (!test_bit(SDATA_STATE_RUNNING, &sdata->state)) ++ goto out; + -+ return 0; + if (sdata->vif.type == NL80211_IFTYPE_AP) + ps = &sdata->bss->ps; + +diff --git a/net/wireless/chan.c b/net/wireless/chan.c +index 1382a5f..5f50ac4 100644 +--- a/net/wireless/chan.c ++++ b/net/wireless/chan.c +@@ -712,6 +712,19 @@ static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy, + return false; } - ieee80211_tx_result -@@ -1128,9 +1129,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx) - struct ieee80211_key *key = tx->key; - struct ieee80211_mmie_16 *mmie; - struct ieee80211_hdr *hdr; -- u8 aad[GMAC_AAD_LEN]; -+ u8 aad[20]; - u64 pn64; -- u8 nonce[GMAC_NONCE_LEN]; -+ u8 nonce[12]; ++static bool ++cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev, ++ struct ieee80211_channel *channel) ++{ ++ if (!rdev->background_radar_wdev) ++ return false; ++ ++ if (!cfg80211_chandef_valid(&rdev->background_radar_chandef)) ++ return false; ++ ++ return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel); ++} ++ + bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) + { +@@ -728,6 +741,9 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, - if (WARN_ON(skb_queue_len(&tx->skbs) != 1)) - return TX_DROP; -@@ -1176,7 +1177,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx) - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_key *key = rx->key; - struct ieee80211_mmie_16 *mmie; -- u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN]; -+ u8 aad[20], *mic, ipn[6], nonce[12]; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan)) + return true; ++ ++ if (cfg80211_offchan_chain_is_active(rdev, chan)) ++ return true; + } - if (!ieee80211_is_mgmt(hdr->frame_control)) + return false; diff --git a/net/wireless/core.c b/net/wireless/core.c -index 587ad11..afdfcc2 100644 +index 72e010e..2ddaaae 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c -@@ -614,21 +614,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) +@@ -543,6 +543,10 @@ use_default_name: + INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work); + INIT_WORK(&rdev->conn_work, cfg80211_conn_work); + INIT_WORK(&rdev->event_work, cfg80211_event_work); ++ INIT_WORK(&rdev->background_cac_abort_wk, ++ cfg80211_background_cac_abort_wk); ++ INIT_DELAYED_WORK(&rdev->background_cac_done_wk, ++ cfg80211_background_cac_done_wk); + + init_waitqueue_head(&rdev->dev_wait); + +@@ -621,21 +625,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) c->limits[j].max > 1)) return -EINVAL; @@ -5773,234 +3240,677 @@ index 587ad11..afdfcc2 100644 cnt += c->limits[j].max; /* * Don't advertise an unsupported type +@@ -1052,11 +1041,13 @@ void wiphy_unregister(struct wiphy *wiphy) + cancel_work_sync(&rdev->conn_work); + flush_work(&rdev->event_work); + cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); ++ cancel_delayed_work_sync(&rdev->background_cac_done_wk); + flush_work(&rdev->destroy_work); + flush_work(&rdev->sched_scan_stop_wk); + flush_work(&rdev->propagate_radar_detect_wk); + flush_work(&rdev->propagate_cac_done_wk); + flush_work(&rdev->mgmt_registrations_update_wk); ++ flush_work(&rdev->background_cac_abort_wk); + + #ifdef CONFIG_PM + if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) +@@ -1205,6 +1196,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, + + cfg80211_pmsr_wdev_down(wdev); + ++ cfg80211_stop_background_radar_detection(wdev); ++ + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + __cfg80211_leave_ibss(rdev, dev, true); +diff --git a/net/wireless/core.h b/net/wireless/core.h +index e263dd9..b588e5a 100644 +--- a/net/wireless/core.h ++++ b/net/wireless/core.h +@@ -84,6 +84,11 @@ struct cfg80211_registered_device { + + struct delayed_work dfs_update_channels_wk; + ++ struct wireless_dev *background_radar_wdev; ++ struct cfg80211_chan_def background_radar_chandef; ++ struct delayed_work background_cac_done_wk; ++ struct work_struct background_cac_abort_wk; ++ + /* netlink port which started critical protocol (0 means not started) */ + u32 crit_proto_nlportid; + +@@ -491,6 +496,17 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, + + void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); + ++int ++cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev, ++ struct wireless_dev *wdev, ++ struct cfg80211_chan_def *chandef); ++ ++void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev); ++ ++void cfg80211_background_cac_done_wk(struct work_struct *work); ++ ++void cfg80211_background_cac_abort_wk(struct work_struct *work); ++ + bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan); + diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c -index 33f4ee6..ea2c4ee 100644 +index dbe0237..00370ca 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c -@@ -4,7 +4,7 @@ - * - * Copyright (c) 2009, Jouni Malinen - * Copyright (c) 2015 Intel Deutschland GmbH -- * Copyright (C) 2019 Intel Corporation -+ * Copyright (C) 2019-2020 Intel Corporation - */ - - #include -@@ -81,7 +81,8 @@ static void cfg80211_process_auth(struct wireless_dev *wdev, +@@ -905,13 +905,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) } - static void cfg80211_process_deauth(struct wireless_dev *wdev, -- const u8 *buf, size_t len) -+ const u8 *buf, size_t len, -+ bool reconnect) + +-void cfg80211_radar_event(struct wiphy *wiphy, +- struct cfg80211_chan_def *chandef, +- gfp_t gfp) ++void __cfg80211_radar_event(struct wiphy *wiphy, ++ struct cfg80211_chan_def *chandef, ++ bool offchan, gfp_t gfp) { - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; -@@ -89,7 +90,7 @@ static void cfg80211_process_deauth(struct wireless_dev *wdev, - u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr); + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); -- nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL); -+ nl80211_send_deauth(rdev, wdev->netdev, buf, len, reconnect, GFP_KERNEL); +- trace_cfg80211_radar_event(wiphy, chandef); ++ trace_cfg80211_radar_event(wiphy, chandef, offchan); - if (!wdev->current_bss || - !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) -@@ -100,7 +101,8 @@ static void cfg80211_process_deauth(struct wireless_dev *wdev, + /* only set the chandef supplied channel to unavailable, in + * case the radar is detected on only one of multiple channels +@@ -919,6 +919,9 @@ void cfg80211_radar_event(struct wiphy *wiphy, + */ + cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); + ++ if (offchan) ++ queue_work(cfg80211_wq, &rdev->background_cac_abort_wk); ++ + cfg80211_sched_dfs_chan_update(rdev); + + nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); +@@ -926,7 +929,7 @@ void cfg80211_radar_event(struct wiphy *wiphy, + memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def)); + queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk); } +-EXPORT_SYMBOL(cfg80211_radar_event); ++EXPORT_SYMBOL(__cfg80211_radar_event); - static void cfg80211_process_disassoc(struct wireless_dev *wdev, -- const u8 *buf, size_t len) -+ const u8 *buf, size_t len, -+ bool reconnect) - { - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; -@@ -108,7 +110,8 @@ static void cfg80211_process_disassoc(struct wireless_dev *wdev, - u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr); - -- nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL); -+ nl80211_send_disassoc(rdev, wdev->netdev, buf, len, reconnect, -+ GFP_KERNEL); - - if (WARN_ON(!wdev->current_bss || - !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) -@@ -133,9 +136,9 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) - if (ieee80211_is_auth(mgmt->frame_control)) - cfg80211_process_auth(wdev, buf, len); - else if (ieee80211_is_deauth(mgmt->frame_control)) -- cfg80211_process_deauth(wdev, buf, len); -+ cfg80211_process_deauth(wdev, buf, len, false); - else if (ieee80211_is_disassoc(mgmt->frame_control)) -- cfg80211_process_disassoc(wdev, buf, len); -+ cfg80211_process_disassoc(wdev, buf, len, false); + void cfg80211_cac_event(struct net_device *netdev, + const struct cfg80211_chan_def *chandef, +@@ -970,3 +973,143 @@ void cfg80211_cac_event(struct net_device *netdev, + nl80211_radar_notify(rdev, chandef, event, netdev, gfp); } - EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt); - -@@ -180,22 +183,23 @@ void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss) - } - EXPORT_SYMBOL(cfg80211_abandon_assoc); - --void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) -+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len, -+ bool reconnect) - { - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_mgmt *mgmt = (void *)buf; - - ASSERT_WDEV_LOCK(wdev); - -- trace_cfg80211_tx_mlme_mgmt(dev, buf, len); -+ trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect); - - if (WARN_ON(len < 2)) - return; - - if (ieee80211_is_deauth(mgmt->frame_control)) -- cfg80211_process_deauth(wdev, buf, len); -+ cfg80211_process_deauth(wdev, buf, len, reconnect); - else -- cfg80211_process_disassoc(wdev, buf, len); -+ cfg80211_process_disassoc(wdev, buf, len, reconnect); - } - EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt); - + EXPORT_SYMBOL(cfg80211_cac_event); ++ ++static void ++__cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, ++ struct wireless_dev *wdev, ++ const struct cfg80211_chan_def *chandef, ++ enum nl80211_radar_event event) ++{ ++ struct wiphy *wiphy = &rdev->wiphy; ++ struct net_device *netdev; ++ ++ lockdep_assert_wiphy(&rdev->wiphy); ++ ++ if (!cfg80211_chandef_valid(chandef)) ++ return; ++ ++ if (!rdev->background_radar_wdev) ++ return; ++ ++ switch (event) { ++ case NL80211_RADAR_CAC_FINISHED: ++ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); ++ memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef)); ++ queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); ++ cfg80211_sched_dfs_chan_update(rdev); ++ wdev = rdev->background_radar_wdev; ++ break; ++ case NL80211_RADAR_CAC_ABORTED: ++ if (!cancel_delayed_work(&rdev->background_cac_done_wk)) ++ return; ++ wdev = rdev->background_radar_wdev; ++ break; ++ case NL80211_RADAR_CAC_STARTED: ++ break; ++ default: ++ return; ++ } ++ ++ netdev = wdev ? wdev->netdev : NULL; ++ nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL); ++} ++ ++static void ++cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, ++ const struct cfg80211_chan_def *chandef, ++ enum nl80211_radar_event event) ++{ ++ wiphy_lock(&rdev->wiphy); ++ __cfg80211_background_cac_event(rdev, rdev->background_radar_wdev, ++ chandef, event); ++ wiphy_unlock(&rdev->wiphy); ++} ++ ++void cfg80211_background_cac_done_wk(struct work_struct *work) ++{ ++ struct delayed_work *delayed_work = to_delayed_work(work); ++ struct cfg80211_registered_device *rdev; ++ ++ rdev = container_of(delayed_work, struct cfg80211_registered_device, ++ background_cac_done_wk); ++ cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef, ++ NL80211_RADAR_CAC_FINISHED); ++} ++ ++void cfg80211_background_cac_abort_wk(struct work_struct *work) ++{ ++ struct cfg80211_registered_device *rdev; ++ ++ rdev = container_of(work, struct cfg80211_registered_device, ++ background_cac_abort_wk); ++ cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef, ++ NL80211_RADAR_CAC_ABORTED); ++} ++ ++void cfg80211_background_cac_abort(struct wiphy *wiphy) ++{ ++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); ++ ++ queue_work(cfg80211_wq, &rdev->background_cac_abort_wk); ++} ++EXPORT_SYMBOL(cfg80211_background_cac_abort); ++ ++int ++cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev, ++ struct wireless_dev *wdev, ++ struct cfg80211_chan_def *chandef) ++{ ++ unsigned int cac_time_ms; ++ int err; ++ ++ lockdep_assert_wiphy(&rdev->wiphy); ++ ++ if (!wiphy_ext_feature_isset(&rdev->wiphy, ++ NL80211_EXT_FEATURE_RADAR_BACKGROUND)) ++ return -EOPNOTSUPP; ++ ++ /* Offchannel chain already locked by another wdev */ ++ if (rdev->background_radar_wdev && rdev->background_radar_wdev != wdev) ++ return -EBUSY; ++ ++ /* CAC already in progress on the offchannel chain */ ++ if (rdev->background_radar_wdev == wdev && ++ delayed_work_pending(&rdev->background_cac_done_wk)) ++ return -EBUSY; ++ ++ err = rdev_set_radar_background(rdev, chandef); ++ if (err) ++ return err; ++ ++ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, chandef); ++ if (!cac_time_ms) ++ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; ++ ++ rdev->background_radar_chandef = *chandef; ++ rdev->background_radar_wdev = wdev; /* Get offchain ownership */ ++ ++ __cfg80211_background_cac_event(rdev, wdev, chandef, ++ NL80211_RADAR_CAC_STARTED); ++ queue_delayed_work(cfg80211_wq, &rdev->background_cac_done_wk, ++ msecs_to_jiffies(cac_time_ms)); ++ ++ return 0; ++} ++ ++void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev) ++{ ++ struct wiphy *wiphy = wdev->wiphy; ++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); ++ ++ lockdep_assert_wiphy(wiphy); ++ ++ if (wdev != rdev->background_radar_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); ++} diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c -index b389cbf..132df74 100644 +index 20df12c..bc6b5ac 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c -@@ -732,6 +732,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { - NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN), - [NL80211_ATTR_S1G_CAPABILITY_MASK] = - NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN), -+ [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT }, +@@ -442,6 +442,16 @@ sar_policy[NL80211_SAR_ATTR_MAX + 1] = { + [NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy), + }; + ++static const struct nla_policy ++nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = { ++ [NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2), ++ [NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY] = ++ NLA_POLICY_MIN(NLA_U8, 1), ++ [NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 }, ++ [NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 }, ++ [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG }, ++}; ++ + 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 }, +@@ -788,6 +798,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { + [NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 }, + [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 }, + [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy), ++ [NL80211_ATTR_MBSSID_CONFIG] = ++ NLA_POLICY_NESTED(nl80211_mbssid_config_policy), ++ [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED }, ++ [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG }, + [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, }; /* policy for the key attributes */ -@@ -3241,6 +3243,20 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) - return result; +@@ -2236,6 +2251,35 @@ fail: + return -ENOBUFS; + } + ++static int nl80211_put_mbssid_support(struct wiphy *wiphy, struct sk_buff *msg) ++{ ++ struct nlattr *config; ++ ++ if (!wiphy->mbssid_max_interfaces) ++ return 0; ++ ++ config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG); ++ if (!config) ++ return -ENOBUFS; ++ ++ if (nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES, ++ wiphy->mbssid_max_interfaces)) ++ goto fail; ++ ++ if (wiphy->ema_max_profile_periodicity && ++ nla_put_u8(msg, ++ NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY, ++ wiphy->ema_max_profile_periodicity)) ++ goto fail; ++ ++ nla_nest_end(msg, config); ++ return 0; ++ ++fail: ++ nla_nest_cancel(msg, config); ++ return -ENOBUFS; ++} ++ + struct nl80211_dump_wiphy_state { + s64 filter_wiphy; + long start; +@@ -2821,6 +2865,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, + if (nl80211_put_sar_specs(rdev, msg)) + goto nla_put_failure; + ++ if (nl80211_put_mbssid_support(&rdev->wiphy, msg)) ++ goto nla_put_failure; ++ + /* done */ + state->split_start = 0; + break; +@@ -3346,6 +3393,22 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) + goto out; } + if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_GAIN]) { + int idx, dbi = 0; + -+ if (!rdev->ops->set_antenna_gain) -+ return -EOPNOTSUPP; ++ if (!rdev->ops->set_antenna_gain) { ++ result = -EOPNOTSUPP; ++ goto out; ++ } + + idx = NL80211_ATTR_WIPHY_ANTENNA_GAIN; + dbi = nla_get_u32(info->attrs[idx]); + + result = rdev->ops->set_antenna_gain(&rdev->wiphy, dbi); + if (result) -+ return result; ++ goto out; + } + - if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && - info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { - u32 tx_ant, rx_ant; -@@ -15899,7 +15915,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, - const u8 *buf, size_t len, - enum nl80211_commands cmd, gfp_t gfp, - int uapsd_queues, const u8 *req_ies, -- size_t req_ies_len) -+ size_t req_ies_len, bool reconnect) - { - struct sk_buff *msg; - void *hdr; -@@ -15921,6 +15937,9 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, - nla_put(msg, NL80211_ATTR_REQ_IE, req_ies_len, req_ies))) - goto nla_put_failure; + if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { + struct wireless_dev *txp_wdev = wdev; + enum nl80211_tx_power_setting type; +@@ -5020,6 +5083,96 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, + return 0; + } -+ if (reconnect && nla_put_flag(msg, NL80211_ATTR_RECONNECT_REQUESTED)) -+ goto nla_put_failure; ++static int nl80211_parse_mbssid_config(struct wiphy *wiphy, ++ struct net_device *dev, ++ struct nlattr *attrs, ++ struct cfg80211_mbssid_config *config, ++ u8 num_elems) ++{ ++ struct nlattr *tb[NL80211_MBSSID_CONFIG_ATTR_MAX + 1]; + - if (uapsd_queues >= 0) { - struct nlattr *nla_wmm = - nla_nest_start_noflag(msg, NL80211_ATTR_STA_WME); -@@ -15949,7 +15968,8 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - size_t len, gfp_t gfp) - { - nl80211_send_mlme_event(rdev, netdev, buf, len, -- NL80211_CMD_AUTHENTICATE, gfp, -1, NULL, 0); -+ NL80211_CMD_AUTHENTICATE, gfp, -1, NULL, 0, -+ false); ++ if (!wiphy->mbssid_max_interfaces) ++ return -EOPNOTSUPP; ++ ++ if (nla_parse_nested(tb, NL80211_MBSSID_CONFIG_ATTR_MAX, attrs, NULL, ++ NULL) || ++ !tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]) ++ return -EINVAL; ++ ++ config->ema = nla_get_flag(tb[NL80211_MBSSID_CONFIG_ATTR_EMA]); ++ if (config->ema) { ++ if (!wiphy->ema_max_profile_periodicity) ++ return -EOPNOTSUPP; ++ ++ if (num_elems > wiphy->ema_max_profile_periodicity) ++ return -EINVAL; ++ } ++ ++ config->index = nla_get_u8(tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]); ++ if (config->index >= wiphy->mbssid_max_interfaces || ++ (!config->index && !num_elems)) ++ return -EINVAL; ++ ++ if (tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]) { ++ u32 tx_ifindex = ++ nla_get_u32(tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]); ++ ++ if ((!config->index && tx_ifindex != dev->ifindex) || ++ (config->index && tx_ifindex == dev->ifindex)) ++ return -EINVAL; ++ ++ if (tx_ifindex != dev->ifindex) { ++ struct net_device *tx_netdev = ++ dev_get_by_index(wiphy_net(wiphy), tx_ifindex); ++ ++ if (!tx_netdev || !tx_netdev->ieee80211_ptr || ++ tx_netdev->ieee80211_ptr->wiphy != wiphy || ++ tx_netdev->ieee80211_ptr->iftype != ++ NL80211_IFTYPE_AP) { ++ dev_put(tx_netdev); ++ return -EINVAL; ++ } ++ ++ config->tx_wdev = tx_netdev->ieee80211_ptr; ++ } else { ++ config->tx_wdev = dev->ieee80211_ptr; ++ } ++ } else if (!config->index) { ++ config->tx_wdev = dev->ieee80211_ptr; ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static struct cfg80211_mbssid_elems * ++nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) ++{ ++ struct nlattr *nl_elems; ++ struct cfg80211_mbssid_elems *elems; ++ int rem_elems; ++ u8 i = 0, num_elems = 0; ++ ++ if (!wiphy->mbssid_max_interfaces) ++ return ERR_PTR(-EINVAL); ++ ++ nla_for_each_nested(nl_elems, attrs, rem_elems) ++ num_elems++; ++ ++ elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL); ++ if (!elems) ++ return ERR_PTR(-ENOMEM); ++ ++ nla_for_each_nested(nl_elems, attrs, rem_elems) { ++ elems->elem[i].data = nla_data(nl_elems); ++ elems->elem[i].len = nla_len(nl_elems); ++ i++; ++ } ++ elems->cnt = num_elems; ++ return elems; ++} ++ + static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, + struct nlattr *attrs[], + struct cfg80211_beacon_data *bcn) +@@ -5100,6 +5253,17 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, + bcn->ftm_responder = -1; + } + ++ if (attrs[NL80211_ATTR_MBSSID_ELEMS]) { ++ struct cfg80211_mbssid_elems *mbssid = ++ nl80211_parse_mbssid_elems(&rdev->wiphy, ++ attrs[NL80211_ATTR_MBSSID_ELEMS]); ++ ++ if (IS_ERR(mbssid)) ++ return PTR_ERR(mbssid); ++ ++ bcn->mbssid_ies = mbssid; ++ } ++ + return 0; } - void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, -@@ -15959,23 +15979,25 @@ void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, - { - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_ASSOCIATE, gfp, uapsd_queues, -- req_ies, req_ies_len); -+ req_ies, req_ies_len, false); +@@ -5556,6 +5720,17 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) + goto out; + } + ++ if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) { ++ err = nl80211_parse_mbssid_config(&rdev->wiphy, dev, ++ info->attrs[NL80211_ATTR_MBSSID_CONFIG], ++ ¶ms.mbssid_config, ++ params.beacon.mbssid_ies ? ++ params.beacon.mbssid_ies->cnt : ++ 0); ++ if (err) ++ goto out; ++ } ++ + nl80211_calculate_ap_params(¶ms); + + if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT]) +@@ -5577,6 +5752,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) + + out: + kfree(params.acl); ++ kfree(params.beacon.mbssid_ies); ++ if (params.mbssid_config.tx_wdev && ++ params.mbssid_config.tx_wdev->netdev && ++ params.mbssid_config.tx_wdev->netdev != dev) ++ dev_put(params.mbssid_config.tx_wdev->netdev); + + return err; + } +@@ -5601,12 +5781,14 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) + + err = nl80211_parse_beacon(rdev, info->attrs, ¶ms); + if (err) +- return err; ++ goto out; + + wdev_lock(wdev); + err = rdev_change_beacon(rdev, dev, ¶ms); + wdev_unlock(wdev); + ++out: ++ kfree(params.mbssid_ies); + return err; } - void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, -- size_t len, gfp_t gfp) -+ size_t len, bool reconnect, gfp_t gfp) - { - nl80211_send_mlme_event(rdev, netdev, buf, len, -- NL80211_CMD_DEAUTHENTICATE, gfp, -1, NULL, 0); -+ NL80211_CMD_DEAUTHENTICATE, gfp, -1, NULL, 0, -+ reconnect); +@@ -9113,38 +9295,60 @@ 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; +- int err; ++ int err = -EINVAL; ++ ++ flush_delayed_work(&rdev->dfs_update_channels_wk); ++ ++ wiphy_lock(wiphy); + + dfs_region = reg_get_dfs_region(wiphy); + if (dfs_region == NL80211_DFS_UNSET) +- return -EINVAL; ++ goto unlock; + + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) +- return err; +- +- if (netif_carrier_ok(dev)) +- return -EBUSY; +- +- if (wdev->cac_started) +- return -EBUSY; ++ goto unlock; + + err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype); + if (err < 0) +- return err; ++ goto unlock; + +- if (err == 0) +- return -EINVAL; ++ if (err == 0) { ++ err = -EINVAL; ++ goto unlock; ++ } + +- if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) +- return -EINVAL; ++ if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) { ++ err = -EINVAL; ++ goto unlock; ++ } ++ ++ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_BACKGROUND])) { ++ err = cfg80211_start_background_radar_detection(rdev, wdev, ++ &chandef); ++ goto unlock; ++ } ++ ++ if (netif_carrier_ok(dev)) { ++ err = -EBUSY; ++ goto unlock; ++ } ++ ++ if (wdev->cac_started) { ++ err = -EBUSY; ++ goto unlock; ++ } + + /* CAC start is offloaded to HW and can't be started manually */ +- if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) +- return -EOPNOTSUPP; ++ if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) { ++ err = -EOPNOTSUPP; ++ goto unlock; ++ } + +- if (!rdev->ops->start_radar_detection) +- return -EOPNOTSUPP; ++ if (!rdev->ops->start_radar_detection) { ++ err = -EOPNOTSUPP; ++ goto unlock; ++ } + + cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef); + if (WARN_ON(!cac_time_ms)) +@@ -9157,6 +9361,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, + wdev->cac_start_time = jiffies; + wdev->cac_time_ms = cac_time_ms; + } ++unlock: ++ wiphy_unlock(wiphy); ++ + return err; } - void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, -- size_t len, gfp_t gfp) -+ size_t len, bool reconnect, gfp_t gfp) - { - nl80211_send_mlme_event(rdev, netdev, buf, len, -- NL80211_CMD_DISASSOCIATE, gfp, -1, NULL, 0); -+ NL80211_CMD_DISASSOCIATE, gfp, -1, NULL, 0, -+ reconnect); +@@ -9283,12 +9490,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) + + err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after); + if (err) +- return err; ++ goto free; + + csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs), + GFP_KERNEL); +- if (!csa_attrs) +- return -ENOMEM; ++ if (!csa_attrs) { ++ err = -ENOMEM; ++ goto free; ++ } + + err = nla_parse_nested_deprecated(csa_attrs, NL80211_ATTR_MAX, + info->attrs[NL80211_ATTR_CSA_IES], +@@ -9407,6 +9616,8 @@ skip_beacons: + wdev_unlock(wdev); + + free: ++ kfree(params.beacon_after.mbssid_ies); ++ kfree(params.beacon_csa.mbssid_ies); + kfree(csa_attrs); + return err; + } +@@ -14959,6 +15170,8 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) + wdev_unlock(wdev); + + out: ++ kfree(params.beacon_next.mbssid_ies); ++ kfree(params.beacon_color_change.mbssid_ies); + kfree(tb); + return err; + } +@@ -15786,7 +15999,8 @@ static const struct genl_small_ops nl80211_small_ops[] = { + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = nl80211_start_radar_detection, + .flags = GENL_UNS_ADMIN_PERM, +- .internal_flags = NL80211_FLAG_NEED_NETDEV_UP, ++ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | ++ 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 36422cf..8555468 100644 +--- a/net/wireless/rdev-ops.h ++++ b/net/wireless/rdev-ops.h +@@ -1381,4 +1381,21 @@ static inline int rdev_color_change(struct cfg80211_registered_device *rdev, + return ret; } - void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, -@@ -16006,7 +16028,7 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, - - trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len); - nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1, -- NULL, 0); -+ NULL, 0, false); - } - EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); - -diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h -index d3e8e42..a3f3877 100644 ---- a/net/wireless/nl80211.h -+++ b/net/wireless/nl80211.h -@@ -1,7 +1,7 @@ - /* SPDX-License-Identifier: GPL-2.0 */ - /* - * Portions of this file -- * Copyright (C) 2018 Intel Corporation -+ * Copyright (C) 2018, 2020 Intel Corporation - */ - #ifndef __NET_WIRELESS_NL80211_H - #define __NET_WIRELESS_NL80211_H -@@ -69,10 +69,12 @@ void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, - const u8 *req_ies, size_t req_ies_len); - void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, -- const u8 *buf, size_t len, gfp_t gfp); -+ const u8 *buf, size_t len, -+ bool reconnect, gfp_t gfp); - void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, -- const u8 *buf, size_t len, gfp_t gfp); -+ const u8 *buf, size_t len, -+ bool reconnect, gfp_t gfp); - void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr, gfp_t gfp); ++static inline int ++rdev_set_radar_background(struct cfg80211_registered_device *rdev, ++ struct cfg80211_chan_def *chandef) ++{ ++ struct wiphy *wiphy = &rdev->wiphy; ++ int ret; ++ ++ if (!rdev->ops->set_radar_background) ++ return -EOPNOTSUPP; ++ ++ trace_rdev_set_radar_background(wiphy, chandef); ++ ret = rdev->ops->set_radar_background(wiphy, chandef); ++ trace_rdev_return_int(wiphy, ret); ++ ++ return ret; ++} ++ + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c -index 93d663c..bd4227b 100644 +index 0c3f05c..30c0dcd 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c -@@ -23,18 +23,35 @@ static inline struct cfg80211_registered_device *dev_to_rdev( +@@ -24,18 +24,35 @@ static inline struct cfg80211_registered_device *dev_to_rdev( return container_of(dev, struct cfg80211_registered_device, wiphy.dev); } @@ -6042,34 +3952,58 @@ index 93d663c..bd4227b 100644 static ssize_t name_show(struct device *dev, struct device_attribute *attr, diff --git a/net/wireless/trace.h b/net/wireless/trace.h -index 0a29cd1..e838ccc 100644 +index 973ce68..97a2937 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h -@@ -2684,19 +2684,23 @@ DEFINE_EVENT(netdev_frame_event, cfg80211_rx_mlme_mgmt, +@@ -3022,18 +3022,21 @@ TRACE_EVENT(cfg80211_ch_switch_started_notify, ); - TRACE_EVENT(cfg80211_tx_mlme_mgmt, -- TP_PROTO(struct net_device *netdev, const u8 *buf, int len), -- TP_ARGS(netdev, buf, len), -+ TP_PROTO(struct net_device *netdev, const u8 *buf, int len, -+ bool reconnect), -+ TP_ARGS(netdev, buf, len, reconnect), + TRACE_EVENT(cfg80211_radar_event, +- TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), +- TP_ARGS(wiphy, chandef), ++ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, ++ bool offchan), ++ TP_ARGS(wiphy, chandef, offchan), TP_STRUCT__entry( - NETDEV_ENTRY - __dynamic_array(u8, frame, len) -+ __field(int, reconnect) + WIPHY_ENTRY + CHAN_DEF_ENTRY ++ __field(bool, offchan) ), TP_fast_assign( - NETDEV_ASSIGN; - memcpy(__get_dynamic_array(frame), buf, len); -+ __entry->reconnect = reconnect; + WIPHY_ASSIGN; + CHAN_DEF_ASSIGN(chandef); ++ __entry->offchan = offchan; ), -- TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x", -+ TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x reconnect:%d", - NETDEV_PR_ARG, -- le16_to_cpup((__le16 *)__get_dynamic_array(frame))) -+ le16_to_cpup((__le16 *)__get_dynamic_array(frame)), -+ __entry->reconnect) +- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, +- WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ++ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", offchan %d", ++ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->offchan) ); - DECLARE_EVENT_CLASS(netdev_mac_evt, + TRACE_EVENT(cfg80211_cac_event, +@@ -3643,6 +3646,25 @@ TRACE_EVENT(cfg80211_bss_color_notify, + __entry->color_bitmap) + ); + ++TRACE_EVENT(rdev_set_radar_background, ++ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), ++ ++ TP_ARGS(wiphy, chandef), ++ ++ TP_STRUCT__entry( ++ WIPHY_ENTRY ++ CHAN_DEF_ENTRY ++ ), ++ ++ TP_fast_assign( ++ WIPHY_ASSIGN; ++ CHAN_DEF_ASSIGN(chandef) ++ ), ++ ++ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, ++ WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ++); ++ + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ + + #undef TRACE_INCLUDE_PATH diff --git a/recipes-kernel/mac80211/mac80211/0003-backport-of-ath-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0003-backport-of-ath-patches-from-openwrt.patch index 57376e3..b3603da 100644 --- a/recipes-kernel/mac80211/mac80211/0003-backport-of-ath-patches-from-openwrt.patch +++ b/recipes-kernel/mac80211/mac80211/0003-backport-of-ath-patches-from-openwrt.patch @@ -1,73 +1,21 @@ -From ac9c73283fd0b8464a1b29d2e8e29a94a1f53fff Mon Sep 17 00:00:00 2001 +From 963c2ed48698063d6b684cabbf31b9c07cab9fd8 Mon Sep 17 00:00:00 2001 From: Patrick Walther -Date: Mon, 22 Mar 2021 16:37:02 +0100 -Subject: [PATCH] 0003 backport of ath patches from openwrt +Date: Wed, 14 Sep 2022 14:27:59 +0200 +Subject: [PATCH] backport of ath patches from openwrt --- - drivers/net/wireless/ath/Kconfig | 5 +- - drivers/net/wireless/ath/Makefile | 2 +- - drivers/net/wireless/ath/ath.h | 8 +- - drivers/net/wireless/ath/ath10k/Kconfig | 16 + - drivers/net/wireless/ath/ath10k/Makefile | 3 +- - drivers/net/wireless/ath/ath10k/core.c | 31 ++ - drivers/net/wireless/ath/ath10k/core.h | 12 + - drivers/net/wireless/ath/ath10k/htt.h | 2 +- - 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 | 65 +++- - 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 ++ - 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 | 28 +- - drivers/net/wireless/ath/ath5k/reset.c | 2 + - drivers/net/wireless/ath/ath9k/Kconfig | 13 + - drivers/net/wireless/ath/ath9k/Makefile | 1 + - drivers/net/wireless/ath/ath9k/ahb.c | 266 ++++++++++++++- - 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 | 37 ++- - .../net/wireless/ath/ath9k/ath9k_pci_owl_loader.c | 17 + - drivers/net/wireless/ath/ath9k/channel.c | 7 + - drivers/net/wireless/ath/ath9k/common.c | 21 +- - drivers/net/wireless/ath/ath9k/debug.c | 202 ++++++++++++ - drivers/net/wireless/ath/ath9k/gpio.c | 365 +++++++++++++++++++-- - drivers/net/wireless/ath/ath9k/hsr.c | 247 ++++++++++++++ - drivers/net/wireless/ath/ath9k/hsr.h | 48 +++ - 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 | 53 ++- - 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 + - drivers/net/wireless/ath/ath9k/xmit.c | 6 + - drivers/net/wireless/ath/regd.c | 72 ++-- - drivers/net/wireless/ath/regd_common.h | 3 + - include/linux/ath5k_platform.h | 30 ++ - include/linux/ath9k_platform.h | 7 + - local-symbols | 4 + - net/wireless/reg.c | 3 + - 55 files changed, 2082 insertions(+), 264 deletions(-) - create mode 100644 drivers/net/wireless/ath/ath10k/leds.c - create mode 100644 drivers/net/wireless/ath/ath10k/leds.h - create mode 100644 drivers/net/wireless/ath/ath9k/hsr.c - create mode 100644 drivers/net/wireless/ath/ath9k/hsr.h - create mode 100644 include/linux/ath5k_platform.h + drivers/net/wireless/ath/Kconfig | 5 +- + drivers/net/wireless/ath/Makefile | 2 +- + drivers/net/wireless/ath/ath.h | 7 --- + drivers/net/wireless/ath/ath5k/pci.c | 26 +++++++++- + drivers/net/wireless/ath/regd.c | 72 ++++++++++++++++++-------- + drivers/net/wireless/ath/regd_common.h | 3 ++ + local-symbols | 1 + + net/wireless/reg.c | 3 ++ + 8 files changed, 88 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig -index 26ed519..aa2b633 100644 +index 0a2f4a5..3cb2efe 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,6 +1,6 @@ @@ -105,18 +53,10 @@ index 7fa5fa1..d154c96 100644 CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h -index a547b2d..2a18e2e 100644 +index 8b2ef78..cfe535a 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h -@@ -149,6 +149,7 @@ struct ath_common { - int debug_mask; - enum ath_device_state state; - unsigned long op_flags; -+ u32 chan_bw; - - struct ath_ani ani; - -@@ -316,14 +317,7 @@ void _ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask, +@@ -317,14 +317,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. */ @@ -131,946 +71,8 @@ index a547b2d..2a18e2e 100644 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/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig -index db70f7f..e360751 100644 ---- a/drivers/net/wireless/ath/ath10k/Kconfig -+++ b/drivers/net/wireless/ath/ath10k/Kconfig -@@ -70,6 +70,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 -@@ -86,6 +96,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 d73ad60..9c57698 100644 ---- a/drivers/net/wireless/ath/ath10k/core.c -+++ b/drivers/net/wireless/ath/ath10k/core.c -@@ -25,6 +25,7 @@ - #include "testmode.h" - #include "wmi-ops.h" - #include "coredump.h" -+#include "leds.h" - - unsigned int ath10k_debug_mask; - EXPORT_SYMBOL(ath10k_debug_mask); -@@ -61,6 +62,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, -@@ -130,6 +132,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, -@@ -335,6 +338,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, -@@ -375,6 +379,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, -@@ -422,6 +427,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, -@@ -2904,6 +2910,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: -@@ -3162,9 +3172,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: -@@ -3189,6 +3208,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); -@@ -3200,6 +3229,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 d8701c2..507c902 100644 ---- a/drivers/net/wireless/ath/ath10k/core.h -+++ b/drivers/net/wireless/ath/ath10k/core.h -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - - #include "htt.h" - #include "htc.h" -@@ -1237,6 +1238,13 @@ struct ath10k { - } 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; - u32 fw_crash_counter; -@@ -1282,6 +1290,10 @@ struct ath10k { - bool coex_support; - int coex_gpio_pin; - -+#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 5df6379..c2a5825 100644 ---- a/drivers/net/wireless/ath/ath10k/htt.h -+++ b/drivers/net/wireless/ath/ath10k/htt.h -@@ -2242,7 +2242,7 @@ struct htt_rx_chan_info { - * Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size, - * rounded up to a cache line size. - */ --#define HTT_RX_BUF_SIZE 1920 -+#define HTT_RX_BUF_SIZE 2048 - #define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) - - /* Refill a bunch of RX buffers for each refill round so that FW/HW can handle -diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h -index c78090e..5bb900c 100644 ---- a/drivers/net/wireless/ath/ath10k/hw.h -+++ b/drivers/net/wireless/ath/ath10k/hw.h -@@ -517,6 +517,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 -+ * 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 -+ -+#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 30dc036..d07a4a1 100644 ---- a/drivers/net/wireless/ath/ath10k/mac.c -+++ b/drivers/net/wireless/ath/ath10k/mac.c -@@ -24,6 +24,7 @@ - #include "wmi-tlv.h" - #include "wmi-ops.h" - #include "wow.h" -+#include "leds.h" - - /*********/ - /* Rates */ -@@ -1005,6 +1006,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; -@@ -1037,7 +1072,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 * 2; -+ 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); -@@ -1483,7 +1519,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 * 2; -+ 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; -@@ -3254,7 +3291,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 * 2; -+ 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 -@@ -9713,6 +9751,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[] = { -@@ -10062,6 +10115,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/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h -index 5fdb020..d76c307 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 7b58341..802d443 100644 ---- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c -+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c -@@ -4585,6 +4585,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 b2f3db5..b4506ed 100644 ---- a/drivers/net/wireless/ath/ath10k/wmi.c -+++ b/drivers/net/wireless/ath/ath10k/wmi.c -@@ -7468,6 +7468,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) -@@ -9156,6 +9199,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 */ -@@ -9226,6 +9272,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 */ -@@ -9298,6 +9346,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 */ - }; - -@@ -9369,6 +9419,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 */ -@@ -9450,6 +9502,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 66ecf09..41796e3 100644 ---- a/drivers/net/wireless/ath/ath10k/wmi.h -+++ b/drivers/net/wireless/ath/ath10k/wmi.h -@@ -3027,6 +3027,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/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h -index 0433631..cff4f6f 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 0575ca7..7b70bc1 100644 ---- a/drivers/net/wireless/ath/ath5k/base.c -+++ b/drivers/net/wireless/ath/ath5k/base.c -@@ -466,6 +466,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 -@@ -1964,7 +1967,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); -@@ -2050,7 +2053,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", -@@ -2516,6 +2519,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 4b41160..257101e 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 */ - -@@ -997,6 +1088,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 e6c52f7..53e075e 100644 ---- a/drivers/net/wireless/ath/ath5k/dma.c -+++ b/drivers/net/wireless/ath/ath5k/dma.c -@@ -869,10 +869,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 5e866a1..47dd580 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 43b4ae8..9c0a2e2 100644 +index 86b8cb9..6c724fa 100644 --- a/drivers/net/wireless/ath/ath5k/pci.c +++ b/drivers/net/wireless/ath/ath5k/pci.c @@ -20,6 +20,7 @@ @@ -1081,16 +83,7 @@ index 43b4ae8..9c0a2e2 100644 #include "../ath.h" #include "ath5k.h" #include "debug.h" -@@ -46,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 } - }; -@@ -71,7 +74,7 @@ static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz) +@@ -71,7 +72,7 @@ static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz) } /* @@ -1099,7 +92,7 @@ index 43b4ae8..9c0a2e2 100644 */ static bool ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data) -@@ -79,6 +82,19 @@ 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 = (struct ath5k_hw *) common->ah; u32 status, timeout; @@ -1119,7 +112,7 @@ index 43b4ae8..9c0a2e2 100644 /* * Initialize EEPROM access */ -@@ -122,6 +138,16 @@ static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) +@@ -122,6 +136,16 @@ static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) u16 data; int octet; @@ -1136,2447 +129,8 @@ index 43b4ae8..9c0a2e2 100644 AR5K_EEPROM_READ(0x20, data); for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { -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/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig -index 491c54a..01e7f0d 100644 ---- a/drivers/net/wireless/ath/ath9k/Kconfig -+++ b/drivers/net/wireless/ath/ath9k/Kconfig -@@ -60,6 +60,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 -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 cdefb8e..762de53 100644 ---- a/drivers/net/wireless/ath/ath9k/ahb.c -+++ b/drivers/net/wireless/ath/ath9k/ahb.c -@@ -20,7 +20,15 @@ - #include - #include - #include -+#include - #include "ath9k.h" -+#include -+ -+#ifdef CONFIG_OF -+#include -+#include -+#include -+#endif - - static const struct platform_device_id ath9k_platform_id_table[] = { - { -@@ -69,6 +77,242 @@ 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,disable-2ghz")) -+ pdata->disable_2ghz = true; -+ -+ if (of_property_read_bool(pdev->dev.of_node, "qca,disable-5ghz")) -+ pdata->disable_5ghz = true; -+ -+ 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 +324,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"); -@@ -122,13 +377,16 @@ 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; -@@ -159,6 +417,9 @@ static int ath_ahb_remove(struct platform_device *pdev) - free_irq(sc->irq, sc); - ieee80211_free_hw(sc->hw); - } -+#ifdef CONFIG_OF -+ pdev->dev.platform_data = NULL; -+#endif - - return 0; - } -@@ -168,6 +429,9 @@ static struct platform_driver ath_ahb_driver = { - .remove = 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 2fa3083..38750de 100644 ---- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c -+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c -@@ -978,55 +978,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, -@@ -1349,9 +1300,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, -@@ -1366,6 +1338,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 7ec5610..6f1a46d 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, -@@ -1077,11 +1063,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) { -@@ -1095,61 +1076,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); -@@ -1927,6 +1853,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, 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_TEST_CTL_STATUS, 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); -@@ -1963,6 +1909,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 ff591e2..b24ea28 100644 ---- a/drivers/net/wireless/ath/ath9k/ath9k.h -+++ b/drivers/net/wireless/ath/ath9k/ath9k.h -@@ -24,6 +24,8 @@ - #include - #include - #include -+#include -+#include - - #include "common.h" - #include "debug.h" -@@ -88,7 +90,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 -@@ -177,7 +179,8 @@ struct ath_frame_info { - s8 txq; - u8 keyix; - u8 rtscts_rate; -- u8 retries : 7; -+ u8 retries : 6; -+ u8 dyn_smps : 1; - u8 baw_tracked : 1; - u8 tx_power; - enum ath9k_key_type keytype:2; -@@ -843,6 +846,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) - { -@@ -979,6 +985,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; -@@ -992,6 +1013,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; -@@ -1032,9 +1056,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/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c -index 56d1a77..8160e72 100644 ---- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c -+++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c -@@ -103,6 +103,7 @@ static void owl_fw_cb(const struct firmware *fw, void *context) - { - struct pci_dev *pdev = (struct pci_dev *)context; - struct owl_ctx *ctx = (struct owl_ctx *)pci_get_drvdata(pdev); -+ struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct pci_bus *bus; - - complete(&ctx->eeprom_load); -@@ -118,6 +119,16 @@ static void owl_fw_cb(const struct firmware *fw, void *context) - goto release; - } - -+ if (pdata) { -+ memcpy(pdata->eeprom_data, fw->data, fw->size); -+ -+ /* -+ * eeprom has been successfully loaded - pass the data to ath9k -+ * but remove the eeprom_name, so it doesn't try to load it too. -+ */ -+ pdata->eeprom_name = NULL; -+ } -+ - if (ath9k_pci_fixup(pdev, (const u16 *)fw->data, fw->size)) - goto release; - -@@ -137,8 +148,14 @@ release: - static const char *owl_get_eeprom_name(struct pci_dev *pdev) - { - struct device *dev = &pdev->dev; -+ struct ath9k_platform_data *pdata; - char *eeprom_name; - -+ /* try the existing platform data first */ -+ pdata = dev_get_platdata(dev); -+ if (pdata && pdata->eeprom_name) -+ return pdata->eeprom_name; -+ - dev_dbg(dev, "using auto-generated eeprom filename\n"); - - eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL); -diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c -index 1a926fc..a37f270 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.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 c43822a..391b046 100644 ---- a/drivers/net/wireless/ath/ath9k/debug.c -+++ b/drivers/net/wireless/ath/ath9k/debug.c -@@ -1361,6 +1361,198 @@ void ath9k_deinit_debug(struct ath_softc *sc) - ath9k_cmn_spectral_deinit_debug(&sc->spec_priv); - } - -+static ssize_t read_file_eeprom(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; -+ 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; -+ } -+ copy_to_user(user_buf, from, bytes); -+ user_buf += bytes; -+ } -+ return *ppos - pos; -+} -+ -+static const struct file_operations fops_eeprom = { -+ .read = read_file_eeprom, -+ .open = simple_open, -+ .owner = THIS_MODULE -+}; -+ -+ -+static ssize_t read_file_chan_bw(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ struct ath_softc *sc = file->private_data; -+ struct ath_common *common = ath9k_hw_common(sc->sc_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_softc *sc = file->private_data; -+ struct ath_common *common = ath9k_hw_common(sc->sc_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)) -+ ath9k_ops.config(sc->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, -+}; -+ -+#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_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); -@@ -1380,6 +1572,16 @@ 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); - -+ debugfs_create_file("eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, -+ &fops_eeprom); -+ debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, -+ sc, &fops_chanbw); -+#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, -diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c -index a8101c9..37a796b 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 -+#include -+#include -+#include -+ -+#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) >> (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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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/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 7caa55e..eb57558 100644 ---- a/drivers/net/wireless/ath/ath9k/hw.c -+++ b/drivers/net/wireless/ath/ath9k/hw.c -@@ -248,6 +248,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 */ - /******************/ -@@ -403,13 +416,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; -@@ -668,6 +676,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: -@@ -1312,39 +1321,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, 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, 0); -+ udelay(10); -+ } -+ -+ REG_WRITE(ah, AR_RTC_RESET, 1); -+ udelay(10); -+ - return true; - } - -@@ -1397,24 +1423,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, rst_flags); - - REGWRITE_BUFFER_FLUSH(ah); -@@ -1435,8 +1461,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; - } -@@ -1537,6 +1570,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; - } - -@@ -1844,8 +1880,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; - } - -@@ -1866,6 +1908,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) - { -@@ -2074,6 +2130,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); -@@ -2084,6 +2141,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 d65c4c4..559609d 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; - -@@ -1075,6 +1086,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 f47c4ce..3162fc2 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"); - -@@ -627,6 +627,12 @@ static int ath9k_of_init(struct ath_softc *sc) - - ath_dbg(common, CONFIG, "parsing configuration from OF node\n"); - -+ if (of_property_read_bool(np, "qca,disable-2ghz")) -+ ah->disable_2ghz = true; -+ -+ if (of_property_read_bool(np, "qca,disable-5ghz")) -+ ah->disable_5ghz = true; -+ - if (of_property_read_bool(np, "qca,no-eeprom")) { - /* ath9k-eeprom--.bin */ - scnprintf(eeprom_name, sizeof(eeprom_name), -@@ -648,6 +654,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) - { -@@ -751,6 +763,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; -@@ -818,7 +833,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[] = { -@@ -830,6 +846,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_WIRELESS_WDS -@@ -926,6 +943,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); -@@ -938,9 +956,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; -@@ -1016,6 +1031,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) - { -@@ -1056,11 +1083,13 @@ 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 - -+ ath_get_initial_entropy(sc); -+ - /* Register with mac80211 */ - error = ieee80211_register_hw(hw); - if (error) -@@ -1144,25 +1173,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 58d02c1..c9d2bf3 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 7f2a633..093eafd 100644 ---- a/drivers/net/wireless/ath/ath9k/main.c -+++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -18,6 +18,7 @@ - #include - #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); -@@ -531,6 +532,11 @@ irqreturn_t ath_isr(int irq, void *dev) - if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) - 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). -@@ -647,6 +653,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; -@@ -725,6 +732,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 c7826aa..23c06bf 100644 ---- a/drivers/net/wireless/ath/ath9k/pci.c -+++ b/drivers/net/wireless/ath/ath9k/pci.c -@@ -774,6 +774,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/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c -index 2c97786..6776da5 100644 ---- a/drivers/net/wireless/ath/ath9k/xmit.c -+++ b/drivers/net/wireless/ath/ath9k/xmit.c -@@ -1271,6 +1271,11 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, - is_40, is_sgi, is_sp); - if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC)) - info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC; -+ if (rix >= 8 && fi->dyn_smps) { -+ info->rates[i].RateFlags |= -+ ATH9K_RATESERIES_RTS_CTS; -+ info->flags |= ATH9K_TXDESC_CTSENA; -+ } - - info->txpower[i] = ath_get_rate_txpower(sc, bf, rix, - is_40, false); -@@ -2114,6 +2119,7 @@ static void setup_frame_info(struct ieee80211_hw *hw, - fi->keyix = an->ps_key; - else - fi->keyix = ATH9K_TXKEYIX_INVALID; -+ fi->dyn_smps = sta && sta->smps_mode == IEEE80211_SMPS_DYNAMIC; - fi->keytype = keytype; - fi->framelen = framelen; - fi->tx_power = txpower; diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c -index f263349..0042ea9 100644 +index 2afdebf..3ba9fc3 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -24,6 +24,7 @@ @@ -3723,7 +277,7 @@ index f263349..0042ea9 100644 for (band = 0; band < NUM_NL80211_BANDS; band++) { if (!wiphy->bands[band]) continue; -@@ -378,6 +395,9 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy, +@@ -379,6 +396,9 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy, { struct ieee80211_supported_band *sband; @@ -3733,7 +287,7 @@ index f263349..0042ea9 100644 sband = wiphy->bands[NL80211_BAND_2GHZ]; if (!sband) return; -@@ -407,6 +427,9 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy, +@@ -408,6 +428,9 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy, struct ieee80211_channel *ch; unsigned int i; @@ -3743,7 +297,7 @@ index f263349..0042ea9 100644 if (!wiphy->bands[NL80211_BAND_5GHZ]) return; -@@ -639,6 +662,13 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, +@@ -640,6 +663,13 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, const struct ieee80211_regdomain *regd; wiphy->reg_notifier = reg_notifier; @@ -3785,65 +339,11 @@ index c4bd26e..364011e 100644 {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/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 -+ * Copyright (c) 2009 Imre Kaloz -+ * Copyright (c) 2010 Daniel Golle -+ * -+ * 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 */ -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 d5a2bfd..7565b2b 100644 +index 354f8eb..9af5dbe 100644 --- a/local-symbols +++ b/local-symbols -@@ -85,6 +85,7 @@ ADM8211= +@@ -106,6 +106,7 @@ ADM8211= ATH_COMMON= WLAN_VENDOR_ATH= ATH_DEBUG= @@ -3851,28 +351,11 @@ index d5a2bfd..7565b2b 100644 ATH_TRACEPOINTS= ATH_REG_DYNAMIC_USER_REG_HINTS= ATH_REG_DYNAMIC_USER_CERT_TESTING= -@@ -111,6 +112,7 @@ ATH9K_WOW= - ATH9K_RFKILL= - ATH9K_CHANNEL_CONTEXT= - ATH9K_PCOEM= -+ATH9K_UBNTHSR= - ATH9K_PCI_NO_EEPROM= - ATH9K_HTC= - ATH9K_HTC_DEBUGFS= -@@ -142,6 +144,8 @@ ATH10K_SNOC= - ATH10K_DEBUG= - ATH10K_DEBUGFS= - ATH10K_SPECTRAL= -+ATH10K_THERMAL= -+ATH10K_LEDS= - ATH10K_TRACING= - ATH10K_DFS_CERTIFIED= - WCN36XX= diff --git a/net/wireless/reg.c b/net/wireless/reg.c -index 0e28e68..b6caa2b 100644 +index 8f96b31..48ab1bb 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c -@@ -3252,6 +3252,8 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band, +@@ -3309,6 +3309,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; @@ -3881,7 +364,7 @@ index 0e28e68..b6caa2b 100644 /* IE len must be evenly divisible by 2 */ if (country_ie_len & 0x01) return; -@@ -3503,6 +3505,7 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag) +@@ -3560,6 +3562,7 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag) void regulatory_hint_disconnect(void) { diff --git a/recipes-kernel/mac80211/mac80211/0004-backport-of-ath5k-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0004-backport-of-ath5k-patches-from-openwrt.patch new file mode 100644 index 0000000..1df52d1 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0004-backport-of-ath5k-patches-from-openwrt.patch @@ -0,0 +1,312 @@ +From adec6feb0823e369ccb1a5aff5ffc148fd22514b Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 14 Sep 2022 14:28:52 +0200 +Subject: [PATCH] backport of ath5k patches from openwrt + +--- + 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 0433631..cff4f6f 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 54fdf8d..74f3591 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 +@@ -1963,7 +1966,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); +@@ -2049,7 +2052,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", +@@ -2515,6 +2518,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 4b41160..257101e 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 */ + +@@ -997,6 +1088,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 e6c52f7..53e075e 100644 +--- a/drivers/net/wireless/ath/ath5k/dma.c ++++ b/drivers/net/wireless/ath/ath5k/dma.c +@@ -869,10 +869,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 532eeac..c40a08f 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 6c724fa..6c993af 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 ++ * Copyright (c) 2009 Imre Kaloz ++ * Copyright (c) 2010 Daniel Golle ++ * ++ * 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 */ diff --git a/recipes-kernel/mac80211/mac80211/0005-backport-of-ath9k-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0005-backport-of-ath9k-patches-from-openwrt.patch new file mode 100644 index 0000000..95da7e3 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0005-backport-of-ath9k-patches-from-openwrt.patch @@ -0,0 +1,2683 @@ +From 9899eb509a1f4a270d6b6fbf9e2c86e0012a137a Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 14 Sep 2022 14:29:42 +0200 +Subject: [PATCH] backport of ath9k patches from openwrt + +--- + drivers/net/wireless/ath/ath.h | 1 + + 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 +- + .../wireless/ath/ath9k/ath9k_pci_owl_loader.c | 105 +++-- + drivers/net/wireless/ath/ath9k/channel.c | 7 + + drivers/net/wireless/ath/ath9k/common.c | 21 +- + drivers/net/wireless/ath/ath9k/debug.c | 202 ++++++++++ + drivers/net/wireless/ath/ath9k/eeprom.c | 12 +- + drivers/net/wireless/ath/ath9k/gpio.c | 365 ++++++++++++++++-- + drivers/net/wireless/ath/ath9k/hsr.c | 247 ++++++++++++ + drivers/net/wireless/ath/ath9k/hsr.h | 48 +++ + drivers/net/wireless/ath/ath9k/hw-ops.h | 6 + + drivers/net/wireless/ath/ath9k/hw.c | 154 ++++++-- + drivers/net/wireless/ath/ath9k/hw.h | 14 + + drivers/net/wireless/ath/ath9k/init.c | 105 ++++- + 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 + + 27 files changed, 1561 insertions(+), 247 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 cfe535a..bb30fd7 100644 +--- a/drivers/net/wireless/ath/ath.h ++++ b/drivers/net/wireless/ath/ath.h +@@ -149,6 +149,7 @@ struct ath_common { + int debug_mask; + enum ath_device_state state; + unsigned long op_flags; ++ u32 chan_bw; + + struct ath_ani ani; + +diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig +index eed0f3a..d2ea086 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 +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 cdefb8e..839d780 100644 +--- a/drivers/net/wireless/ath/ath9k/ahb.c ++++ b/drivers/net/wireless/ath/ath9k/ahb.c +@@ -20,7 +20,15 @@ + #include + #include + #include ++#include + #include "ath9k.h" ++#include ++ ++#ifdef CONFIG_OF ++#include ++#include ++#include ++#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"); +@@ -122,13 +371,16 @@ 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; +@@ -159,6 +411,9 @@ static int ath_ahb_remove(struct platform_device *pdev) + free_irq(sc->irq, sc); + ieee80211_free_hw(sc->hw); + } ++#ifdef CONFIG_OF ++ pdev->dev.platform_data = NULL; ++#endif + + return 0; + } +@@ -168,6 +423,9 @@ static struct platform_driver ath_ahb_driver = { + .remove = 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 6610d76..ab6d7f7 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 7ec5610..6f1a46d 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, +@@ -1077,11 +1063,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) { +@@ -1095,61 +1076,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); +@@ -1927,6 +1853,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, 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_TEST_CTL_STATUS, 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); +@@ -1963,6 +1909,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 ebffd4b..3b6c037 100644 +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -24,6 +24,8 @@ + #include + #include + #include ++#include ++#include + + #include "common.h" + #include "debug.h" +@@ -88,7 +90,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 +@@ -843,6 +845,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) + { +@@ -979,6 +984,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; +@@ -992,6 +1012,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; +@@ -1032,9 +1055,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/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c +index 56d1a77..708c896 100644 +--- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c ++++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c +@@ -19,9 +19,14 @@ + #include + #include + #include ++#include ++#include + + struct owl_ctx { ++ struct pci_dev *pdev; + struct completion eeprom_load; ++ struct work_struct work; ++ struct nvmem_cell *cell; + }; + + #define EEPROM_FILENAME_LEN 100 +@@ -42,6 +47,12 @@ static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data, + u32 bar0; + bool swap_needed = false; + ++ /* also note that we are doing *u16 operations on the file */ ++ if (cal_len > 4096 || cal_len < 0x200 || (cal_len & 1) == 1) { ++ dev_err(&pdev->dev, "eeprom has an invalid size.\n"); ++ return -EINVAL; ++ } ++ + if (*cal_data != AR5416_EEPROM_MAGIC) { + if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) { + dev_err(&pdev->dev, "invalid calibration data\n"); +@@ -99,38 +110,31 @@ static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data, + return 0; + } + +-static void owl_fw_cb(const struct firmware *fw, void *context) ++static void owl_rescan(struct pci_dev *pdev) + { +- struct pci_dev *pdev = (struct pci_dev *)context; +- struct owl_ctx *ctx = (struct owl_ctx *)pci_get_drvdata(pdev); +- struct pci_bus *bus; +- +- complete(&ctx->eeprom_load); +- +- if (!fw) { +- dev_err(&pdev->dev, "no eeprom data received.\n"); +- goto release; +- } +- +- /* also note that we are doing *u16 operations on the file */ +- if (fw->size > 4096 || fw->size < 0x200 || (fw->size & 1) == 1) { +- dev_err(&pdev->dev, "eeprom file has an invalid size.\n"); +- goto release; +- } +- +- if (ath9k_pci_fixup(pdev, (const u16 *)fw->data, fw->size)) +- goto release; ++ struct pci_bus *bus = pdev->bus; + + pci_lock_rescan_remove(); +- bus = pdev->bus; + pci_stop_and_remove_bus_device(pdev); + /* the device should come back with the proper + * ProductId. But we have to initiate a rescan. + */ + pci_rescan_bus(bus); + pci_unlock_rescan_remove(); ++} ++ ++static void owl_fw_cb(const struct firmware *fw, void *context) ++{ ++ struct owl_ctx *ctx = (struct owl_ctx *)context; ++ ++ complete(&ctx->eeprom_load); + +-release: ++ if (fw) { ++ ath9k_pci_fixup(ctx->pdev, (const u16 *)fw->data, fw->size); ++ owl_rescan(ctx->pdev); ++ } else { ++ dev_err(&ctx->pdev->dev, "no eeprom data received.\n"); ++ } + release_firmware(fw); + } + +@@ -152,6 +156,43 @@ static const char *owl_get_eeprom_name(struct pci_dev *pdev) + return eeprom_name; + } + ++static void owl_nvmem_work(struct work_struct *work) ++{ ++ struct owl_ctx *ctx = container_of(work, struct owl_ctx, work); ++ void *buf; ++ size_t len; ++ ++ complete(&ctx->eeprom_load); ++ ++ buf = nvmem_cell_read(ctx->cell, &len); ++ if (!IS_ERR(buf)) { ++ ath9k_pci_fixup(ctx->pdev, buf, len); ++ kfree(buf); ++ owl_rescan(ctx->pdev); ++ } else { ++ dev_err(&ctx->pdev->dev, "no nvmem data received.\n"); ++ } ++} ++ ++static int owl_nvmem_probe(struct owl_ctx *ctx) ++{ ++ int err; ++ ++ ctx->cell = devm_nvmem_cell_get(&ctx->pdev->dev, "calibration"); ++ if (IS_ERR(ctx->cell)) { ++ err = PTR_ERR(ctx->cell); ++ if (err == -ENOENT || err == -EOPNOTSUPP) ++ return 1; /* not present, try firmware_request */ ++ ++ return err; ++ } ++ ++ INIT_WORK(&ctx->work, owl_nvmem_work); ++ schedule_work(&ctx->work); ++ ++ return 0; ++} ++ + static int owl_probe(struct pci_dev *pdev, + const struct pci_device_id *id) + { +@@ -164,21 +205,27 @@ static int owl_probe(struct pci_dev *pdev, + + pcim_pin_device(pdev); + +- eeprom_name = owl_get_eeprom_name(pdev); +- if (!eeprom_name) { +- dev_err(&pdev->dev, "no eeprom filename found.\n"); +- return -ENODEV; +- } +- + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + init_completion(&ctx->eeprom_load); ++ ctx->pdev = pdev; + + pci_set_drvdata(pdev, ctx); ++ ++ err = owl_nvmem_probe(ctx); ++ if (err <= 0) ++ return err; ++ ++ eeprom_name = owl_get_eeprom_name(pdev); ++ if (!eeprom_name) { ++ dev_err(&pdev->dev, "no eeprom filename found.\n"); ++ return -ENODEV; ++ } ++ + err = request_firmware_nowait(THIS_MODULE, true, eeprom_name, +- &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb); ++ &pdev->dev, GFP_KERNEL, ctx, owl_fw_cb); + if (err) + dev_err(&pdev->dev, "failed to request caldata (%d).\n", err); + +diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c +index 1a926fc..a37f270 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.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 75395f3..490574c 100644 +--- a/drivers/net/wireless/ath/ath9k/debug.c ++++ b/drivers/net/wireless/ath/ath9k/debug.c +@@ -1364,6 +1364,198 @@ void ath9k_deinit_debug(struct ath_softc *sc) + ath9k_cmn_spectral_deinit_debug(&sc->spec_priv); + } + ++static ssize_t read_file_eeprom(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; ++ 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; ++ } ++ copy_to_user(user_buf, from, bytes); ++ user_buf += bytes; ++ } ++ return *ppos - pos; ++} ++ ++static const struct file_operations fops_eeprom = { ++ .read = read_file_eeprom, ++ .open = simple_open, ++ .owner = THIS_MODULE ++}; ++ ++ ++static ssize_t read_file_chan_bw(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath_softc *sc = file->private_data; ++ struct ath_common *common = ath9k_hw_common(sc->sc_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_softc *sc = file->private_data; ++ struct ath_common *common = ath9k_hw_common(sc->sc_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)) ++ ath9k_ops.config(sc->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, ++}; ++ ++#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_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); +@@ -1383,6 +1575,16 @@ 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); + ++ debugfs_create_file("eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, ++ &fops_eeprom); ++ debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, ++ sc, &fops_chanbw); ++#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, +diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c +index c22d457..e6b3cd4 100644 +--- a/drivers/net/wireless/ath/ath9k/eeprom.c ++++ b/drivers/net/wireless/ath/ath9k/eeprom.c +@@ -135,13 +135,23 @@ static bool ath9k_hw_nvram_read_firmware(const struct firmware *eeprom_blob, + offset, data); + } + ++static bool ath9k_hw_nvram_read_nvmem(struct ath_hw *ah, off_t offset, ++ u16 *data) ++{ ++ return ath9k_hw_nvram_read_array(ah->nvmem_blob, ++ ah->nvmem_blob_len / sizeof(u16), ++ offset, data); ++} ++ + bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) + { + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_platform_data *pdata = ah->dev->platform_data; + bool ret; + +- if (ah->eeprom_blob) ++ if (ah->nvmem_blob) ++ ret = ath9k_hw_nvram_read_nvmem(ah, off, data); ++ else if (ah->eeprom_blob) + ret = ath9k_hw_nvram_read_firmware(ah->eeprom_blob, off, data); + else if (pdata && !pdata->use_eeprom) + ret = ath9k_hw_nvram_read_pdata(pdata, off, data); +diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c +index a8101c9..37a796b 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 ++#include ++#include ++#include ++ ++#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) >> (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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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/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 09e750b..bd9b021 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, 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, 0); ++ udelay(10); ++ } ++ ++ REG_WRITE(ah, AR_RTC_RESET, 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, 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 ebd5871..6252006 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; + +@@ -977,6 +988,8 @@ struct ath_hw { + bool disable_5ghz; + + const struct firmware *eeprom_blob; ++ u16 *nvmem_blob; /* devres managed */ ++ size_t nvmem_blob_len; + + struct ath_dynack dynack; + +@@ -1076,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 d09d75e..06176a2 100644 +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -47,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"); + +@@ -568,6 +569,57 @@ static void ath9k_eeprom_release(struct ath_softc *sc) + release_firmware(sc->sc_ah->eeprom_blob); + } + ++static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) ++{ ++ struct ath_hw *ah = sc->sc_ah; ++ struct nvmem_cell *cell; ++ void *buf; ++ size_t len; ++ int err; ++ ++ cell = devm_nvmem_cell_get(sc->dev, "calibration"); ++ if (IS_ERR(cell)) { ++ err = PTR_ERR(cell); ++ ++ /* nvmem cell might not be defined, or the nvmem ++ * subsystem isn't included. In this case, follow ++ * the established "just return 0;" convention of ++ * ath9k_init_platform to say: ++ * "All good. Nothing to see here. Please go on." ++ */ ++ if (err == -ENOENT || err == -EOPNOTSUPP) ++ return 0; ++ ++ return err; ++ } ++ ++ buf = nvmem_cell_read(cell, &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ ++ /* run basic sanity checks on the returned nvram cell length. ++ * That length has to be a multiple of a "u16" (i.e.: & 1). ++ * Furthermore, it has to be more than "let's say" 512 bytes ++ * but less than the maximum of AR9300_EEPROM_SIZE (16kb). ++ */ ++ if (((len & 1) == 1) || (len < 512) || (len >= AR9300_EEPROM_SIZE)) { ++ kfree(buf); ++ return -EINVAL; ++ } ++ ++ /* devres manages the calibration values release on shutdown */ ++ ah->nvmem_blob = (u16 *)devm_kmemdup(sc->dev, buf, len, GFP_KERNEL); ++ kfree(buf); ++ if (!ah->nvmem_blob) ++ return -ENOMEM; ++ ++ ah->nvmem_blob_len = len; ++ ah->ah_flags &= ~AH_USE_EEPROM; ++ ah->ah_flags |= AH_NO_EEP_SWAP; ++ ++ return 0; ++} ++ + static int ath9k_init_platform(struct ath_softc *sc) + { + struct ath9k_platform_data *pdata = sc->dev->platform_data; +@@ -644,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) + { +@@ -704,6 +762,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, + if (ret) + return ret; + ++ ret = ath9k_nvmem_request_eeprom(sc); ++ if (ret) ++ return ret; ++ + if (ath9k_led_active_high != -1) + ah->config.led_active_high = ath9k_led_active_high == 1; + +@@ -747,6 +809,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; +@@ -814,7 +879,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[] = { +@@ -826,6 +892,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 +@@ -906,6 +973,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); +@@ -918,9 +986,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; +@@ -993,6 +1058,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) + { +@@ -1033,11 +1110,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) +@@ -1121,25 +1202,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 58d02c1..c9d2bf3 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 c6f4dea..063d1b1 100644 +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -18,6 +18,7 @@ + #include + #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 04d83ab..052cbe2 100644 +--- a/drivers/net/wireless/ath/ath9k/pci.c ++++ b/drivers/net/wireless/ath/ath9k/pci.c +@@ -774,6 +774,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 9af5dbe..395c284 100644 +--- a/local-symbols ++++ b/local-symbols +@@ -133,6 +133,7 @@ ATH9K_WOW= + ATH9K_RFKILL= + ATH9K_CHANNEL_CONTEXT= + ATH9K_PCOEM= ++ATH9K_UBNTHSR= + ATH9K_PCI_NO_EEPROM= + ATH9K_HTC= + ATH9K_HTC_DEBUGFS= diff --git a/recipes-kernel/mac80211/mac80211/0005-backport-of-mwl-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0005-backport-of-mwl-patches-from-openwrt.patch deleted file mode 100644 index f13ccd6..0000000 --- a/recipes-kernel/mac80211/mac80211/0005-backport-of-mwl-patches-from-openwrt.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 03cb19a3d185defe5b22d5c83833de4847b15d29 Mon Sep 17 00:00:00 2001 -From: Patrick Walther -Date: Mon, 22 Mar 2021 16:39:42 +0100 -Subject: [PATCH] 0005 backport of mwl patches from openwrt - ---- - drivers/net/wireless/marvell/libertas/cfg.c | 4 ++++ - drivers/net/wireless/marvell/libertas/main.c | 1 + - drivers/net/wireless/marvell/mwl8k.c | 5 +++-- - 3 files changed, 8 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c -index 4e3de68..c861532 100644 ---- a/drivers/net/wireless/marvell/libertas/cfg.c -+++ b/drivers/net/wireless/marvell/libertas/cfg.c -@@ -2053,6 +2053,8 @@ struct wireless_dev *lbs_cfg_alloc(struct device *dev) - goto err_wiphy_new; - } - -+ set_wiphy_dev(wdev->wiphy, dev); -+ - return wdev; - - err_wiphy_new: -@@ -2127,6 +2129,8 @@ int lbs_cfg_register(struct lbs_private *priv) - wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); - wdev->wiphy->reg_notifier = lbs_reg_notifier; - -+ memcpy(wdev->wiphy->perm_addr, priv->current_addr, ETH_ALEN); -+ - ret = wiphy_register(wdev->wiphy); - if (ret < 0) - pr_err("cannot register wiphy device\n"); -diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c -index ee4cf34..2ed6228 100644 ---- a/drivers/net/wireless/marvell/libertas/main.c -+++ b/drivers/net/wireless/marvell/libertas/main.c -@@ -935,6 +935,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) - goto err_adapter; - } - -+ dev_net_set(dev, wiphy_net(wdev->wiphy)); - dev->ieee80211_ptr = wdev; - dev->ml_priv = priv; - SET_NETDEV_DEV(dev, dmdev); -diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c -index 23efd70..e8985c7 100644 ---- a/drivers/net/wireless/marvell/mwl8k.c -+++ b/drivers/net/wireless/marvell/mwl8k.c -@@ -5694,6 +5694,7 @@ MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); - MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API)); - - static const struct pci_device_id mwl8k_pci_id_table[] = { -+ { PCI_VDEVICE(MARVELL, 0x2a02), .driver_data = MWL8363, }, - { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, }, - { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, - { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, -@@ -6278,6 +6279,8 @@ static int mwl8k_probe(struct pci_dev *pdev, - - priv->running_bsses = 0; - -+ wait_for_completion(&priv->firmware_loading_complete); -+ - return rc; - - err_stop_firmware: -@@ -6311,8 +6314,6 @@ static void mwl8k_remove(struct pci_dev *pdev) - return; - priv = hw->priv; - -- wait_for_completion(&priv->firmware_loading_complete); -- - if (priv->fw_state == FW_STATE_ERROR) { - mwl8k_hw_reset(priv); - goto unmap; diff --git a/recipes-kernel/mac80211/mac80211/0006-backport-of-ath10k-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0006-backport-of-ath10k-patches-from-openwrt.patch new file mode 100644 index 0000000..7580c1c --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0006-backport-of-ath10k-patches-from-openwrt.patch @@ -0,0 +1,1315 @@ +From 08eadf05f847b1b2472f8d5a0afcab03f832923f Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 14 Sep 2022 14:30:15 +0200 +Subject: [PATCH] backport of ath10k patches from openwrt + +--- + drivers/net/wireless/ath/ath10k/Kconfig | 16 +++ + drivers/net/wireless/ath/ath10k/Makefile | 3 +- + drivers/net/wireless/ath/ath10k/core.c | 122 ++++++++++++++++++-- + drivers/net/wireless/ath/ath10k/core.h | 19 ++++ + drivers/net/wireless/ath/ath10k/htt.h | 4 + + drivers/net/wireless/ath/ath10k/htt_tx.c | 61 +++++----- + 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 | 132 +++++++++++++++++++--- + drivers/net/wireless/ath/ath10k/pci.c | 16 +++ + drivers/net/wireless/ath/ath10k/thermal.h | 2 +- + drivers/net/wireless/ath/ath10k/txrx.c | 15 ++- + 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 + + 18 files changed, 606 insertions(+), 52 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 49373f9..5d53bc3 100644 +--- a/drivers/net/wireless/ath/ath10k/Kconfig ++++ b/drivers/net/wireless/ath/ath10k/Kconfig +@@ -71,6 +71,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 +@@ -86,6 +96,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 58e86e6..3b34c59 100644 +--- a/drivers/net/wireless/ath/ath10k/core.c ++++ b/drivers/net/wireless/ath/ath10k/core.c +@@ -8,10 +8,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + + #include "core.h" +@@ -25,6 +27,7 @@ + #include "testmode.h" + #include "wmi-ops.h" + #include "coredump.h" ++#include "leds.h" + + unsigned int ath10k_debug_mask; + EXPORT_SYMBOL(ath10k_debug_mask); +@@ -32,9 +35,11 @@ EXPORT_SYMBOL(ath10k_debug_mask); + static unsigned int ath10k_cryptmode_param; + static bool uart_print; + static bool skip_otp; +-static bool rawmode; + static bool fw_diag_log; + ++/* frame mode values are mapped as per enum ath10k_hw_txrx_mode */ ++unsigned int ath10k_frame_mode = ATH10K_HW_TXRX_NATIVE_WIFI; ++ + unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) | + BIT(ATH10K_FW_CRASH_DUMP_CE_DATA); + +@@ -43,15 +48,16 @@ module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); + module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644); + module_param(uart_print, bool, 0644); + module_param(skip_otp, bool, 0644); +-module_param(rawmode, bool, 0644); + module_param(fw_diag_log, bool, 0644); ++module_param_named(frame_mode, ath10k_frame_mode, uint, 0644); + module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444); + + MODULE_PARM_DESC(debug_mask, "Debugging mask"); + MODULE_PARM_DESC(uart_print, "Uart target debugging"); + MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); + MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software"); +-MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath"); ++MODULE_PARM_DESC(frame_mode, ++ "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)"); + MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file"); + MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging"); + +@@ -61,6 +67,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, +@@ -134,6 +141,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, +@@ -351,6 +359,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, +@@ -393,6 +402,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, +@@ -442,6 +452,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, +@@ -952,7 +963,8 @@ static int ath10k_core_get_board_id_from_otp(struct ath10k *ar) + } + + if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT || +- ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE) ++ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE || ++ ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM) + bmi_board_id_param = BMI_PARAM_GET_FLASH_BOARD_ID; + else + bmi_board_id_param = BMI_PARAM_GET_EEPROM_BOARD_ID; +@@ -1199,6 +1211,7 @@ success: + static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type) + { + const struct firmware *fw; ++ char boardname[100]; + + if (bd_ie_type == ATH10K_BD_IE_BOARD) { + if (!ar->hw_params.fw.board) { +@@ -1206,9 +1219,19 @@ static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type) + return -EINVAL; + } + ++ scnprintf(boardname, sizeof(boardname), "board-%s-%s.bin", ++ ath10k_bus_str(ar->hif.bus), dev_name(ar->dev)); ++ + ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, +- ar->hw_params.fw.board); ++ boardname); ++ if (IS_ERR(ar->normal_mode_fw.board)) { ++ fw = ath10k_fetch_fw_file(ar, ++ ar->hw_params.fw.dir, ++ ar->hw_params.fw.board); ++ ar->normal_mode_fw.board = fw; ++ } ++ + if (IS_ERR(ar->normal_mode_fw.board)) + return PTR_ERR(ar->normal_mode_fw.board); + +@@ -1743,7 +1766,8 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) + + /* As of now pre-cal is valid for 10_4 variants */ + if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT || +- ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE) ++ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE || ++ ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM) + bmi_otp_exe_param = BMI_PARAM_FLASH_SECTION_ALL; + + ret = ath10k_bmi_execute(ar, address, bmi_otp_exe_param, &result); +@@ -1870,6 +1894,39 @@ out_free: + return ret; + } + ++static int ath10k_download_cal_nvmem(struct ath10k *ar, const char *cell_name) ++{ ++ struct nvmem_cell *cell; ++ void *buf; ++ size_t len; ++ int ret; ++ ++ cell = devm_nvmem_cell_get(ar->dev, cell_name); ++ if (IS_ERR(cell)) { ++ ret = PTR_ERR(cell); ++ return ret; ++ } ++ ++ buf = nvmem_cell_read(cell, &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ ++ if (ar->hw_params.cal_data_len != len) { ++ kfree(buf); ++ ath10k_warn(ar, "invalid calibration data length in nvmem-cell '%s': %zu != %u\n", ++ cell_name, len, ar->hw_params.cal_data_len); ++ return -EMSGSIZE; ++ } ++ ++ ret = ath10k_download_board_data(ar, buf, len); ++ kfree(buf); ++ if (ret) ++ ath10k_warn(ar, "failed to download calibration data from nvmem-cell '%s': %d\n", ++ cell_name, ret); ++ ++ return ret; ++} ++ + int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name, + struct ath10k_fw_file *fw_file) + { +@@ -2104,6 +2161,18 @@ static int ath10k_core_pre_cal_download(struct ath10k *ar) + { + int ret; + ++ ret = ath10k_download_cal_nvmem(ar, "pre-calibration"); ++ if (ret == 0) { ++ ar->cal_mode = ATH10K_PRE_CAL_MODE_NVMEM; ++ goto success; ++ } else if (ret == -EPROBE_DEFER) { ++ return ret; ++ } ++ ++ ath10k_dbg(ar, ATH10K_DBG_BOOT, ++ "boot did not find a pre-calibration nvmem-cell, try file next: %d\n", ++ ret); ++ + ret = ath10k_download_cal_file(ar, ar->pre_cal_file); + if (ret == 0) { + ar->cal_mode = ATH10K_PRE_CAL_MODE_FILE; +@@ -2170,6 +2239,18 @@ static int ath10k_download_cal_data(struct ath10k *ar) + "pre cal download procedure failed, try cal file: %d\n", + ret); + ++ ret = ath10k_download_cal_nvmem(ar, "calibration"); ++ if (ret == 0) { ++ ar->cal_mode = ATH10K_CAL_MODE_NVMEM; ++ goto done; ++ } else if (ret == -EPROBE_DEFER) { ++ return ret; ++ } ++ ++ ath10k_dbg(ar, ATH10K_DBG_BOOT, ++ "boot did not find a calibration nvmem-cell, try file next: %d\n", ++ ret); ++ + ret = ath10k_download_cal_file(ar, ar->cal_file); + if (ret == 0) { + ar->cal_mode = ATH10K_CAL_MODE_FILE; +@@ -2487,7 +2568,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) + ar->htt.max_num_amsdu = ATH10K_HTT_MAX_NUM_AMSDU_DEFAULT; + ar->htt.max_num_ampdu = ATH10K_HTT_MAX_NUM_AMPDU_DEFAULT; + +- if (rawmode) { ++ if (ath10k_frame_mode == ATH10K_HW_TXRX_RAW) { + if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT, + fw_file->fw_features)) { + ath10k_err(ar, "rawmode = 1 requires support from firmware"); +@@ -3084,6 +3165,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: +@@ -3250,6 +3335,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar) + + device_get_mac_address(ar->dev, ar->mac_addr, sizeof(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", +@@ -3342,9 +3429,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: +@@ -3369,6 +3465,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); +@@ -3380,6 +3486,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 4f5ca94..dfe7b0e 100644 +--- a/drivers/net/wireless/ath/ath10k/core.h ++++ b/drivers/net/wireless/ath/ath10k/core.h +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "htt.h" + #include "htc.h" +@@ -877,8 +878,10 @@ enum ath10k_cal_mode { + ATH10K_CAL_MODE_FILE, + ATH10K_CAL_MODE_OTP, + ATH10K_CAL_MODE_DT, ++ ATH10K_CAL_MODE_NVMEM, + ATH10K_PRE_CAL_MODE_FILE, + ATH10K_PRE_CAL_MODE_DT, ++ ATH10K_PRE_CAL_MODE_NVMEM, + ATH10K_CAL_MODE_EEPROM, + }; + +@@ -898,10 +901,14 @@ static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode) + return "otp"; + case ATH10K_CAL_MODE_DT: + return "dt"; ++ case ATH10K_CAL_MODE_NVMEM: ++ return "nvmem"; + case ATH10K_PRE_CAL_MODE_FILE: + return "pre-cal-file"; + case ATH10K_PRE_CAL_MODE_DT: + return "pre-cal-dt"; ++ case ATH10K_PRE_CAL_MODE_NVMEM: ++ return "pre-cal-nvmem"; + case ATH10K_CAL_MODE_EEPROM: + return "eeprom"; + } +@@ -1249,6 +1256,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; +@@ -1298,6 +1312,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 *)); + }; +@@ -1311,6 +1329,7 @@ static inline bool ath10k_peer_stats_enabled(struct ath10k *ar) + return false; + } + ++extern unsigned int ath10k_frame_mode; + extern unsigned long ath10k_coredump_mask; + + void ath10k_core_napi_sync_disable(struct ath10k *ar); +diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h +index ec689e3..4c70917 100644 +--- a/drivers/net/wireless/ath/ath10k/htt.h ++++ b/drivers/net/wireless/ath/ath10k/htt.h +@@ -235,7 +235,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/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c +index b793eac..9863343 100644 +--- a/drivers/net/wireless/ath/ath10k/htt_tx.c ++++ b/drivers/net/wireless/ath/ath10k/htt_tx.c +@@ -1295,7 +1295,6 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm + struct ath10k *ar = htt->ar; + int res, data_len; + struct htt_cmd_hdr *cmd_hdr; +- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct htt_data_tx_desc *tx_desc; + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + struct sk_buff *tmp_skb; +@@ -1306,11 +1305,15 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm + u16 flags1 = 0; + u16 msdu_id = 0; + +- if ((ieee80211_is_action(hdr->frame_control) || +- ieee80211_is_deauth(hdr->frame_control) || +- ieee80211_is_disassoc(hdr->frame_control)) && +- ieee80211_has_protected(hdr->frame_control)) { +- skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ if (!is_eth) { ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; ++ ++ if ((ieee80211_is_action(hdr->frame_control) || ++ ieee80211_is_deauth(hdr->frame_control) || ++ ieee80211_is_disassoc(hdr->frame_control)) && ++ ieee80211_has_protected(hdr->frame_control)) { ++ skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ } + } + + data_len = msdu->len; +@@ -1407,7 +1410,6 @@ static int ath10k_htt_tx_32(struct ath10k_htt *htt, + { + struct ath10k *ar = htt->ar; + struct device *dev = ar->dev; +- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + struct ath10k_hif_sg_item sg_items[2]; +@@ -1439,15 +1441,19 @@ static int ath10k_htt_tx_32(struct ath10k_htt *htt, + txbuf_paddr = htt->txbuf.paddr + + (sizeof(struct ath10k_htt_txbuf_32) * msdu_id); + +- if ((ieee80211_is_action(hdr->frame_control) || +- ieee80211_is_deauth(hdr->frame_control) || +- ieee80211_is_disassoc(hdr->frame_control)) && +- ieee80211_has_protected(hdr->frame_control)) { +- skb_put(msdu, IEEE80211_CCMP_MIC_LEN); +- } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) && +- txmode == ATH10K_HW_TXRX_RAW && +- ieee80211_has_protected(hdr->frame_control)) { +- skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ if (!is_eth) { ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; ++ ++ if ((ieee80211_is_action(hdr->frame_control) || ++ ieee80211_is_deauth(hdr->frame_control) || ++ ieee80211_is_disassoc(hdr->frame_control)) && ++ ieee80211_has_protected(hdr->frame_control)) { ++ skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) && ++ txmode == ATH10K_HW_TXRX_RAW && ++ ieee80211_has_protected(hdr->frame_control)) { ++ skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ } + } + + skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len, +@@ -1609,7 +1615,6 @@ static int ath10k_htt_tx_64(struct ath10k_htt *htt, + { + struct ath10k *ar = htt->ar; + struct device *dev = ar->dev; +- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + struct ath10k_hif_sg_item sg_items[2]; +@@ -1641,15 +1646,19 @@ static int ath10k_htt_tx_64(struct ath10k_htt *htt, + txbuf_paddr = htt->txbuf.paddr + + (sizeof(struct ath10k_htt_txbuf_64) * msdu_id); + +- if ((ieee80211_is_action(hdr->frame_control) || +- ieee80211_is_deauth(hdr->frame_control) || +- ieee80211_is_disassoc(hdr->frame_control)) && +- ieee80211_has_protected(hdr->frame_control)) { +- skb_put(msdu, IEEE80211_CCMP_MIC_LEN); +- } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) && +- txmode == ATH10K_HW_TXRX_RAW && +- ieee80211_has_protected(hdr->frame_control)) { +- skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ if (!is_eth) { ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; ++ ++ if ((ieee80211_is_action(hdr->frame_control) || ++ ieee80211_is_deauth(hdr->frame_control) || ++ ieee80211_is_disassoc(hdr->frame_control)) && ++ ieee80211_has_protected(hdr->frame_control)) { ++ skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) && ++ txmode == ATH10K_HW_TXRX_RAW && ++ ieee80211_has_protected(hdr->frame_control)) { ++ skb_put(msdu, IEEE80211_CCMP_MIC_LEN); ++ } + } + + skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len, +diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h +index 79ae9e4..9d65f3e 100644 +--- a/drivers/net/wireless/ath/ath10k/hw.h ++++ b/drivers/net/wireless/ath/ath10k/hw.h +@@ -517,6 +517,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 ++ * 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 ++ ++#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 266ba48..7a5311b 100644 +--- a/drivers/net/wireless/ath/ath10k/mac.c ++++ b/drivers/net/wireless/ath/ath10k/mac.c +@@ -24,6 +24,7 @@ + #include "wmi-tlv.h" + #include "wmi-ops.h" + #include "wow.h" ++#include "leds.h" + + /*********/ + /* Rates */ +@@ -1020,6 +1021,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; +@@ -1052,7 +1087,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); +@@ -1498,7 +1534,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; +@@ -3426,7 +3463,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 +@@ -3710,6 +3748,9 @@ ath10k_mac_tx_h_get_txmode(struct ath10k *ar, + const struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + __le16 fc = hdr->frame_control; + ++ if (IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) ++ return ATH10K_HW_TXRX_ETHERNET; ++ + if (!vif || vif->type == NL80211_IFTYPE_MONITOR) + return ATH10K_HW_TXRX_RAW; + +@@ -3870,6 +3911,12 @@ static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar, + bool noack = false; + + cb->flags = 0; ++ ++ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { ++ cb->flags |= ATH10K_SKB_F_QOS; /* Assume data frames are QoS */ ++ goto finish_cb_fill; ++ } ++ + if (!ath10k_tx_h_use_hwcrypto(vif, skb)) + cb->flags |= ATH10K_SKB_F_NO_HWCRYPT; + +@@ -3908,6 +3955,7 @@ static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar, + cb->flags |= ATH10K_SKB_F_RAW_TX; + } + ++finish_cb_fill: + cb->vif = vif; + cb->txq = txq; + cb->airtime_est = airtime; +@@ -4031,7 +4079,11 @@ static int ath10k_mac_tx(struct ath10k *ar, + ath10k_tx_h_seq_no(vif, skb); + break; + case ATH10K_HW_TXRX_ETHERNET: +- ath10k_tx_h_8023(skb); ++ /* Convert 802.11->802.3 header only if the frame was erlier ++ * encapsulated to 802.11 by mac80211. Otherwise pass it as is. ++ */ ++ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) ++ ath10k_tx_h_8023(skb); + break; + case ATH10K_HW_TXRX_RAW: + if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags) && +@@ -4643,12 +4695,10 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif = info->control.vif; + struct ieee80211_sta *sta = control->sta; + struct ieee80211_txq *txq = NULL; +- struct ieee80211_hdr *hdr = (void *)skb->data; + enum ath10k_hw_txrx_mode txmode; + enum ath10k_mac_tx_path txpath; + bool is_htt; + bool is_mgmt; +- bool is_presp; + int ret; + u16 airtime; + +@@ -4662,8 +4712,14 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw, + is_mgmt = (txpath == ATH10K_MAC_TX_HTT_MGMT); + + if (is_htt) { ++ bool is_presp = false; ++ + spin_lock_bh(&ar->htt.tx_lock); +- is_presp = ieee80211_is_probe_resp(hdr->frame_control); ++ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { ++ struct ieee80211_hdr *hdr = (void *)skb->data; ++ ++ is_presp = ieee80211_is_probe_resp(hdr->frame_control); ++ } + + ret = ath10k_htt_tx_inc_pending(htt); + if (ret) { +@@ -5463,6 +5519,30 @@ static int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif) + ar->wmi.vdev_param->txbf, value); + } + ++static void ath10k_update_vif_offload(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct ath10k_vif *arvif = (void *)vif->drv_priv; ++ struct ath10k *ar = hw->priv; ++ u32 vdev_param; ++ int ret; ++ ++ if (ath10k_frame_mode != ATH10K_HW_TXRX_ETHERNET || ++ ar->wmi.vdev_param->tx_encap_type == WMI_VDEV_PARAM_UNSUPPORTED || ++ (vif->type != NL80211_IFTYPE_STATION && ++ vif->type != NL80211_IFTYPE_AP)) ++ vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; ++ ++ vdev_param = ar->wmi.vdev_param->tx_encap_type; ++ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ++ ATH10K_HW_TXRX_NATIVE_WIFI); ++ /* 10.X firmware does not support this VDEV parameter. Do not warn */ ++ if (ret && ret != -EOPNOTSUPP) { ++ ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n", ++ arvif->vdev_id, ret); ++ } ++} ++ + /* + * TODO: + * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE, +@@ -5672,15 +5752,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, + + arvif->def_wep_key_idx = -1; + +- vdev_param = ar->wmi.vdev_param->tx_encap_type; +- ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, +- ATH10K_HW_TXRX_NATIVE_WIFI); +- /* 10.X firmware does not support this VDEV parameter. Do not warn */ +- if (ret && ret != -EOPNOTSUPP) { +- ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n", +- arvif->vdev_id, ret); +- goto err_vdev_delete; +- } ++ ath10k_update_vif_offload(hw, vif); + + /* Configuring number of spatial stream for monitor interface is causing + * target assert in qca9888 and qca6174. +@@ -9368,6 +9440,7 @@ static const struct ieee80211_ops ath10k_ops = { + .stop = ath10k_stop, + .config = ath10k_config, + .add_interface = ath10k_add_interface, ++ .update_vif_offload = ath10k_update_vif_offload, + .remove_interface = ath10k_remove_interface, + .configure_filter = ath10k_configure_filter, + .bss_info_changed = ath10k_bss_info_changed, +@@ -9859,6 +9932,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[] = { +@@ -10037,6 +10125,12 @@ int ath10k_mac_register(struct ath10k *ar) + if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, ar->wmi.svc_map)) + ieee80211_hw_set(ar->hw, SUPPORTS_TDLS_BUFFER_STA); + ++ if (ath10k_frame_mode == ATH10K_HW_TXRX_ETHERNET) { ++ if (ar->wmi.vdev_param->tx_encap_type != ++ WMI_VDEV_PARAM_UNSUPPORTED) ++ ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD); ++ } ++ + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + ar->hw->wiphy->max_remain_on_channel_duration = 5000; +@@ -10211,6 +10305,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 f6191b5..e9e08b0 100644 +--- a/drivers/net/wireless/ath/ath10k/pci.c ++++ b/drivers/net/wireless/ath/ath10k/pci.c +@@ -131,7 +131,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, + }, + +@@ -140,7 +144,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, + }, + +@@ -167,7 +175,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, + }, + +@@ -192,7 +204,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 5fdb020..d76c307 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/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c +index 6f8b642..08e079e 100644 +--- a/drivers/net/wireless/ath/ath10k/txrx.c ++++ b/drivers/net/wireless/ath/ath10k/txrx.c +@@ -43,6 +43,7 @@ out: + int ath10k_txrx_tx_unref(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done) + { ++ struct ieee80211_tx_status status; + struct ath10k *ar = htt->ar; + struct device *dev = ar->dev; + struct ieee80211_tx_info *info; +@@ -128,7 +129,19 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt, + info->status.is_valid_ack_signal = true; + } + +- ieee80211_tx_status(htt->ar->hw, msdu); ++ memset(&status, 0, sizeof(status)); ++ status.skb = msdu; ++ status.info = info; ++ ++ rcu_read_lock(); ++ ++ if (txq) ++ status.sta = txq->sta; ++ ++ ieee80211_tx_status_ext(htt->ar->hw, &status); ++ ++ rcu_read_unlock(); ++ + /* we do not own the msdu anymore */ + + return 0; +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 7efbe03..6a391b2 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c ++++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c +@@ -4594,6 +4594,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 416f2b9..ed57499 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.c ++++ b/drivers/net/wireless/ath/ath10k/wmi.c +@@ -7472,6 +7472,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) +@@ -9160,6 +9203,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 */ +@@ -9230,6 +9276,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 */ +@@ -9302,6 +9350,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 */ + }; + +@@ -9373,6 +9423,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 */ +@@ -9454,6 +9506,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 01bfd09..1a39a9a 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.h ++++ b/drivers/net/wireless/ath/ath10k/wmi.h +@@ -3030,6 +3030,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 395c284..ee80d12 100644 +--- a/local-symbols ++++ b/local-symbols +@@ -165,6 +165,8 @@ ATH10K_SNOC= + ATH10K_DEBUG= + ATH10K_DEBUGFS= + ATH10K_SPECTRAL= ++ATH10K_THERMAL= ++ATH10K_LEDS= + ATH10K_TRACING= + ATH10K_DFS_CERTIFIED= + WCN36XX= diff --git a/recipes-kernel/mac80211/mac80211/0006-backport-of-brcm-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0007-backport-of-brcm-patches-from-openwrt.patch similarity index 64% rename from recipes-kernel/mac80211/mac80211/0006-backport-of-brcm-patches-from-openwrt.patch rename to recipes-kernel/mac80211/mac80211/0007-backport-of-brcm-patches-from-openwrt.patch index 7aeb06a..4e27797 100644 --- a/recipes-kernel/mac80211/mac80211/0006-backport-of-brcm-patches-from-openwrt.patch +++ b/recipes-kernel/mac80211/mac80211/0007-backport-of-brcm-patches-from-openwrt.patch @@ -1,27 +1,30 @@ -From 103bfc86c246ba529893dfc54b7ceb19ec1a03c5 Mon Sep 17 00:00:00 2001 +From a1dd3f2a734c1e96a7c8fd1ae64126e901a966d5 Mon Sep 17 00:00:00 2001 From: Patrick Walther -Date: Mon, 22 Mar 2021 16:40:53 +0100 -Subject: [PATCH] 0006 backport of brcm patches from openwrt +Date: Wed, 14 Sep 2022 14:30:49 +0200 +Subject: [PATCH] backport of brcm patches from openwrt --- - drivers/net/wireless/broadcom/b43/Kconfig | 2 +- - drivers/net/wireless/broadcom/b43/Makefile | 2 +- - drivers/net/wireless/broadcom/b43/b43.h | 3 + - drivers/net/wireless/broadcom/b43/dma.h | 2 +- - drivers/net/wireless/broadcom/b43/main.c | 82 ++++++++++++++--- - drivers/net/wireless/broadcom/b43/pio.h | 34 ++++++- - drivers/net/wireless/broadcom/brcm80211/Kconfig | 2 +- - .../broadcom/brcm80211/brcmfmac/cfg80211.c | 102 +++++++++++++++++++++ - .../wireless/broadcom/brcm80211/brcmfmac/common.c | 4 + - .../wireless/broadcom/brcm80211/brcmfmac/core.c | 9 ++ - .../wireless/broadcom/brcm80211/brcmfmac/core.h | 8 ++ - .../broadcom/brcm80211/brcmfmac/firmware.c | 14 +++ - .../net/wireless/broadcom/brcm80211/brcmfmac/of.c | 32 +++++++ - .../wireless/broadcom/brcm80211/brcmsmac/channel.c | 19 ++-- - 14 files changed, 286 insertions(+), 29 deletions(-) + drivers/net/wireless/broadcom/b43/Kconfig | 2 +- + drivers/net/wireless/broadcom/b43/Makefile | 2 +- + drivers/net/wireless/broadcom/b43/b43.h | 3 + + drivers/net/wireless/broadcom/b43/dma.h | 2 +- + drivers/net/wireless/broadcom/b43/main.c | 82 ++++++++++++-- + drivers/net/wireless/broadcom/b43/pio.h | 34 +++++- + .../net/wireless/broadcom/brcm80211/Kconfig | 2 +- + .../broadcom/brcm80211/brcmfmac/cfg80211.c | 102 ++++++++++++++++++ + .../broadcom/brcm80211/brcmfmac/common.c | 27 +++-- + .../broadcom/brcm80211/brcmfmac/common.h | 1 + + .../broadcom/brcm80211/brcmfmac/core.c | 12 ++- + .../broadcom/brcm80211/brcmfmac/core.h | 8 ++ + .../broadcom/brcm80211/brcmfmac/firmware.c | 14 +++ + .../wireless/broadcom/brcm80211/brcmfmac/of.c | 71 ++++++++++++ + .../wireless/broadcom/brcm80211/brcmfmac/of.h | 7 ++ + .../broadcom/brcm80211/brcmfmac/sdio.c | 47 +++++++- + .../broadcom/brcm80211/brcmsmac/channel.c | 19 ++-- + 17 files changed, 396 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig -index ffdf951..c43d931 100644 +index 84cbe38..5f008b4 100644 --- a/drivers/net/wireless/broadcom/b43/Kconfig +++ b/drivers/net/wireless/broadcom/b43/Kconfig @@ -100,7 +100,7 @@ config B43_BCMA_PIO @@ -74,7 +77,7 @@ index dfebc64..ec72414 100644 #define B43_DMA0_RX_FW351_BUFSIZE (B43_DMA0_RX_FW351_FO + IEEE80211_MAX_FRAME_LEN) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c -index 2ef3263..8062ded 100644 +index f378356..86ab6fb 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -72,6 +72,11 @@ MODULE_FIRMWARE("b43/ucode40.fw"); @@ -180,7 +183,7 @@ index 2ef3263..8062ded 100644 if (wl->radio_enabled != phy->radio_on) { if (wl->radio_enabled) { -@@ -5169,6 +5181,47 @@ static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, +@@ -5168,6 +5180,47 @@ static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, return 0; } @@ -228,7 +231,7 @@ index 2ef3263..8062ded 100644 static const struct ieee80211_ops b43_hw_ops = { .tx = b43_op_tx, .conf_tx = b43_op_conf_tx, -@@ -5190,6 +5243,8 @@ static const struct ieee80211_ops b43_hw_ops = { +@@ -5189,6 +5242,8 @@ static const struct ieee80211_ops b43_hw_ops = { .sw_scan_complete = b43_op_sw_scan_complete_notifier, .get_survey = b43_op_get_survey, .rfkill_poll = b43_rfkill_poll, @@ -237,7 +240,7 @@ index 2ef3263..8062ded 100644 }; /* Hard-reset the chip. Do not call this directly. -@@ -5491,6 +5546,8 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) +@@ -5490,6 +5545,8 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) if (!wldev) goto out; @@ -246,7 +249,7 @@ index 2ef3263..8062ded 100644 wldev->use_pio = b43_modparam_pio; wldev->dev = dev; wldev->wl = wl; -@@ -5585,6 +5642,9 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) +@@ -5581,6 +5638,9 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); @@ -308,7 +311,7 @@ index ffbfec6..a3742b7 100644 #endif /* B43_PIO_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig -index ae090a4..2dc7cab 100644 +index 64f6d2f..c163838 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -1,6 +1,6 @@ @@ -320,7 +323,7 @@ index ae090a4..2dc7cab 100644 config BRCMSMAC diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c -index 7e1d6f9..0373fac 100644 +index 7a58e05..9e629c2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -715,8 +715,36 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, @@ -360,10 +363,11 @@ index 7e1d6f9..0373fac 100644 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type); if (err) { -@@ -2882,6 +2910,63 @@ done: +@@ -2892,6 +2920,63 @@ done: + return err; } - static int ++static int +brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, + int idx, struct survey_info *survey) +{ @@ -420,11 +424,10 @@ index 7e1d6f9..0373fac 100644 + return 0; +} + -+static int + static int brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev, int idx, u8 *mac, struct station_info *sinfo) - { -@@ -2930,6 +3015,10 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, +@@ -2946,6 +3031,10 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, * preference in cfg struct to apply this to * FW later while initializing the dongle */ @@ -435,7 +438,7 @@ index 7e1d6f9..0373fac 100644 cfg->pwr_save = enabled; if (!check_vif_up(ifp->vif)) { -@@ -2973,6 +3062,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, +@@ -2989,6 +3078,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, struct brcmu_chan ch; u16 channel; u32 freq; @@ -443,7 +446,7 @@ index 7e1d6f9..0373fac 100644 u16 notify_capability; u16 notify_interval; u8 *notify_ie; -@@ -2997,6 +3087,17 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, +@@ -3013,6 +3103,17 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, band = NL80211_BAND_5GHZ; freq = ieee80211_channel_to_frequency(channel, band); @@ -461,7 +464,7 @@ index 7e1d6f9..0373fac 100644 bss_data.chan = ieee80211_get_channel(wiphy, freq); bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20; bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime()); -@@ -5483,6 +5584,7 @@ static struct cfg80211_ops brcmf_cfg80211_ops = { +@@ -5541,6 +5642,7 @@ static struct cfg80211_ops brcmf_cfg80211_ops = { .leave_ibss = brcmf_cfg80211_leave_ibss, .get_station = brcmf_cfg80211_get_station, .dump_station = brcmf_cfg80211_dump_station, @@ -470,7 +473,7 @@ index 7e1d6f9..0373fac 100644 .get_tx_power = brcmf_cfg80211_get_tx_power, .add_key = brcmf_cfg80211_add_key, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c -index 3512a3f..41b1a8b 100644 +index 3512a3f..bb7cbe2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -59,7 +59,11 @@ static int brcmf_fcmode; @@ -485,11 +488,72 @@ index 3512a3f..41b1a8b 100644 module_param_named(roamoff, brcmf_roamoff, int, 0400); MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); +@@ -202,13 +206,24 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) + char *ptr; + s32 err; + +- /* retreive mac address */ +- err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, +- sizeof(ifp->mac_addr)); +- if (err < 0) { +- bphy_err(drvr, "Retrieving cur_etheraddr failed, %d\n", err); +- goto done; ++ if (is_valid_ether_addr(ifp->mac_addr)) { ++ /* set mac address */ ++ err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr, ++ ETH_ALEN); ++ if (err < 0) { ++ bphy_err(ifp->drvr, "Setting cur_etheraddr failed, %d\n", err); ++ goto done; ++ } ++ } else { ++ /* retrieve mac address */ ++ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, ++ sizeof(ifp->mac_addr)); ++ if (err < 0) { ++ bphy_err(drvr, "Retrieving cur_etheraddr failed, %d\n", err); ++ goto done; ++ } + } ++ + memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); + memcpy(ifp->drvr->wiphy->perm_addr, ifp->drvr->mac, ETH_ALEN); + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +index 8b5f499..15accc8 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +@@ -50,6 +50,7 @@ struct brcmf_mp_device { + bool ignore_probe_fail; + struct brcmfmac_pd_cc *country_codes; + const char *board_type; ++ unsigned char mac[ETH_ALEN]; + union { + struct brcmfmac_sdio_pd sdio; + } bus; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c -index 011f9fa..4309ad7 100644 +index 9fbedb8..96e2311 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c -@@ -1356,6 +1356,8 @@ int brcmf_attach(struct device *dev) +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1226,7 +1227,8 @@ static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops) + brcmf_dbg(TRACE, "\n"); + + /* add primary networking interface */ +- ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL); ++ ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", ++ is_valid_ether_addr(drvr->settings->mac) ? drvr->settings->mac : NULL); + if (IS_ERR(ifp)) + return PTR_ERR(ifp); + +@@ -1361,6 +1363,8 @@ int brcmf_attach(struct device *dev) /* Link to bus module */ drvr->hdrlen = 0; @@ -498,7 +562,7 @@ index 011f9fa..4309ad7 100644 /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); -@@ -1438,6 +1440,12 @@ void brcmf_detach(struct device *dev) +@@ -1443,6 +1447,12 @@ void brcmf_detach(struct device *dev) if (drvr == NULL) return; @@ -511,16 +575,8 @@ index 011f9fa..4309ad7 100644 #ifdef CONFIG_INET unregister_inetaddr_notifier(&drvr->inetaddr_notifier); #endif -@@ -1557,6 +1565,7 @@ int __init brcmf_core_init(void) - { - if (!schedule_work(&brcmf_driver_work)) - return -EBUSY; -+ flush_work(&brcmf_driver_work); - - return 0; - } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h -index 5767d66..0ae1cc2 100644 +index 8212c9d..c57b8bb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -91,6 +91,11 @@ struct brcmf_rev_info { @@ -546,7 +602,7 @@ index 5767d66..0ae1cc2 100644 uint hdrlen; /* Total BRCMF header length (proto + bus) */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c -index d821a47..47f72f7 100644 +index dcbe55b..3ddc390 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -431,6 +431,7 @@ struct brcmf_fw { @@ -556,8 +612,8 @@ index d821a47..47f72f7 100644 + struct completion *completion; }; - static void brcmf_fw_request_done(const struct firmware *fw, void *ctx); -@@ -638,6 +639,8 @@ static void brcmf_fw_request_done(const struct firmware *fw, void *ctx) + #ifdef CONFIG_EFI +@@ -655,6 +656,8 @@ static void brcmf_fw_request_done(const struct firmware *fw, void *ctx) fwctx->req = NULL; } fwctx->done(fwctx->dev, ret, fwctx->req); @@ -566,16 +622,16 @@ index d821a47..47f72f7 100644 kfree(fwctx); } -@@ -662,6 +665,8 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req, +@@ -695,6 +698,8 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req, { struct brcmf_fw_item *first = &req->items[0]; struct brcmf_fw *fwctx; + struct completion completion; + unsigned long time_left; + char *alt_path = NULL; int ret; - brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); -@@ -678,6 +683,9 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req, +@@ -712,6 +717,9 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req, fwctx->dev = dev; fwctx->req = req; fwctx->done = fw_cb; @@ -583,9 +639,9 @@ index d821a47..47f72f7 100644 + init_completion(&completion); + fwctx->completion = &completion; - ret = request_firmware_nowait(THIS_MODULE, true, first->path, - fwctx->dev, GFP_KERNEL, fwctx, -@@ -685,6 +693,12 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req, + /* First try alternative board-specific path if any */ + if (fwctx->req->board_type) +@@ -730,6 +738,12 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req, if (ret < 0) brcmf_fw_request_done(NULL, fwctx); @@ -599,13 +655,27 @@ index d821a47..47f72f7 100644 } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c -index a755426..e9c23c4 100644 +index 2f7bc3a..8cf1ad1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c -@@ -12,6 +12,36 @@ +@@ -5,11 +5,13 @@ + #include + #include + #include ++#include + + #include + #include "debug.h" + #include "core.h" #include "common.h" ++#include "firmware.h" #include "of.h" + static int brcmf_of_get_country_codes(struct device *dev, +@@ -58,6 +60,36 @@ static int brcmf_of_get_country_codes(struct device *dev, + return 0; + } + +/* TODO: FIXME: Use DT */ +static void brcmf_of_probe_cc(struct device *dev, + struct brcmf_mp_device *settings) @@ -639,15 +709,198 @@ index a755426..e9c23c4 100644 void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, struct brcmf_mp_device *settings) { -@@ -43,6 +73,8 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, +@@ -90,6 +122,8 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, of_node_put(root); } + brcmf_of_probe_cc(dev, settings); + - if (!np || bus_type != BRCMF_BUSTYPE_SDIO || - !of_device_is_compatible(np, "brcm,bcm4329-fmac")) + if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) return; + +@@ -97,6 +131,8 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, + if (err) + brcmf_err("failed to get OF country code map (err=%d)\n", err); + ++ of_get_mac_address(np, settings->mac); ++ + if (bus_type != BRCMF_BUSTYPE_SDIO) + return; + +@@ -118,3 +154,38 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, + sdio->oob_irq_nr = irq; + sdio->oob_irq_flags = irqf; + } ++ ++struct brcmf_firmware_mapping * ++brcmf_of_fwnames(struct device *dev, u32 *fwname_count) ++{ ++ struct device_node *np = dev->of_node; ++ struct brcmf_firmware_mapping *fwnames; ++ struct device_node *map_np, *fw_np; ++ int of_count; ++ int count = 0; ++ ++ map_np = of_get_child_by_name(np, "firmwares"); ++ of_count = of_get_child_count(map_np); ++ if (!of_count) ++ return NULL; ++ ++ fwnames = devm_kcalloc(dev, of_count, ++ sizeof(struct brcmf_firmware_mapping), ++ GFP_KERNEL); ++ ++ for_each_child_of_node(map_np, fw_np) ++ { ++ struct brcmf_firmware_mapping *cur = &fwnames[count]; ++ ++ if (of_property_read_u32(fw_np, "chipid", &cur->chipid) || ++ of_property_read_u32(fw_np, "revmask", &cur->revmask)) ++ continue; ++ cur->fw_base = of_get_property(fw_np, "fw_base", NULL); ++ if (cur->fw_base) ++ count++; ++ } ++ ++ *fwname_count = count; ++ ++ return count ? fwnames : NULL; ++} +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h +index 10bf522..5b39a39 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h +@@ -5,9 +5,16 @@ + #ifdef CONFIG_OF + void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, + struct brcmf_mp_device *settings); ++struct brcmf_firmware_mapping * ++brcmf_of_fwnames(struct device *dev, u32 *map_count); + #else + static void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, + struct brcmf_mp_device *settings) + { + } ++static struct brcmf_firmware_mapping * ++brcmf_of_fwnames(struct device *dev, u32 *map_count) ++{ ++ return NULL; ++} + #endif /* CONFIG_OF */ +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +index f7961b2..3c5989b 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +@@ -35,6 +35,7 @@ + #include "core.h" + #include "common.h" + #include "bcdc.h" ++#include "of.h" + + #define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500) + #define CTL_DONE_TIMEOUT msecs_to_jiffies(2500) +@@ -633,7 +634,7 @@ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.txt"); + /* per-board firmware binaries */ + MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.bin"); + +-static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { ++static const struct brcmf_firmware_mapping sdio_fwnames[] = { + BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), + BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0), + BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4), +@@ -659,6 +660,9 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { + BRCMF_FW_ENTRY(CY_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752) + }; + ++static const struct brcmf_firmware_mapping *brcmf_sdio_fwnames = sdio_fwnames; ++static u32 brcmf_sdio_fwnames_count = ARRAY_SIZE(sdio_fwnames); ++ + #define TXCTL_CREDITS 2 + + static void pkt_align(struct sk_buff *p, int len, int align) +@@ -4140,7 +4144,7 @@ int brcmf_sdio_get_fwname(struct device *dev, const char *ext, u8 *fw_name) + + fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev, + brcmf_sdio_fwnames, +- ARRAY_SIZE(brcmf_sdio_fwnames), ++ brcmf_sdio_fwnames_count, + fwnames, ARRAY_SIZE(fwnames)); + if (!fwreq) + return -ENOMEM; +@@ -4196,6 +4200,9 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = { + #define BRCMF_SDIO_FW_CODE 0 + #define BRCMF_SDIO_FW_NVRAM 1 + ++static struct brcmf_fw_request * ++brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus); ++ + static void brcmf_sdio_firmware_callback(struct device *dev, int err, + struct brcmf_fw_request *fwreq) + { +@@ -4211,6 +4218,22 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, + + brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err); + ++ if (err && brcmf_sdio_fwnames != sdio_fwnames) { ++ /* Try again with the standard firmware names */ ++ brcmf_sdio_fwnames = sdio_fwnames; ++ brcmf_sdio_fwnames_count = ARRAY_SIZE(sdio_fwnames); ++ kfree(fwreq); ++ fwreq = brcmf_sdio_prepare_fw_request(bus); ++ if (!fwreq) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ err = brcmf_fw_get_firmwares(dev, fwreq, ++ brcmf_sdio_firmware_callback); ++ if (!err) ++ return; ++ } ++ + if (err) + goto fail; + +@@ -4419,7 +4442,7 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus) + + fwreq = brcmf_fw_alloc_request(bus->ci->chip, bus->ci->chiprev, + brcmf_sdio_fwnames, +- ARRAY_SIZE(brcmf_sdio_fwnames), ++ brcmf_sdio_fwnames_count, + fwnames, ARRAY_SIZE(fwnames)); + if (!fwreq) + return NULL; +@@ -4437,6 +4460,9 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) + struct brcmf_sdio *bus; + struct workqueue_struct *wq; + struct brcmf_fw_request *fwreq; ++ struct brcmf_firmware_mapping *of_fwnames, *fwnames = NULL; ++ const int fwname_size = sizeof(struct brcmf_firmware_mapping); ++ u32 of_fw_count; + + brcmf_dbg(TRACE, "Enter\n"); + +@@ -4519,6 +4545,21 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) + + brcmf_dbg(INFO, "completed!!\n"); + ++ of_fwnames = brcmf_of_fwnames(sdiodev->dev, &of_fw_count); ++ if (of_fwnames) ++ fwnames = devm_kcalloc(sdiodev->dev, ++ of_fw_count + brcmf_sdio_fwnames_count, ++ fwname_size, GFP_KERNEL); ++ ++ if (fwnames) { ++ /* The array is scanned in order, so overrides come first */ ++ memcpy(fwnames, of_fwnames, of_fw_count * fwname_size); ++ memcpy(fwnames + of_fw_count, sdio_fwnames, ++ brcmf_sdio_fwnames_count * fwname_size); ++ brcmf_sdio_fwnames = fwnames; ++ brcmf_sdio_fwnames_count += of_fw_count; ++ } ++ + fwreq = brcmf_sdio_prepare_fw_request(bus); + if (!fwreq) { + ret = -ENOMEM; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c index 5a6d9c8..dbf197f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c diff --git a/recipes-kernel/mac80211/mac80211/0007-backport-of-rtl-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0007-backport-of-rtl-patches-from-openwrt.patch deleted file mode 100644 index d7c9cd2..0000000 --- a/recipes-kernel/mac80211/mac80211/0007-backport-of-rtl-patches-from-openwrt.patch +++ /dev/null @@ -1,811 +0,0 @@ -From 5219cf1f08755b8d8ca8019a47ccc29cba9f69ef Mon Sep 17 00:00:00 2001 -From: Patrick Walther -Date: Mon, 22 Mar 2021 16:44:09 +0100 -Subject: [PATCH] 0007 backport of rtl patches from openwrt - ---- - .../net/wireless/realtek/rtlwifi/rtl8821ae/table.c | 500 +++++++++++++++------ - 1 file changed, 370 insertions(+), 130 deletions(-) - -diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c -index 85093b3..ed72a2a 100644 ---- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c -+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c -@@ -249,7 +249,7 @@ u32 RTL8821AE_PHY_REG_ARRAY[] = { - 0x824, 0x00030FE0, - 0x828, 0x00000000, - 0x82C, 0x002081DD, -- 0x830, 0x2AAA8E24, -+ 0x830, 0x2AAAEEC8, - 0x834, 0x0037A706, - 0x838, 0x06489B44, - 0x83C, 0x0000095B, -@@ -324,10 +324,10 @@ u32 RTL8821AE_PHY_REG_ARRAY[] = { - 0x9D8, 0x00000000, - 0x9DC, 0x00000000, - 0x9E0, 0x00005D00, -- 0x9E4, 0x00000002, -+ 0x9E4, 0x00000003, - 0x9E8, 0x00000001, - 0xA00, 0x00D047C8, -- 0xA04, 0x01FF000C, -+ 0xA04, 0x01FF800C, - 0xA08, 0x8C8A8300, - 0xA0C, 0x2E68000F, - 0xA10, 0x9500BB78, -@@ -1320,7 +1320,11 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x083, 0x00021800, - 0x084, 0x00028000, - 0x085, 0x00048000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, -+ 0x086, 0x0009483A, -+ 0xA0000000, 0x00000000, - 0x086, 0x00094838, -+ 0xB0000000, 0x00000000, - 0x087, 0x00044980, - 0x088, 0x00048000, - 0x089, 0x0000D480, -@@ -1409,36 +1413,32 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x03C, 0x000CA000, - 0x0EF, 0x00000000, - 0x0EF, 0x00001100, -- 0xFF0F0104, 0xABCD, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0004ADF3, - 0x034, 0x00049DF0, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0004ADF3, - 0x034, 0x00049DF0, -- 0xFF0F0404, 0xCDEF, -- 0x034, 0x0004ADF3, -- 0x034, 0x00049DF0, -- 0xFF0F0200, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0004ADF5, - 0x034, 0x00049DF2, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x0004A0F3, -+ 0x034, 0x000490B1, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0004A0F3, - 0x034, 0x000490B1, -- 0xCDCDCDCD, 0xCDCD, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x0004ADF5, -+ 0x034, 0x00049DF2, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x0004ADF3, -+ 0x034, 0x00049DF0, -+ 0xA0000000, 0x00000000, - 0x034, 0x0004ADF7, - 0x034, 0x00049DF3, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0104, 0xABCD, -- 0x034, 0x00048DED, -- 0x034, 0x00047DEA, -- 0x034, 0x00046DE7, -- 0x034, 0x00045CE9, -- 0x034, 0x00044CE6, -- 0x034, 0x000438C6, -- 0x034, 0x00042886, -- 0x034, 0x00041486, -- 0x034, 0x00040447, -- 0xFF0F0204, 0xCDEF, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00048DED, - 0x034, 0x00047DEA, - 0x034, 0x00046DE7, -@@ -1448,7 +1448,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00042886, - 0x034, 0x00041486, - 0x034, 0x00040447, -- 0xFF0F0404, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00048DED, - 0x034, 0x00047DEA, - 0x034, 0x00046DE7, -@@ -1458,7 +1458,17 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00042886, - 0x034, 0x00041486, - 0x034, 0x00040447, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x000480AE, -+ 0x034, 0x000470AB, -+ 0x034, 0x0004608B, -+ 0x034, 0x00045069, -+ 0x034, 0x00044048, -+ 0x034, 0x00043045, -+ 0x034, 0x00042026, -+ 0x034, 0x00041023, -+ 0x034, 0x00040002, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x000480AE, - 0x034, 0x000470AB, - 0x034, 0x0004608B, -@@ -1468,7 +1478,17 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00042026, - 0x034, 0x00041023, - 0x034, 0x00040002, -- 0xCDCDCDCD, 0xCDCD, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x00048DED, -+ 0x034, 0x00047DEA, -+ 0x034, 0x00046DE7, -+ 0x034, 0x00045CE9, -+ 0x034, 0x00044CE6, -+ 0x034, 0x000438C6, -+ 0x034, 0x00042886, -+ 0x034, 0x00041486, -+ 0x034, 0x00040447, -+ 0xA0000000, 0x00000000, - 0x034, 0x00048DEF, - 0x034, 0x00047DEC, - 0x034, 0x00046DE9, -@@ -1478,38 +1498,36 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x0004248A, - 0x034, 0x0004108D, - 0x034, 0x0004008A, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0200, 0xABCD, -+ 0xB0000000, 0x00000000, -+ 0x80000210, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0002ADF4, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x0002A0F3, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0002A0F3, -- 0xCDCDCDCD, 0xCDCD, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x0002ADF4, -+ 0xA0000000, 0x00000000, - 0x034, 0x0002ADF7, -- 0xFF0F0200, 0xDEAD, -- 0xFF0F0104, 0xABCD, -- 0x034, 0x00029DF4, -- 0xFF0F0204, 0xCDEF, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00029DF4, -- 0xFF0F0404, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00029DF4, -- 0xFF0F0200, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00029DF1, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x000290F0, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x000290F0, -- 0xCDCDCDCD, 0xCDCD, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x00029DF1, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x00029DF4, -+ 0xA0000000, 0x00000000, - 0x034, 0x00029DF2, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0104, 0xABCD, -- 0x034, 0x00028DF1, -- 0x034, 0x00027DEE, -- 0x034, 0x00026DEB, -- 0x034, 0x00025CEC, -- 0x034, 0x00024CE9, -- 0x034, 0x000238CA, -- 0x034, 0x00022889, -- 0x034, 0x00021489, -- 0x034, 0x0002044A, -- 0xFF0F0204, 0xCDEF, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00028DF1, - 0x034, 0x00027DEE, - 0x034, 0x00026DEB, -@@ -1519,7 +1537,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00022889, - 0x034, 0x00021489, - 0x034, 0x0002044A, -- 0xFF0F0404, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00028DF1, - 0x034, 0x00027DEE, - 0x034, 0x00026DEB, -@@ -1529,7 +1547,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00022889, - 0x034, 0x00021489, - 0x034, 0x0002044A, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x000280AF, - 0x034, 0x000270AC, - 0x034, 0x0002608B, -@@ -1539,7 +1557,27 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00022026, - 0x034, 0x00021023, - 0x034, 0x00020002, -- 0xCDCDCDCD, 0xCDCD, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x000280AF, -+ 0x034, 0x000270AC, -+ 0x034, 0x0002608B, -+ 0x034, 0x00025069, -+ 0x034, 0x00024048, -+ 0x034, 0x00023045, -+ 0x034, 0x00022026, -+ 0x034, 0x00021023, -+ 0x034, 0x00020002, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x00028DF1, -+ 0x034, 0x00027DEE, -+ 0x034, 0x00026DEB, -+ 0x034, 0x00025CEC, -+ 0x034, 0x00024CE9, -+ 0x034, 0x000238CA, -+ 0x034, 0x00022889, -+ 0x034, 0x00021489, -+ 0x034, 0x0002044A, -+ 0xA0000000, 0x00000000, - 0x034, 0x00028DEE, - 0x034, 0x00027DEB, - 0x034, 0x00026CCD, -@@ -1549,27 +1587,24 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00022849, - 0x034, 0x00021449, - 0x034, 0x0002004D, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F02C0, 0xABCD, -+ 0xB0000000, 0x00000000, -+ 0x8000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x0000A0D7, -+ 0x034, 0x000090D3, -+ 0x034, 0x000080B1, -+ 0x034, 0x000070AE, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0000A0D7, - 0x034, 0x000090D3, - 0x034, 0x000080B1, - 0x034, 0x000070AE, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x034, 0x0000ADF7, - 0x034, 0x00009DF4, - 0x034, 0x00008DF1, - 0x034, 0x00007DEE, -- 0xFF0F02C0, 0xDEAD, -- 0xFF0F0104, 0xABCD, -- 0x034, 0x00006DEB, -- 0x034, 0x00005CEC, -- 0x034, 0x00004CE9, -- 0x034, 0x000038CA, -- 0x034, 0x00002889, -- 0x034, 0x00001489, -- 0x034, 0x0000044A, -- 0xFF0F0204, 0xCDEF, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00006DEB, - 0x034, 0x00005CEC, - 0x034, 0x00004CE9, -@@ -1577,7 +1612,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00002889, - 0x034, 0x00001489, - 0x034, 0x0000044A, -- 0xFF0F0404, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x00006DEB, - 0x034, 0x00005CEC, - 0x034, 0x00004CE9, -@@ -1585,7 +1620,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00002889, - 0x034, 0x00001489, - 0x034, 0x0000044A, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, - 0x034, 0x0000608D, - 0x034, 0x0000506B, - 0x034, 0x0000404A, -@@ -1593,7 +1628,23 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00002044, - 0x034, 0x00001025, - 0x034, 0x00000004, -- 0xCDCDCDCD, 0xCDCD, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x0000608D, -+ 0x034, 0x0000506B, -+ 0x034, 0x0000404A, -+ 0x034, 0x00003047, -+ 0x034, 0x00002044, -+ 0x034, 0x00001025, -+ 0x034, 0x00000004, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, -+ 0x034, 0x00006DEB, -+ 0x034, 0x00005CEC, -+ 0x034, 0x00004CE9, -+ 0x034, 0x000038CA, -+ 0x034, 0x00002889, -+ 0x034, 0x00001489, -+ 0x034, 0x0000044A, -+ 0xA0000000, 0x00000000, - 0x034, 0x00006DCD, - 0x034, 0x00005CCD, - 0x034, 0x00004CCA, -@@ -1601,11 +1652,11 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x034, 0x00002888, - 0x034, 0x00001488, - 0x034, 0x00000486, -- 0xFF0F0104, 0xDEAD, -+ 0xB0000000, 0x00000000, - 0x0EF, 0x00000000, - 0x018, 0x0001712A, - 0x0EF, 0x00000040, -- 0xFF0F0104, 0xABCD, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x035, 0x00000187, - 0x035, 0x00008187, - 0x035, 0x00010187, -@@ -1615,7 +1666,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x035, 0x00040188, - 0x035, 0x00048188, - 0x035, 0x00050188, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x035, 0x00000187, - 0x035, 0x00008187, - 0x035, 0x00010187, -@@ -1625,7 +1676,37 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x035, 0x00040188, - 0x035, 0x00048188, - 0x035, 0x00050188, -- 0xFF0F0404, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, -+ 0x035, 0x00000128, -+ 0x035, 0x00008128, -+ 0x035, 0x00010128, -+ 0x035, 0x000201C8, -+ 0x035, 0x000281C8, -+ 0x035, 0x000301C8, -+ 0x035, 0x000401C8, -+ 0x035, 0x000481C8, -+ 0x035, 0x000501C8, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x035, 0x00000145, -+ 0x035, 0x00008145, -+ 0x035, 0x00010145, -+ 0x035, 0x00020196, -+ 0x035, 0x00028196, -+ 0x035, 0x00030196, -+ 0x035, 0x000401C7, -+ 0x035, 0x000481C7, -+ 0x035, 0x000501C7, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x035, 0x00000128, -+ 0x035, 0x00008128, -+ 0x035, 0x00010128, -+ 0x035, 0x000201C8, -+ 0x035, 0x000281C8, -+ 0x035, 0x000301C8, -+ 0x035, 0x000401C8, -+ 0x035, 0x000481C8, -+ 0x035, 0x000501C8, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, - 0x035, 0x00000187, - 0x035, 0x00008187, - 0x035, 0x00010187, -@@ -1635,7 +1716,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x035, 0x00040188, - 0x035, 0x00048188, - 0x035, 0x00050188, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x035, 0x00000145, - 0x035, 0x00008145, - 0x035, 0x00010145, -@@ -1645,11 +1726,11 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x035, 0x000401C7, - 0x035, 0x000481C7, - 0x035, 0x000501C7, -- 0xFF0F0104, 0xDEAD, -+ 0xB0000000, 0x00000000, - 0x0EF, 0x00000000, - 0x018, 0x0001712A, - 0x0EF, 0x00000010, -- 0xFF0F0104, 0xABCD, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x036, 0x00085733, - 0x036, 0x0008D733, - 0x036, 0x00095733, -@@ -1662,7 +1743,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x036, 0x000CE4B4, - 0x036, 0x000D64B4, - 0x036, 0x000DE4B4, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x036, 0x00085733, - 0x036, 0x0008D733, - 0x036, 0x00095733, -@@ -1675,7 +1756,46 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x036, 0x000CE4B4, - 0x036, 0x000D64B4, - 0x036, 0x000DE4B4, -- 0xFF0F0404, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, -+ 0x036, 0x000063B5, -+ 0x036, 0x0000E3B5, -+ 0x036, 0x000163B5, -+ 0x036, 0x0001E3B5, -+ 0x036, 0x000263B5, -+ 0x036, 0x0002E3B5, -+ 0x036, 0x000363B5, -+ 0x036, 0x0003E3B5, -+ 0x036, 0x000463B5, -+ 0x036, 0x0004E3B5, -+ 0x036, 0x000563B5, -+ 0x036, 0x0005E3B5, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x036, 0x000056B3, -+ 0x036, 0x0000D6B3, -+ 0x036, 0x000156B3, -+ 0x036, 0x0001D6B3, -+ 0x036, 0x00026634, -+ 0x036, 0x0002E634, -+ 0x036, 0x00036634, -+ 0x036, 0x0003E634, -+ 0x036, 0x000467B4, -+ 0x036, 0x0004E7B4, -+ 0x036, 0x000567B4, -+ 0x036, 0x0005E7B4, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x036, 0x000063B5, -+ 0x036, 0x0000E3B5, -+ 0x036, 0x000163B5, -+ 0x036, 0x0001E3B5, -+ 0x036, 0x000263B5, -+ 0x036, 0x0002E3B5, -+ 0x036, 0x000363B5, -+ 0x036, 0x0003E3B5, -+ 0x036, 0x000463B5, -+ 0x036, 0x0004E3B5, -+ 0x036, 0x000563B5, -+ 0x036, 0x0005E3B5, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, - 0x036, 0x00085733, - 0x036, 0x0008D733, - 0x036, 0x00095733, -@@ -1688,7 +1808,7 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x036, 0x000CE4B4, - 0x036, 0x000D64B4, - 0x036, 0x000DE4B4, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x036, 0x000056B3, - 0x036, 0x0000D6B3, - 0x036, 0x000156B3, -@@ -1701,103 +1821,162 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x036, 0x0004E7B4, - 0x036, 0x000567B4, - 0x036, 0x0005E7B4, -- 0xFF0F0104, 0xDEAD, -+ 0xB0000000, 0x00000000, - 0x0EF, 0x00000000, - 0x0EF, 0x00000008, -- 0xFF0F0104, 0xABCD, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x03C, 0x000001C8, - 0x03C, 0x00000492, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x03C, 0x000001C8, - 0x03C, 0x00000492, -- 0xFF0F0404, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, -+ 0x03C, 0x000001B6, -+ 0x03C, 0x00000492, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x03C, 0x0000022A, -+ 0x03C, 0x00000594, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x03C, 0x000001B6, -+ 0x03C, 0x00000492, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, - 0x03C, 0x000001C8, - 0x03C, 0x00000492, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x03C, 0x0000022A, - 0x03C, 0x00000594, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0104, 0xABCD, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x03C, 0x00000800, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x03C, 0x00000800, -- 0xFF0F0404, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, - 0x03C, 0x00000800, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, - 0x03C, 0x00000820, -- 0xCDCDCDCD, 0xCDCD, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x03C, 0x00000820, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x03C, 0x00000800, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, -+ 0x03C, 0x00000800, -+ 0xA0000000, 0x00000000, - 0x03C, 0x00000900, -- 0xFF0F0104, 0xDEAD, -+ 0xB0000000, 0x00000000, - 0x0EF, 0x00000000, - 0x018, 0x0001712A, - 0x0EF, 0x00000002, -- 0xFF0F0104, 0xABCD, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x008, 0x0004E400, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x008, 0x0004E400, -- 0xFF0F0404, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, -+ 0x008, 0x00002000, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x008, 0x00002000, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x008, 0x00002000, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x008, 0x00002000, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, - 0x008, 0x0004E400, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x008, 0x00002000, -- 0xFF0F0104, 0xDEAD, -+ 0xB0000000, 0x00000000, - 0x0EF, 0x00000000, - 0x0DF, 0x000000C0, -- 0x01F, 0x00040064, -- 0xFF0F0104, 0xABCD, -+ 0x01F, 0x00000064, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x058, 0x000A7284, - 0x059, 0x000600EC, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x058, 0x000A7284, - 0x059, 0x000600EC, -- 0xFF0F0404, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x058, 0x00081184, -+ 0x059, 0x0006016C, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x058, 0x00081184, -+ 0x059, 0x0006016C, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x058, 0x00081184, -+ 0x059, 0x0006016C, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, - 0x058, 0x000A7284, - 0x059, 0x000600EC, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x058, 0x00081184, - 0x059, 0x0006016C, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0104, 0xABCD, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x061, 0x000E8D73, - 0x062, 0x00093FC5, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x061, 0x000E8D73, - 0x062, 0x00093FC5, -- 0xFF0F0404, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, -+ 0x061, 0x000EFD83, -+ 0x062, 0x00093FCC, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x061, 0x000EAD53, -+ 0x062, 0x00093BC4, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x061, 0x000EFD83, -+ 0x062, 0x00093FCC, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, - 0x061, 0x000E8D73, - 0x062, 0x00093FC5, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x061, 0x000EAD53, - 0x062, 0x00093BC4, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0104, 0xABCD, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, - 0x063, 0x000110E9, -- 0xFF0F0204, 0xCDEF, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, - 0x063, 0x000110E9, -- 0xFF0F0404, 0xCDEF, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, -+ 0x063, 0x000110EB, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, - 0x063, 0x000110E9, -- 0xFF0F0200, 0xCDEF, -- 0x063, 0x000710E9, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, - 0x063, 0x000110E9, -- 0xCDCDCDCD, 0xCDCD, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x063, 0x000110EB, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, -+ 0x063, 0x000110E9, -+ 0xA0000000, 0x00000000, - 0x063, 0x000714E9, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0104, 0xABCD, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, -+ 0x064, 0x0001C27C, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, -+ 0x064, 0x0001C27C, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, - 0x064, 0x0001C27C, -- 0xFF0F0204, 0xCDEF, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x064, 0x0001C67C, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, - 0x064, 0x0001C27C, -- 0xFF0F0404, 0xCDEF, -+ 0x90000410, 0x00000000, 0x40000000, 0x00000000, - 0x064, 0x0001C27C, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x064, 0x0001C67C, -- 0xFF0F0104, 0xDEAD, -- 0xFF0F0200, 0xABCD, -+ 0xB0000000, 0x00000000, -+ 0x80000111, 0x00000000, 0x40000000, 0x00000000, -+ 0x065, 0x00091016, -+ 0x90000110, 0x00000000, 0x40000000, 0x00000000, -+ 0x065, 0x00091016, -+ 0x90000210, 0x00000000, 0x40000000, 0x00000000, - 0x065, 0x00093016, -- 0xFF0F02C0, 0xCDEF, -+ 0x9000020c, 0x00000000, 0x40000000, 0x00000000, - 0x065, 0x00093015, -- 0xCDCDCDCD, 0xCDCD, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, -+ 0x065, 0x00093015, -+ 0x90000200, 0x00000000, 0x40000000, 0x00000000, -+ 0x065, 0x00093016, -+ 0xA0000000, 0x00000000, - 0x065, 0x00091016, -- 0xFF0F0200, 0xDEAD, -+ 0xB0000000, 0x00000000, - 0x018, 0x00000006, - 0x0EF, 0x00002000, - 0x03B, 0x0003824B, -@@ -1895,9 +2074,10 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { - 0x0B4, 0x0001214C, - 0x0B7, 0x0003000C, - 0x01C, 0x000539D2, -+ 0x0C4, 0x000AFE00, - 0x018, 0x0001F12A, -- 0x0FE, 0x00000000, -- 0x0FE, 0x00000000, -+ 0xFFE, 0x00000000, -+ 0xFFE, 0x00000000, - 0x018, 0x0001712A, - - }; -@@ -2017,6 +2197,7 @@ u32 RTL8812AE_MAC_REG_ARRAY[] = { - u32 RTL8812AE_MAC_1T_ARRAYLEN = ARRAY_SIZE(RTL8812AE_MAC_REG_ARRAY); - - u32 RTL8821AE_MAC_REG_ARRAY[] = { -+ 0x421, 0x0000000F, - 0x428, 0x0000000A, - 0x429, 0x00000010, - 0x430, 0x00000000, -@@ -2485,7 +2666,7 @@ u32 RTL8821AE_AGC_TAB_ARRAY[] = { - 0x81C, 0xA6360001, - 0x81C, 0xA5380001, - 0x81C, 0xA43A0001, -- 0x81C, 0xA33C0001, -+ 0x81C, 0x683C0001, - 0x81C, 0x673E0001, - 0x81C, 0x66400001, - 0x81C, 0x65420001, -@@ -2519,7 +2700,66 @@ u32 RTL8821AE_AGC_TAB_ARRAY[] = { - 0x81C, 0x017A0001, - 0x81C, 0x017C0001, - 0x81C, 0x017E0001, -- 0xFF0F02C0, 0xABCD, -+ 0x8000020c, 0x00000000, 0x40000000, 0x00000000, -+ 0x81C, 0xFB000101, -+ 0x81C, 0xFA020101, -+ 0x81C, 0xF9040101, -+ 0x81C, 0xF8060101, -+ 0x81C, 0xF7080101, -+ 0x81C, 0xF60A0101, -+ 0x81C, 0xF50C0101, -+ 0x81C, 0xF40E0101, -+ 0x81C, 0xF3100101, -+ 0x81C, 0xF2120101, -+ 0x81C, 0xF1140101, -+ 0x81C, 0xF0160101, -+ 0x81C, 0xEF180101, -+ 0x81C, 0xEE1A0101, -+ 0x81C, 0xED1C0101, -+ 0x81C, 0xEC1E0101, -+ 0x81C, 0xEB200101, -+ 0x81C, 0xEA220101, -+ 0x81C, 0xE9240101, -+ 0x81C, 0xE8260101, -+ 0x81C, 0xE7280101, -+ 0x81C, 0xE62A0101, -+ 0x81C, 0xE52C0101, -+ 0x81C, 0xE42E0101, -+ 0x81C, 0xE3300101, -+ 0x81C, 0xA5320101, -+ 0x81C, 0xA4340101, -+ 0x81C, 0xA3360101, -+ 0x81C, 0x87380101, -+ 0x81C, 0x863A0101, -+ 0x81C, 0x853C0101, -+ 0x81C, 0x843E0101, -+ 0x81C, 0x69400101, -+ 0x81C, 0x68420101, -+ 0x81C, 0x67440101, -+ 0x81C, 0x66460101, -+ 0x81C, 0x49480101, -+ 0x81C, 0x484A0101, -+ 0x81C, 0x474C0101, -+ 0x81C, 0x2A4E0101, -+ 0x81C, 0x29500101, -+ 0x81C, 0x28520101, -+ 0x81C, 0x27540101, -+ 0x81C, 0x26560101, -+ 0x81C, 0x25580101, -+ 0x81C, 0x245A0101, -+ 0x81C, 0x235C0101, -+ 0x81C, 0x055E0101, -+ 0x81C, 0x04600101, -+ 0x81C, 0x03620101, -+ 0x81C, 0x02640101, -+ 0x81C, 0x01660101, -+ 0x81C, 0x01680101, -+ 0x81C, 0x016A0101, -+ 0x81C, 0x016C0101, -+ 0x81C, 0x016E0101, -+ 0x81C, 0x01700101, -+ 0x81C, 0x01720101, -+ 0x9000040c, 0x00000000, 0x40000000, 0x00000000, - 0x81C, 0xFB000101, - 0x81C, 0xFA020101, - 0x81C, 0xF9040101, -@@ -2578,7 +2818,7 @@ u32 RTL8821AE_AGC_TAB_ARRAY[] = { - 0x81C, 0x016E0101, - 0x81C, 0x01700101, - 0x81C, 0x01720101, -- 0xCDCDCDCD, 0xCDCD, -+ 0xA0000000, 0x00000000, - 0x81C, 0xFF000101, - 0x81C, 0xFF020101, - 0x81C, 0xFE040101, -@@ -2637,7 +2877,7 @@ u32 RTL8821AE_AGC_TAB_ARRAY[] = { - 0x81C, 0x046E0101, - 0x81C, 0x03700101, - 0x81C, 0x02720101, -- 0xFF0F02C0, 0xDEAD, -+ 0xB0000000, 0x00000000, - 0x81C, 0x01740101, - 0x81C, 0x01760101, - 0x81C, 0x01780101, diff --git a/recipes-kernel/mac80211/mac80211/0008-backport-of-mwl-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0008-backport-of-mwl-patches-from-openwrt.patch new file mode 100644 index 0000000..8cc85be --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0008-backport-of-mwl-patches-from-openwrt.patch @@ -0,0 +1,280 @@ +From 7230aa4b1ad120a06c0607563c4358c609bbe8b3 Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 14 Sep 2022 14:31:25 +0200 +Subject: [PATCH] backport of mwl patches from openwrt + +--- + drivers/net/wireless/marvell/libertas/cfg.c | 4 + + drivers/net/wireless/marvell/libertas/main.c | 1 + + drivers/net/wireless/marvell/mwifiex/cmdevt.c | 96 +++++++++++++++++-- + drivers/net/wireless/marvell/mwifiex/decl.h | 4 +- + drivers/net/wireless/marvell/mwifiex/main.h | 2 + + .../wireless/marvell/mwifiex/sta_cmdresp.c | 5 +- + .../net/wireless/marvell/mwifiex/uap_cmd.c | 3 +- + drivers/net/wireless/marvell/mwl8k.c | 5 +- + 8 files changed, 105 insertions(+), 15 deletions(-) + +diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c +index 4e3de68..c861532 100644 +--- a/drivers/net/wireless/marvell/libertas/cfg.c ++++ b/drivers/net/wireless/marvell/libertas/cfg.c +@@ -2053,6 +2053,8 @@ struct wireless_dev *lbs_cfg_alloc(struct device *dev) + goto err_wiphy_new; + } + ++ set_wiphy_dev(wdev->wiphy, dev); ++ + return wdev; + + err_wiphy_new: +@@ -2127,6 +2129,8 @@ int lbs_cfg_register(struct lbs_private *priv) + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + wdev->wiphy->reg_notifier = lbs_reg_notifier; + ++ memcpy(wdev->wiphy->perm_addr, priv->current_addr, ETH_ALEN); ++ + ret = wiphy_register(wdev->wiphy); + if (ret < 0) + pr_err("cannot register wiphy device\n"); +diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c +index 64fc5e4..7048794 100644 +--- a/drivers/net/wireless/marvell/libertas/main.c ++++ b/drivers/net/wireless/marvell/libertas/main.c +@@ -935,6 +935,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) + goto err_adapter; + } + ++ dev_net_set(dev, wiphy_net(wdev->wiphy)); + dev->ieee80211_ptr = wdev; + dev->ml_priv = priv; + SET_NETDEV_DEV(dev, dmdev); +diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c +index 171a257..b0b8b1f 100644 +--- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c ++++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c +@@ -28,6 +28,85 @@ + + static void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); + ++const char * ++mwifiex_cmd_to_str(u16 command) ++{ ++ switch (command) { ++ case HostCmd_CMD_GET_HW_SPEC: return "GET_HW_SPEC"; ++ case HostCmd_CMD_802_11_SCAN: return "SCAN"; ++ case HostCmd_CMD_802_11_GET_LOG: return "GET_LOG"; ++ case HostCmd_CMD_MAC_MULTICAST_ADR: return "MAC_MULTICAST_ADR"; ++ case HostCmd_CMD_802_11_EEPROM_ACCESS: return "EEPROM_ACCESS"; ++ case HostCmd_CMD_802_11_ASSOCIATE: return "ASSOCIATE"; ++ case HostCmd_CMD_802_11_SNMP_MIB: return "SNMP_MIB"; ++ case HostCmd_CMD_MAC_REG_ACCESS: return "MAC_REG_ACCESS"; ++ case HostCmd_CMD_BBP_REG_ACCESS: return "BBP_REG_ACCESS"; ++ case HostCmd_CMD_RF_REG_ACCESS: return "RF_REG_ACCESS"; ++ case HostCmd_CMD_PMIC_REG_ACCESS: return "PMIC_REG_ACCESS"; ++ case HostCmd_CMD_RF_TX_PWR: return "RF_TX_PWR"; ++ case HostCmd_CMD_RF_ANTENNA: return "RF_ANTENNA"; ++ case HostCmd_CMD_802_11_DEAUTHENTICATE: return "DEAUTHENTICATE"; ++ case HostCmd_CMD_MAC_CONTROL: return "MAC_CONTROL"; ++ case HostCmd_CMD_802_11_AD_HOC_START: return "AD_HOC_START"; ++ case HostCmd_CMD_802_11_AD_HOC_JOIN: return "AD_HOC_JOIN"; ++ case HostCmd_CMD_802_11_AD_HOC_STOP: return "AD_HOC_STOP"; ++ case HostCmd_CMD_802_11_MAC_ADDRESS: return "MAC_ADDRESS"; ++ case HostCmd_CMD_802_11D_DOMAIN_INFO: return "DOMAIN_INFO"; ++ case HostCmd_CMD_802_11_KEY_MATERIAL: return "KEY_MATERIAL"; ++ case HostCmd_CMD_802_11_BG_SCAN_CONFIG: return "BG_SCAN_CONFIG"; ++ case HostCmd_CMD_802_11_BG_SCAN_QUERY: return "BG_SCAN_QUERY"; ++ case HostCmd_CMD_WMM_GET_STATUS: return "WMM_GET_STATUS"; ++ case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: return "SUBSCRIBE_EVENT"; ++ case HostCmd_CMD_802_11_TX_RATE_QUERY: return "TX_RATE_QUERY"; ++ case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: return "IBSS_COALESCING_STATUS"; ++ case HostCmd_CMD_MEM_ACCESS: return "MEM_ACCESS"; ++ case HostCmd_CMD_CFG_DATA: return "CFG_DATA"; ++ case HostCmd_CMD_VERSION_EXT: return "VERSION_EXT"; ++ case HostCmd_CMD_MEF_CFG: return "MEF_CFG"; ++ case HostCmd_CMD_RSSI_INFO: return "RSSI_INFO"; ++ case HostCmd_CMD_FUNC_INIT: return "FUNC_INIT"; ++ case HostCmd_CMD_FUNC_SHUTDOWN: return "FUNC_SHUTDOWN"; ++ case HOST_CMD_APCMD_SYS_RESET: return "SYS_RESET"; ++ case HostCmd_CMD_UAP_SYS_CONFIG: return "UAP_SYS_CONFIG"; ++ case HostCmd_CMD_UAP_BSS_START: return "UAP_BSS_START"; ++ case HostCmd_CMD_UAP_BSS_STOP: return "UAP_BSS_STOP"; ++ case HOST_CMD_APCMD_STA_LIST: return "STA_LIST"; ++ case HostCmd_CMD_UAP_STA_DEAUTH: return "UAP_STA_DEAUTH"; ++ case HostCmd_CMD_11N_CFG: return "11N_CFG"; ++ case HostCmd_CMD_11N_ADDBA_REQ: return "ADDBA_REQ"; ++ case HostCmd_CMD_11N_ADDBA_RSP: return "ADDBA_RSP"; ++ case HostCmd_CMD_11N_DELBA: return "DELBA"; ++ case HostCmd_CMD_RECONFIGURE_TX_BUFF: return "RECONFIGURE_TX_BUFF"; ++ case HostCmd_CMD_CHAN_REPORT_REQUEST: return "CHAN_REPORT_REQUEST"; ++ case HostCmd_CMD_AMSDU_AGGR_CTRL: return "AMSDU_AGGR_CTRL"; ++ case HostCmd_CMD_TXPWR_CFG: return "TXPWR_CFG"; ++ case HostCmd_CMD_TX_RATE_CFG: return "TX_RATE_CFG"; ++ case HostCmd_CMD_ROBUST_COEX: return "ROBUST_COEX"; ++ case HostCmd_CMD_802_11_PS_MODE_ENH: return "PS_MODE_ENH"; ++ case HostCmd_CMD_802_11_HS_CFG_ENH: return "HS_CFG_ENH"; ++ case HostCmd_CMD_P2P_MODE_CFG: return "P2P_MODE_CFG"; ++ case HostCmd_CMD_CAU_REG_ACCESS: return "CAU_REG_ACCESS"; ++ case HostCmd_CMD_SET_BSS_MODE: return "SET_BSS_MODE"; ++ case HostCmd_CMD_PCIE_DESC_DETAILS: return "PCIE_DESC_DETAILS"; ++ case HostCmd_CMD_802_11_SCAN_EXT: return "SCAN_EXT"; ++ case HostCmd_CMD_COALESCE_CFG: return "COALESCE_CFG"; ++ case HostCmd_CMD_MGMT_FRAME_REG: return "MGMT_FRAME_REG"; ++ case HostCmd_CMD_REMAIN_ON_CHAN: return "REMAIN_ON_CHAN"; ++ case HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG: return "GTK_REKEY_OFFLOAD_CFG"; ++ case HostCmd_CMD_11AC_CFG: return "11AC_CFG"; ++ case HostCmd_CMD_HS_WAKEUP_REASON: return "HS_WAKEUP_REASON"; ++ case HostCmd_CMD_TDLS_CONFIG: return "TDLS_CONFIG"; ++ case HostCmd_CMD_MC_POLICY: return "MC_POLICY"; ++ case HostCmd_CMD_TDLS_OPER: return "TDLS_OPER"; ++ case HostCmd_CMD_FW_DUMP_EVENT: return "FW_DUMP_EVENT"; ++ case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: return "SDIO_SP_RX_AGGR_CFG"; ++ case HostCmd_CMD_STA_CONFIGURE: return "STA_CONFIGURE"; ++ case HostCmd_CMD_CHAN_REGION_CFG: return "CHAN_REGION_CFG"; ++ case HostCmd_CMD_PACKET_AGGR_CTRL: return "PACKET_AGGR_CTRL"; ++ default: return "UNKNOWN"; ++ } ++} ++ + /* + * This function initializes a command node. + * +@@ -205,8 +284,8 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, + cmd_code != HostCmd_CMD_FUNC_SHUTDOWN && + cmd_code != HostCmd_CMD_FUNC_INIT) { + mwifiex_dbg(adapter, ERROR, +- "DNLD_CMD: FW in reset state, ignore cmd %#x\n", +- cmd_code); ++ "DNLD_CMD: FW in reset state, ignore cmd %s (%#x)\n", ++ mwifiex_cmd_to_str(cmd_code), cmd_code); + mwifiex_recycle_cmd_node(adapter, cmd_node); + queue_work(adapter->workqueue, &adapter->main_work); + return -1; +@@ -660,8 +739,8 @@ int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, + /* Return error, since the command preparation failed */ + if (ret) { + mwifiex_dbg(adapter, ERROR, +- "PREP_CMD: cmd %#x preparation failed\n", +- cmd_no); ++ "PREP_CMD: cmd %s (%#x) preparation failed\n", ++ mwifiex_cmd_to_str(cmd_no), cmd_no); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + return -1; + } +@@ -900,8 +979,9 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { + if (ret) { + mwifiex_dbg(adapter, ERROR, +- "%s: cmd %#x failed during\t" +- "initialization\n", __func__, cmdresp_no); ++ "%s: cmd %s (%#x) failed during\t" ++ "initialization\n", __func__, ++ mwifiex_cmd_to_str(cmdresp_no), cmdresp_no); + mwifiex_init_fw_complete(adapter); + return -1; + } else if (adapter->last_init_cmd == cmdresp_no) +@@ -1264,8 +1344,8 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, + + if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { + mwifiex_dbg(adapter, ERROR, +- "%s: rcvd unexpected resp for cmd %#x, result = %x\n", +- __func__, command, result); ++ "%s: rcvd unexpected resp for cmd %s (%#x), result = %x\n", ++ __func__, mwifiex_cmd_to_str(command), command, result); + return; + } + +diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h +index 6bd23c9..518dce4 100644 +--- a/drivers/net/wireless/marvell/mwifiex/decl.h ++++ b/drivers/net/wireless/marvell/mwifiex/decl.h +@@ -30,7 +30,7 @@ + #include + + #define MWIFIEX_BSS_COEX_COUNT 2 +-#define MWIFIEX_MAX_BSS_NUM (3) ++#define MWIFIEX_MAX_BSS_NUM (4) + + #define MWIFIEX_DMA_ALIGN_SZ 64 + #define MWIFIEX_RX_HEADROOM 64 +@@ -112,7 +112,7 @@ + #define MWIFIEX_RATE_INDEX_OFDM0 4 + + #define MWIFIEX_MAX_STA_NUM 3 +-#define MWIFIEX_MAX_UAP_NUM 3 ++#define MWIFIEX_MAX_UAP_NUM 4 + #define MWIFIEX_MAX_P2P_NUM 3 + + #define MWIFIEX_A_BAND_START_FREQ 5000 +diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h +index 5923c5c..0d30f61 100644 +--- a/drivers/net/wireless/marvell/mwifiex/main.h ++++ b/drivers/net/wireless/marvell/mwifiex/main.h +@@ -1106,6 +1106,8 @@ void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); + void mwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter); + void mwifiex_cancel_scan(struct mwifiex_adapter *adapter); + ++const char *mwifiex_cmd_to_str(u16 command); ++ + void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +index 6b5d35d..ae6554f 100644 +--- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c ++++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +@@ -48,8 +48,9 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, + struct host_cmd_ds_802_11_ps_mode_enh *pm; + + mwifiex_dbg(adapter, ERROR, +- "CMD_RESP: cmd %#x error, result=%#x\n", +- resp->command, resp->result); ++ "CMD_RESP: cmd %s (%#x) error, result=%#x\n", ++ mwifiex_cmd_to_str(le16_to_cpu(resp->command)), ++ le16_to_cpu(resp->command), le16_to_cpu(resp->result)); + + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; +diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +index 18e8977..29b8af5 100644 +--- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c ++++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +@@ -806,7 +806,8 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, + break; + default: + mwifiex_dbg(priv->adapter, ERROR, +- "PREP_CMD: unknown cmd %#x\n", cmd_no); ++ "PREP_CMD: unknown cmd (%s) %#x\n", ++ mwifiex_cmd_to_str(cmd_no), cmd_no); + return -1; + } + +diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c +index 529e325..fa1f639 100644 +--- a/drivers/net/wireless/marvell/mwl8k.c ++++ b/drivers/net/wireless/marvell/mwl8k.c +@@ -5699,6 +5699,7 @@ MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); + MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API)); + + static const struct pci_device_id mwl8k_pci_id_table[] = { ++ { PCI_VDEVICE(MARVELL, 0x2a02), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, +@@ -6284,6 +6285,8 @@ static int mwl8k_probe(struct pci_dev *pdev, + + priv->running_bsses = 0; + ++ wait_for_completion(&priv->firmware_loading_complete); ++ + return rc; + + err_stop_firmware: +@@ -6317,8 +6320,6 @@ static void mwl8k_remove(struct pci_dev *pdev) + return; + priv = hw->priv; + +- wait_for_completion(&priv->firmware_loading_complete); +- + if (priv->fw_state == FW_STATE_ERROR) { + mwl8k_hw_reset(priv); + goto unmap; diff --git a/recipes-kernel/mac80211/mac80211/0008-fragattack.patch b/recipes-kernel/mac80211/mac80211/0008-fragattack.patch deleted file mode 100644 index 1e7657b..0000000 --- a/recipes-kernel/mac80211/mac80211/0008-fragattack.patch +++ /dev/null @@ -1,1083 +0,0 @@ -diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h -index c2a5825..58b7749 100644 ---- a/drivers/net/wireless/ath/ath10k/htt.h -+++ b/drivers/net/wireless/ath/ath10k/htt.h -@@ -846,6 +846,7 @@ enum htt_security_types { - - #define ATH10K_HTT_TXRX_PEER_SECURITY_MAX 2 - #define ATH10K_TXRX_NUM_EXT_TIDS 19 -+#define ATH10K_TXRX_NON_QOS_TID 16 - - enum htt_security_flags { - #define HTT_SECURITY_TYPE_MASK 0x7F -diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c -index 5c1af20..28ec3c5 100644 ---- a/drivers/net/wireless/ath/ath10k/htt_rx.c -+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c -@@ -1746,16 +1746,97 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu) - msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu); - } - -+static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb, -+ u16 offset, -+ enum htt_rx_mpdu_encrypt_type enctype) -+{ -+ struct ieee80211_hdr *hdr; -+ u64 pn = 0; -+ u8 *ehdr; -+ -+ hdr = (struct ieee80211_hdr *)(skb->data + offset); -+ ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control); -+ -+ if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) { -+ pn = ehdr[0]; -+ pn |= (u64)ehdr[1] << 8; -+ pn |= (u64)ehdr[4] << 16; -+ pn |= (u64)ehdr[5] << 24; -+ pn |= (u64)ehdr[6] << 32; -+ pn |= (u64)ehdr[7] << 40; -+ } -+ return pn; -+} -+ -+static bool ath10k_htt_rx_h_frag_multicast_check(struct ath10k *ar, -+ struct sk_buff *skb, -+ u16 offset) -+{ -+ struct ieee80211_hdr *hdr; -+ -+ hdr = (struct ieee80211_hdr *)(skb->data + offset); -+ return !is_multicast_ether_addr(hdr->addr1); -+} -+ -+static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar, -+ struct sk_buff *skb, -+ u16 peer_id, -+ u16 offset, -+ enum htt_rx_mpdu_encrypt_type enctype) -+{ -+ struct ath10k_peer *peer; -+ union htt_rx_pn_t *last_pn, new_pn = {0}; -+ struct ieee80211_hdr *hdr; -+ bool more_frags; -+ u8 tid, frag_number; -+ u32 seq; -+ -+ peer = ath10k_peer_find_by_id(ar, peer_id); -+ if (!peer) { -+ ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer for frag pn check\n"); -+ return false; -+ } -+ -+ hdr = (struct ieee80211_hdr *)(skb->data + offset); -+ if (ieee80211_is_data_qos(hdr->frame_control)) -+ tid = ieee80211_get_tid(hdr); -+ else -+ tid = ATH10K_TXRX_NON_QOS_TID; -+ -+ last_pn = &peer->frag_tids_last_pn[tid]; -+ new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype); -+ more_frags = ieee80211_has_morefrags(hdr->frame_control); -+ frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; -+ seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; -+ -+ if (frag_number == 0) { -+ last_pn->pn48 = new_pn.pn48; -+ peer->frag_tids_seq[tid] = seq; -+ } else { -+ if (seq != peer->frag_tids_seq[tid]) -+ return false; -+ -+ if (new_pn.pn48 != last_pn->pn48 + 1) -+ return false; -+ -+ last_pn->pn48 = new_pn.pn48; -+ } -+ -+ return true; -+} -+ - static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, - struct sk_buff_head *amsdu, - struct ieee80211_rx_status *status, - bool fill_crypt_header, - u8 *rx_hdr, -- enum ath10k_pkt_rx_err *err) -+ enum ath10k_pkt_rx_err *err, -+ u16 peer_id, -+ bool frag) - { - struct sk_buff *first; - struct sk_buff *last; -- struct sk_buff *msdu; -+ struct sk_buff *msdu, *temp; - struct htt_rx_desc *rxd; - struct ieee80211_hdr *hdr; - enum htt_rx_mpdu_encrypt_type enctype; -@@ -1768,6 +1849,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, - bool is_decrypted; - bool is_mgmt; - u32 attention; -+ bool frag_pn_check = true, multicast_check = true; - - if (skb_queue_empty(amsdu)) - return; -@@ -1866,7 +1948,37 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, - } - - skb_queue_walk(amsdu, msdu) { -+ if (frag && !fill_crypt_header && is_decrypted && -+ enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) -+ frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar, -+ msdu, -+ peer_id, -+ 0, -+ enctype); -+ -+ if (frag) -+ multicast_check = ath10k_htt_rx_h_frag_multicast_check(ar, -+ msdu, -+ 0); -+ -+ if (!frag_pn_check || !multicast_check) { -+ /* Discard the fragment with invalid PN or multicast DA -+ */ -+ temp = msdu->prev; -+ __skb_unlink(msdu, amsdu); -+ dev_kfree_skb_any(msdu); -+ msdu = temp; -+ frag_pn_check = true; -+ multicast_check = true; -+ continue; -+ } -+ - ath10k_htt_rx_h_csum_offload(msdu); -+ -+ if (frag && !fill_crypt_header && -+ enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) -+ status->flag &= ~RX_FLAG_MMIC_STRIPPED; -+ - ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype, - is_decrypted); - -@@ -1884,6 +1996,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, - - hdr = (void *)msdu->data; - hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); -+ -+ if (frag && !fill_crypt_header && -+ enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) -+ status->flag &= ~RX_FLAG_IV_STRIPPED & -+ ~RX_FLAG_MMIC_STRIPPED; - } - } - -@@ -1991,14 +2108,62 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, - ath10k_unchain_msdu(amsdu, unchain_cnt); - } - -+static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar, -+ struct sk_buff_head *amsdu) -+{ -+ u8 *subframe_hdr; -+ struct sk_buff *first; -+ bool is_first, is_last; -+ struct htt_rx_desc *rxd; -+ struct ieee80211_hdr *hdr; -+ size_t hdr_len, crypto_len; -+ enum htt_rx_mpdu_encrypt_type enctype; -+ int bytes_aligned = ar->hw_params.decap_align_bytes; -+ -+ first = skb_peek(amsdu); -+ -+ rxd = (void *)first->data - sizeof(*rxd); -+ hdr = (void *)rxd->rx_hdr_status; -+ -+ is_first = !!(rxd->msdu_end.common.info0 & -+ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)); -+ is_last = !!(rxd->msdu_end.common.info0 & -+ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)); -+ -+ /* Return in case of non-aggregated msdu */ -+ if (is_first && is_last) -+ return true; -+ -+ /* First msdu flag is not set for the first msdu of the list */ -+ if (!is_first) -+ return false; -+ -+ enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), -+ RX_MPDU_START_INFO0_ENCRYPT_TYPE); -+ -+ hdr_len = ieee80211_hdrlen(hdr->frame_control); -+ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype); -+ -+ subframe_hdr = (u8 *)hdr + round_up(hdr_len, bytes_aligned) + -+ crypto_len; -+ -+ /* Validate if the amsdu has a proper first subframe. -+ * There are chances a single msdu can be received as amsdu when -+ * the unauthenticated amsdu flag of a QoS header -+ * gets flipped in non-SPP AMSDU's, in such cases the first -+ * subframe has llc/snap header in place of a valid da. -+ * return false if the da matches rfc1042 pattern -+ */ -+ if (ether_addr_equal(subframe_hdr, rfc1042_header)) -+ return false; -+ -+ return true; -+} -+ - static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar, - struct sk_buff_head *amsdu, - struct ieee80211_rx_status *rx_status) - { -- /* FIXME: It might be a good idea to do some fuzzy-testing to drop -- * invalid/dangerous frames. -- */ -- - if (!rx_status->freq) { - ath10k_dbg(ar, ATH10K_DBG_HTT, "no channel configured; ignoring frame(s)!\n"); - return false; -@@ -2009,6 +2174,11 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar, - return false; - } - -+ if (!ath10k_htt_rx_validate_amsdu(ar, amsdu)) { -+ ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid amsdu received\n"); -+ return false; -+ } -+ - return true; - } - -@@ -2071,7 +2241,8 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt) - ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt); - - ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter); -- ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err); -+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err, 0, -+ false); - msdus_to_queue = skb_queue_len(&amsdu); - ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status); - -@@ -2204,6 +2375,11 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, - fw_desc = &rx->fw_desc; - rx_desc_len = fw_desc->len; - -+ if (fw_desc->u.bits.discard) { -+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt discard mpdu\n"); -+ goto err; -+ } -+ - /* I have not yet seen any case where num_mpdu_ranges > 1. - * qcacld does not seem handle that case either, so we introduce the - * same limitiation here as well. -@@ -2509,6 +2685,13 @@ static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt, - rx_desc = (struct htt_hl_rx_desc *)(skb->data + tot_hdr_len); - rx_desc_info = __le32_to_cpu(rx_desc->info); - -+ hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len); -+ -+ if (is_multicast_ether_addr(hdr->addr1)) { -+ /* Discard the fragment with multicast DA */ -+ goto err; -+ } -+ - if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) { - spin_unlock_bh(&ar->data_lock); - return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb, -@@ -2516,8 +2699,6 @@ static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt, - HTT_RX_NON_TKIP_MIC); - } - -- hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len); -- - if (ieee80211_has_retry(hdr->frame_control)) - goto err; - -@@ -3027,7 +3208,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) - ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id); - ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL); - ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL, -- NULL); -+ NULL, peer_id, frag); - ath10k_htt_rx_h_enqueue(ar, &amsdu, status); - break; - case -EAGAIN: -diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h -index dec1582..13a1cae 100644 ---- a/drivers/net/wireless/ath/ath10k/rx_desc.h -+++ b/drivers/net/wireless/ath/ath10k/rx_desc.h -@@ -1282,7 +1282,19 @@ struct fw_rx_desc_base { - #define FW_RX_DESC_UDP (1 << 6) - - struct fw_rx_desc_hl { -- u8 info0; -+ union { -+ struct { -+ u8 discard:1, -+ forward:1, -+ any_err:1, -+ dup_err:1, -+ reserved:1, -+ inspect:1, -+ extension:2; -+ } bits; -+ u8 info0; -+ } u; -+ - u8 version; - u8 len; - u8 flags; -diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c -index 3638501..1ab59d6 100644 ---- a/drivers/net/wireless/ath/ath11k/dp_rx.c -+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c -@@ -262,6 +262,16 @@ static u32 ath11k_dp_rxdesc_get_ppduid(struct hal_rx_desc *rx_desc) - return __le16_to_cpu(rx_desc->mpdu_start.phy_ppdu_id); - } - -+static bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab, -+ struct hal_rx_desc *desc) -+{ -+ struct rx_attention *attn = ath11k_dp_rx_get_attention(ab, desc); -+ -+ return ath11k_dp_rx_h_msdu_end_first_msdu(ab, desc) && -+ (!!FIELD_GET(RX_ATTENTION_INFO1_MCAST_BCAST, -+ __le32_to_cpu(attn->info1))); -+} -+ - static void ath11k_dp_service_mon_ring(struct timer_list *t) - { - struct ath11k_base *ab = from_timer(ab, t, mon_reap_timer); -@@ -832,6 +842,24 @@ static void ath11k_dp_rx_frags_cleanup(struct dp_rx_tid *rx_tid, bool rel_link_d - __skb_queue_purge(&rx_tid->rx_frags); - } - -+void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer) -+{ -+ struct dp_rx_tid *rx_tid; -+ int i; -+ -+ lockdep_assert_held(&ar->ab->base_lock); -+ -+ for (i = 0; i <= IEEE80211_NUM_TIDS; i++) { -+ rx_tid = &peer->rx_tid[i]; -+ -+ spin_unlock_bh(&ar->ab->base_lock); -+ del_timer_sync(&rx_tid->frag_timer); -+ spin_lock_bh(&ar->ab->base_lock); -+ -+ ath11k_dp_rx_frags_cleanup(rx_tid, true); -+ } -+} -+ - void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) - { - struct dp_rx_tid *rx_tid; -@@ -3415,6 +3443,7 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, - u8 tid; - int ret = 0; - bool more_frags; -+ bool is_mcbc; - - rx_desc = (struct hal_rx_desc *)msdu->data; - peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(rx_desc); -@@ -3422,6 +3451,11 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, - seqno = ath11k_dp_rx_h_mpdu_start_seq_no(rx_desc); - frag_no = ath11k_dp_rx_h_mpdu_start_frag_no(msdu); - more_frags = ath11k_dp_rx_h_mpdu_start_more_frags(msdu); -+ is_mcbc = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc); -+ -+ /* Multicast/Broadcast fragments are not expected */ -+ if (is_mcbc) -+ return -EINVAL; - - if (!ath11k_dp_rx_h_mpdu_start_seq_ctrl_valid(rx_desc) || - !ath11k_dp_rx_h_mpdu_start_fc_valid(rx_desc) || -diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h -index fbea45f..6986752 100644 ---- a/drivers/net/wireless/ath/ath11k/dp_rx.h -+++ b/drivers/net/wireless/ath/ath11k/dp_rx.h -@@ -49,6 +49,7 @@ int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif, - const u8 *peer_addr, - enum set_key_cmd key_cmd, - struct ieee80211_key_conf *key); -+void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer); - void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer); - void ath11k_peer_rx_tid_delete(struct ath11k *ar, - struct ath11k_peer *peer, u8 tid); -diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c -index e33f598..cfda83d 100644 ---- a/drivers/net/wireless/ath/ath11k/mac.c -+++ b/drivers/net/wireless/ath/ath11k/mac.c -@@ -2525,6 +2525,12 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - */ - spin_lock_bh(&ab->base_lock); - peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); -+ -+ /* flush the fragments cache during key (re)install to -+ * ensure all frags in the new frag list belong to the same key. -+ */ -+ if (peer && cmd == SET_KEY) -+ ath11k_peer_frags_flush(ar, peer); - spin_unlock_bh(&ab->base_lock); - - if (!peer) { -diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h -index 449df09..092156b 100644 ---- a/include/net/cfg80211.h -+++ b/include/net/cfg80211.h -@@ -5630,7 +5630,7 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr); - */ - int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, - const u8 *addr, enum nl80211_iftype iftype, -- u8 data_offset); -+ u8 data_offset, bool is_amsdu); - - /** - * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3 -@@ -5642,7 +5642,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, - static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype) - { -- return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0); -+ return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0, false); - } - - /** -diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h -index d588fb5..d924e92 100644 ---- a/net/mac80211/ieee80211_i.h -+++ b/net/mac80211/ieee80211_i.h -@@ -50,12 +50,6 @@ struct ieee80211_local; - #define IEEE80211_ENCRYPT_HEADROOM 8 - #define IEEE80211_ENCRYPT_TAILROOM 18 - --/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent -- * reception of at least three fragmented frames. This limit can be increased -- * by changing this define, at the cost of slower frame reassembly and -- * increased memory use (about 2 kB of RAM per entry). */ --#define IEEE80211_FRAGMENT_MAX 4 -- - /* power level hasn't been configured (or set to automatic) */ - #define IEEE80211_UNSET_POWER_LEVEL INT_MIN - -@@ -88,18 +82,6 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS]; - - #define IEEE80211_MAX_NAN_INSTANCE_ID 255 - --struct ieee80211_fragment_entry { -- struct sk_buff_head skb_list; -- unsigned long first_frag_time; -- u16 seq; -- u16 extra_len; -- u16 last_frag; -- u8 rx_queue; -- bool check_sequential_pn; /* needed for CCMP/GCMP */ -- u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ --}; -- -- - struct ieee80211_bss { - u32 device_ts_beacon, device_ts_presp; - -@@ -245,8 +227,15 @@ struct ieee80211_rx_data { - */ - int security_idx; - -- u32 tkip_iv32; -- u16 tkip_iv16; -+ union { -+ struct { -+ u32 iv32; -+ u16 iv16; -+ } tkip; -+ struct { -+ u8 pn[IEEE80211_CCMP_PN_LEN]; -+ } ccm_gcm; -+ }; - }; - - struct ieee80211_csa_settings { -@@ -911,9 +900,7 @@ struct ieee80211_sub_if_data { - - char name[IFNAMSIZ]; - -- /* Fragment table for host-based reassembly */ -- struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; -- unsigned int fragment_next; -+ struct ieee80211_fragment_cache frags; - - /* TID bitmap for NoAck policy */ - u16 noack_map; -@@ -2334,4 +2321,7 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, - #define debug_noinline - #endif - -+void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache); -+void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); -+ - #endif /* IEEE80211_I_H */ -diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c -index bf17fa9..bf305b2 100644 ---- a/net/mac80211/iface.c -+++ b/net/mac80211/iface.c -@@ -8,7 +8,7 @@ - * Copyright 2008, Johannes Berg - * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (c) 2016 Intel Deutschland GmbH -- * Copyright (C) 2018-2020 Intel Corporation -+ * Copyright (C) 2018-2021 Intel Corporation - */ - #include - #include -@@ -679,16 +679,12 @@ static void ieee80211_set_multicast_list(struct net_device *dev) - */ - static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) - { -- int i; -- - /* free extra data */ - ieee80211_free_keys(sdata, false); - - ieee80211_debugfs_remove_netdev(sdata); - -- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) -- __skb_queue_purge(&sdata->fragments[i].skb_list); -- sdata->fragment_next = 0; -+ ieee80211_destroy_frag_cache(&sdata->frags); - - if (ieee80211_vif_is_mesh(&sdata->vif)) - ieee80211_mesh_teardown_sdata(sdata); -@@ -2038,8 +2034,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, - sdata->wdev.wiphy = local->hw.wiphy; - sdata->local = local; - -- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) -- skb_queue_head_init(&sdata->fragments[i].skb_list); -+ ieee80211_init_frag_cache(&sdata->frags); - - INIT_LIST_HEAD(&sdata->key_list); - -diff --git a/net/mac80211/key.c b/net/mac80211/key.c -index 8c5f829..6a72c33 100644 ---- a/net/mac80211/key.c -+++ b/net/mac80211/key.c -@@ -799,6 +799,7 @@ int ieee80211_key_link(struct ieee80211_key *key, - struct ieee80211_sub_if_data *sdata, - struct sta_info *sta) - { -+ static atomic_t key_color = ATOMIC_INIT(0); - struct ieee80211_key *old_key; - int idx = key->conf.keyidx; - bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; -@@ -850,6 +851,12 @@ int ieee80211_key_link(struct ieee80211_key *key, - key->sdata = sdata; - key->sta = sta; - -+ /* -+ * Assign a unique ID to every key so we can easily prevent mixed -+ * key and fragment cache attacks. -+ */ -+ key->color = atomic_inc_return(&key_color); -+ - increment_tailroom_need_count(sdata); - - ret = ieee80211_key_replace(sdata, sta, pairwise, old_key, key); -diff --git a/net/mac80211/key.h b/net/mac80211/key.h -index 81d3370..591ebcc 100644 ---- a/net/mac80211/key.h -+++ b/net/mac80211/key.h -@@ -128,6 +128,8 @@ struct ieee80211_key { - } debugfs; - #endif - -+ unsigned int color; -+ - /* - * key config, must be last because it contains key - * material as variable length member -diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c -index adeac74..aada4f6 100644 ---- a/net/mac80211/rx.c -+++ b/net/mac80211/rx.c -@@ -6,7 +6,7 @@ - * Copyright 2007-2010 Johannes Berg - * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2017 Intel Deutschland GmbH -- * Copyright (C) 2018-2020 Intel Corporation -+ * Copyright (C) 2018-2021 Intel Corporation - */ - - #include -@@ -2133,19 +2133,34 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) - return result; - } - -+void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(cache->entries); i++) -+ skb_queue_head_init(&cache->entries[i].skb_list); -+} -+ -+void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(cache->entries); i++) -+ __skb_queue_purge(&cache->entries[i].skb_list); -+} -+ - static inline struct ieee80211_fragment_entry * --ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, -+ieee80211_reassemble_add(struct ieee80211_fragment_cache *cache, - unsigned int frag, unsigned int seq, int rx_queue, - struct sk_buff **skb) - { - struct ieee80211_fragment_entry *entry; - -- entry = &sdata->fragments[sdata->fragment_next++]; -- if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) -- sdata->fragment_next = 0; -+ entry = &cache->entries[cache->next++]; -+ if (cache->next >= IEEE80211_FRAGMENT_MAX) -+ cache->next = 0; - -- if (!skb_queue_empty(&entry->skb_list)) -- __skb_queue_purge(&entry->skb_list); -+ __skb_queue_purge(&entry->skb_list); - - __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */ - *skb = NULL; -@@ -2160,14 +2175,14 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, - } - - static inline struct ieee80211_fragment_entry * --ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, -+ieee80211_reassemble_find(struct ieee80211_fragment_cache *cache, - unsigned int frag, unsigned int seq, - int rx_queue, struct ieee80211_hdr *hdr) - { - struct ieee80211_fragment_entry *entry; - int i, idx; - -- idx = sdata->fragment_next; -+ idx = cache->next; - for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { - struct ieee80211_hdr *f_hdr; - struct sk_buff *f_skb; -@@ -2176,7 +2191,7 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, - if (idx < 0) - idx = IEEE80211_FRAGMENT_MAX - 1; - -- entry = &sdata->fragments[idx]; -+ entry = &cache->entries[idx]; - if (skb_queue_empty(&entry->skb_list) || entry->seq != seq || - entry->rx_queue != rx_queue || - entry->last_frag + 1 != frag) -@@ -2204,15 +2219,27 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, - return NULL; - } - -+static bool requires_sequential_pn(struct ieee80211_rx_data *rx, __le16 fc) -+{ -+ return rx->key && -+ (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP || -+ rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256 || -+ rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP || -+ rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) && -+ ieee80211_has_protected(fc); -+} -+ - static ieee80211_rx_result debug_noinline - ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) - { -+ struct ieee80211_fragment_cache *cache = &rx->sdata->frags; - struct ieee80211_hdr *hdr; - u16 sc; - __le16 fc; - unsigned int frag, seq; - struct ieee80211_fragment_entry *entry; - struct sk_buff *skb; -+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - - hdr = (struct ieee80211_hdr *)rx->skb->data; - fc = hdr->frame_control; -@@ -2228,6 +2255,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) - goto out_no_led; - } - -+ if (rx->sta) -+ cache = &rx->sta->frags; -+ - if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) - goto out; - -@@ -2246,20 +2276,17 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) - - if (frag == 0) { - /* This is the first fragment of a new frame. */ -- entry = ieee80211_reassemble_add(rx->sdata, frag, seq, -+ entry = ieee80211_reassemble_add(cache, frag, seq, - rx->seqno_idx, &(rx->skb)); -- if (rx->key && -- (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP || -- rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256 || -- rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP || -- rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) && -- ieee80211_has_protected(fc)) { -+ if (requires_sequential_pn(rx, fc)) { - int queue = rx->security_idx; - - /* Store CCMP/GCMP PN so that we can verify that the - * next fragment has a sequential PN value. - */ - entry->check_sequential_pn = true; -+ entry->is_protected = true; -+ entry->key_color = rx->key->color; - memcpy(entry->last_pn, - rx->key->u.ccmp.rx_pn[queue], - IEEE80211_CCMP_PN_LEN); -@@ -2271,6 +2298,11 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) - sizeof(rx->key->u.gcmp.rx_pn[queue])); - BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != - IEEE80211_GCMP_PN_LEN); -+ } else if (rx->key && -+ (ieee80211_has_protected(fc) || -+ (status->flag & RX_FLAG_DECRYPTED))) { -+ entry->is_protected = true; -+ entry->key_color = rx->key->color; - } - return RX_QUEUED; - } -@@ -2278,7 +2310,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) - /* This is a fragment for a frame that should already be pending in - * fragment cache. Add this fragment to the end of the pending entry. - */ -- entry = ieee80211_reassemble_find(rx->sdata, frag, seq, -+ entry = ieee80211_reassemble_find(cache, frag, seq, - rx->seqno_idx, hdr); - if (!entry) { - I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); -@@ -2293,25 +2325,39 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) - if (entry->check_sequential_pn) { - int i; - u8 pn[IEEE80211_CCMP_PN_LEN], *rpn; -- int queue; - -- if (!rx->key || -- (rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP && -- rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256 && -- rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP && -- rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP_256)) -+ if (!requires_sequential_pn(rx, fc)) -+ return RX_DROP_UNUSABLE; -+ -+ /* Prevent mixed key and fragment cache attacks */ -+ if (entry->key_color != rx->key->color) - return RX_DROP_UNUSABLE; -+ - memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); - for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) { - pn[i]++; - if (pn[i]) - break; - } -- queue = rx->security_idx; -- rpn = rx->key->u.ccmp.rx_pn[queue]; -+ -+ rpn = rx->ccm_gcm.pn; - if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN)) - return RX_DROP_UNUSABLE; - memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN); -+ } else if (entry->is_protected && -+ (!rx->key || -+ (!ieee80211_has_protected(fc) && -+ !(status->flag & RX_FLAG_DECRYPTED)) || -+ rx->key->color != entry->key_color)) { -+ /* Drop this as a mixed key or fragment cache attack, even -+ * if for TKIP Michael MIC should protect us, and WEP is a -+ * lost cause anyway. -+ */ -+ return RX_DROP_UNUSABLE; -+ } else if (entry->is_protected && rx->key && -+ entry->key_color != rx->key->color && -+ (status->flag & RX_FLAG_DECRYPTED)) { -+ return RX_DROP_UNUSABLE; - } - - skb_pull(rx->skb, ieee80211_hdrlen(fc)); -@@ -2504,13 +2550,13 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc) - struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; - - /* -- * Allow EAPOL frames to us/the PAE group address regardless -- * of whether the frame was encrypted or not. -+ * Allow EAPOL frames to us/the PAE group address regardless of -+ * whether the frame was encrypted or not, and always disallow -+ * all other destination addresses for them. - */ -- if (ehdr->h_proto == rx->sdata->control_port_protocol && -- (ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || -- ether_addr_equal(ehdr->h_dest, pae_group_addr))) -- return true; -+ if (unlikely(ehdr->h_proto == rx->sdata->control_port_protocol)) -+ return ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || -+ ether_addr_equal(ehdr->h_dest, pae_group_addr); - - if (ieee80211_802_1x_port_control(rx) || - ieee80211_drop_unencrypted(rx, fc)) -@@ -2535,8 +2581,28 @@ static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb, - cfg80211_rx_control_port(dev, skb, noencrypt); - dev_kfree_skb(skb); - } else { -+ struct ethhdr *ehdr = (void *)skb_mac_header(skb); -+ - memset(skb->cb, 0, sizeof(skb->cb)); - -+ /* -+ * 802.1X over 802.11 requires that the authenticator address -+ * be used for EAPOL frames. However, 802.1X allows the use of -+ * the PAE group address instead. If the interface is part of -+ * a bridge and we pass the frame with the PAE group address, -+ * then the bridge will forward it to the network (even if the -+ * client was not associated yet), which isn't supposed to -+ * happen. -+ * To avoid that, rewrite the destination address to our own -+ * address, so that the authenticator (e.g. hostapd) will see -+ * the frame, but bridge won't forward it anywhere else. Note -+ * that due to earlier filtering, the only other address can -+ * be the PAE group address. -+ */ -+ if (unlikely(skb->protocol == sdata->control_port_protocol && -+ !ether_addr_equal(ehdr->h_dest, sdata->vif.addr))) -+ ether_addr_copy(ehdr->h_dest, sdata->vif.addr); -+ - /* deliver to local stack */ - if (rx->list) - #if LINUX_VERSION_IS_GEQ(4,19,0) -@@ -2580,6 +2646,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) - if ((sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && - !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && -+ ehdr->h_proto != rx->sdata->control_port_protocol && - (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) { - if (is_multicast_ether_addr(ehdr->h_dest) && - ieee80211_vif_get_num_mcast_if(sdata) != 0) { -@@ -2689,7 +2756,7 @@ __ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx, u8 data_offset) - if (ieee80211_data_to_8023_exthdr(skb, ðhdr, - rx->sdata->vif.addr, - rx->sdata->vif.type, -- data_offset)) -+ data_offset, true)) - return RX_DROP_UNUSABLE; - - ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, -@@ -2746,6 +2813,23 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) - if (is_multicast_ether_addr(hdr->addr1)) - return RX_DROP_UNUSABLE; - -+ if (rx->key) { -+ /* -+ * We should not receive A-MSDUs on pre-HT connections, -+ * and HT connections cannot use old ciphers. Thus drop -+ * them, as in those cases we couldn't even have SPP -+ * A-MSDUs or such. -+ */ -+ switch (rx->key->conf.cipher) { -+ case WLAN_CIPHER_SUITE_WEP40: -+ case WLAN_CIPHER_SUITE_WEP104: -+ case WLAN_CIPHER_SUITE_TKIP: -+ return RX_DROP_UNUSABLE; -+ default: -+ break; -+ } -+ } -+ - return __ieee80211_rx_h_amsdu(rx, 0); - } - -diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c -index 83c2d15..a9df381 100644 ---- a/net/mac80211/sta_info.c -+++ b/net/mac80211/sta_info.c -@@ -4,7 +4,7 @@ - * Copyright 2006-2007 Jiri Benc - * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 - 2017 Intel Deutschland GmbH -- * Copyright (C) 2018-2020 Intel Corporation -+ * Copyright (C) 2018-2021 Intel Corporation - */ - - #include -@@ -393,6 +393,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, - - u64_stats_init(&sta->rx_stats.syncp); - -+ ieee80211_init_frag_cache(&sta->frags); -+ - sta->sta_state = IEEE80211_STA_NONE; - - /* Mark TID as unreserved */ -@@ -1103,6 +1105,8 @@ static void __sta_info_destroy_part2(struct sta_info *sta) - - ieee80211_sta_debugfs_remove(sta); - -+ ieee80211_destroy_frag_cache(&sta->frags); -+ - cleanup_single_sta(sta); - } - -diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h -index df67568..68ba47c 100644 ---- a/net/mac80211/sta_info.h -+++ b/net/mac80211/sta_info.h -@@ -3,7 +3,7 @@ - * Copyright 2002-2005, Devicescape Software, Inc. - * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright(c) 2015-2017 Intel Deutschland GmbH -- * Copyright(c) 2020 Intel Corporation -+ * Copyright(c) 2020-2021 Intel Corporation - */ - - #ifndef STA_INFO_H -@@ -439,6 +439,34 @@ struct ieee80211_sta_rx_stats { - }; - - /* -+ * IEEE 802.11-2016 (10.6 "Defragmentation") recommends support for "concurrent -+ * reception of at least one MSDU per access category per associated STA" -+ * on APs, or "at least one MSDU per access category" on other interface types. -+ * -+ * This limit can be increased by changing this define, at the cost of slower -+ * frame reassembly and increased memory use while fragments are pending. -+ */ -+#define IEEE80211_FRAGMENT_MAX 4 -+ -+struct ieee80211_fragment_entry { -+ struct sk_buff_head skb_list; -+ unsigned long first_frag_time; -+ u16 seq; -+ u16 extra_len; -+ u16 last_frag; -+ u8 rx_queue; -+ u8 check_sequential_pn:1, /* needed for CCMP/GCMP */ -+ is_protected:1; -+ u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ -+ unsigned int key_color; -+}; -+ -+struct ieee80211_fragment_cache { -+ struct ieee80211_fragment_entry entries[IEEE80211_FRAGMENT_MAX]; -+ unsigned int next; -+}; -+ -+/* - * The bandwidth threshold below which the per-station CoDel parameters will be - * scaled to be more lenient (to prevent starvation of slow stations). This - * value will be scaled by the number of active stations when it is being -@@ -531,6 +559,7 @@ struct ieee80211_sta_rx_stats { - * @status_stats.last_ack_signal: last ACK signal - * @status_stats.ack_signal_filled: last ACK signal validity - * @status_stats.avg_ack_signal: average ACK signal -+ * @frags: fragment cache - */ - struct sta_info { - /* General information, mostly static */ -@@ -639,6 +668,8 @@ struct sta_info { - - struct cfg80211_chan_def tdls_chandef; - -+ struct ieee80211_fragment_cache frags; -+ - /* keep last! */ - struct ieee80211_sta sta; - }; -diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c -index 690fe47..eb42d18 100644 ---- a/net/mac80211/wpa.c -+++ b/net/mac80211/wpa.c -@@ -3,6 +3,7 @@ - * Copyright 2002-2004, Instant802 Networks, Inc. - * Copyright 2008, Jouni Malinen - * Copyright (C) 2016-2017 Intel Deutschland GmbH -+ * Copyright (C) 2020-2021 Intel Corporation - */ - - #include -@@ -167,8 +168,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) - - update_iv: - /* update IV in key information to be able to detect replays */ -- rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32; -- rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16; -+ rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip.iv32; -+ rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip.iv16; - - return RX_CONTINUE; - -@@ -294,8 +295,8 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) - key, skb->data + hdrlen, - skb->len - hdrlen, rx->sta->sta.addr, - hdr->addr1, hwaccel, rx->security_idx, -- &rx->tkip_iv32, -- &rx->tkip_iv16); -+ &rx->tkip.iv32, -+ &rx->tkip.iv16); - if (res != TKIP_DECRYPT_OK) - return RX_DROP_UNUSABLE; - -@@ -552,6 +553,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, - } - - memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); -+ if (unlikely(ieee80211_is_frag(hdr))) -+ memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN); - } - - /* Remove CCMP header and MIC */ -@@ -782,6 +785,8 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx) - } - - memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN); -+ if (unlikely(ieee80211_is_frag(hdr))) -+ memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN); - } - - /* Remove GCMP header and MIC */ -diff --git a/net/wireless/util.c b/net/wireless/util.c -index 304bbd4..ff262ea 100644 ---- a/net/wireless/util.c -+++ b/net/wireless/util.c -@@ -541,7 +541,7 @@ EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen); - - int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, - const u8 *addr, enum nl80211_iftype iftype, -- u8 data_offset) -+ u8 data_offset, bool is_amsdu) - { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct { -@@ -629,7 +629,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, - skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)); - tmp.h_proto = payload.proto; - -- if (likely((ether_addr_equal(payload.hdr, rfc1042_header) && -+ if (likely((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) && - tmp.h_proto != htons(ETH_P_AARP) && - tmp.h_proto != htons(ETH_P_IPX)) || - ether_addr_equal(payload.hdr, bridge_tunnel_header))) -@@ -775,6 +775,9 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, - remaining = skb->len - offset; - if (subframe_len > remaining) - goto purge; -+ /* mitigate A-MSDU aggregation injection attacks */ -+ if (ether_addr_equal(eth.h_dest, rfc1042_header)) -+ goto purge; - - offset += sizeof(struct ethhdr); - last = remaining <= subframe_len + padding; diff --git a/recipes-kernel/mac80211/mac80211/0009-backport-of-rt2x00-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0009-backport-of-rt2x00-patches-from-openwrt.patch new file mode 100644 index 0000000..5430d37 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0009-backport-of-rt2x00-patches-from-openwrt.patch @@ -0,0 +1,3200 @@ +From e1de2839d7ea4f775f09458306db54e043318ba4 Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 14 Sep 2022 14:31:55 +0200 +Subject: [PATCH] backport of rt2x00 patches from openwrt + +--- + drivers/net/wireless/ralink/rt2x00/Kconfig | 23 +- + drivers/net/wireless/ralink/rt2x00/Makefile | 1 + + drivers/net/wireless/ralink/rt2x00/rt2800.h | 6 + + .../net/wireless/ralink/rt2x00/rt2800lib.c | 2165 +++++++++++++++-- + .../net/wireless/ralink/rt2x00/rt2800lib.h | 45 + + .../net/wireless/ralink/rt2x00/rt2800pci.c | 7 + + .../net/wireless/ralink/rt2x00/rt2800soc.c | 51 +- + .../net/wireless/ralink/rt2x00/rt2800usb.c | 7 + + drivers/net/wireless/ralink/rt2x00/rt2x00.h | 14 + + .../net/wireless/ralink/rt2x00/rt2x00dev.c | 60 +- + .../net/wireless/ralink/rt2x00/rt2x00eeprom.c | 187 ++ + .../net/wireless/ralink/rt2x00/rt2x00leds.c | 3 + + .../net/wireless/ralink/rt2x00/rt2x00lib.h | 16 + + .../net/wireless/ralink/rt2x00/rt2x00soc.c | 16 + + include/linux/rt2x00_platform.h | 23 + + local-symbols | 1 + + 16 files changed, 2437 insertions(+), 188 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 c94a53b..58abe9a 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 d758e88..cdc69c4 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h +@@ -1042,6 +1042,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: + */ +@@ -2739,6 +2744,7 @@ enum rt2800_eeprom_word { + #define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) + #define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) + #define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) ++#define EEPROM_NIC_CONF2_EXTERNAL_PA FIELD16(0xc000) + + /* + * EEPROM LNA +diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +index af17931..77774ff 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #include "rt2x00.h" + #include "rt2800lib.h" +@@ -136,6 +137,26 @@ static u8 rt2800_bbp_read(struct rt2x00_dev *rt2x00dev, const unsigned int word) + + return value; + } ++//serge: move here for use in test ++static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev, ++ const u8 reg, const u8 value) ++{ ++ rt2800_bbp_write(rt2x00dev, 195, reg); ++ rt2800_bbp_write(rt2x00dev, 196, value); ++} ++ ++static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev, ++ const u8 reg, const u8 value) ++{ ++ rt2800_bbp_write(rt2x00dev, 158, reg); ++ rt2800_bbp_write(rt2x00dev, 159, value); ++} ++ ++static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg) ++{ ++ rt2800_bbp_write(rt2x00dev, 158, reg); ++ return rt2800_bbp_read(rt2x00dev, 159); ++} + + static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +@@ -283,6 +304,28 @@ 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) { ++ rt2x00_warn(rt2x00dev, "cannot enable PA pin! no default pinctrl\n"); ++ return; ++ } ++ ++ pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_default); ++ } else { ++ if (!rt2x00dev->pins_pa_gpio) { ++ rt2x00_warn(rt2x00dev, "cannot disable PA pin! no pa_gpio pinctrl\n"); ++ 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, +@@ -3697,14 +3740,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, +@@ -3738,18 +3783,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)) { +@@ -3793,6 +3843,20 @@ static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev, + rfcsr |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 7, 59, rfcsr); + } ++ ++ if (conf_is_ht40(conf)) {//serge:skipped this step (1) ++ rt2800_bbp_write(rt2x00dev, 195, 141); ++ rt2800_bbp_write(rt2x00dev, 196, 0x10); ++ rt2800_bbp_write(rt2x00dev, 195, 157); ++ rt2800_bbp_write(rt2x00dev, 196, 0x2f); ++ //rt2800_bbp_write(rt2x00dev, 105, 0x3C); ++ } else { ++ rt2800_bbp_write(rt2x00dev, 195, 141); ++ rt2800_bbp_write(rt2x00dev, 196, 0x1a); ++ rt2800_bbp_write(rt2x00dev, 195, 157); ++ rt2800_bbp_write(rt2x00dev, 196, 0x40); ++ //rt2800_bbp_write(rt2x00dev, 105, 0x1C); ++ } + } + + static void rt2800_config_alc(struct rt2x00_dev *rt2x00dev, +@@ -3849,25 +3913,29 @@ static void rt2800_config_alc(struct rt2x00_dev *rt2x00dev, + if (i == 10000) + rt2x00_warn(rt2x00dev, "Wait MAC Status to MAX !!!\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); +@@ -4160,6 +4228,11 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, + rt2800_bbp_write(rt2x00dev, 86, 0x46); + else + rt2800_bbp_write(rt2x00dev, 86, 0); ++ } else if (rt2x00_rt(rt2x00dev, RT6352)) {//serge: don't overwite bbp r86 (5) ++ rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); ++ rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); ++ rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); ++ rt2800_bbp_write(rt2x00dev, 86, 0x38); + } else { + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); +@@ -4365,7 +4438,72 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, + reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2*rt2x00dev->lna_gain; + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + +- rt2800_iq_calibrate(rt2x00dev, rf->channel); ++ if (!rt2x00_rt(rt2x00dev, RT6352))//serge: this function for rt5592 only, for rt6352 it switch off compensations (5) ++ rt2800_iq_calibrate(rt2x00dev, rf->channel); ++ } ++ ++ if (rt2x00_rt(rt2x00dev, RT6352)) { ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, ++ &rt2x00dev->cap_flags)) { ++ rt2x00_warn(rt2x00dev, "Using incomplete support for " \ ++ "external PA\n"); ++ reg = rt2800_register_read(rt2x00dev, RF_CONTROL3); ++ reg |= 0x00000101; ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, reg); ++ ++ reg = rt2800_register_read(rt2x00dev, RF_BYPASS3); ++ reg |= 0x00000101; ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, reg); ++ ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0x73); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0x73); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0x73); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0xC8); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xA4); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x05); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0xC8); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xA4); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x05); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x27); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0xC8); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xA4); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x05); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 05, 0x00); ++ ++ rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT, ++ 0x36303636); ++ rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN, ++ 0x6C6C6B6C); ++ rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN, ++ 0x6C6C6B6C); ++ } ++ ++ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {//serge: for support eLNA (7a) ++ rt2x00_warn(rt2x00dev, "Correct RF/BBP for eLNA!\n"); ++ reg = rt2800_register_read(rt2x00dev, RF_CONTROL3); ++ reg |= 0x00000101; ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, reg); ++ ++ reg = rt2800_register_read(rt2x00dev, RF_BYPASS3); ++ reg |= 0x00000101; ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, reg); ++ ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 18, 0x42); ++ rt2800_bbp_write(rt2x00dev, 75, 0x68);//serge: move bbp eLNA init here? ++ rt2800_bbp_write(rt2x00dev, 76, 0x4C); ++ rt2800_bbp_write(rt2x00dev, 79, 0x1C); ++ rt2800_bbp_write(rt2x00dev, 80, 0x0C); ++ rt2800_bbp_write(rt2x00dev, 82, 0xB6); ++ /* bank 0 RF reg 42 and glrt BBP reg 141 ++ will be set in config channel function ++ in dependence of channel and HT20/HT40 ++ so don't touch it ++ */ ++ } + } + + bbp = rt2800_bbp_read(rt2x00dev, 4); +@@ -4406,6 +4544,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, + rt2x00_set_field8(&bbp, BBP49_UPDATE_FLAG, 0); + rt2800_bbp_write(rt2x00dev, 49, bbp); + } ++//serge:just print results after config channel - don't forget to remove nahren (c) <- this is copyright, not ref to comments :) ++ bbp = rt2800_bbp_dcoc_read(rt2x00dev, 0x03); ++ pr_info("BBP tx/rx compensation control=0x%02x\n", bbp); + } + + static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) +@@ -5476,7 +5617,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) + } + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + +- if (rt2x00_rt(rt2x00dev, RT6352)) { ++ if (rt2x00_rt(rt2x00dev, RT6352)) {//serge:remark - move all this code to rfcsr_6352 init? + if (rt2x00dev->default_ant.rx_chain_num == 1) { + rt2800_bbp_write(rt2x00dev, 91, 0x07); + rt2800_bbp_write(rt2x00dev, 95, 0x1A); +@@ -5644,7 +5785,8 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, + if (qual->vgc_level != vgc_level) { + if (rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || +- rt2x00_rt(rt2x00dev, RT3883)) { ++ rt2x00_rt(rt2x00dev, RT3883) || ++ rt2x00_rt(rt2x00dev, RT6352)) {//serge: rt6352 too (3) + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, + vgc_level); + } else if (rt2x00_rt(rt2x00dev, RT5592)) { +@@ -5866,18 +6008,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, 0x000C0000); +- 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);//serge:was 0x000C0000 (2) ++ 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); +@@ -6129,6 +6286,29 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) + reg = rt2800_register_read(rt2x00dev, US_CYC_CNT); + rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 125); + rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); ++ } else if (rt2x00_is_soc(rt2x00dev)) {//serge:which value correct? (4) ++ struct clk *clk = clk_get_sys("bus", NULL); ++ int rate; ++ ++ if (IS_ERR(clk)) { ++ rt2x00_warn(rt2x00dev, "system bus clock undefined\n"); ++ clk = clk_get_sys("cpu", NULL); ++ ++ if (IS_ERR(clk)) ++ rate = 125; ++ else { ++ rate = clk_get_rate(clk) / 3000000; ++ clk_put(clk); ++ } ++ } else { ++ rate = clk_get_rate(clk) / 1000000; ++ clk_put(clk); ++ } ++ ++ rt2x00_info(rt2x00dev, "set US_CYC=%dMHz\n", rate); ++ reg = rt2800_register_read(rt2x00dev, US_CYC_CNT); ++ rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, rate); ++ rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); + } + + reg = rt2800_register_read(rt2x00dev, HT_FBK_CFG0); +@@ -6915,26 +7095,7 @@ static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + } +- +-static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev, +- const u8 reg, const u8 value) +-{ +- rt2800_bbp_write(rt2x00dev, 195, reg); +- rt2800_bbp_write(rt2x00dev, 196, value); +-} +- +-static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev, +- const u8 reg, const u8 value) +-{ +- rt2800_bbp_write(rt2x00dev, 158, reg); +- rt2800_bbp_write(rt2x00dev, 159, value); +-} +- +-static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg) +-{ +- rt2800_bbp_write(rt2x00dev, 158, reg); +- return rt2800_bbp_read(rt2x00dev, 159); +-} ++//serge: move these function upper + + static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev) + { +@@ -7021,14 +7182,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); +@@ -8398,6 +8561,1605 @@ static void rt2800_init_rfcsr_5592(struct rt2x00_dev *rt2x00dev) + rt2800_led_open_drain_enable(rt2x00dev); + } + ++static void rt2800_rf_self_txdc_cal(struct rt2x00_dev *rt2x00dev) ++{ ++ u8 rfb5r1_org, rfb7r1_org, rfvalue; ++ u32 mac0518, mac051c, mac0528, mac052c; ++ u8 i; ++ ++ rt2x00_info(rt2x00dev, "RF Tx self calibration start\n"); ++ mac0518 = rt2800_register_read(rt2x00dev, RF_CONTROL0); ++ mac051c = rt2800_register_read(rt2x00dev, RF_BYPASS0); ++ mac0528 = rt2800_register_read(rt2x00dev, RF_CONTROL2); ++ mac052c = rt2800_register_read(rt2x00dev, RF_BYPASS2); ++ ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x0); ++ rt2800_register_write(rt2x00dev, RF_BYPASS2, 0x0); ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0xC); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x3306); ++ rt2800_register_write(rt2x00dev, RF_CONTROL2, 0x3330); ++ rt2800_register_write(rt2x00dev, RF_BYPASS2, 0xfffff); ++ rfb5r1_org = rt2800_rfcsr_read_bank(rt2x00dev, 5, 1); ++ rfb7r1_org = rt2800_rfcsr_read_bank(rt2x00dev, 7, 1); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, 0x4); ++ for (i = 0; i < 100; i = i + 1) { ++ udelay(50); ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 1); ++ if((rfvalue & 0x04) != 0x4) ++ break; ++ } ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, rfb5r1_org); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 1, 0x4); ++ for (i = 0; i < 100; i = i + 1) { ++ udelay(50); ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 1); ++ if((rfvalue & 0x04) != 0x4) ++ break; ++ } ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 1, rfb7r1_org); ++ ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x0); ++ rt2800_register_write(rt2x00dev, RF_BYPASS2, 0x0); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, mac0518); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, mac051c); ++ rt2800_register_write(rt2x00dev, RF_CONTROL2, mac0528); ++ rt2800_register_write(rt2x00dev, RF_BYPASS2, mac052c); ++ ++ rt2x00_info(rt2x00dev, "RF Tx self calibration end\n"); ++} ++ ++static int rt2800_calcrcalibrationcode(struct rt2x00_dev *rt2x00dev, int d1, int d2) ++{ ++ int calcode; ++ calcode = ((d2 - d1) * 1000) / 43; ++ if ((calcode%10) >= 5) ++ calcode += 10; ++ calcode = (calcode / 10); ++ ++ return calcode; ++} ++ ++static void rt2800_r_calibration(struct rt2x00_dev *rt2x00dev) ++{ ++ u32 savemacsysctrl; ++ u8 saverfb0r1, saverfb0r34, saverfb0r35; ++ u8 saverfb5r4, saverfb5r17, saverfb5r18; ++ u8 saverfb5r19, saverfb5r20; ++ u8 savebbpr22, savebbpr47, savebbpr49; ++ u8 bytevalue = 0; ++ int rcalcode; ++ u8 r_cal_code = 0; ++ char d1 = 0, d2 = 0; ++ u8 rfvalue; ++ u32 MAC_RF_BYPASS0, MAC_RF_CONTROL0, MAC_PWR_PIN_CFG; ++ u32 maccfg, macstatus; ++ int i; ++ ++ saverfb0r1 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1); ++ saverfb0r34 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 34); ++ saverfb0r35 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 35); ++ saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4); ++ saverfb5r17 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 17); ++ saverfb5r18 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 18); ++ saverfb5r19 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 19); ++ saverfb5r20 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 20); ++ ++ savebbpr22 = rt2800_bbp_read(rt2x00dev, 22); ++ savebbpr47 = rt2800_bbp_read(rt2x00dev, 47); ++ savebbpr49 = rt2800_bbp_read(rt2x00dev, 49); ++ ++ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ MAC_RF_BYPASS0 = rt2800_register_read(rt2x00dev, RF_BYPASS0); ++ MAC_RF_CONTROL0 = rt2800_register_read(rt2x00dev, RF_CONTROL0); ++ MAC_PWR_PIN_CFG = rt2800_register_read(rt2x00dev, PWR_PIN_CFG); ++ ++ maccfg = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ maccfg &= (~0x04); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, maccfg); ++ ++ for (i = 0; i < 10000; i++) { ++ macstatus = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macstatus & 0x1) ++ udelay(50); ++ else ++ break; ++ } ++ ++ if (i == 10000) ++ rt2x00_warn(rt2x00dev, "Wait MAC Tx Status to MAX !!!\n"); ++ ++ maccfg = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ maccfg &= (~0x04); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, maccfg); ++ ++ for (i = 0; i < 10000; i++) { ++ macstatus = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macstatus & 0x2) ++ udelay(50); ++ else ++ break; ++ } ++ ++ if (i == 10000) ++ rt2x00_warn(rt2x00dev, "Wait MAC Rx Status to MAX !!!\n"); ++ ++ rfvalue = (MAC_RF_BYPASS0 | 0x3004); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, rfvalue); ++ rfvalue = (MAC_RF_CONTROL0 | (~0x3002)); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, rfvalue); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, 0x27); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, 0x80); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, 0x83); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, 0x00); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, 0x20); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, 0x00); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 34, 0x13); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x00); ++ ++ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x1); ++ ++ rt2800_bbp_write(rt2x00dev, 47, 0x04); ++ rt2800_bbp_write(rt2x00dev, 22, 0x80); ++ udelay(100); ++ bytevalue = rt2800_bbp_read(rt2x00dev, 49); ++ if (bytevalue > 128) ++ d1 = bytevalue - 256; ++ else ++ d1 = (char)bytevalue; ++ rt2800_bbp_write(rt2x00dev, 22, 0x0); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x01); ++ ++ rt2800_bbp_write(rt2x00dev, 22, 0x80); ++ udelay(100); ++ bytevalue = rt2800_bbp_read(rt2x00dev, 49); ++ if (bytevalue > 128) ++ d2 = bytevalue - 256; ++ else ++ d2 = (char)bytevalue; ++ rt2800_bbp_write(rt2x00dev, 22, 0x0); ++ ++ rcalcode = rt2800_calcrcalibrationcode(rt2x00dev, d1, d2); ++ if (rcalcode < 0) ++ r_cal_code = 256 + rcalcode; ++ else ++ r_cal_code = (u8)rcalcode; ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 7, r_cal_code); ++ pr_info("RF bank 0 reg 5=0x%02x\n", r_cal_code);//serge: just for info to compare with vendor driver ++ rt2800_bbp_write(rt2x00dev, 22, 0x0); ++ ++ bytevalue = rt2800_bbp_read(rt2x00dev, 21); ++ bytevalue |= 0x1; ++ rt2800_bbp_write(rt2x00dev, 21, bytevalue); ++ bytevalue = rt2800_bbp_read(rt2x00dev, 21); ++ bytevalue &= (~0x1); ++ rt2800_bbp_write(rt2x00dev, 21, bytevalue); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, saverfb0r1); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 34, saverfb0r34); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, saverfb0r35); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, saverfb5r4); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, saverfb5r17); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, saverfb5r18); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, saverfb5r19); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, saverfb5r20); ++ ++ rt2800_bbp_write(rt2x00dev, 22, savebbpr22); ++ rt2800_bbp_write(rt2x00dev, 47, savebbpr47); ++ rt2800_bbp_write(rt2x00dev, 49, savebbpr49); ++ ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, MAC_RF_BYPASS0); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, MAC_RF_CONTROL0); ++ ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl); ++ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, MAC_PWR_PIN_CFG); ++} ++ ++static void rt2800_rxdcoc_calibration(struct rt2x00_dev *rt2x00dev) ++{ ++ u8 bbpreg = 0; ++ u32 macvalue = 0, macvalue1 = 0; ++ u8 saverfb0r2, saverfb5r4, saverfb7r4, rfvalue; ++ int i; ++ ++ saverfb0r2 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2); ++ rfvalue = saverfb0r2; ++ rfvalue |= 0x03; ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfvalue); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 141); ++ bbpreg = rt2800_bbp_read(rt2x00dev, 159); ++ bbpreg |= 0x10; ++ rt2800_bbp_write(rt2x00dev, 159, bbpreg); ++ ++ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x8); ++ ++ for (i = 0; i < 10000; i++) { ++ macvalue1 = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macvalue1 & 0x1) ++ udelay(50); ++ else ++ break; ++ } ++ ++ saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4);//serge: was 0 - typo? (6) ++ saverfb7r4 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4); ++ saverfb5r4 = saverfb5r4 & (~0x40); ++ saverfb7r4 = saverfb7r4 & (~0x40); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x64); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, saverfb5r4); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 4, saverfb7r4); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 141); ++ bbpreg = rt2800_bbp_read(rt2x00dev, 159); ++ bbpreg = bbpreg & (~0x40); ++ rt2800_bbp_write(rt2x00dev, 159, bbpreg); ++ bbpreg |= 0x48; ++ rt2800_bbp_write(rt2x00dev, 159, bbpreg); ++ ++ for (i = 0; i < 10000; i++) { ++ bbpreg = rt2800_bbp_read(rt2x00dev, 159); ++ if ((bbpreg & 0x40)==0) ++ break; ++ udelay(50); ++ } ++ ++ bbpreg = rt2800_bbp_read(rt2x00dev, 159); ++ bbpreg = bbpreg & (~0x40); ++ rt2800_bbp_write(rt2x00dev, 159, bbpreg); ++ ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 141); ++ bbpreg = rt2800_bbp_read(rt2x00dev, 159); ++ bbpreg &= (~0x10); ++ rt2800_bbp_write(rt2x00dev, 159, bbpreg); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, saverfb0r2); ++} ++ ++static u32 rt2800_do_sqrt_accumulation(u32 si) { ++ u32 root, root_pre, bit; ++ char i; ++ bit = 1 << 15; ++ root = 0; ++ for (i = 15; i >= 0; i = i - 1) { ++ root_pre = root + bit; ++ if ((root_pre*root_pre) <= si) ++ root = root_pre; ++ bit = bit >> 1; ++ } ++ ++ return root; ++} ++ ++static void rt2800_rxiq_calibration(struct rt2x00_dev *rt2x00dev) { ++ u8 rfb0r1, rfb0r2, rfb0r42; ++ u8 rfb4r0, rfb4r19; ++ u8 rfb5r3, rfb5r4, rfb5r17, rfb5r18, rfb5r19, rfb5r20; ++ u8 rfb6r0, rfb6r19; ++ u8 rfb7r3, rfb7r4, rfb7r17, rfb7r18, rfb7r19, rfb7r20; ++ ++ u8 bbp1, bbp4; ++ u8 bbpr241, bbpr242; ++ u32 i; ++ u8 ch_idx; ++ u8 bbpval; ++ u8 rfval, vga_idx = 0; ++ int mi = 0, mq = 0, si = 0, sq = 0, riq = 0; ++ int sigma_i, sigma_q, r_iq, g_rx; ++ int g_imb; ++ int ph_rx; ++ u32 savemacsysctrl = 0; ++ u32 orig_RF_CONTROL0 = 0; ++ u32 orig_RF_BYPASS0 = 0; ++ u32 orig_RF_CONTROL1 = 0; ++ u32 orig_RF_BYPASS1 = 0; ++ u32 orig_RF_CONTROL3 = 0; ++ u32 orig_RF_BYPASS3 = 0; ++ u32 macstatus, bbpval1 = 0; ++ u8 rf_vga_table[] = {0x20, 0x21, 0x22, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}; ++ ++ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ orig_RF_CONTROL0 = rt2800_register_read(rt2x00dev, RF_CONTROL0); ++ orig_RF_BYPASS0 = rt2800_register_read(rt2x00dev, RF_BYPASS0); ++ orig_RF_CONTROL1 = rt2800_register_read(rt2x00dev, RF_CONTROL1); ++ orig_RF_BYPASS1 = rt2800_register_read(rt2x00dev, RF_BYPASS1); ++ orig_RF_CONTROL3 = rt2800_register_read(rt2x00dev, RF_CONTROL3); ++ orig_RF_BYPASS3 = rt2800_register_read(rt2x00dev, RF_BYPASS3); ++ ++ bbp1 = rt2800_bbp_read(rt2x00dev, 1); ++ bbp4 = rt2800_bbp_read(rt2x00dev, 4); ++ ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x0); ++ ++ for (i = 0; i < 10000; i++) { ++ macstatus = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macstatus & 0x3) ++ udelay(50); ++ else ++ break; ++ } ++ ++ if (i == 10000) ++ rt2x00_warn(rt2x00dev, "Wait MAC Status to MAX !!!\n"); ++ ++ bbpval = bbp4 & (~0x18); ++ bbpval = bbp4 | 0x00; ++ rt2800_bbp_write(rt2x00dev, 4, bbpval); ++ ++ bbpval = rt2800_bbp_read(rt2x00dev, 21); ++ bbpval = bbpval | 1; ++ rt2800_bbp_write(rt2x00dev, 21, bbpval); ++ bbpval = bbpval & 0xfe; ++ rt2800_bbp_write(rt2x00dev, 21, bbpval); ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL1, 0x00000202); ++ rt2800_register_write(rt2x00dev, RF_BYPASS1, 0x00000303); ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x0101); ++ else ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x0000); ++ ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, 0xf1f1); ++ ++ rfb0r1 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1); ++ rfb0r2 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2); ++ rfb0r42 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42); ++ rfb4r0 = rt2800_rfcsr_read_bank(rt2x00dev, 4, 0); ++ rfb4r19 = rt2800_rfcsr_read_bank(rt2x00dev, 4, 19); ++ rfb5r3 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 3); ++ rfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4); ++ rfb5r17 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 17); ++ rfb5r18 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 18); ++ rfb5r19 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 19); ++ rfb5r20 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 20); ++ ++ rfb6r0 = rt2800_rfcsr_read_bank(rt2x00dev, 6, 0); ++ rfb6r19 = rt2800_rfcsr_read_bank(rt2x00dev, 6, 19); ++ rfb7r3 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 3); ++ rfb7r4 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4); ++ rfb7r17 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 17); ++ rfb7r18 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 18); ++ rfb7r19 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 19); ++ rfb7r20 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 20); ++ ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 0, 0x87); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0x27); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x38); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x38); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x80); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 18, 0xC1); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 19, 0x60); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 20, 0x00); ++ ++ rt2800_bbp_write(rt2x00dev, 23, 0x0); ++ rt2800_bbp_write(rt2x00dev, 24, 0x0); ++ ++ rt2800_bbp_dcoc_write(rt2x00dev, 5, 0x0); ++ ++ bbpr241 = rt2800_bbp_read(rt2x00dev, 241); ++ bbpr242 = rt2800_bbp_read(rt2x00dev, 242); ++ ++ rt2800_bbp_write(rt2x00dev, 241, 0x10); ++ rt2800_bbp_write(rt2x00dev, 242, 0x84); ++ rt2800_bbp_write(rt2x00dev, 244, 0x31); ++ ++ bbpval = rt2800_bbp_dcoc_read(rt2x00dev, 3); ++ bbpval = bbpval & (~0x7); ++ rt2800_bbp_dcoc_write(rt2x00dev, 3, bbpval); ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004); ++ udelay(1); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000006); ++ usleep_range(1, 200); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00003376); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001006); ++ udelay(1); ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2800_bbp_write(rt2x00dev, 23, 0x06); ++ rt2800_bbp_write(rt2x00dev, 24, 0x06); ++ } else { ++ rt2800_bbp_write(rt2x00dev, 23, 0x02); ++ rt2800_bbp_write(rt2x00dev, 24, 0x02); ++ } ++ ++ for (ch_idx = 0; ch_idx < 2; ch_idx = ch_idx + 1) { ++ if (ch_idx == 0) { ++ rfval = rfb0r1 & (~0x3); ++ rfval = rfb0r1 | 0x1; ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, rfval); ++ rfval = rfb0r2 & (~0x33); ++ rfval = rfb0r2 | 0x11; ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfval); ++ rfval = rfb0r42 & (~0x50); ++ rfval = rfb0r42 | 0x10; ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfval); ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001006); ++ udelay(1); ++ ++ bbpval = bbp1 & (~ 0x18); ++ bbpval = bbpval | 0x00; ++ rt2800_bbp_write(rt2x00dev, 1, bbpval); ++ ++ rt2800_bbp_dcoc_write(rt2x00dev, 1, 0x00); ++ } else { ++ rfval = rfb0r1 & (~0x3); ++ rfval = rfb0r1 | 0x2; ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, rfval); ++ rfval = rfb0r2 & (~0x33); ++ rfval = rfb0r2 | 0x22; ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfval); ++ rfval = rfb0r42 & (~0x50); ++ rfval = rfb0r42 | 0x40; ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfval); ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002006); ++ udelay(1); ++ ++ bbpval = bbp1 & (~ 0x18); ++ bbpval = bbpval | 0x08; ++ rt2800_bbp_write(rt2x00dev, 1, bbpval); ++ ++ rt2800_bbp_dcoc_write(rt2x00dev, 1, 0x01); ++ } ++ udelay(500); ++ ++ vga_idx = 0; ++ while (vga_idx < 11) { ++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rf_vga_table[vga_idx]); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rf_vga_table[vga_idx]); ++ ++ rt2800_bbp_dcoc_write(rt2x00dev, 0, 0x93); ++ ++ for (i = 0; i < 10000; i++) { ++ bbpval = rt2800_bbp_read(rt2x00dev, 159); ++ if ((bbpval & 0xff) == 0x93) ++ udelay(50); ++ else ++ break; ++ } ++ ++ if ((bbpval & 0xff) == 0x93) { ++ rt2x00_warn(rt2x00dev, "Fatal Error: Calibration doesn't finish"); ++ goto restore_value; ++ } ++ ++ for (i = 0; i < 5; i++) { ++ u32 bbptemp = 0; ++ u8 value = 0; ++ int result = 0; ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x1e); ++ rt2800_bbp_write(rt2x00dev, 159, i); ++ rt2800_bbp_write(rt2x00dev, 158, 0x22); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + (value << 24); ++ rt2800_bbp_write(rt2x00dev, 158, 0x21); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + (value << 16); ++ rt2800_bbp_write(rt2x00dev, 158, 0x20); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + (value << 8); ++ rt2800_bbp_write(rt2x00dev, 158, 0x1f); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + value; ++ ++ if ((i < 2) && (bbptemp & 0x800000)) ++ result = (bbptemp & 0xffffff) - 0x1000000; ++ else if (i == 4) ++ result = bbptemp; ++ else ++ result = bbptemp; ++ ++ if (i == 0) ++ mi = result/4096; ++ else if (i == 1) ++ mq = result/4096; ++ else if (i == 2) ++ si = bbptemp/4096; ++ else if (i == 3) ++ sq = bbptemp/4096; ++ else ++ riq = result/4096; ++ } ++ ++ bbpval1 = si - mi*mi; ++ rt2x00_dbg(rt2x00dev, "RXIQ si=%d, sq=%d, riq=%d, bbpval %d, vga_idx %d", si, sq, riq, bbpval1, vga_idx); ++ ++ if (bbpval1 >= (100*100)) ++ break; ++ ++ if (bbpval1 <= 100) ++ vga_idx = vga_idx + 9; ++ else if (bbpval1 <= 158) ++ vga_idx = vga_idx + 8; ++ else if (bbpval1 <= 251) ++ vga_idx = vga_idx + 7; ++ else if (bbpval1 <= 398) ++ vga_idx = vga_idx + 6; ++ else if (bbpval1 <= 630) ++ vga_idx = vga_idx + 5; ++ else if (bbpval1 <= 1000) ++ vga_idx = vga_idx + 4; ++ else if (bbpval1 <= 1584) ++ vga_idx = vga_idx + 3; ++ else if (bbpval1 <= 2511) ++ vga_idx = vga_idx + 2; ++ else ++ vga_idx = vga_idx + 1; ++ } ++ ++ sigma_i = rt2800_do_sqrt_accumulation(100*(si - mi*mi)); ++ sigma_q = rt2800_do_sqrt_accumulation(100*(sq - mq*mq)); ++ r_iq = 10*(riq-(mi*mq)); ++ ++ rt2x00_dbg(rt2x00dev, "Sigma_i=%d, Sigma_q=%d, R_iq=%d", sigma_i, sigma_q, r_iq); ++ ++ if (((sigma_i <= 1400 ) && (sigma_i >= 1000)) ++ && ((sigma_i - sigma_q) <= 112) ++ && ((sigma_i - sigma_q) >= -112) ++ && ((mi <= 32) && (mi >= -32)) ++ && ((mq <= 32) && (mq >= -32))) { ++ r_iq = 10*(riq-(mi*mq)); ++ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n", sigma_i, sigma_q, r_iq); ++ ++ g_rx = (1000 * sigma_q) / sigma_i; ++ g_imb = ((-2) * 128 * (1000 - g_rx)) / (1000 + g_rx); ++ ph_rx = (r_iq * 2292) / (sigma_i * sigma_q); ++ rt2x00_info(rt2x00dev, "RXIQ G_imb=%d, Ph_rx=%d\n", g_imb, ph_rx); ++ ++ if ((ph_rx > 20) || (ph_rx < -20)) { ++ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL(ph_rx=%d out of [-20..20]", ph_rx);//serge:just to see value ++ ph_rx = 0; ++ //rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); ++ } ++ ++ if ((g_imb > 12) || (g_imb < -12)) { ++ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL(g_imb=%d out of (-12..12]", g_imb);//serge:just to see the reason ++ g_imb = 0; ++ //rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); ++ } ++ } ++ else { ++ g_imb = 0; ++ ph_rx = 0; ++ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n", sigma_i, sigma_q, r_iq); ++ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); ++ } ++ ++ if (ch_idx == 0) { ++ //serge: just to see values ++ pr_info("RXIQ RX0 g_imb (0x37, %2x) ph_rx (0x35, %2x)\n", ++ g_imb & 0x3f, ++ ph_rx & 0x3f ++ ); ++ rt2800_bbp_write(rt2x00dev, 158, 0x37); ++ rt2800_bbp_write(rt2x00dev, 159, g_imb & 0x3f); ++ rt2800_bbp_write(rt2x00dev, 158, 0x35); ++ rt2800_bbp_write(rt2x00dev, 159, ph_rx & 0x3f); ++ } else { ++ //serge: just to see values ++ pr_info("RXIQ RX1 g_imb (0x55, %2x) ph_rx (0x53, %2x)\n", ++ g_imb & 0x3f, ++ ph_rx & 0x3f ++ ); ++ rt2800_bbp_write(rt2x00dev, 158, 0x55); ++ rt2800_bbp_write(rt2x00dev, 159, g_imb & 0x3f); ++ rt2800_bbp_write(rt2x00dev, 158, 0x53); ++ rt2800_bbp_write(rt2x00dev, 159, ph_rx & 0x3f); ++ } ++ } ++ ++restore_value: ++ rt2800_bbp_write(rt2x00dev, 158, 0x3); ++ bbpval = rt2800_bbp_read(rt2x00dev, 159); ++ rt2800_bbp_write(rt2x00dev, 159, (bbpval | 0x07)); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x00); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ rt2800_bbp_write(rt2x00dev, 1, bbp1); ++ rt2800_bbp_write(rt2x00dev, 4, bbp4); ++ rt2800_bbp_write(rt2x00dev, 241, bbpr241); ++ rt2800_bbp_write(rt2x00dev, 242, bbpr242); ++ ++ rt2800_bbp_write(rt2x00dev, 244, 0x00); ++ bbpval = rt2800_bbp_read(rt2x00dev, 21); ++ bbpval |= 0x1; ++ rt2800_bbp_write(rt2x00dev, 21, bbpval); ++ usleep_range(10, 200); ++ bbpval &= 0xfe; ++ rt2800_bbp_write(rt2x00dev, 21, bbpval); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, rfb0r1); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfb0r2); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfb0r42); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 4, 0, rfb4r0); ++ rt2800_rfcsr_write_bank(rt2x00dev, 4, 19, rfb4r19); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 3, rfb5r3); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, rfb5r4); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, rfb5r17); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, rfb5r18); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, rfb5r19); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, rfb5r20); ++ ++ rt2800_rfcsr_write_bank(rt2x00dev, 6, 0, rfb6r0); ++ rt2800_rfcsr_write_bank(rt2x00dev, 6, 19, rfb6r19); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 3, rfb7r3); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 4, rfb7r4); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 17, rfb7r17); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 18, rfb7r18); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 19, rfb7r19); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 20, rfb7r20); ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000006); ++ udelay(1); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004); ++ udelay(1); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, orig_RF_CONTROL0); ++ udelay(1); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, orig_RF_BYPASS0); ++ rt2800_register_write(rt2x00dev, RF_CONTROL1, orig_RF_CONTROL1); ++ rt2800_register_write(rt2x00dev, RF_BYPASS1, orig_RF_BYPASS1); ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, orig_RF_CONTROL3); ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, orig_RF_BYPASS3); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl); ++} ++ ++static void rt2800_rf_configstore(struct rt2x00_dev *rt2x00dev, rf_reg_pair rf_reg_record[][13], u8 chain) ++{ ++ u8 rfvalue = 0; ++ ++ if (chain == CHAIN_0) { ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1); ++ rf_reg_record[CHAIN_0][0].bank = 0; ++ rf_reg_record[CHAIN_0][0].reg = 1; ++ rf_reg_record[CHAIN_0][0].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2); ++ rf_reg_record[CHAIN_0][1].bank = 0; ++ rf_reg_record[CHAIN_0][1].reg = 2; ++ rf_reg_record[CHAIN_0][1].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 35); ++ rf_reg_record[CHAIN_0][2].bank = 0; ++ rf_reg_record[CHAIN_0][2].reg = 35; ++ rf_reg_record[CHAIN_0][2].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42); ++ rf_reg_record[CHAIN_0][3].bank = 0; ++ rf_reg_record[CHAIN_0][3].reg = 42; ++ rf_reg_record[CHAIN_0][3].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 4, 0); ++ rf_reg_record[CHAIN_0][4].bank = 4; ++ rf_reg_record[CHAIN_0][4].reg = 0; ++ rf_reg_record[CHAIN_0][4].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 4, 2); ++ rf_reg_record[CHAIN_0][5].bank = 4; ++ rf_reg_record[CHAIN_0][5].reg = 2; ++ rf_reg_record[CHAIN_0][5].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 4, 34); ++ rf_reg_record[CHAIN_0][6].bank = 4; ++ rf_reg_record[CHAIN_0][6].reg = 34; ++ rf_reg_record[CHAIN_0][6].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 3); ++ rf_reg_record[CHAIN_0][7].bank = 5; ++ rf_reg_record[CHAIN_0][7].reg = 3; ++ rf_reg_record[CHAIN_0][7].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4); ++ rf_reg_record[CHAIN_0][8].bank = 5; ++ rf_reg_record[CHAIN_0][8].reg = 4; ++ rf_reg_record[CHAIN_0][8].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 17); ++ rf_reg_record[CHAIN_0][9].bank = 5; ++ rf_reg_record[CHAIN_0][9].reg = 17; ++ rf_reg_record[CHAIN_0][9].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 18); ++ rf_reg_record[CHAIN_0][10].bank = 5; ++ rf_reg_record[CHAIN_0][10].reg = 18; ++ rf_reg_record[CHAIN_0][10].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 19); ++ rf_reg_record[CHAIN_0][11].bank = 5; ++ rf_reg_record[CHAIN_0][11].reg = 19; ++ rf_reg_record[CHAIN_0][11].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 20); ++ rf_reg_record[CHAIN_0][12].bank = 5; ++ rf_reg_record[CHAIN_0][12].reg = 20; ++ rf_reg_record[CHAIN_0][12].value = rfvalue; ++ } else if (chain == CHAIN_1) { ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1); ++ rf_reg_record[CHAIN_1][0].bank = 0; ++ rf_reg_record[CHAIN_1][0].reg = 1; ++ rf_reg_record[CHAIN_1][0].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2); ++ rf_reg_record[CHAIN_1][1].bank = 0; ++ rf_reg_record[CHAIN_1][1].reg = 2; ++ rf_reg_record[CHAIN_1][1].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 35); ++ rf_reg_record[CHAIN_1][2].bank = 0; ++ rf_reg_record[CHAIN_1][2].reg = 35; ++ rf_reg_record[CHAIN_1][2].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42); ++ rf_reg_record[CHAIN_1][3].bank = 0; ++ rf_reg_record[CHAIN_1][3].reg = 42; ++ rf_reg_record[CHAIN_1][3].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 6, 0); ++ rf_reg_record[CHAIN_1][4].bank = 6; ++ rf_reg_record[CHAIN_1][4].reg = 0; ++ rf_reg_record[CHAIN_1][4].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 6, 2); ++ rf_reg_record[CHAIN_1][5].bank = 6; ++ rf_reg_record[CHAIN_1][5].reg = 2; ++ rf_reg_record[CHAIN_1][5].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 6, 34); ++ rf_reg_record[CHAIN_1][6].bank = 6; ++ rf_reg_record[CHAIN_1][6].reg = 34; ++ rf_reg_record[CHAIN_1][6].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 3); ++ rf_reg_record[CHAIN_1][7].bank = 7; ++ rf_reg_record[CHAIN_1][7].reg = 3; ++ rf_reg_record[CHAIN_1][7].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4); ++ rf_reg_record[CHAIN_1][8].bank = 7; ++ rf_reg_record[CHAIN_1][8].reg = 4; ++ rf_reg_record[CHAIN_1][8].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 17); ++ rf_reg_record[CHAIN_1][9].bank = 7; ++ rf_reg_record[CHAIN_1][9].reg = 17; ++ rf_reg_record[CHAIN_1][9].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 18); ++ rf_reg_record[CHAIN_1][10].bank = 7; ++ rf_reg_record[CHAIN_1][10].reg = 18; ++ rf_reg_record[CHAIN_1][10].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 19); ++ rf_reg_record[CHAIN_1][11].bank = 7; ++ rf_reg_record[CHAIN_1][11].reg = 19; ++ rf_reg_record[CHAIN_1][11].value = rfvalue; ++ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 20); ++ rf_reg_record[CHAIN_1][12].bank = 7; ++ rf_reg_record[CHAIN_1][12].reg = 20; ++ rf_reg_record[CHAIN_1][12].value = rfvalue; ++ } else { ++ rt2x00_warn(rt2x00dev, "Unknown chain = %u\n", chain); ++ return; ++ } ++ ++ return; ++} ++ ++static void rt2800_rf_configrecover(struct rt2x00_dev *rt2x00dev, rf_reg_pair rf_record[][13]) ++{ ++ u8 chain_index = 0, record_index = 0; ++ u8 bank = 0, rf_register = 0, value = 0; ++ ++ for (chain_index = 0; chain_index < 2; chain_index++) { ++ for (record_index = 0; record_index < 13; record_index++) { ++ bank = rf_record[chain_index][record_index].bank; ++ rf_register = rf_record[chain_index][record_index].reg; ++ value = rf_record[chain_index][record_index].value; ++ rt2800_rfcsr_write_bank(rt2x00dev, bank, rf_register, value); ++ rt2x00_dbg(rt2x00dev, "bank: %d, rf_register: %d, value: %x\n", bank, rf_register, value); ++ } ++ } ++ ++ return; ++} ++ ++static void rt2800_setbbptonegenerator(struct rt2x00_dev *rt2x00dev) ++{ ++ rt2800_bbp_write(rt2x00dev, 158, 0xAA); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xAB); ++ rt2800_bbp_write(rt2x00dev, 159, 0x0A); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xAC); ++ rt2800_bbp_write(rt2x00dev, 159, 0x3F); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xAD); ++ rt2800_bbp_write(rt2x00dev, 159, 0x3F); ++ ++ rt2800_bbp_write(rt2x00dev, 244, 0x40); ++ ++ return; ++} ++ ++static u32 rt2800_do_fft_accumulation(struct rt2x00_dev *rt2x00dev, u8 tidx, u8 read_neg) ++{ ++ u32 macvalue = 0; ++ int fftout_i = 0, fftout_q = 0; ++ u32 ptmp=0, pint = 0; ++ u8 bbp = 0; ++ u8 tidxi; ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x00); ++ rt2800_bbp_write(rt2x00dev, 159, 0x9b); ++ ++ bbp = 0x9b; ++ ++ while (bbp == 0x9b) { ++ udelay(10); ++ bbp = rt2800_bbp_read(rt2x00dev, 159); ++ bbp = bbp & 0xff; ++ } ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xba); ++ rt2800_bbp_write(rt2x00dev, 159, tidx); ++ rt2800_bbp_write(rt2x00dev, 159, tidx); ++ rt2800_bbp_write(rt2x00dev, 159, tidx); ++ ++ macvalue = rt2800_register_read(rt2x00dev, 0x057C); ++ ++ fftout_i = (macvalue >> 16); ++ fftout_i = (fftout_i & 0x8000) ? (fftout_i - 0x10000) : fftout_i; ++ fftout_q = (macvalue & 0xffff); ++ fftout_q = (fftout_q & 0x8000) ? (fftout_q - 0x10000) : fftout_q; ++ ptmp = (fftout_i * fftout_i); ++ ptmp = ptmp + (fftout_q * fftout_q); ++ pint = ptmp; ++ rt2x00_dbg(rt2x00dev, "I = %d, Q = %d, power = %x\n", fftout_i, fftout_q, pint); ++ if (read_neg) { ++ pint = pint >> 1; ++ tidxi = 0x40 - tidx; ++ tidxi = tidxi & 0x3f; ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xba); ++ rt2800_bbp_write(rt2x00dev, 159, tidxi); ++ rt2800_bbp_write(rt2x00dev, 159, tidxi); ++ rt2800_bbp_write(rt2x00dev, 159, tidxi); ++ ++ macvalue = rt2800_register_read(rt2x00dev, 0x057C); ++ ++ fftout_i = (macvalue >> 16); ++ fftout_i = (fftout_i & 0x8000) ? (fftout_i - 0x10000) : fftout_i; ++ fftout_q = (macvalue & 0xffff); ++ fftout_q = (fftout_q & 0x8000) ? (fftout_q - 0x10000) : fftout_q; ++ ptmp = (fftout_i * fftout_i); ++ ptmp = ptmp + (fftout_q * fftout_q); ++ ptmp = ptmp >> 1; ++ pint = pint + ptmp; ++ } ++ ++ return pint; ++} ++ ++static u32 rt2800_read_fft_accumulation(struct rt2x00_dev *rt2x00dev, u8 tidx) { ++ u32 macvalue = 0; ++ int fftout_i = 0, fftout_q = 0; ++ u32 ptmp=0, pint = 0; ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xBA); ++ rt2800_bbp_write(rt2x00dev, 159, tidx); ++ rt2800_bbp_write(rt2x00dev, 159, tidx); ++ rt2800_bbp_write(rt2x00dev, 159, tidx); ++ ++ macvalue = rt2800_register_read(rt2x00dev, 0x057C); ++ ++ fftout_i = (macvalue >> 16); ++ fftout_i = (fftout_i & 0x8000) ? (fftout_i - 0x10000) : fftout_i; ++ fftout_q = (macvalue & 0xffff); ++ fftout_q = (fftout_q & 0x8000) ? (fftout_q - 0x10000) : fftout_q; ++ ptmp = (fftout_i * fftout_i); ++ ptmp = ptmp + (fftout_q * fftout_q); ++ pint = ptmp; ++ rt2x00_info(rt2x00dev, "I = %d, Q = %d, power = %x\n", fftout_i, fftout_q, pint); ++ ++ return pint; ++} ++ ++static void rt2800_write_dc(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 alc, u8 iorq, u8 dc) ++{ ++ u8 bbp = 0; ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xb0); ++ bbp = alc | 0x80; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ ++ if (ch_idx == 0) ++ bbp = (iorq == 0) ? 0xb1: 0xb2; ++ else ++ bbp = (iorq == 0) ? 0xb8: 0xb9; ++ ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ bbp = dc; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ ++ return; ++} ++ ++static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 alc_idx, u8 dc_result[][RF_ALC_NUM][2]) ++{ ++ u32 p0 = 0, p1 = 0, pf = 0; ++ char idx0 = 0, idx1 = 0; ++ u8 idxf[] = {0x00, 0x00}; ++ u8 ibit = 0x20; ++ u8 iorq; ++ char bidx; ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xb0); ++ rt2800_bbp_write(rt2x00dev, 159, 0x80); ++ ++ for (bidx = 5; bidx >= 0; bidx--) { ++ for (iorq = 0; iorq <= 1; iorq++) { ++ rt2x00_dbg(rt2x00dev, "\n========================================================\n"); ++ ++ if (idxf[iorq] == 0x20) { ++ idx0 = 0x20; ++ p0 = pf; ++ } else { ++ idx0 = idxf[iorq] - ibit; ++ idx0 = idx0 & 0x3F; ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idx0); ++ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0); ++ } ++ ++ idx1 = idxf[iorq] + ((bidx == 5) ? 0 : ibit); ++ idx1 = idx1 & 0x3F; ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idx1); ++ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0); ++ ++ rt2x00_dbg(rt2x00dev, "alc=%u, IorQ=%u, idx_final=%2x\n", alc_idx, iorq, idxf[iorq]); ++ rt2x00_dbg(rt2x00dev, "p0=%x, p1=%x, pf=%x, idx_0=%x, idx_1=%x, ibit=%x !\n", p0, p1, pf, idx0, idx1, ibit); ++ ++ if ((bidx != 5) && (pf <= p0) && (pf < p1)) { ++ pf = pf; ++ idxf[iorq] = idxf[iorq]; ++ } else if (p0 < p1) { ++ pf = p0; ++ idxf[iorq] = idx0 & 0x3F; ++ } else { ++ pf = p1; ++ idxf[iorq] = idx1 & 0x3F; ++ } ++ rt2x00_dbg(rt2x00dev, "IorQ=%u, idx_final[%u]:%x, pf:%8x\n", iorq, iorq, idxf[iorq], pf); ++ ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idxf[iorq]); ++ ++ } ++ ibit = ibit >> 1; ++ } ++ dc_result[ch_idx][alc_idx][0] = idxf[0]; ++ dc_result[ch_idx][alc_idx][1] = idxf[1]; ++ ++ return; ++} ++ ++static void rt2800_iq_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 *ges, u8 *pes) ++{ ++ u32 p0 = 0, p1 = 0, pf = 0; ++ char perr = 0, gerr = 0, iq_err = 0; ++ char pef = 0, gef = 0; ++ char psta, pend; ++ char gsta, gend; ++ ++ u8 ibit = 0x20; ++ u8 first_search = 0x00, touch_neg_max = 0x00; ++ char idx0 = 0, idx1 = 0; ++ u8 gop; ++ u8 bbp = 0; ++ char bidx; ++ ++ rt2x00_info(rt2x00dev, "IQCalibration Start!\n"); ++ for (bidx = 5; bidx >= 1; bidx--) { ++ for (gop = 0; gop < 2; gop++) { ++ rt2x00_dbg(rt2x00dev, "\n========================================================\n"); ++ ++ if ((gop == 1) || (bidx < 4)) { ++ if (gop == 0) ++ iq_err = gerr; ++ else ++ iq_err = perr; ++ ++ first_search = (gop == 0) ? (bidx == 3) : (bidx == 5); ++ touch_neg_max = (gop) ? ((iq_err & 0x0F) == 0x08) : ((iq_err & 0x3F) == 0x20); ++ ++ if (touch_neg_max) { ++ p0 = pf; ++ idx0 = iq_err; ++ } else { ++ idx0 = iq_err - ibit; ++ bbp = (ch_idx == 0) ? ((gop == 0) ? 0x28 : 0x29): ((gop == 0) ? 0x46 : 0x47); ++ ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, idx0); ++ ++ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1); ++ } ++ ++ idx1 = iq_err + (first_search ? 0 : ibit); ++ idx1 = (gop == 0) ? (idx1 & 0x0F) : (idx1 & 0x3F); ++ ++ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 : (gop == 0) ? 0x46 : 0x47; ++ ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, idx1); ++ ++ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1); ++ ++ rt2x00_dbg(rt2x00dev, "p0=%x, p1=%x, pwer_final=%x, idx0=%x, idx1=%x, iq_err=%x, gop=%d, ibit=%x !\n", p0, p1, pf, idx0, idx1, iq_err, gop, ibit); ++ ++ if ((!first_search) && (pf <= p0) && (pf < p1)) { ++ pf = pf; ++ } else if (p0 < p1) { ++ pf = p0; ++ iq_err = idx0; ++ } else { ++ pf = p1; ++ iq_err = idx1; ++ } ++ ++ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 : (gop == 0) ? 0x46 : 0x47; ++ ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, iq_err); ++ ++ if (gop == 0) ++ gerr = iq_err; ++ else ++ perr = iq_err; ++ ++ rt2x00_dbg(rt2x00dev, "IQCalibration pf=%8x (%2x, %2x) !\n", pf, gerr & 0x0F, perr & 0x3F); ++ ++ } ++ } ++ ++ if (bidx > 0) ++ ibit = (ibit >> 1); ++ } ++ gerr = (gerr & 0x08) ? (gerr & 0x0F) - 0x10 : (gerr & 0x0F); ++ perr = (perr & 0x20) ? (perr & 0x3F) - 0x40 : (perr & 0x3F); ++ ++ gerr = (gerr < -0x07) ? -0x07 : (gerr > 0x05) ? 0x05 : gerr; ++ gsta = gerr - 1; ++ gend = gerr + 2; ++ ++ perr = (perr < -0x1f) ? -0x1f : (perr > 0x1d) ? 0x1d : perr; ++ psta = perr - 1; ++ pend = perr + 2; ++ ++ for (gef = gsta; gef <= gend; gef = gef + 1) ++ for (pef = psta; pef <= pend; pef = pef + 1) { ++ bbp = (ch_idx == 0) ? 0x28 : 0x46; ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, gef & 0x0F); ++ ++ bbp = (ch_idx == 0) ? 0x29 : 0x47; ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, pef & 0x3F); ++ ++ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1); ++ if ((gef == gsta) && (pef == psta)) { ++ pf = p1; ++ gerr = gef; ++ perr = pef; ++ } ++ else if (pf > p1){ ++ pf = p1; ++ gerr = gef; ++ perr = pef; ++ } ++ rt2x00_dbg(rt2x00dev, "Fine IQCalibration p1=%8x pf=%8x (%2x, %2x) !\n", p1, pf, gef & 0x0F, pef & 0x3F); ++ } ++ ++ ges[ch_idx] = gerr & 0x0F; ++ pes[ch_idx] = perr & 0x3F; ++ ++ rt2x00_info(rt2x00dev, "IQCalibration Done! CH = %u, (gain=%2x, phase=%2x)\n", ch_idx, gerr & 0x0F, perr & 0x3F); ++ ++ return; ++} ++ ++static void rt2800_rf_aux_tx0_loopback(struct rt2x00_dev *rt2x00dev) ++{ ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, 0x21); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, 0x10); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x00); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, 0x1b); ++ rt2800_rfcsr_write_bank(rt2x00dev, 4, 0, 0x81); ++ rt2800_rfcsr_write_bank(rt2x00dev, 4, 2, 0x81); ++ rt2800_rfcsr_write_bank(rt2x00dev, 4, 34, 0xee); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 3, 0x2d); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, 0x2d); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, 0x80); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, 0xd7); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, 0xa2); ++ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, 0x20); ++} ++ ++static void rt2800_rf_aux_tx1_loopback(struct rt2x00_dev *rt2x00dev) ++{ ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, 0x22); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, 0x20); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x00); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, 0x4b); ++ rt2800_rfcsr_write_bank(rt2x00dev, 6, 0, 0x81); ++ rt2800_rfcsr_write_bank(rt2x00dev, 6, 2, 0x81); ++ rt2800_rfcsr_write_bank(rt2x00dev, 6, 34, 0xee); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 3, 0x2d); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 4, 0x2d); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 17, 0x80); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 18, 0xd7); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 19, 0xa2); ++ rt2800_rfcsr_write_bank(rt2x00dev, 7, 20, 0x20); ++} ++ ++void rt2800_loft_iq_calibration(struct rt2x00_dev *rt2x00dev) ++{ ++ rf_reg_pair rf_store[CHAIN_NUM][13]; ++ u32 macorg1 = 0; ++ u32 macorg2 = 0; ++ u32 macorg3 = 0; ++ u32 macorg4 = 0; ++ u32 macorg5 = 0; ++ u32 orig528 = 0; ++ u32 orig52c = 0; ++ ++ u32 savemacsysctrl = 0, mtxcycle = 0; ++ u32 macvalue = 0; ++ u32 mac13b8 = 0; ++ u32 p0 = 0, p1 = 0; ++ u32 p0_idx10 = 0, p1_idx10 = 0; ++ ++ u8 rfvalue; ++ u8 loft_dc_search_result[CHAIN_NUM][RF_ALC_NUM][2]; ++ u8 ger[CHAIN_NUM], per[CHAIN_NUM]; ++ u8 rf_gain[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x0c}; ++ u8 rfvga_gain_table[] = {0x24, 0x25, 0x26, 0x27, 0x28, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3F}; ++ ++ u8 vga_gain[] = {14, 14}; ++ u8 bbp_2324gain[] = {0x16, 0x14, 0x12, 0x10, 0x0c, 0x08}; ++ u8 bbp = 0, ch_idx = 0, rf_alc_idx = 0, idx = 0; ++ u8 bbpr30, rfb0r39, rfb0r42; ++ u8 bbpr1; ++ u8 bbpr4; ++ u8 bbpr241, bbpr242; ++ u8 count_step; ++ ++ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ macorg1 = rt2800_register_read(rt2x00dev, TX_PIN_CFG); ++ macorg2 = rt2800_register_read(rt2x00dev, RF_CONTROL0); ++ macorg3 = rt2800_register_read(rt2x00dev, RF_BYPASS0); ++ macorg4 = rt2800_register_read(rt2x00dev, RF_CONTROL3); ++ macorg5 = rt2800_register_read(rt2x00dev, RF_BYPASS3); ++ mac13b8 = rt2800_register_read(rt2x00dev, 0x13b8); ++ orig528 = rt2800_register_read(rt2x00dev, RF_CONTROL2); ++ orig52c = rt2800_register_read(rt2x00dev, RF_BYPASS2); ++ ++ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ macvalue &= (~0x04); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); ++ ++ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { ++ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macvalue & 0x01) ++ udelay(50); ++ else ++ break; ++ } ++ ++ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ macvalue &= (~0x08); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); ++ ++ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { ++ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macvalue & 0x02) ++ udelay(50); ++ else ++ break; ++ } ++ ++ for (ch_idx = 0; ch_idx < 2; ch_idx++) { ++ rt2800_rf_configstore(rt2x00dev, rf_store, ch_idx); ++ } ++ ++ bbpr30 = rt2800_bbp_read(rt2x00dev, 30); ++ rfb0r39 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 39); ++ rfb0r42 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42); ++ ++ rt2800_bbp_write(rt2x00dev, 30, 0x1F); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 39, 0x80); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, 0x5B); ++ ++ rt2800_bbp_write(rt2x00dev, 23, 0x00); ++ rt2800_bbp_write(rt2x00dev, 24, 0x00); ++ ++ rt2800_setbbptonegenerator(rt2x00dev); ++ ++ for (ch_idx = 0; ch_idx < 2; ch_idx ++) { ++ rt2800_bbp_write(rt2x00dev, 23, 0x00); ++ rt2800_bbp_write(rt2x00dev, 24, 0x00); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00); ++ rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0x0000000F); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00003306); ++ rt2800_register_write(rt2x00dev, 0x13b8, 0x10); ++ udelay(1); ++ ++ if (ch_idx == 0) { ++ rt2800_rf_aux_tx0_loopback(rt2x00dev); ++ } else { ++ rt2800_rf_aux_tx1_loopback(rt2x00dev); ++ } ++ udelay(1); ++ ++ if (ch_idx == 0) { ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001004); ++ } else { ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002004); ++ } ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x05); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x01); ++ if (ch_idx == 0) ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ else ++ rt2800_bbp_write(rt2x00dev, 159, 0x01); ++ ++ vga_gain[ch_idx] = 18; ++ for (rf_alc_idx = 0; rf_alc_idx < 3; rf_alc_idx++) { ++ rt2800_bbp_write(rt2x00dev, 23, bbp_2324gain[rf_alc_idx]); ++ rt2800_bbp_write(rt2x00dev, 24, bbp_2324gain[rf_alc_idx]); ++ ++ macvalue = rt2800_register_read(rt2x00dev, RF_CONTROL3); ++ macvalue &= (~0x0000F1F1); ++ macvalue |= (rf_gain[rf_alc_idx] << 4); ++ macvalue |= (rf_gain[rf_alc_idx] << 12); ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, macvalue); ++ macvalue = (0x0000F1F1); ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, macvalue); ++ ++ if (rf_alc_idx == 0) { ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x21); ++ for (;vga_gain[ch_idx] > 0;vga_gain[ch_idx] = vga_gain[ch_idx] - 2) { ++ rfvalue = rfvga_gain_table[vga_gain[ch_idx]]; ++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue); ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x00); ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x00); ++ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0); ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x21); ++ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0); ++ rt2x00_dbg(rt2x00dev, "LOFT AGC %d %d\n", p0, p1); ++ if ((p0 < 7000*7000) && (p1 < (7000*7000))) { ++ break; ++ } ++ } ++ ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x00); ++ rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x00); ++ ++ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n",vga_gain[ch_idx], rfvga_gain_table[vga_gain[ch_idx]]); ++ ++ if (vga_gain[ch_idx] < 0) ++ vga_gain[ch_idx] = 0; ++ } ++ ++ rfvalue = rfvga_gain_table[vga_gain[ch_idx]]; ++ ++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue); ++ ++ rt2800_loft_search(rt2x00dev, ch_idx, rf_alc_idx, loft_dc_search_result); ++ } ++ } ++ ++ for (rf_alc_idx = 0; rf_alc_idx < 3; rf_alc_idx++) { ++ //serge: just to see values ++ pr_info("LOFT ALC (0xb0, %2x) I0 (0xb1, %2x) Q0 (0xb2, %2x) I1 (0xb8, %2x) Q1 (0xb9, %2x)\n", ++ rf_alc_idx, ++ loft_dc_search_result[CHAIN_0][rf_alc_idx][0x00] & 0x3F, ++ loft_dc_search_result[CHAIN_0][rf_alc_idx][0x01] & 0x3F, ++ loft_dc_search_result[CHAIN_1][rf_alc_idx][0x00] & 0x3F, ++ loft_dc_search_result[CHAIN_1][rf_alc_idx][0x01] & 0x3F ++ ); ++ ++ for (idx = 0; idx < 4; idx++) { ++ rt2800_bbp_write(rt2x00dev, 158, 0xB0); ++ bbp = (idx<<2) + rf_alc_idx; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ rt2x00_dbg(rt2x00dev, " ALC %2x,", bbp); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xb1); ++ bbp = loft_dc_search_result[CHAIN_0][rf_alc_idx][0x00]; ++ bbp = bbp & 0x3F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ rt2x00_dbg(rt2x00dev, " I0 %2x,", bbp); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xb2); ++ bbp = loft_dc_search_result[CHAIN_0][rf_alc_idx][0x01]; ++ bbp = bbp & 0x3F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ rt2x00_dbg(rt2x00dev, " Q0 %2x,", bbp); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xb8); ++ bbp = loft_dc_search_result[CHAIN_1][rf_alc_idx][0x00]; ++ bbp = bbp & 0x3F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ rt2x00_dbg(rt2x00dev, " I1 %2x,", bbp); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0xb9); ++ bbp = loft_dc_search_result[CHAIN_1][rf_alc_idx][0x01]; ++ bbp = bbp & 0x3F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ rt2x00_dbg(rt2x00dev, " Q1 %2x\n", bbp); ++ } ++ } ++ ++ rt2800_bbp_write(rt2x00dev, 23, 0x00); ++ rt2800_bbp_write(rt2x00dev, 24, 0x00); ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x04); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x00); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ ++ bbp = 0x00; ++ rt2800_bbp_write(rt2x00dev, 244, 0x00); ++ ++ rt2800_bbp_write(rt2x00dev, 21, 0x01); ++ udelay(1); ++ rt2800_bbp_write(rt2x00dev, 21, 0x00); ++ ++ rt2800_rf_configrecover(rt2x00dev, rf_store); ++ ++ rt2800_register_write(rt2x00dev, TX_PIN_CFG, macorg1); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x04); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, macorg2); ++ udelay(1); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, macorg3); ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, macorg4); ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, macorg5); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl); ++ rt2800_register_write(rt2x00dev, RF_CONTROL2, orig528); ++ rt2800_register_write(rt2x00dev, RF_BYPASS2, orig52c); ++ rt2800_register_write(rt2x00dev, 0x13b8, mac13b8); ++ ++ rt2x00_info(rt2x00dev, "LOFT Calibration Done!\n"); ++ ++ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ macorg1 = rt2800_register_read(rt2x00dev, TX_PIN_CFG); ++ macorg2 = rt2800_register_read(rt2x00dev, RF_CONTROL0); ++ macorg3 = rt2800_register_read(rt2x00dev, RF_BYPASS0); ++ macorg4 = rt2800_register_read(rt2x00dev, RF_CONTROL3); ++ macorg5 = rt2800_register_read(rt2x00dev, RF_BYPASS3); ++ ++ bbpr1 = rt2800_bbp_read(rt2x00dev, 1); ++ bbpr4 = rt2800_bbp_read(rt2x00dev, 4); ++ bbpr241 = rt2800_bbp_read(rt2x00dev, 241); ++ bbpr242 = rt2800_bbp_read(rt2x00dev, 242); ++ mac13b8 = rt2800_register_read(rt2x00dev, 0x13b8); ++ ++ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ macvalue &= (~0x04); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); ++ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { ++ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macvalue & 0x01) ++ udelay(50); ++ else ++ break; ++ } ++ ++ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); ++ macvalue &= (~0x08); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); ++ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { ++ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (macvalue & 0x02) ++ udelay(50); ++ else ++ break; ++ } ++ ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x00000101); ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, 0x0000F1F1); ++ } ++ ++ rt2800_bbp_write(rt2x00dev, 23, 0x00); ++ rt2800_bbp_write(rt2x00dev, 24, 0x00); ++ ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2800_bbp_write(rt2x00dev, 4, bbpr4 & (~0x18)); ++ rt2800_bbp_write(rt2x00dev, 21, 0x01); ++ udelay(1); ++ rt2800_bbp_write(rt2x00dev, 21, 0x00); ++ ++ rt2800_bbp_write(rt2x00dev, 241, 0x14); ++ rt2800_bbp_write(rt2x00dev, 242, 0x80); ++ rt2800_bbp_write(rt2x00dev, 244, 0x31); ++ } else { ++ rt2800_setbbptonegenerator(rt2x00dev); ++ } ++ ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00003306); ++ udelay(1); ++ ++ rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0x0000000F); ++ ++ if (!test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x00000000); ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, 0x0000F1F1); ++ } ++ ++ rt2800_register_write(rt2x00dev, 0x13b8, 0x00000010); ++ ++ for (ch_idx = 0; ch_idx < 2; ch_idx++) { ++ rt2800_rf_configstore(rt2x00dev, rf_store, ch_idx); ++ } ++ ++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x3B); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x3B); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x03); ++ rt2800_bbp_write(rt2x00dev, 159, 0x60); ++ rt2800_bbp_write(rt2x00dev, 158, 0xB0); ++ rt2800_bbp_write(rt2x00dev, 159, 0x80); ++ ++ for (ch_idx = 0; ch_idx < 2; ch_idx ++) { ++ rt2800_bbp_write(rt2x00dev, 23, 0x00); ++ rt2800_bbp_write(rt2x00dev, 24, 0x00); ++ ++ if (ch_idx == 0) { ++ rt2800_bbp_write(rt2x00dev, 158, 0x01); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ bbp = bbpr1 & (~0x18); ++ bbp = bbp | 0x00; ++ rt2800_bbp_write(rt2x00dev, 1, bbp); ++ } ++ rt2800_rf_aux_tx0_loopback(rt2x00dev); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001004); ++ } else { ++ rt2800_bbp_write(rt2x00dev, 158, 0x01); ++ rt2800_bbp_write(rt2x00dev, 159, 0x01); ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags)) { ++ bbp = bbpr1 & (~0x18); ++ bbp = bbp | 0x08; ++ rt2800_bbp_write(rt2x00dev, 1, bbp); ++ } ++ rt2800_rf_aux_tx1_loopback(rt2x00dev); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002004); ++ } ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x05); ++ rt2800_bbp_write(rt2x00dev, 159, 0x04); ++ ++ bbp = (ch_idx == 0) ? 0x28 : 0x46; ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2800_bbp_write(rt2x00dev, 23, 0x06); ++ rt2800_bbp_write(rt2x00dev, 24, 0x06); ++ count_step = 1; ++ } else { ++ rt2800_bbp_write(rt2x00dev, 23, 0x1F); ++ rt2800_bbp_write(rt2x00dev, 24, 0x1F); ++ count_step = 2; ++ } ++ ++ for (;vga_gain[ch_idx] < 19; vga_gain[ch_idx]=(vga_gain[ch_idx] + count_step)) { ++ rfvalue = rfvga_gain_table[vga_gain[ch_idx]]; ++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue); ++ ++ bbp = (ch_idx == 0) ? 0x29 : 0x47; ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 0); ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ p0_idx10 = rt2800_read_fft_accumulation(rt2x00dev, 0x0A); ++ } ++ ++ bbp = (ch_idx == 0) ? 0x29 : 0x47; ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, 0x21); ++ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 0); ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags)) { ++ p1_idx10 = rt2800_read_fft_accumulation(rt2x00dev, 0x0A); ++ } ++ ++ rt2x00_dbg(rt2x00dev, "IQ AGC %d %d\n", p0, p1); ++ ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2x00_dbg(rt2x00dev, "IQ AGC IDX 10 %d %d\n", p0_idx10, p1_idx10); ++ if ((p0_idx10 > 7000*7000) || (p1_idx10 > 7000*7000)) { ++ if (vga_gain[ch_idx]!=0) ++ vga_gain[ch_idx] = vga_gain[ch_idx]-1; ++ break; ++ } ++ } ++ ++ if ((p0 > 2500*2500) || (p1 > 2500*2500)) { ++ break; ++ } ++ } ++ ++ if (vga_gain[ch_idx] > 18) ++ vga_gain[ch_idx] = 18; ++ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n",vga_gain[ch_idx], rfvga_gain_table[vga_gain[ch_idx]]); ++ ++ bbp = (ch_idx == 0) ? 0x29 : 0x47; ++ rt2800_bbp_write(rt2x00dev, 158, bbp); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ ++ rt2800_iq_search(rt2x00dev, ch_idx, ger, per); ++ } ++ ++ rt2800_bbp_write(rt2x00dev, 23, 0x00); ++ rt2800_bbp_write(rt2x00dev, 24, 0x00); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x04); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x28); ++ bbp = ger[CHAIN_0] & 0x0F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x29); ++ bbp = per[CHAIN_0] & 0x3F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x46); ++ bbp = ger[CHAIN_1] & 0x0F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x47); ++ bbp = per[CHAIN_1] & 0x3F; ++ rt2800_bbp_write(rt2x00dev, 159, bbp); ++ ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2800_bbp_write(rt2x00dev, 1, bbpr1); ++ rt2800_bbp_write(rt2x00dev, 241, bbpr241); ++ rt2800_bbp_write(rt2x00dev, 242, bbpr242); ++ } ++ rt2800_bbp_write(rt2x00dev, 244, 0x00); ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x00); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ rt2800_bbp_write(rt2x00dev, 158, 0xB0); ++ rt2800_bbp_write(rt2x00dev, 159, 0x00); ++ ++ rt2800_bbp_write(rt2x00dev, 30, bbpr30); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 39, rfb0r39); ++ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfb0r42); ++ ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ rt2800_bbp_write(rt2x00dev, 4, bbpr4); ++ } ++ ++ rt2800_bbp_write(rt2x00dev, 21, 0x01); ++ udelay(1); ++ rt2800_bbp_write(rt2x00dev, 21, 0x00); ++ ++ rt2800_rf_configrecover(rt2x00dev, rf_store); ++ ++ rt2800_register_write(rt2x00dev, TX_PIN_CFG, macorg1); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00); ++ rt2800_register_write(rt2x00dev, RF_CONTROL0, macorg2); ++ udelay(1); ++ rt2800_register_write(rt2x00dev, RF_BYPASS0, macorg3); ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, macorg4); ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, macorg5); ++ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl); ++ rt2800_register_write(rt2x00dev, 0x13b8, mac13b8); ++ ++ rt2x00_info(rt2x00dev, "TX IQ Calibration Done!\n"); ++ ++ return; ++} ++ + static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev, + bool set_bw, bool is_ht40) + { +@@ -8789,31 +10551,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); +@@ -8879,63 +10646,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); +@@ -8998,15 +10773,49 @@ 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); ++ } + ++ rt6352_enable_pa_pin(rt2x00dev, 0);//serge: vendor driver do it before calibration (7b) ++ rt2800_r_calibration(rt2x00dev); ++ rt2800_rf_self_txdc_cal(rt2x00dev); ++ rt2800_rxdcoc_calibration(rt2x00dev); + rt2800_bw_filter_calibration(rt2x00dev, true); + rt2800_bw_filter_calibration(rt2x00dev, false); ++ rt2800_loft_iq_calibration(rt2x00dev); ++ rt2800_rxiq_calibration(rt2x00dev); ++ rt6352_enable_pa_pin(rt2x00dev, 1);//serge: vendor driver do it after calibration (7b) ++ /* Vendor driver restore iLNA/iPA before ++ recalibration and set correct values after. ++ Openwrt driver init iLNA and iPA but restore only ++ ePA values after recalibration. ++ So set eLNA values only ++ */ ++ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {//serge: rf regs never corrected for eLNA (7a) ++ rt2x00_info(rt2x00dev, "Correct RF/BBP for eLNA!\n"); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 18, 0x42); ++ rt2800_bbp_write(rt2x00dev, 75, 0x68);//serge: move bbp eLNA init here? ++ rt2800_bbp_write(rt2x00dev, 76, 0x4C); ++ rt2800_bbp_write(rt2x00dev, 79, 0x1C); ++ rt2800_bbp_write(rt2x00dev, 80, 0x0C); ++ rt2800_bbp_write(rt2x00dev, 82, 0xB6); ++ /* bank 0 RF reg 42 and glrt BBP reg 141 ++ will be set in config channel function ++ in dependence of channel and HT20/HT40 ++ so don't touch it ++ */ ++ } + } + + static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) +@@ -9435,6 +11244,8 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) + rf = RF3853; + else if (rt2x00_rt(rt2x00dev, RT5350)) + rf = RF5350; ++ else if (rt2x00_rt(rt2x00dev, RT5592)) ++ rf = RF5592; + else + rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); + +@@ -9547,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 */ + +@@ -9564,7 +11386,8 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) + */ + eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1); + +- if (rt2x00_rt(rt2x00dev, RT3352)) { ++ if (rt2x00_rt(rt2x00dev, RT3352) || ++ rt2x00_rt(rt2x00dev, RT6352)) { + if (rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_EXTERNAL_TX0_PA_3352)) + __set_bit(CAPABILITY_EXTERNAL_PA_TX0, +@@ -9575,6 +11398,18 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) + &rt2x00dev->cap_flags); + } + ++ eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF2); ++ ++ if (rt2x00_rt(rt2x00dev, RT6352) && eeprom != 0 && eeprom != 0xffff) { ++ if (rt2x00_get_field16(eeprom, ++ EEPROM_NIC_CONF2_EXTERNAL_PA)) { ++ __set_bit(CAPABILITY_EXTERNAL_PA_TX0, ++ &rt2x00dev->cap_flags); ++ __set_bit(CAPABILITY_EXTERNAL_PA_TX1, ++ &rt2x00dev->cap_flags); ++ } ++ } ++ + return 0; + } + +diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +index 1139405..7099098 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +@@ -17,6 +17,16 @@ + #define WCID_START 33 + #define WCID_END 222 + #define STA_IDS_SIZE (WCID_END - WCID_START + 2) ++#define CHAIN_0 0x0 ++#define CHAIN_1 0x1 ++#define RF_ALC_NUM 6 ++#define CHAIN_NUM 2 ++ ++typedef struct rf_reg_pair { ++ u8 bank; ++ u8 reg; ++ u8 value; ++} rf_reg_pair; + + /* RT2800 driver data structure */ + struct rt2800_drv_data { +@@ -37,6 +47,8 @@ struct rt2800_drv_data { + struct ieee80211_sta *wcid_to_sta[STA_IDS_SIZE]; + }; + ++#include "rt2800.h" ++ + struct rt2800_ops { + u32 (*register_read)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset); +@@ -66,6 +78,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, +@@ -135,6 +150,15 @@ static inline int rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev) + { + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + ++ if (rt2x00dev->eeprom_file) { ++ memcpy(rt2x00dev->eeprom, rt2x00dev->eeprom_file->data, ++ EEPROM_SIZE); ++ return 0; ++ } ++ ++ if (!rt2800ops->read_eeprom) ++ return -EINVAL; ++ + return rt2800ops->read_eeprom(rt2x00dev); + } + +@@ -174,6 +198,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 82c8608..64e556e 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 = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, +@@ -328,6 +332,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 472a1fc..d31daaf 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 ++#include ++#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 = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, +@@ -167,12 +181,14 @@ 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, + .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 = { +@@ -238,10 +254,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 12fdcf9..d27ba67 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 = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, +@@ -671,6 +675,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 bd754c5..f804d95 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h +@@ -28,6 +28,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -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 +@@ -978,6 +982,11 @@ struct rt2x00_dev { + */ + const struct firmware *fw; + ++ /* ++ * EEPROM image. ++ */ ++ const struct firmware *eeprom_file; ++ + /* + * FIFO for storing tx status reports between isr and tasklet. + */ +@@ -1021,6 +1030,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 { +diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +index 2412cb1..f555a30 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +@@ -989,6 +989,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)) { +@@ -1006,6 +1012,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) +@@ -1093,6 +1125,19 @@ static void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) + kfree(rt2x00dev->spec.channels_info); + } + ++static const struct ieee80211_tpt_blink rt2x00_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 }, ++}; ++ + static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) + { + struct hw_mode_spec *spec = &rt2x00dev->spec; +@@ -1174,6 +1219,10 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) + + #undef RT2X00_TASKLET_INIT + ++ ieee80211_create_tpt_led_trigger(rt2x00dev->hw, ++ IEEE80211_TPT_LEDTRIG_FL_RADIO, rt2x00_tpt_blink, ++ ARRAY_SIZE(rt2x00_tpt_blink)); ++ + /* + * Register HW. + */ +@@ -1308,7 +1357,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 +@@ -1401,6 +1450,10 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) + INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); + INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep); + ++ retval = rt2x00lib_load_eeprom_file(rt2x00dev); ++ if (retval) ++ goto exit; ++ + /* + * Let the driver probe the device to detect the capabilities. + */ +@@ -1541,6 +1594,11 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) + * Free the driver data. + */ + kfree(rt2x00dev->drv_data); ++ ++ /* ++ * Free EEPROM image. ++ */ ++ rt2x00lib_free_eeprom_file(rt2x00dev); + } + EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); + +diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c +new file mode 100644 +index 0000000..2dd0123 +--- /dev/null ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c +@@ -0,0 +1,187 @@ ++/* ++ Copyright (C) 2004 - 2009 Ivo van Doorn ++ Copyright (C) 2004 - 2009 Gertjan van Wingerde ++ ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the ++ Free Software Foundation, Inc., ++ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++/* ++ Module: rt2x00lib ++ Abstract: rt2x00 eeprom file loading routines. ++ */ ++ ++#include ++#include ++#if IS_ENABLED(CONFIG_MTD) ++#include ++#include ++#endif ++#include ++ ++#include "rt2x00.h" ++#include "rt2x00lib.h" ++ ++#if IS_ENABLED(CONFIG_MTD) ++static int rt2800lib_read_eeprom_mtd(struct rt2x00_dev *rt2x00dev) ++{ ++ int ret = -EINVAL; ++#ifdef CONFIG_OF ++ static struct firmware mtd_fw; ++ struct device_node *np = rt2x00dev->dev->of_node, *mtd_np = NULL; ++ size_t retlen, len = rt2x00dev->ops->eeprom_size; ++ int i, size, offset = 0; ++ struct mtd_info *mtd; ++ const char *part; ++ const __be32 *list; ++ phandle phandle; ++ ++ 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, len, &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; ++ } ++ ++ if (of_find_property(np, "ralink,mtd-eeprom-swap", NULL)) ++ for (i = 0; i < len/sizeof(u16); i++) ++ rt2x00dev->eeprom[i] = swab16(rt2x00dev->eeprom[i]); ++ ++ rt2x00dev->eeprom_file = &mtd_fw; ++ mtd_fw.data = (const u8 *) rt2x00dev->eeprom; ++ ++ dev_info(rt2x00dev->dev, "loaded eeprom from mtd device \"%s\"\n", part); ++#endif ++ ++ return ret; ++} ++#endif ++ ++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) == 0) ++ return eep; ++#endif ++ ++ return NULL; ++} ++ ++static int rt2x00lib_request_eeprom_file(struct rt2x00_dev *rt2x00dev) ++{ ++ const struct firmware *ee; ++ const char *ee_name; ++ int retval; ++ ++#if IS_ENABLED(CONFIG_MTD) ++ if (!rt2800lib_read_eeprom_mtd(rt2x00dev)) ++ return 0; ++#endif ++ ++ 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; ++ } ++ ++ rt2x00dev->eeprom_file = ee; ++ return 0; ++ ++err_release_ee: ++ release_firmware(ee); ++err_exit: ++ return retval; ++} ++ ++int rt2x00lib_load_eeprom_file(struct rt2x00_dev *rt2x00dev) ++{ ++ int retval; ++ ++ retval = rt2x00lib_request_eeprom_file(rt2x00dev); ++ if (retval) ++ return retval; ++ ++ return 0; ++} ++ ++void rt2x00lib_free_eeprom_file(struct rt2x00_dev *rt2x00dev) ++{ ++ if (rt2x00dev->eeprom_file && rt2x00dev->eeprom_file->size) ++ release_firmware(rt2x00dev->eeprom_file); ++ rt2x00dev->eeprom_file = NULL; ++} +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/rt2x00lib.h b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h +index 776046c..b08ca7c 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h +@@ -285,6 +285,22 @@ static inline void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) + } + #endif /* CPTCFG_RT2X00_LIB_FIRMWARE */ + ++/* ++ * EEPROM file handlers. ++ */ ++#ifdef CPTCFG_RT2X00_LIB_EEPROM ++int rt2x00lib_load_eeprom_file(struct rt2x00_dev *rt2x00dev); ++void rt2x00lib_free_eeprom_file(struct rt2x00_dev *rt2x00dev); ++#else ++static inline int rt2x00lib_load_eeprom_file(struct rt2x00_dev *rt2x00dev) ++{ ++ return 0; ++} ++static inline void rt2x00lib_free_eeprom_file(struct rt2x00_dev *rt2x00dev) ++{ ++} ++#endif /* CPTCFG_RT2X00_LIB_EEPROM */ ++ + /* + * Debugfs handlers. + */ +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/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 ++ * ++ * 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 ee80d12..ae52d3c 100644 +--- a/local-symbols ++++ b/local-symbols +@@ -345,6 +345,7 @@ RT2X00_LIB_FIRMWARE= + RT2X00_LIB_CRYPTO= + RT2X00_LIB_LEDS= + RT2X00_LIB_DEBUGFS= ++RT2X00_LIB_EEPROM= + RT2X00_DEBUG= + WLAN_VENDOR_REALTEK= + RTL8180= diff --git a/recipes-kernel/mac80211/mac80211/0009-netmodule-patches.patch b/recipes-kernel/mac80211/mac80211/0010-netmodule-patches.patch similarity index 55% rename from recipes-kernel/mac80211/mac80211/0009-netmodule-patches.patch rename to recipes-kernel/mac80211/mac80211/0010-netmodule-patches.patch index 61a2bfe..30c8e1d 100644 --- a/recipes-kernel/mac80211/mac80211/0009-netmodule-patches.patch +++ b/recipes-kernel/mac80211/mac80211/0010-netmodule-patches.patch @@ -1,62 +1,37 @@ -From 7ddf76afb4aa795fa6ec60defe149b36abaa9e8b Mon Sep 17 00:00:00 2001 +From e3b2a5bb9212437f59b215bc4d5f9052667dd40e Mon Sep 17 00:00:00 2001 From: Patrick Walther -Date: Mon, 22 Mar 2021 17:52:02 +0100 -Subject: [PATCH] 0007 netmodule patches +Date: Wed, 14 Sep 2022 15:09:20 +0200 +Subject: [PATCH] netmodule patches --- - drivers/net/wireless/ath/ath.h | 3 +- - drivers/net/wireless/ath/ath10k/htt_rx.c | 6 +- - drivers/net/wireless/ath/ath10k/mac.c | 40 ++++++++--- - drivers/net/wireless/ath/ath10k/pci.c | 2 +- - drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +- - drivers/net/wireless/ath/ath9k/htc_drv_main.c | 2 +- - drivers/net/wireless/ath/ath9k/hw.h | 1 + - drivers/net/wireless/ath/ath9k/main.c | 95 ++++++++++++++++++++++++++- - drivers/net/wireless/ath/key.c | 41 +++++++----- - drivers/net/wireless/ath/regd.c | 2 +- - drivers/net/wireless/ath/regd.h | 3 +- - drivers/net/wireless/ath/regd_common.h | 1 + - drivers/net/wireless/ti/wlcore/cmd.c | 7 -- - drivers/net/wireless/ti/wlcore/cmd.h | 1 + - drivers/net/wireless/ti/wlcore/conf.h | 3 + - drivers/net/wireless/ti/wlcore/init.c | 22 +++++-- - drivers/net/wireless/ti/wlcore/main.c | 28 ++++++-- - include/net/cfg80211.h | 11 +++- - include/net/mac80211.h | 8 ++- - include/uapi/linux/nl80211.h | 3 + - net/mac80211/cfg.c | 13 ++++ - net/mac80211/iface.c | 9 ++- - net/mac80211/main.c | 4 +- - net/wireless/core.c | 41 ++++++++++-- - net/wireless/nl80211.c | 13 ++++ - net/wireless/reg.c | 23 +++++-- - net/wireless/wext-compat.c | 3 +- - 27 files changed, 312 insertions(+), 75 deletions(-) + drivers/net/wireless/ath/ath10k/htt_rx.c | 6 ++-- + drivers/net/wireless/ath/ath10k/mac.c | 28 +++++++++++++--- + drivers/net/wireless/ath/ath10k/pci.c | 2 +- + drivers/net/wireless/ath/regd.c | 2 +- + drivers/net/wireless/ath/regd.h | 3 +- + drivers/net/wireless/ath/regd_common.h | 1 + + drivers/net/wireless/ti/wlcore/cmd.c | 7 ---- + drivers/net/wireless/ti/wlcore/cmd.h | 1 + + drivers/net/wireless/ti/wlcore/conf.h | 3 ++ + drivers/net/wireless/ti/wlcore/init.c | 22 ++++++++++--- + drivers/net/wireless/ti/wlcore/main.c | 28 ++++++++++++---- + include/net/cfg80211.h | 12 +++++-- + include/net/mac80211.h | 8 +++-- + include/uapi/linux/nl80211.h | 3 ++ + net/mac80211/cfg.c | 13 ++++++++ + net/mac80211/iface.c | 4 +++ + net/mac80211/main.c | 10 +++--- + net/wireless/core.c | 41 ++++++++++++++++++++---- + net/wireless/nl80211.c | 14 ++++++++ + net/wireless/reg.c | 27 +++++++++++++--- + net/wireless/wext-compat.c | 3 +- + 21 files changed, 188 insertions(+), 50 deletions(-) -diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h -index 2a18e2e..bb30fd7 100644 ---- a/drivers/net/wireless/ath/ath.h -+++ b/drivers/net/wireless/ath/ath.h -@@ -198,12 +198,13 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, - bool ath_is_mybeacon(struct ath_common *common, struct ieee80211_hdr *hdr); - - void ath_hw_setbssidmask(struct ath_common *common); --void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key); -+void ath_key_delete(struct ath_common *common, u8 hw_key_idx); - int ath_key_config(struct ath_common *common, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); - bool ath_hw_keyreset(struct ath_common *common, u16 entry); -+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac); - void ath_hw_cycle_counters_update(struct ath_common *common); - int32_t ath_hw_get_listen_time(struct ath_common *common); - diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c -index 28ec3c5..9bb791c 100644 +index adbaeb6..6bdddb0 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c -@@ -3864,8 +3864,10 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data) +@@ -3862,8 +3862,10 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data) spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find_by_id(ar, peer_id); if (!peer || !peer->sta) { @@ -70,29 +45,10 @@ index 28ec3c5..9bb791c 100644 } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c -index d07a4a1..9547476 100644 +index 7a5311b..a96ec1c 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c -@@ -5097,11 +5097,13 @@ static int ath10k_start(struct ieee80211_hw *hw) - } - - param = ar->wmi.pdev_param->idle_ps_config; -- ret = ath10k_wmi_pdev_set_param(ar, param, 1); -- if (ret && ret != -EOPNOTSUPP) { -- ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret); -- goto err_core_stop; -- } -+ if (param != WMI_PDEV_PARAM_UNSUPPORTED) { -+ ret = ath10k_wmi_pdev_set_param(ar, param, 1); -+ if (ret && ret != -EOPNOTSUPP) { -+ ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret); -+ goto err_core_stop; -+ } -+ } - - __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask); - -@@ -5273,10 +5275,11 @@ static int ath10k_mac_txpower_setup(struct ath10k *ar, int txpower) +@@ -2990,10 +2990,11 @@ static int ath10k_mac_txpower_setup(struct ath10k *ar, int txpower) return 0; } @@ -105,27 +61,27 @@ index d07a4a1..9547476 100644 lockdep_assert_held(&ar->conf_mutex); -@@ -5285,10 +5288,28 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar) +@@ -3002,10 +3003,28 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar) if (arvif->txpower == INT_MIN) continue; -+ pwr = arvif->txpower; ++ pwr = arvif->txpower; + -+ if (ar && ar->rx_channel ) { -+ if (ar->rx_channel->band == NL80211_BAND_5GHZ) { -+ pwr -= ATH10k_ADJ_POWER_DENSITY; -+ if (ar->rx_channel->flags & IEEE80211_CHAN_RADAR) { -+ pwr -= ATH10k_ADJ_POWER_DENSITY; -+ if (arvif->vif->type == NL80211_IFTYPE_STATION || -+ arvif->vif->type == NL80211_IFTYPE_MESH_POINT) { -+ pwr -= ATH10k_ADJ_POWER_DENSITY; -+ } -+ } -+ } -+ } -+ if (pwr < 0) { -+ pwr = 0; -+ } ++ if (ar && ar->rx_channel ) { ++ if (ar->rx_channel->band == NL80211_BAND_5GHZ) { ++ pwr -= ATH10k_ADJ_POWER_DENSITY; ++ if (ar->rx_channel->flags & IEEE80211_CHAN_RADAR) { ++ pwr -= ATH10k_ADJ_POWER_DENSITY; ++ if (arvif->vif->type == NL80211_IFTYPE_STATION || ++ arvif->vif->type == NL80211_IFTYPE_MESH_POINT) { ++ pwr -= ATH10k_ADJ_POWER_DENSITY; ++ } ++ } ++ } ++ } ++ if (pwr < 0) { ++ pwr = 0; ++ } + if (txpower == -1) - txpower = arvif->txpower; @@ -136,15 +92,15 @@ index d07a4a1..9547476 100644 } if (txpower == -1) -@@ -5303,6 +5324,7 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar) +@@ -3020,6 +3039,7 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar) return 0; } +#undef ATH10k_CLIENT_ADJ_POWER_DENSITY - static int ath10k_config(struct ieee80211_hw *hw, u32 changed) + static int ath10k_mac_set_sar_power(struct ath10k *ar) { -@@ -9440,7 +9462,7 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { +@@ -9623,7 +9643,7 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { #endif }, { @@ -154,7 +110,7 @@ index d07a4a1..9547476 100644 }, }; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c -index a7b6b8c..dc8c753 100644 +index e9e08b0..7bfa164 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -28,7 +28,7 @@ enum ath10k_pci_reset_mode { @@ -166,268 +122,11 @@ index a7b6b8c..dc8c753 100644 static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO; module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644); -diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c -index 47dd580..9b7cbe7 100644 ---- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c -+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c -@@ -516,7 +516,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - } - break; - case DISABLE_KEY: -- ath_key_delete(common, key); -+ ath_key_delete(common, key->hw_key_idx); - break; - default: - ret = -EINVAL; -diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c -index abe6411..0909814 100644 ---- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c -+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c -@@ -1461,7 +1461,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, - } - break; - case DISABLE_KEY: -- ath_key_delete(common, key); -+ ath_key_delete(common, key->hw_key_idx); - break; - default: - ret = -EINVAL; -diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h -index 559609d..d152b4b 100644 ---- a/drivers/net/wireless/ath/ath9k/hw.h -+++ b/drivers/net/wireless/ath/ath9k/hw.h -@@ -831,6 +831,7 @@ struct ath_hw { - struct ath9k_pacal_info pacal_info; - struct ar5416Stats stats; - struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES]; -+ DECLARE_BITMAP(pending_del_keymap, ATH_KEYMAX); - - enum ath9k_int imask; - u32 imrs2_reg; -diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c -index 093eafd..dbd33d7 100644 ---- a/drivers/net/wireless/ath/ath9k/main.c -+++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -833,12 +833,80 @@ exit: - ieee80211_free_txskb(hw, skb); - } - -+static bool ath9k_txq_list_has_key(struct list_head *txq_list, u32 keyix) -+{ -+ struct ath_buf *bf; -+ struct ieee80211_tx_info *txinfo; -+ struct ath_frame_info *fi; -+ -+ list_for_each_entry(bf, txq_list, list) { -+ if (bf->bf_state.stale || !bf->bf_mpdu) -+ continue; -+ -+ txinfo = IEEE80211_SKB_CB(bf->bf_mpdu); -+ fi = (struct ath_frame_info *)&txinfo->rate_driver_data[0]; -+ if (fi->keyix == keyix) -+ return true; -+ } -+ -+ return false; -+} -+ -+static bool ath9k_txq_has_key(struct ath_softc *sc, u32 keyix) -+{ -+ struct ath_hw *ah = sc->sc_ah; -+ int i; -+ struct ath_txq *txq; -+ bool key_in_use = false; -+ -+ for (i = 0; !key_in_use && i < ATH9K_NUM_TX_QUEUES; i++) { -+ if (!ATH_TXQ_SETUP(sc, i)) -+ continue; -+ txq = &sc->tx.txq[i]; -+ if (!txq->axq_depth) -+ continue; -+ if (!ath9k_hw_numtxpending(ah, txq->axq_qnum)) -+ continue; -+ -+ ath_txq_lock(sc, txq); -+ key_in_use = ath9k_txq_list_has_key(&txq->axq_q, keyix); -+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { -+ int idx = txq->txq_tailidx; -+ -+ while (!key_in_use && -+ !list_empty(&txq->txq_fifo[idx])) { -+ key_in_use = ath9k_txq_list_has_key( -+ &txq->txq_fifo[idx], keyix); -+ INCR(idx, ATH_TXFIFO_DEPTH); -+ } -+ } -+ ath_txq_unlock(sc, txq); -+ } -+ -+ return key_in_use; -+} -+ -+static void ath9k_pending_key_del(struct ath_softc *sc, u8 keyix) -+{ -+ struct ath_hw *ah = sc->sc_ah; -+ struct ath_common *common = ath9k_hw_common(ah); -+ -+ if (!test_bit(keyix, ah->pending_del_keymap) || -+ ath9k_txq_has_key(sc, keyix)) -+ return; -+ -+ /* No more TXQ frames point to this key cache entry, so delete it. */ -+ clear_bit(keyix, ah->pending_del_keymap); -+ ath_key_delete(common, keyix); -+} -+ - static void ath9k_stop(struct ieee80211_hw *hw) - { - struct ath_softc *sc = hw->priv; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - bool prev_idle; -+ int i; - - ath9k_deinit_channel_context(sc); - -@@ -906,6 +974,14 @@ static void ath9k_stop(struct ieee80211_hw *hw) - - spin_unlock_bh(&sc->sc_pcu_lock); - -+ for (i = 0; i < ATH_KEYMAX; i++) -+ ath9k_pending_key_del(sc, i); -+ -+ /* Clear key cache entries explicitly to get rid of any potentially -+ * remaining keys. -+ */ -+ ath9k_cmn_init_crypto(sc->sc_ah); -+ - ath9k_ps_restore(sc); - - sc->ps_idle = prev_idle; -@@ -1555,12 +1631,11 @@ static void ath9k_del_ps_key(struct ath_softc *sc, - { - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_node *an = (struct ath_node *) sta->drv_priv; -- struct ieee80211_key_conf ps_key = { .hw_key_idx = an->ps_key }; - - if (!an->ps_key) - return; - -- ath_key_delete(common, &ps_key); -+ ath_key_delete(common, an->ps_key); - an->ps_key = 0; - an->key_idx[0] = 0; - } -@@ -1731,6 +1806,12 @@ static int ath9k_set_key(struct ieee80211_hw *hw, - if (sta) - an = (struct ath_node *)sta->drv_priv; - -+ /* Delete pending key cache entries if no more frames are pointing to -+ * them in TXQs. -+ */ -+ for (i = 0; i < ATH_KEYMAX; i++) -+ ath9k_pending_key_del(sc, i); -+ - switch (cmd) { - case SET_KEY: - if (sta) -@@ -1760,7 +1841,15 @@ static int ath9k_set_key(struct ieee80211_hw *hw, - } - break; - case DISABLE_KEY: -- ath_key_delete(common, key); -+ if (ath9k_txq_has_key(sc, key->hw_key_idx)) { -+ /* Delay key cache entry deletion until there are no -+ * remaining TXQ frames pointing to this entry. -+ */ -+ set_bit(key->hw_key_idx, sc->sc_ah->pending_del_keymap); -+ ath_hw_keysetmac(common, key->hw_key_idx, NULL); -+ } else { -+ ath_key_delete(common, key->hw_key_idx); -+ } - if (an) { - for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { - if (an->key_idx[i] != key->hw_key_idx) -diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c -index 1816b4e..61b59a8 100644 ---- a/drivers/net/wireless/ath/key.c -+++ b/drivers/net/wireless/ath/key.c -@@ -84,8 +84,7 @@ bool ath_hw_keyreset(struct ath_common *common, u16 entry) - } - EXPORT_SYMBOL(ath_hw_keyreset); - --static bool ath_hw_keysetmac(struct ath_common *common, -- u16 entry, const u8 *mac) -+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac) - { - u32 macHi, macLo; - u32 unicast_flag = AR_KEYTABLE_VALID; -@@ -125,6 +124,7 @@ static bool ath_hw_keysetmac(struct ath_common *common, - - return true; - } -+EXPORT_SYMBOL(ath_hw_keysetmac); - - static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry, - const struct ath_keyval *k, -@@ -581,29 +581,38 @@ EXPORT_SYMBOL(ath_key_config); - /* - * Delete Key. - */ --void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key) -+void ath_key_delete(struct ath_common *common, u8 hw_key_idx) - { -- ath_hw_keyreset(common, key->hw_key_idx); -- if (key->hw_key_idx < IEEE80211_WEP_NKID) -+ /* Leave CCMP and TKIP (main key) configured to avoid disabling -+ * encryption for potentially pending frames already in a TXQ with the -+ * keyix pointing to this key entry. Instead, only clear the MAC address -+ * to prevent RX processing from using this key cache entry. -+ */ -+ if (test_bit(hw_key_idx, common->ccmp_keymap) || -+ test_bit(hw_key_idx, common->tkip_keymap)) -+ ath_hw_keysetmac(common, hw_key_idx, NULL); -+ else -+ ath_hw_keyreset(common, hw_key_idx); -+ if (hw_key_idx < IEEE80211_WEP_NKID) - return; - -- clear_bit(key->hw_key_idx, common->keymap); -- clear_bit(key->hw_key_idx, common->ccmp_keymap); -- if (key->cipher != WLAN_CIPHER_SUITE_TKIP) -+ clear_bit(hw_key_idx, common->keymap); -+ clear_bit(hw_key_idx, common->ccmp_keymap); -+ if (!test_bit(hw_key_idx, common->tkip_keymap)) - return; - -- clear_bit(key->hw_key_idx + 64, common->keymap); -+ clear_bit(hw_key_idx + 64, common->keymap); - -- clear_bit(key->hw_key_idx, common->tkip_keymap); -- clear_bit(key->hw_key_idx + 64, common->tkip_keymap); -+ clear_bit(hw_key_idx, common->tkip_keymap); -+ clear_bit(hw_key_idx + 64, common->tkip_keymap); - - if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { -- ath_hw_keyreset(common, key->hw_key_idx + 32); -- clear_bit(key->hw_key_idx + 32, common->keymap); -- clear_bit(key->hw_key_idx + 64 + 32, common->keymap); -+ ath_hw_keyreset(common, hw_key_idx + 32); -+ clear_bit(hw_key_idx + 32, common->keymap); -+ clear_bit(hw_key_idx + 64 + 32, common->keymap); - -- clear_bit(key->hw_key_idx + 32, common->tkip_keymap); -- clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap); -+ clear_bit(hw_key_idx + 32, common->tkip_keymap); -+ clear_bit(hw_key_idx + 64 + 32, common->tkip_keymap); - } - } - EXPORT_SYMBOL(ath_key_delete); diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c -index 0042ea9..f7b15cf 100644 +index 3ba9fc3..9c8ff76 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c -@@ -733,7 +733,7 @@ static int __ath_regd_init(struct ath_regulatory *reg) +@@ -734,7 +734,7 @@ static int __ath_regd_init(struct ath_regulatory *reg) regdmn == CTRY_DEFAULT) { printk(KERN_DEBUG "ath: EEPROM indicates default " "country code should be used\n"); @@ -463,7 +162,7 @@ index 364011e..4db829c 100644 #endif diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c -index 32a2e27..a82b0ee 100644 +index 8b798b5..bf1b921 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1568,13 +1568,6 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -552,7 +251,7 @@ index 03b49ba..5275d6b 100644 rc.long_retry_limit = 10; rc.aflags = 0; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c -index b5e3a42..8fbff25 100644 +index 8448549..2c8977c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2271,13 +2271,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) @@ -578,7 +277,7 @@ index b5e3a42..8fbff25 100644 /* TODO: this seems to be used only for STA, check it */ wlvif->rate_set = CONF_TX_ENABLED_RATES; } -@@ -3555,6 +3557,9 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, +@@ -3542,6 +3544,9 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, key_type = KEY_AES; key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; break; @@ -588,7 +287,7 @@ index b5e3a42..8fbff25 100644 case WL1271_CIPHER_SUITE_GEM: key_type = KEY_GEM; break; -@@ -5811,9 +5816,16 @@ static void wlcore_op_sta_statistics(struct ieee80211_hw *hw, +@@ -5798,9 +5803,16 @@ static void wlcore_op_sta_statistics(struct ieee80211_hw *hw, { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); @@ -605,7 +304,7 @@ index b5e3a42..8fbff25 100644 wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi"); mutex_lock(&wl->mutex); -@@ -6227,6 +6239,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) +@@ -6214,6 +6226,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WL1271_CIPHER_SUITE_GEM, @@ -613,7 +312,7 @@ index b5e3a42..8fbff25 100644 }; /* The tx descriptor buffer */ -@@ -6290,6 +6303,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) +@@ -6277,6 +6290,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) WIPHY_FLAG_IBSS_RSN; wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; @@ -622,35 +321,36 @@ index b5e3a42..8fbff25 100644 /* make sure all our channels fit in the scanned_ch bitmask */ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h -index 092156b..c70a04f 100644 +index ab83553..2bf72f3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h -@@ -129,6 +129,7 @@ enum ieee80211_channel_flags { +@@ -136,6 +136,8 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_4MHZ = 1<<16, IEEE80211_CHAN_8MHZ = 1<<17, IEEE80211_CHAN_16MHZ = 1<<18, -+ IEEE80211_CHAN_SRD = 1<<19, ++ IEEE80211_CHAN_SRD = 1<<19, ++ }; #define IEEE80211_CHAN_NO_HT40 \ -@@ -3738,6 +3739,8 @@ struct mgmt_frame_regs { +@@ -3871,6 +3873,8 @@ struct mgmt_frame_regs { * return 0 if successful * @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary * + * @get_antenna_gain: get antenna gain -+ * - * @set_wds_peer: set the WDS peer for a WDS interface - * ++ + * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting -@@ -4060,6 +4063,7 @@ struct cfg80211_ops { + * functions to adjust rfkill hw state + * +@@ -4204,6 +4208,7 @@ struct cfg80211_ops { int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, int *dbm); int (*set_antenna_gain)(struct wiphy *wiphy, int dbi); -+ int (*get_antenna_gain)(struct wiphy *wiphy, int *dbi); ++ int (*get_antenna_gain)(struct wiphy *wiphy, int *dbi); - int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr); -@@ -5101,6 +5105,9 @@ static inline const char *wiphy_name(const struct wiphy *wiphy) + void (*rfkill_poll)(struct wiphy *wiphy); + +@@ -5268,6 +5273,9 @@ static inline const char *wiphy_name(const struct wiphy *wiphy) * @requested_name: Request a particular name. * NULL is valid value, and means use the default phy%d naming. * @@ -660,29 +360,29 @@ index 092156b..c70a04f 100644 * Create a new wiphy and associate the given operations with it. * @sizeof_priv bytes are allocated for private use. * -@@ -5108,7 +5115,7 @@ static inline const char *wiphy_name(const struct wiphy *wiphy) +@@ -5275,7 +5283,7 @@ static inline const char *wiphy_name(const struct wiphy *wiphy) * assigned to each netdev's ieee80211_ptr for proper operation. */ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, - const char *requested_name); -+ const char *requested_name, const int requested_index); ++ const char *requested_name, const int requested_index); /** * wiphy_new - create a new wiphy for use with cfg80211 -@@ -5125,7 +5132,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, +@@ -5292,7 +5300,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { - return wiphy_new_nm(ops, sizeof_priv, NULL); -+ return wiphy_new_nm(ops, sizeof_priv, NULL, -1); ++ return wiphy_new_nm(ops, sizeof_priv, NULL, -1); } /** diff --git a/include/net/mac80211.h b/include/net/mac80211.h -index 9f185ee..f9e10e3 100644 +index c85050f..07c8277 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h -@@ -4228,11 +4228,15 @@ struct ieee80211_ops { +@@ -4309,11 +4309,15 @@ struct ieee80211_ops { * @requested_name: Requested name for this device. * NULL is valid value, and means use the default naming (phy%d) * @@ -699,7 +399,7 @@ index 9f185ee..f9e10e3 100644 /** * ieee80211_alloc_hw - Allocate a new hardware device -@@ -4252,7 +4256,7 @@ static inline +@@ -4333,7 +4337,7 @@ static inline struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, const struct ieee80211_ops *ops) { @@ -709,10 +409,10 @@ index 9f185ee..f9e10e3 100644 /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h -index 0d90183..4f5ba93 100644 +index 019f065..190af2f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h -@@ -3779,6 +3779,8 @@ enum nl80211_wmm_rule { +@@ -3883,6 +3883,8 @@ enum nl80211_wmm_rule { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_16MHZ: 16 MHz operation is allowed * on this channel in current regulatory domain. @@ -721,50 +421,52 @@ index 0d90183..4f5ba93 100644 * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use -@@ -3815,6 +3817,7 @@ enum nl80211_frequency_attr { +@@ -3919,6 +3921,9 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_4MHZ, NL80211_FREQUENCY_ATTR_8MHZ, NL80211_FREQUENCY_ATTR_16MHZ, -+ NL80211_FREQUENCY_ATTR_SRD_CHANNEL, ++ NL80211_FREQUENCY_ATTR_NO_320MHZ, ++ NL80211_FREQUENCY_ATTR_NO_EHT, ++ NL80211_FREQUENCY_ATTR_SRD_CHANNEL, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c -index 272b433..2a30e87 100644 +index abe7318..fc59297 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c -@@ -2720,6 +2720,18 @@ static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi) +@@ -2825,6 +2825,18 @@ static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi) return 0; } +static int ieee80211_get_antenna_gain(struct wiphy *wiphy, -+ int *dbi) ++ int *dbi) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (local) { -+ *dbi = local->user_antenna_gain; ++ *dbi = local->user_antenna_gain; + } + + return 0; +} + - static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr) + static void ieee80211_rfkill_poll(struct wiphy *wiphy) { -@@ -4151,6 +4163,7 @@ const struct cfg80211_ops mac80211_config_ops = { + struct ieee80211_local *local = wiphy_priv(wiphy); +@@ -4530,6 +4542,7 @@ const struct cfg80211_ops mac80211_config_ops = { .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, .set_antenna_gain = ieee80211_set_antenna_gain, + .get_antenna_gain = ieee80211_get_antenna_gain, - .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) + CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c -index bf305b2..7998620 100644 +index b777921..ba9c038 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c -@@ -65,6 +65,10 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) +@@ -66,6 +66,10 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL) power = min(power, sdata->ap_power_level); @@ -776,7 +478,7 @@ index bf305b2..7998620 100644 sdata->vif.bss_conf.txpower = power; ieee80211_hw_config(sdata->local, 0); diff --git a/net/mac80211/main.c b/net/mac80211/main.c -index 293bfec..5513370 100644 +index 09e5bf1..72238e9 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -96,7 +96,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) @@ -800,7 +502,7 @@ index 293bfec..5513370 100644 } if (local->hw.conf.power_level != power) { -@@ -532,7 +532,7 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = { +@@ -548,7 +546,7 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = { struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, const struct ieee80211_ops *ops, @@ -809,7 +511,7 @@ index 293bfec..5513370 100644 { struct ieee80211_local *local; int priv_size, i; -@@ -572,7 +572,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, +@@ -588,7 +586,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, */ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; @@ -819,7 +521,7 @@ index 293bfec..5513370 100644 if (!wiphy) return NULL; diff --git a/net/wireless/core.c b/net/wireless/core.c -index afdfcc2..cefbcbc 100644 +index 2ddaaae..fbb56ee 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -123,6 +123,19 @@ static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, @@ -842,7 +544,7 @@ index afdfcc2..cefbcbc 100644 int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { -@@ -398,7 +411,7 @@ static void cfg80211_propagate_cac_done_wk(struct work_struct *work) +@@ -411,7 +424,7 @@ static void cfg80211_propagate_cac_done_wk(struct work_struct *work) /* exported functions */ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, @@ -851,7 +553,7 @@ index afdfcc2..cefbcbc 100644 { static atomic_t wiphy_counter = ATOMIC_INIT(0); -@@ -441,17 +454,31 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, +@@ -454,17 +467,31 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, rdev->ops = ops; @@ -890,10 +592,10 @@ index afdfcc2..cefbcbc 100644 /* give it a proper name */ if (requested_name && requested_name[0]) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c -index 132df74..80b8caf 100644 +index bc6b5ac..6cd3b76 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c -@@ -1043,6 +1043,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, +@@ -1129,6 +1129,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, } } @@ -904,7 +606,7 @@ index 132df74..80b8caf 100644 if (large) { if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS)) -@@ -3503,6 +3506,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag +@@ -3710,6 +3714,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag goto nla_put_failure; } @@ -922,10 +624,10 @@ index 132df74..80b8caf 100644 switch (wdev->iftype) { case NL80211_IFTYPE_AP: diff --git a/net/wireless/reg.c b/net/wireless/reg.c -index b6caa2b..fbe581a 100644 +index 48ab1bb..185460d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c -@@ -68,6 +68,7 @@ +@@ -69,6 +69,7 @@ * channels allowed by the current regulatory domain. */ #define REG_ENFORCE_GRACE_MS 60000 @@ -933,7 +635,7 @@ index b6caa2b..fbe581a 100644 /** * enum reg_request_treatment - regulatory request treatment -@@ -1788,11 +1789,21 @@ static void handle_channel_single_rule(struct wiphy *wiphy, +@@ -1812,11 +1813,21 @@ static void handle_channel_single_rule(struct wiphy *wiphy, MBI_TO_DBI(power_rule->max_antenna_gain)); chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); @@ -959,7 +661,7 @@ index b6caa2b..fbe581a 100644 } if (chan->orig_mpwr) { -@@ -1932,6 +1932,10 @@ static void handle_channel_adjacent_rules(struct wiphy *wiphy, +@@ -1929,6 +1940,10 @@ static void handle_channel_adjacent_rules(struct wiphy *wiphy, chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; } @@ -970,7 +672,7 @@ index b6caa2b..fbe581a 100644 if (chan->orig_mpwr) { /* Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER * will always follow the passed country IE power settings. -@@ -2522,6 +2533,10 @@ static void handle_channel_custom(struct wiphy *wiphy, +@@ -2551,6 +2566,10 @@ static void handle_channel_custom(struct wiphy *wiphy, } chan->max_power = chan->max_reg_power; @@ -982,15 +684,15 @@ index b6caa2b..fbe581a 100644 static void handle_band_custom(struct wiphy *wiphy, diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c -index 78f2927..4fa5b0d 100644 +index a32065d..3a545c4 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c -@@ -895,7 +895,8 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev, +@@ -964,7 +964,8 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev, /* well... oh well */ data->txpower.fixed = 1; - data->txpower.disabled = rfkill_blocked(rdev->rfkill); + data->txpower.disabled = rfkill_blocked(rdev->wiphy.rfkill); - data->txpower.value = val; -+ /* show same behavior as iw and show txpower 0dBm if not initialized */ ++ /* show same behavior as iw and show txpower 0dBm if not initialized */ + data->txpower.value = val == INT_MIN ? 0 : val; data->txpower.flags = IW_TXPOW_DBM; diff --git a/recipes-kernel/mac80211/mac80211_5.10.16-1.bb b/recipes-kernel/mac80211/mac80211_5.15.58-1.bb similarity index 78% rename from recipes-kernel/mac80211/mac80211_5.10.16-1.bb rename to recipes-kernel/mac80211/mac80211_5.15.58-1.bb index ebab407..d2deba6 100644 --- a/recipes-kernel/mac80211/mac80211_5.10.16-1.bb +++ b/recipes-kernel/mac80211/mac80211_5.15.58-1.bb @@ -18,15 +18,16 @@ SRC_URI = "http://www.kernel.org/pub/linux/kernel/projects/backports/stable/v${S file://0001-backport-of-build-patches-from-openwrt.patch \ file://0002-backport-of-subsys-patches-from-openwrt.patch \ file://0003-backport-of-ath-patches-from-openwrt.patch \ - file://0004-backport-of-rt2x00-patches-from-openwrt.patch \ - file://0005-backport-of-mwl-patches-from-openwrt.patch \ - file://0006-backport-of-brcm-patches-from-openwrt.patch \ - file://0007-backport-of-rtl-patches-from-openwrt.patch \ - file://0008-fragattack.patch \ - file://0009-netmodule-patches.patch \ + file://0004-backport-of-ath5k-patches-from-openwrt.patch \ + file://0005-backport-of-ath9k-patches-from-openwrt.patch \ + file://0006-backport-of-ath10k-patches-from-openwrt.patch \ + file://0007-backport-of-brcm-patches-from-openwrt.patch \ + file://0008-backport-of-mwl-patches-from-openwrt.patch \ + file://0009-backport-of-rt2x00-patches-from-openwrt.patch \ + file://0010-netmodule-patches.patch \ " -SRC_URI[sha256sum] = "90005f3598b4b1fac4b0088f0b345ef2e8312df9f9f80c50aeb28497453888f5" +SRC_URI[sha256sum] = "4c6b2af699e5e557dfc44bc7e30a10f1d6299a451ea50443084bdf7c850cbb24" S = "${WORKDIR}/backports-${PV}"