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 new file mode 100644 index 0000000..ce64818 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0001-backport-of-build-patches-from-openwrt.patch @@ -0,0 +1,801 @@ +From 2ebe60c979204b804a5ea70e1dfb77331c7297ac Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 27 May 2020 19:18:11 +0200 +Subject: [PATCH] FIX: [mac80211] backport of build patches from openwrt + +--- + 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(-) + +diff --git a/Kconfig.local b/Kconfig.local +index 0b3e89c..d5a218a 100644 +--- a/Kconfig.local ++++ b/Kconfig.local +@@ -1240,117 +1240,6 @@ config BACKPORTED_USB_NET_CH9200 + config BACKPORTED_USB_NET_AQC111 + tristate + default USB_NET_AQC111 +-config BACKPORTED_SSB_POSSIBLE +- tristate +- default SSB_POSSIBLE +-config BACKPORTED_SSB +- tristate +- default SSB +-config BACKPORTED_SSB_SPROM +- tristate +- default SSB_SPROM +-config BACKPORTED_SSB_BLOCKIO +- tristate +- default SSB_BLOCKIO +-config BACKPORTED_SSB_PCIHOST_POSSIBLE +- tristate +- default SSB_PCIHOST_POSSIBLE +-config BACKPORTED_SSB_PCIHOST +- tristate +- default SSB_PCIHOST +-config BACKPORTED_SSB_B43_PCI_BRIDGE +- tristate +- default SSB_B43_PCI_BRIDGE +-config BACKPORTED_SSB_PCMCIAHOST_POSSIBLE +- tristate +- default SSB_PCMCIAHOST_POSSIBLE +-config BACKPORTED_SSB_PCMCIAHOST +- tristate +- default SSB_PCMCIAHOST +-config BACKPORTED_SSB_SDIOHOST_POSSIBLE +- tristate +- default SSB_SDIOHOST_POSSIBLE +-config BACKPORTED_SSB_SDIOHOST +- tristate +- default SSB_SDIOHOST +-config BACKPORTED_SSB_HOST_SOC +- tristate +- default SSB_HOST_SOC +-config BACKPORTED_SSB_SERIAL +- tristate +- default SSB_SERIAL +-config BACKPORTED_SSB_DRIVER_PCICORE_POSSIBLE +- tristate +- default SSB_DRIVER_PCICORE_POSSIBLE +-config BACKPORTED_SSB_DRIVER_PCICORE +- tristate +- default SSB_DRIVER_PCICORE +-config BACKPORTED_SSB_PCICORE_HOSTMODE +- tristate +- default SSB_PCICORE_HOSTMODE +-config BACKPORTED_SSB_DRIVER_MIPS +- tristate +- default SSB_DRIVER_MIPS +-config BACKPORTED_SSB_SFLASH +- tristate +- default SSB_SFLASH +-config BACKPORTED_SSB_EMBEDDED +- tristate +- default SSB_EMBEDDED +-config BACKPORTED_SSB_DRIVER_EXTIF +- tristate +- default SSB_DRIVER_EXTIF +-config BACKPORTED_SSB_DRIVER_GIGE +- tristate +- default SSB_DRIVER_GIGE +-config BACKPORTED_SSB_DRIVER_GPIO +- tristate +- default SSB_DRIVER_GPIO +-config BACKPORTED_BCMA_POSSIBLE +- tristate +- default BCMA_POSSIBLE +-config BACKPORTED_BCMA +- tristate +- default BCMA +-config BACKPORTED_BCMA_BLOCKIO +- tristate +- default BCMA_BLOCKIO +-config BACKPORTED_BCMA_HOST_PCI_POSSIBLE +- tristate +- default BCMA_HOST_PCI_POSSIBLE +-config BACKPORTED_BCMA_HOST_PCI +- tristate +- default BCMA_HOST_PCI +-config BACKPORTED_BCMA_HOST_SOC +- tristate +- default BCMA_HOST_SOC +-config BACKPORTED_BCMA_DRIVER_PCI +- tristate +- default BCMA_DRIVER_PCI +-config BACKPORTED_BCMA_DRIVER_PCI_HOSTMODE +- tristate +- default BCMA_DRIVER_PCI_HOSTMODE +-config BACKPORTED_BCMA_DRIVER_MIPS +- tristate +- default BCMA_DRIVER_MIPS +-config BACKPORTED_BCMA_PFLASH +- tristate +- default BCMA_PFLASH +-config BACKPORTED_BCMA_SFLASH +- tristate +- default BCMA_SFLASH +-config BACKPORTED_BCMA_NFLASH +- tristate +- default BCMA_NFLASH +-config BACKPORTED_BCMA_DRIVER_GMAC_CMN +- tristate +- default BCMA_DRIVER_GMAC_CMN +-config BACKPORTED_BCMA_DRIVER_GPIO +- tristate +- default BCMA_DRIVER_GPIO +-config BACKPORTED_BCMA_DEBUG +- tristate +- default BCMA_DEBUG + config BACKPORTED_USB_ACM + tristate + default USB_ACM +diff --git a/Kconfig.sources b/Kconfig.sources +index 5fb4478..347f1c5 100644 +--- a/Kconfig.sources ++++ b/Kconfig.sources +@@ -7,9 +7,6 @@ source "$BACKPORT_DIR/net/mac80211/Kconfig" + source "$BACKPORT_DIR/drivers/net/wireless/Kconfig" + source "$BACKPORT_DIR/drivers/net/usb/Kconfig" + +-source "$BACKPORT_DIR/drivers/ssb/Kconfig" +-source "$BACKPORT_DIR/drivers/bcma/Kconfig" +- + source "$BACKPORT_DIR/drivers/usb/class/Kconfig" + + source "$BACKPORT_DIR/drivers/staging/Kconfig" +diff --git a/Makefile b/Makefile +index ee7df4b..79e5fab 100644 +--- a/Makefile ++++ b/Makefile +@@ -2,10 +2,10 @@ + # Makefile for the output source package + # + +-ifeq ($(KERNELRELEASE),) ++ifeq ($(KERNELVERSION),) + + MAKEFLAGS += --no-print-directory +-SHELL := /bin/bash ++SHELL := /usr/bin/env bash + BACKPORT_DIR := $(shell pwd) + + KMODDIR ?= updates +@@ -19,6 +19,7 @@ KLIB_BUILD ?= $(KLIB)/build/ + KERNEL_CONFIG := $(KLIB_BUILD)/.config + KERNEL_MAKEFILE := $(KLIB_BUILD)/Makefile + CONFIG_MD5 := $(shell md5sum $(KERNEL_CONFIG) 2>/dev/null | sed 's/\s.*//') ++STAMP_KERNEL_CONFIG := .kernel_config_md5_$(CONFIG_MD5) + + export KLIB KLIB_BUILD BACKPORT_DIR KMODDIR KMODPATH_ARG + +@@ -36,7 +37,8 @@ mrproper: + @rm -f .kernel_config_md5 Kconfig.versions Kconfig.kernel + @rm -f backport-include/backport/autoconf.h + +-.DEFAULT: ++.SILENT: $(STAMP_KERNEL_CONFIG) ++$(STAMP_KERNEL_CONFIG): + @set -e ; test -f local-symbols || ( \ + echo "/--------------" ;\ + echo "| You shouldn't run make in the backports tree, but only in" ;\ +@@ -60,58 +62,62 @@ mrproper: + echo "| (that isn't currently running.)" ;\ + echo "\\--" ;\ + false) +- @set -e ; if [ "$$(cat .kernel_config_md5 2>/dev/null)" != "$(CONFIG_MD5)" ] ;\ +- then \ +- echo -n "Generating local configuration database from kernel ..." ;\ +- grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | ( \ +- while read l ; do \ +- if [ "$${l:0:7}" != "CONFIG_" ] ; then \ +- continue ;\ +- fi ;\ +- l=$${l:7} ;\ +- n=$${l%%=*} ;\ +- v=$${l#*=} ;\ +- if [ "$$v" = "m" ] ; then \ +- echo config $$n ;\ +- echo ' tristate' ;\ +- elif [ "$$v" = "y" ] ; then \ +- echo config $$n ;\ +- echo ' bool' ;\ +- else \ +- continue ;\ +- fi ;\ +- echo " default $$v" ;\ +- echo "" ;\ +- done \ +- ) > Kconfig.kernel ;\ +- kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion | \ +- sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d') ;\ +- test "$$kver" != "" || echo "Kernel version parse failed!" ;\ +- test "$$kver" != "" ;\ +- kvers="$$(seq 14 39 | sed 's/^/2.6./')" ;\ +- kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\ +- kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')" ;\ +- kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')" ;\ +- print=0 ;\ +- for v in $$kvers ; do \ +- if [ "$$print" = "1" ] ; then \ +- echo config KERNEL_$$(echo $$v | tr . _) ;\ +- echo " def_bool y" ;\ +- fi ;\ +- if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\ +- done > Kconfig.versions ;\ +- # RHEL as well, sadly we need to grep for it ;\ +- RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | \ +- sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\ +- RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | \ +- sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\ +- for v in $$(seq 0 $$RHEL_MINOR) ; do \ +- echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v ;\ +- echo " def_bool y" ;\ +- done >> Kconfig.versions ;\ +- echo " done." ;\ +- fi ;\ +- echo "$(CONFIG_MD5)" > .kernel_config_md5 ++ @rm -f .kernel_config_md5_* ++ @touch $@ ++ ++Kconfig.kernel: $(STAMP_KERNEL_CONFIG) local-symbols ++ @printf "Generating local configuration database from kernel ..." ++ @grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | ( \ ++ while read l ; do \ ++ if [ "$${l:0:7}" != "CONFIG_" ] ; then \ ++ continue ;\ ++ fi ;\ ++ l=$${l:7} ;\ ++ n=$${l%%=*} ;\ ++ v=$${l#*=} ;\ ++ if [ "$$v" = "m" ] ; then \ ++ echo config $$n ;\ ++ echo ' tristate' ;\ ++ elif [ "$$v" = "y" ] ; then \ ++ echo config $$n ;\ ++ echo ' bool' ;\ ++ else \ ++ continue ;\ ++ fi ;\ ++ echo " default $$v" ;\ ++ echo "" ;\ ++ done \ ++ ) > $@ ++ @echo " done." ++ ++Kconfig.versions: Kconfig.kernel ++ @kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion | \ ++ sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d') ;\ ++ test "$$kver" != "" || echo "Kernel version parse failed!" ;\ ++ test "$$kver" != "" ;\ ++ kvers="$$(seq 14 39 | sed 's/^/2.6./')" ;\ ++ kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\ ++ kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')" ;\ ++ kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')" ;\ ++ print=0 ;\ ++ for v in $$kvers ; do \ ++ if [ "$$print" = "1" ] ; then \ ++ echo config KERNEL_$$(echo $$v | tr . _) ;\ ++ echo " def_bool y" ;\ ++ fi ;\ ++ if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\ ++ done > $@ ++ @RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | \ ++ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\ ++ RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | \ ++ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\ ++ for v in $$(seq 0 $$RHEL_MINOR) ; do \ ++ echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v ;\ ++ echo " def_bool y" ;\ ++ done >> $@ ++ ++.DEFAULT: ++ @$(MAKE) Kconfig.versions + @$(MAKE) -f Makefile.real "$@" + + .PHONY: defconfig-help +diff --git a/Makefile.kernel b/Makefile.kernel +index 5c96697..23b2688 100644 +--- a/Makefile.kernel ++++ b/Makefile.kernel +@@ -40,8 +40,6 @@ obj-y += compat/ + obj-$(CPTCFG_CFG80211) += net/wireless/ + obj-$(CPTCFG_MAC80211) += net/mac80211/ + obj-$(CPTCFG_WLAN) += drivers/net/wireless/ +-obj-$(CPTCFG_SSB) += drivers/ssb/ +-obj-$(CPTCFG_BCMA) += drivers/bcma/ + obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/ + + obj-$(CPTCFG_USB_WDM) += drivers/usb/class/ +diff --git a/Makefile.real b/Makefile.real +index 6550802..d0a8c20 100644 +--- a/Makefile.real ++++ b/Makefile.real +@@ -59,7 +59,7 @@ defconfig-%:: + + backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel + @$(MAKE) oldconfig +- @echo -n "Building backport-include/backport/autoconf.h ..." ++ @printf "Building backport-include/backport/autoconf.h ..." + @grep -f local-symbols .config | ( \ + echo "#ifndef COMPAT_AUTOCONF_INCLUDED" ;\ + echo "#define COMPAT_AUTOCONF_INCLUDED" ;\ +@@ -80,7 +80,12 @@ backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel + esac ;\ + done ;\ + echo "#endif /* COMPAT_AUTOCONF_INCLUDED */" ;\ +- ) > backport-include/backport/autoconf.h ++ ) > $@.new ++ @if cmp -s $@ $@.new; then \ ++ rm -f $@.new; \ ++ else \ ++ mv $@.new $@; \ ++ fi + @echo " done." + + .PHONY: modules +diff --git a/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 +--- a/compat/main.c ++++ b/compat/main.c +@@ -20,31 +20,6 @@ MODULE_LICENSE("GPL"); + #error "You need a CPTCFG_VERSION" + #endif + +-static char *backported_kernel_name = CPTCFG_KERNEL_NAME; +- +-module_param(backported_kernel_name, charp, 0400); +-MODULE_PARM_DESC(backported_kernel_name, +- "The kernel tree name that was used for this backport (" CPTCFG_KERNEL_NAME ")"); +- +-#ifdef BACKPORTS_GIT_TRACKED +-static char *backports_tracker_id = BACKPORTS_GIT_TRACKED; +-module_param(backports_tracker_id, charp, 0400); +-MODULE_PARM_DESC(backports_tracker_id, +- "The version of the tree containing this backport (" BACKPORTS_GIT_TRACKED ")"); +-#else +-static char *backported_kernel_version = CPTCFG_KERNEL_VERSION; +-static char *backports_version = CPTCFG_VERSION; +- +-module_param(backported_kernel_version, charp, 0400); +-MODULE_PARM_DESC(backported_kernel_version, +- "The kernel version that was used for this backport (" CPTCFG_KERNEL_VERSION ")"); +- +-module_param(backports_version, charp, 0400); +-MODULE_PARM_DESC(backports_version, +- "The git version of the backports tree used to generate this backport (" CPTCFG_VERSION ")"); +- +-#endif +- + void backport_dependency_symbol(void) + { + } +diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig +index 07e67e8..b8f1262 100644 +--- a/drivers/net/wireless/broadcom/b43/Kconfig ++++ b/drivers/net/wireless/broadcom/b43/Kconfig +@@ -63,21 +63,21 @@ endchoice + config B43_PCI_AUTOSELECT + bool + depends on B43 && SSB_PCIHOST_POSSIBLE +- select SSB_PCIHOST +- select SSB_B43_PCI_BRIDGE ++ depends on SSB_PCIHOST ++ depends on SSB_B43_PCI_BRIDGE + default y + + # Auto-select SSB PCICORE driver, if possible + config B43_PCICORE_AUTOSELECT + bool + depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE +- select SSB_DRIVER_PCICORE ++ depends on SSB_DRIVER_PCICORE + default y + + config B43_SDIO + bool "Broadcom 43xx SDIO device support" + depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE +- select SSB_SDIOHOST ++ depends on SSB_SDIOHOST + ---help--- + Broadcom 43xx device support for Soft-MAC SDIO devices. + +@@ -96,13 +96,13 @@ config B43_SDIO + config B43_BCMA_PIO + bool + depends on B43 && B43_BCMA +- select BCMA_BLOCKIO ++ depends on BCMA_BLOCKIO + default y + + config B43_PIO + bool + depends on B43 && B43_SSB +- select SSB_BLOCKIO ++ depends on SSB_BLOCKIO + default y + + config B43_PHY_G +diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c +index 0c96714..99d0b4a 100644 +--- a/drivers/net/wireless/broadcom/b43/main.c ++++ b/drivers/net/wireless/broadcom/b43/main.c +@@ -2851,7 +2851,7 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) + { + struct ssb_bus *bus = dev->dev->sdev->bus; + +-#ifdef CPTCFG_SSB_DRIVER_PCICORE ++#ifdef CONFIG_SSB_DRIVER_PCICORE + return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev); + #else + return bus->chipco.dev; +@@ -4868,7 +4868,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) + } + if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) + hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ +-#if defined(CPTCFG_B43_SSB) && defined(CPTCFG_SSB_DRIVER_PCICORE) ++#if defined(CPTCFG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE) + if (dev->dev->bus_type == B43_BUS_SSB && + dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && + dev->dev->sdev->bus->pcicore.dev->id.revision <= 10) +diff --git a/drivers/net/wireless/broadcom/b43legacy/Kconfig b/drivers/net/wireless/broadcom/b43legacy/Kconfig +index 8205796..8a75208 100644 +--- a/drivers/net/wireless/broadcom/b43legacy/Kconfig ++++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig +@@ -3,7 +3,7 @@ config B43LEGACY + tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)" + depends on m + depends on SSB_POSSIBLE && MAC80211 && HAS_DMA +- select SSB ++ depends on SSB + depends on FW_LOADER + ---help--- + b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and +@@ -25,15 +25,15 @@ config B43LEGACY + config B43LEGACY_PCI_AUTOSELECT + bool + depends on B43LEGACY && SSB_PCIHOST_POSSIBLE +- select SSB_PCIHOST +- select SSB_B43_PCI_BRIDGE ++ depends on SSB_PCIHOST ++ depends on SSB_B43_PCI_BRIDGE + default y + + # Auto-select SSB PCICORE driver, if possible + config B43LEGACY_PCICORE_AUTOSELECT + bool + depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE +- select SSB_DRIVER_PCICORE ++ depends on SSB_DRIVER_PCICORE + default y + + # LED support +diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c +index fef1686..cd8058b 100644 +--- a/drivers/net/wireless/broadcom/b43legacy/main.c ++++ b/drivers/net/wireless/broadcom/b43legacy/main.c +@@ -1906,7 +1906,7 @@ static int b43legacy_gpio_init(struct b43legacy_wldev *dev) + if (dev->dev->id.revision >= 2) + mask |= 0x0010; /* FIXME: This is redundant. */ + +-#ifdef CPTCFG_SSB_DRIVER_PCICORE ++#ifdef CONFIG_SSB_DRIVER_PCICORE + pcidev = bus->pcicore.dev; + #endif + gpiodev = bus->chipco.dev ? : pcidev; +@@ -1925,7 +1925,7 @@ static void b43legacy_gpio_cleanup(struct b43legacy_wldev *dev) + struct ssb_bus *bus = dev->dev->bus; + struct ssb_device *gpiodev, *pcidev = NULL; + +-#ifdef CPTCFG_SSB_DRIVER_PCICORE ++#ifdef CONFIG_SSB_DRIVER_PCICORE + pcidev = bus->pcicore.dev; + #endif + gpiodev = bus->chipco.dev ? : pcidev; +diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig +index 9f99ad8..2c426b5 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/Kconfig ++++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig +@@ -8,7 +8,7 @@ config BRCMSMAC + depends on m + depends on MAC80211 + depends on BCMA_POSSIBLE +- select BCMA ++ depends on BCMA + select NEW_LEDS if BCMA_DRIVER_GPIO + 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 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 := \ + brcms_trace_events.o \ + debug.o + +-brcmsmac-$(CPTCFG_BCMA_DRIVER_GPIO) += led.o ++brcmsmac-$(CONFIG_BCMA_DRIVER_GPIO) += led.o + + obj-$(CPTCFG_BRCMSMAC) += brcmsmac.o +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h +index 09a5bc0..17a0b1f 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h +@@ -22,7 +22,7 @@ struct brcms_led { + bool active_low; + }; + +-#ifdef CPTCFG_BCMA_DRIVER_GPIO ++#ifdef CONFIG_BCMA_DRIVER_GPIO + void brcms_led_unregister(struct brcms_info *wl); + int brcms_led_register(struct brcms_info *wl); + #else +diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c +index 9a99ede..03f7c2a 100644 +--- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c ++++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c +@@ -11482,6 +11482,15 @@ static const struct attribute_group ipw_attribute_group = { + .attrs = ipw_sysfs_entries, + }; + ++#if LINUX_VERSION_IS_LESS(4,10,0) ++static int __change_mtu(struct net_device *ndev, int new_mtu){ ++ if (new_mtu < 68 || new_mtu > LIBIPW_DATA_LEN) ++ return -EINVAL; ++ ndev->mtu = new_mtu; ++ return 0; ++} ++#endif ++ + #ifdef CPTCFG_IPW2200_PROMISCUOUS + static int ipw_prom_open(struct net_device *dev) + { +@@ -11530,15 +11539,6 @@ static netdev_tx_t ipw_prom_hard_start_xmit(struct sk_buff *skb, + return NETDEV_TX_OK; + } + +-#if LINUX_VERSION_IS_LESS(4,10,0) +-static int __change_mtu(struct net_device *ndev, int new_mtu){ +- if (new_mtu < 68 || new_mtu > LIBIPW_DATA_LEN) +- return -EINVAL; +- ndev->mtu = new_mtu; +- return 0; +-} +-#endif +- + static const struct net_device_ops ipw_prom_netdev_ops = { + #if LINUX_VERSION_IS_LESS(4,10,0) + .ndo_change_mtu = __change_mtu, +diff --git a/kconf/Makefile b/kconf/Makefile +index 2004c44..a2790b1 100644 +--- a/kconf/Makefile ++++ b/kconf/Makefile +@@ -1,9 +1,9 @@ +-CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer ++CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -DKBUILD_NO_NLS + + LXDIALOG := lxdialog/checklist.o lxdialog/inputbox.o lxdialog/menubox.o lxdialog/textbox.o lxdialog/util.o lxdialog/yesno.o + + conf: conf.o zconf.tab.o +-mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) -DLOCALE ++mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) + mconf_LDFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC)) + mconf: CFLAGS += $(mconf_CFLAGS) + +diff --git a/kconf/conf.c b/kconf/conf.c +index 283eeed..1707f05 100644 +--- a/kconf/conf.c ++++ b/kconf/conf.c +@@ -598,40 +598,12 @@ int main(int ac, char **av) + case oldconfig: + case listnewconfig: + case olddefconfig: +- conf_read(NULL); +- break; + case allnoconfig: + case allyesconfig: + case allmodconfig: + case alldefconfig: + case randconfig: +- name = getenv("KCONFIG_ALLCONFIG"); +- if (!name) +- break; +- if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) { +- if (conf_read_simple(name, S_DEF_USER)) { +- fprintf(stderr, +- _("*** Can't read seed configuration \"%s\"!\n"), +- name); +- exit(1); +- } +- break; +- } +- switch (input_mode) { +- case allnoconfig: name = "allno.config"; break; +- case allyesconfig: name = "allyes.config"; break; +- case allmodconfig: name = "allmod.config"; break; +- case alldefconfig: name = "alldef.config"; break; +- case randconfig: name = "allrandom.config"; break; +- default: break; +- } +- if (conf_read_simple(name, S_DEF_USER) && +- conf_read_simple("all.config", S_DEF_USER)) { +- fprintf(stderr, +- _("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"), +- name); +- exit(1); +- } ++ conf_read(NULL); + break; + default: + break; +diff --git a/kconf/confdata.c b/kconf/confdata.c +index df26c7b..1038c30 100644 +--- a/kconf/confdata.c ++++ b/kconf/confdata.c +@@ -1170,6 +1170,8 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode) + } + bool has_changed = false; + ++ sym_clear_all_valid(); ++ + for_all_symbols(i, sym) { + if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID)) + continue; +@@ -1213,8 +1215,6 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode) + + } + +- sym_clear_all_valid(); +- + /* + * We have different type of choice blocks. + * If curr.tri equals to mod then we can select several +diff --git a/local-symbols b/local-symbols +index 33ee823..e7064fd 100644 +--- a/local-symbols ++++ b/local-symbols +@@ -412,43 +412,6 @@ USB_SIERRA_NET= + USB_VL600= + USB_NET_CH9200= + USB_NET_AQC111= +-SSB_POSSIBLE= +-SSB= +-SSB_SPROM= +-SSB_BLOCKIO= +-SSB_PCIHOST_POSSIBLE= +-SSB_PCIHOST= +-SSB_B43_PCI_BRIDGE= +-SSB_PCMCIAHOST_POSSIBLE= +-SSB_PCMCIAHOST= +-SSB_SDIOHOST_POSSIBLE= +-SSB_SDIOHOST= +-SSB_HOST_SOC= +-SSB_SERIAL= +-SSB_DRIVER_PCICORE_POSSIBLE= +-SSB_DRIVER_PCICORE= +-SSB_PCICORE_HOSTMODE= +-SSB_DRIVER_MIPS= +-SSB_SFLASH= +-SSB_EMBEDDED= +-SSB_DRIVER_EXTIF= +-SSB_DRIVER_GIGE= +-SSB_DRIVER_GPIO= +-BCMA_POSSIBLE= +-BCMA= +-BCMA_BLOCKIO= +-BCMA_HOST_PCI_POSSIBLE= +-BCMA_HOST_PCI= +-BCMA_HOST_SOC= +-BCMA_DRIVER_PCI= +-BCMA_DRIVER_PCI_HOSTMODE= +-BCMA_DRIVER_MIPS= +-BCMA_PFLASH= +-BCMA_SFLASH= +-BCMA_NFLASH= +-BCMA_DRIVER_GMAC_CMN= +-BCMA_DRIVER_GPIO= +-BCMA_DEBUG= + USB_ACM= + USB_PRINTER= + USB_WDM= +diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig +index 3613e8e..0f9d708 100644 +--- a/net/wireless/Kconfig ++++ b/net/wireless/Kconfig +@@ -186,7 +186,7 @@ config CFG80211_WEXT_EXPORT + endif # CFG80211 + + config LIB80211 +- tristate ++ tristate "lib80211" + depends on m + default n + help +@@ -196,18 +196,18 @@ config LIB80211 + Drivers should select this themselves if needed. + + config LIB80211_CRYPT_WEP +- tristate ++ tristate "lib80211 WEP support" + depends on m + select BPAUTO_CRYPTO_LIB_ARC4 + + config LIB80211_CRYPT_CCMP +- tristate ++ tristate "lib80211 CCMP support" + depends on m + depends on CRYPTO_AES + depends on CRYPTO_CCM + + config LIB80211_CRYPT_TKIP +- tristate ++ tristate "lib80211 TKIP support" + depends on m + select BPAUTO_CRYPTO_LIB_ARC4 + 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 new file mode 100644 index 0000000..f4c7422 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0002-backport-of-subsys-patches-from-openwrt.patch @@ -0,0 +1,3539 @@ +From 9d247a8381fa8a1b8d960edf2f6a5f8f931791be Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 27 May 2020 19:18:57 +0200 +Subject: [PATCH] FIX: [wlan] backport of subsys patches from openwrt + +--- + drivers/net/wireless/ath/ath10k/mac.c | 1 + + include/net/cfg80211.h | 9 + + include/net/mac80211.h | 70 +++- + include/uapi/linux/nl80211.h | 11 + + net/mac80211/Kconfig | 3 - + net/mac80211/Makefile | 6 +- + net/mac80211/aead_api.h | 23 -- + net/mac80211/aes_ccm.c | 144 +++++++ + net/mac80211/aes_ccm.h | 46 +-- + net/mac80211/aes_cmac.c | 126 ++++-- + net/mac80211/aes_cmac.h | 11 +- + net/mac80211/{aead_api.c => aes_gcm.c} | 55 ++- + net/mac80211/aes_gcm.h | 30 +- + net/mac80211/aes_gmac.h | 22 +- + net/mac80211/airtime.c | 597 +++++++++++++++++++++++++++++ + net/mac80211/cfg.c | 19 +- + net/mac80211/debugfs.c | 85 ++++ + net/mac80211/debugfs_sta.c | 82 +++- + net/mac80211/fils_aead.c | 2 +- + net/mac80211/fils_aead.h | 2 +- + net/mac80211/ieee80211_i.h | 12 + + net/mac80211/key.c | 20 +- + net/mac80211/key.h | 4 +- + net/mac80211/main.c | 35 +- + net/mac80211/rc80211_minstrel.c | 48 +-- + net/mac80211/rc80211_minstrel.h | 57 ++- + net/mac80211/rc80211_minstrel_debugfs.c | 8 +- + net/mac80211/rc80211_minstrel_ht.c | 73 ++-- + net/mac80211/rc80211_minstrel_ht.h | 2 +- + net/mac80211/rc80211_minstrel_ht_debugfs.c | 8 +- + net/mac80211/sta_info.c | 56 +++ + net/mac80211/sta_info.h | 12 + + net/mac80211/status.c | 56 ++- + net/mac80211/tx.c | 234 +++++++---- + net/mac80211/wpa.c | 43 ++- + net/wireless/core.c | 15 - + net/wireless/nl80211.c | 15 + + net/wireless/sysfs.c | 27 +- + 38 files changed, 1679 insertions(+), 390 deletions(-) + delete mode 100644 net/mac80211/aead_api.h + create mode 100644 net/mac80211/aes_ccm.c + rename net/mac80211/{aead_api.c => aes_gcm.c} (50%) + create mode 100644 net/mac80211/airtime.c + +diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c +index 7f0d03a..aad1bda 100644 +--- a/drivers/net/wireless/ath/ath10k/mac.c ++++ b/drivers/net/wireless/ath/ath10k/mac.c +@@ -8870,6 +8870,7 @@ int ath10k_mac_register(struct ath10k *ar) + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_SET_SCAN_DWELL); ++ wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_AQL); + + if (test_bit(WMI_SERVICE_TX_DATA_ACK_RSSI, ar->wmi.svc_map) || + test_bit(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, ar->wmi.svc_map)) +diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h +index 9827613..8099b10 100644 +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -2603,6 +2603,13 @@ enum wiphy_params_flags { + + #define IEEE80211_DEFAULT_AIRTIME_WEIGHT 256 + ++/* The per TXQ device queue limit in airtime */ ++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000 ++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000 ++ ++/* The per interface airtime threshold to switch to lower queue limit */ ++#define IEEE80211_AQL_THRESHOLD 24000 ++ + /** + * struct cfg80211_pmksa - PMK Security Association + * +@@ -3447,6 +3454,7 @@ struct cfg80211_update_owe_info { + * (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 + * +@@ -3762,6 +3770,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); +diff --git a/include/net/mac80211.h b/include/net/mac80211.h +index b2d7dad..bca141e 100644 +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -967,6 +967,7 @@ ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate) + * @band: the band to transmit on (use for checking for races) + * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC + * @ack_frame_id: internal frame ID for TX status, used internally ++ * @tx_time_est: TX time estimate in units of 4us, used internally + * @control: union part for control data + * @control.rates: TX rates array to try + * @control.rts_cts_rate_idx: rate for RTS or CTS +@@ -1003,11 +1004,11 @@ ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate) + struct ieee80211_tx_info { + /* common information */ + u32 flags; +- u8 band; +- +- u8 hw_queue; +- +- u16 ack_frame_id; ++ u32 band:3, ++ ack_frame_id:13, ++ hw_queue:4, ++ tx_time_est:10; ++ /* 2 free bits */ + + union { + struct { +@@ -1058,6 +1059,22 @@ struct ieee80211_tx_info { + }; + }; + ++static inline u16 ++ieee80211_info_set_tx_time_est(struct ieee80211_tx_info *info, u16 tx_time_est) ++{ ++ /* We only have 10 bits in tx_time_est, so store airtime ++ * in increments of 4us and clamp the maximum to 2**12-1 ++ */ ++ info->tx_time_est = min_t(u16, tx_time_est, 4095) >> 2; ++ return info->tx_time_est << 2; ++} ++ ++static inline u16 ++ieee80211_info_get_tx_time_est(struct ieee80211_tx_info *info) ++{ ++ return info->tx_time_est << 2; ++} ++ + /** + * struct ieee80211_tx_status - extended tx staus info for rate control + * +@@ -1484,6 +1501,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 ++ * @max_antenna_gain: maximum antenna gain adjusted by user config (in dBi) + * + * @chandef: the channel definition to tune to + * @radar_enabled: whether radar detection is enabled +@@ -1504,6 +1522,7 @@ enum ieee80211_smps_mode { + struct ieee80211_conf { + u32 flags; + int power_level, dynamic_ps_timeout; ++ int max_antenna_gain; + + u16 listen_interval; + u8 ps_dtim_period; +@@ -5557,6 +5576,18 @@ void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid, + u32 tx_airtime, u32 rx_airtime); + + /** ++ * ieee80211_txq_airtime_check - check if a txq can send frame to device ++ * ++ * @hw: pointer obtained from ieee80211_alloc_hw() ++ * @txq: pointer obtained from station or virtual interface ++ * ++ * Return true if the AQL's airtime limit has not been reached and the txq can ++ * continue to send more packets to the device. Otherwise return false. ++ */ ++bool ++ieee80211_txq_airtime_check(struct ieee80211_hw *hw, struct ieee80211_txq *txq); ++ ++/** + * ieee80211_iter_keys - iterate keys programmed into the device + * @hw: pointer obtained from ieee80211_alloc_hw() + * @vif: virtual interface to iterate, may be %NULL for all +@@ -6415,4 +6446,33 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif, + struct cfg80211_nan_match_params *match, + gfp_t gfp); + ++/** ++ * ieee80211_calc_rx_airtime - calculate estimated transmission airtime for RX. ++ * ++ * This function calculates the estimated airtime usage of a frame based on the ++ * rate information in the RX status struct and the frame length. ++ * ++ * @hw: pointer as obtained from ieee80211_alloc_hw() ++ * @status: &struct ieee80211_rx_status containing the transmission rate ++ * information. ++ * @len: frame length in bytes ++ */ ++u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw, ++ struct ieee80211_rx_status *status, ++ int len); ++ ++/** ++ * ieee80211_calc_tx_airtime - calculate estimated transmission airtime for TX. ++ * ++ * This function calculates the estimated airtime usage of a frame based on the ++ * rate information in the TX info struct and the frame length. ++ * ++ * @hw: pointer as obtained from ieee80211_alloc_hw() ++ * @info: &struct ieee80211_tx_info of the frame. ++ * @len: frame length in bytes ++ */ ++u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw, ++ struct ieee80211_tx_info *info, ++ int len); ++ + #endif /* MAC80211_H */ +diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h +index beee59c..ef08258 100644 +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -2373,6 +2373,9 @@ enum nl80211_commands { + * the allowed channel bandwidth configurations. (u8 attribute) + * Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13. + * ++ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce ++ * transmit power to stay within regulatory limits. u32, dBi. ++ * + * @NUM_NL80211_ATTR: total number of nl80211_attrs available + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use +@@ -2835,6 +2838,8 @@ enum nl80211_attrs { + NL80211_ATTR_WIPHY_EDMG_CHANNELS, + NL80211_ATTR_WIPHY_EDMG_BW_CONFIG, + ++ NL80211_ATTR_WIPHY_ANTENNA_GAIN, ++ + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, +@@ -5484,6 +5489,10 @@ enum nl80211_feature_flags { + * @NL80211_EXT_FEATURE_SAE_OFFLOAD: Device wants to do SAE authentication in + * station mode (SAE password is passed as part of the connect command). + * ++ * @NL80211_EXT_FEATURE_AQL: The driver supports the Airtime Queue Limit (AQL) ++ * feature, which prevents bufferbloat by using the expected transmission ++ * time to limit the amount of data buffered in the hardware. ++ * + * @NUM_NL80211_EXT_FEATURES: number of extended features. + * @MAX_NL80211_EXT_FEATURES: highest extended feature index. + */ +@@ -5529,6 +5538,8 @@ enum nl80211_ext_feature_index { + NL80211_EXT_FEATURE_EXT_KEY_ID, + NL80211_EXT_FEATURE_STA_TX_PWR, + NL80211_EXT_FEATURE_SAE_OFFLOAD, ++ NL80211_EXT_FEATURE_VLAN_OFFLOAD, ++ NL80211_EXT_FEATURE_AQL, + + /* add new features before the definition below */ + NUM_NL80211_EXT_FEATURES, +diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig +index dd981a0..f7f8247 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 7bb0a16..910982f 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 \ +@@ -18,8 +17,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 \ +@@ -32,7 +31,8 @@ mac80211-y := \ + chan.o \ + trace.o mlme.o \ + tdls.o \ +- ocb.o ++ ocb.o \ ++ airtime.o + + mac80211-$(CPTCFG_MAC80211_LEDS) += led.o + mac80211-$(CPTCFG_MAC80211_DEBUGFS) += \ +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. ++ * ++ * Rewrite: Copyright (C) 2013 Linaro Ltd ++ * ++ * 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 ++#include "key.h" ++#include "aes_ccm.h" ++ ++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 57748ca..6568a97 100644 +--- a/net/mac80211/aes_cmac.c ++++ b/net/mac80211/aes_cmac.c +@@ -19,50 +19,126 @@ + #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, +- const u8 *data, size_t data_len, u8 *mic) ++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) + { +- SHASH_DESC_ON_STACK(desc, tfm); +- u8 out[AES_BLOCK_SIZE]; ++ 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; ++ } + +- desc->tfm = tfm; ++ memset(pad, 0, AES_BLOCK_SIZE); ++ crypto_cipher_encrypt_one(tfm, pad, pad); ++ gf_mulx(pad); + +- crypto_shash_init(desc); +- crypto_shash_update(desc, aad, AAD_LEN); +- crypto_shash_update(desc, data, data_len - CMAC_TLEN); +- crypto_shash_finup(desc, zero, CMAC_TLEN, out); ++ 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); ++ } + +- memcpy(mic, out, CMAC_TLEN); ++ 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_256(struct crypto_shash *tfm, const u8 *aad, ++ ++void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, ++ const u8 *data, size_t data_len, u8 *mic) ++{ ++ const u8 *addr[3]; ++ size_t len[3]; ++ u8 zero[CMAC_TLEN]; ++ ++ memset(zero, 0, CMAC_TLEN); ++ addr[0] = aad; ++ len[0] = AAD_LEN; ++ 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); ++} ++ ++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[3]; ++ size_t len[3]; ++ u8 zero[CMAC_TLEN_256]; + +- desc->tfm = tfm; ++ memset(zero, 0, CMAC_TLEN_256); ++ addr[0] = aad; ++ len[0] = AAD_LEN; ++ addr[1] = data; ++ len[1] = data_len - CMAC_TLEN_256; ++ addr[2] = zero; ++ len[2] = CMAC_TLEN_256; + +- crypto_shash_init(desc); +- crypto_shash_update(desc, aad, AAD_LEN); +- crypto_shash_update(desc, data, data_len - CMAC_TLEN_256); +- crypto_shash_finup(desc, zero, CMAC_TLEN_256, mic); ++ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256); + } + +-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/aead_api.c b/net/mac80211/aes_gcm.c +similarity index 50% +rename from net/mac80211/aead_api.c +rename to net/mac80211/aes_gcm.c +index c5fe95e..8a4397c 100644 +--- a/net/mac80211/aead_api.c ++++ b/net/mac80211/aes_gcm.c +@@ -1,55 +1,52 @@ +-// 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 ++ * 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 "aead_api.h" ++#include ++#include "key.h" ++#include "aes_gcm.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) ++int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, ++ 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); ++ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC); + if (!aead_req) + return -ENOMEM; + + __aad = (u8 *)aead_req + reqsize; +- memcpy(__aad, aad, aad_len); ++ memcpy(__aad, aad, GCM_AAD_LEN); + + sg_init_table(sg, 3); +- sg_set_buf(&sg[0], __aad, aad_len); ++ 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, mic_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, b_0); ++ 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 aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, +- u8 *data, size_t data_len, u8 *mic) ++int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, ++ 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); +@@ -59,20 +56,21 @@ int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, + if (data_len == 0) + return -EINVAL; + +- aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC); ++ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC); + if (!aead_req) + return -ENOMEM; + + __aad = (u8 *)aead_req + reqsize; +- memcpy(__aad, aad, aad_len); ++ memcpy(__aad, aad, GCM_AAD_LEN); + + sg_init_table(sg, 3); +- sg_set_buf(&sg[0], __aad, aad_len); ++ 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, mic_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 + mic_len, b_0); ++ 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); +@@ -81,21 +79,20 @@ int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, + 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 *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], ++ size_t key_len) + { + struct crypto_aead *tfm; + int err; + +- tfm = crypto_alloc_aead(alg, 0, CRYPTO_ALG_ASYNC); ++ 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, mic_len); ++ err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN); + if (err) + goto free_aead; + +@@ -106,7 +103,7 @@ free_aead: + return ERR_PTR(err); + } + +-void aead_key_free(struct crypto_aead *tfm) ++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 */ +diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c +new file mode 100644 +index 0000000..9fc2968 +--- /dev/null ++++ b/net/mac80211/airtime.c +@@ -0,0 +1,597 @@ ++// SPDX-License-Identifier: ISC ++/* ++ * Copyright (C) 2019 Felix Fietkau ++ */ ++ ++#include ++#include "ieee80211_i.h" ++#include "sta_info.h" ++ ++#define AVG_PKT_SIZE 1024 ++ ++/* Number of bits for an average sized packet */ ++#define MCS_NBITS (AVG_PKT_SIZE << 3) ++ ++/* Number of kilo-symbols (symbols * 1024) for a packet with (bps) bits per ++ * symbol. We use k-symbols to avoid rounding in the _TIME macros below. ++ */ ++#define MCS_N_KSYMS(bps) DIV_ROUND_UP(MCS_NBITS << 10, (bps)) ++ ++/* Transmission time (in 1024 * usec) for a packet containing (ksyms) * 1024 ++ * symbols. ++ */ ++#define MCS_SYMBOL_TIME(sgi, ksyms) \ ++ (sgi ? \ ++ ((ksyms) * 4 * 18) / 20 : /* 3.6 us per sym */ \ ++ ((ksyms) * 4) /* 4.0 us per sym */ \ ++ ) ++ ++/* Transmit duration for the raw data part of an average sized packet */ ++#define MCS_DURATION(streams, sgi, bps) \ ++ ((u32)MCS_SYMBOL_TIME(sgi, MCS_N_KSYMS((streams) * (bps)))) ++ ++#define MCS_DURATION_S(shift, streams, sgi, bps) \ ++ ((u16)((MCS_DURATION(streams, sgi, bps) >> shift))) ++ ++/* These should match the values in enum nl80211_he_gi */ ++#define HE_GI_08 0 ++#define HE_GI_16 1 ++#define HE_GI_32 2 ++ ++/* Transmission time (1024 usec) for a packet containing (ksyms) * k-symbols */ ++#define HE_SYMBOL_TIME(gi, ksyms) \ ++ (gi == HE_GI_08 ? \ ++ ((ksyms) * 16 * 17) / 20 : /* 13.6 us per sym */ \ ++ (gi == HE_GI_16 ? \ ++ ((ksyms) * 16 * 18) / 20 : /* 14.4 us per sym */ \ ++ ((ksyms) * 16) /* 16.0 us per sym */ \ ++ )) ++ ++/* Transmit duration for the raw data part of an average sized packet */ ++#define HE_DURATION(streams, gi, bps) \ ++ ((u32)HE_SYMBOL_TIME(gi, MCS_N_KSYMS((streams) * (bps)))) ++ ++#define HE_DURATION_S(shift, streams, gi, bps) \ ++ (HE_DURATION(streams, gi, bps) >> shift) ++ ++#define BW_20 0 ++#define BW_40 1 ++#define BW_80 2 ++#define BW_160 3 ++ ++/* ++ * Define group sort order: HT40 -> SGI -> #streams ++ */ ++#define IEEE80211_MAX_STREAMS 4 ++#define IEEE80211_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */ ++#define IEEE80211_VHT_STREAM_GROUPS 8 /* BW(=4) * SGI(=2) */ ++ ++#define IEEE80211_HE_MAX_STREAMS 8 ++#define IEEE80211_HE_STREAM_GROUPS 12 /* BW(=4) * GI(=3) */ ++ ++#define IEEE80211_HT_GROUPS_NB (IEEE80211_MAX_STREAMS * \ ++ IEEE80211_HT_STREAM_GROUPS) ++#define IEEE80211_VHT_GROUPS_NB (IEEE80211_MAX_STREAMS * \ ++ IEEE80211_VHT_STREAM_GROUPS) ++#define IEEE80211_HE_GROUPS_NB (IEEE80211_HE_MAX_STREAMS * \ ++ IEEE80211_HE_STREAM_GROUPS) ++#define IEEE80211_GROUPS_NB (IEEE80211_HT_GROUPS_NB + \ ++ IEEE80211_VHT_GROUPS_NB + \ ++ IEEE80211_HE_GROUPS_NB) ++ ++#define IEEE80211_HT_GROUP_0 0 ++#define IEEE80211_VHT_GROUP_0 (IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB) ++#define IEEE80211_HE_GROUP_0 (IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB) ++ ++#define MCS_GROUP_RATES 12 ++ ++#define HT_GROUP_IDX(_streams, _sgi, _ht40) \ ++ IEEE80211_HT_GROUP_0 + \ ++ IEEE80211_MAX_STREAMS * 2 * _ht40 + \ ++ IEEE80211_MAX_STREAMS * _sgi + \ ++ _streams - 1 ++ ++#define _MAX(a, b) (((a)>(b))?(a):(b)) ++ ++#define GROUP_SHIFT(duration) \ ++ _MAX(0, 16 - __builtin_clz(duration)) ++ ++/* MCS rate information for an MCS group */ ++#define __MCS_GROUP(_streams, _sgi, _ht40, _s) \ ++ [HT_GROUP_IDX(_streams, _sgi, _ht40)] = { \ ++ .shift = _s, \ ++ .duration = { \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 54 : 26), \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 108 : 52), \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 162 : 78), \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 216 : 104), \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 324 : 156), \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 432 : 208), \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 486 : 234), \ ++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 540 : 260) \ ++ } \ ++} ++ ++#define MCS_GROUP_SHIFT(_streams, _sgi, _ht40) \ ++ GROUP_SHIFT(MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26)) ++ ++#define MCS_GROUP(_streams, _sgi, _ht40) \ ++ __MCS_GROUP(_streams, _sgi, _ht40, \ ++ MCS_GROUP_SHIFT(_streams, _sgi, _ht40)) ++ ++#define VHT_GROUP_IDX(_streams, _sgi, _bw) \ ++ (IEEE80211_VHT_GROUP_0 + \ ++ IEEE80211_MAX_STREAMS * 2 * (_bw) + \ ++ IEEE80211_MAX_STREAMS * (_sgi) + \ ++ (_streams) - 1) ++ ++#define BW2VBPS(_bw, r4, r3, r2, r1) \ ++ (_bw == BW_160 ? r4 : _bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1) ++ ++#define __VHT_GROUP(_streams, _sgi, _bw, _s) \ ++ [VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \ ++ .shift = _s, \ ++ .duration = { \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 234, 117, 54, 26)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 468, 234, 108, 52)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 702, 351, 162, 78)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 936, 468, 216, 104)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 1404, 702, 324, 156)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 1872, 936, 432, 208)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 2106, 1053, 486, 234)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 2340, 1170, 540, 260)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 2808, 1404, 648, 312)), \ ++ MCS_DURATION_S(_s, _streams, _sgi, \ ++ BW2VBPS(_bw, 3120, 1560, 720, 346)) \ ++ } \ ++} ++ ++#define VHT_GROUP_SHIFT(_streams, _sgi, _bw) \ ++ GROUP_SHIFT(MCS_DURATION(_streams, _sgi, \ ++ BW2VBPS(_bw, 243, 117, 54, 26))) ++ ++#define VHT_GROUP(_streams, _sgi, _bw) \ ++ __VHT_GROUP(_streams, _sgi, _bw, \ ++ VHT_GROUP_SHIFT(_streams, _sgi, _bw)) ++ ++ ++#define HE_GROUP_IDX(_streams, _gi, _bw) \ ++ (IEEE80211_HE_GROUP_0 + \ ++ IEEE80211_HE_MAX_STREAMS * 3 * (_bw) + \ ++ IEEE80211_HE_MAX_STREAMS * (_gi) + \ ++ (_streams) - 1) ++ ++#define __HE_GROUP(_streams, _gi, _bw, _s) \ ++ [HE_GROUP_IDX(_streams, _gi, _bw)] = { \ ++ .shift = _s, \ ++ .duration = { \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 979, 489, 230, 115)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 1958, 979, 475, 230)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 2937, 1468, 705, 345)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 3916, 1958, 936, 475)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 5875, 2937, 1411, 705)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 7833, 3916, 1872, 936)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 8827, 4406, 2102, 1051)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 9806, 4896, 2347, 1166)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 11764, 5875, 2808, 1411)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 13060, 6523, 3124, 1555)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 14702, 7344, 3513, 1756)), \ ++ HE_DURATION_S(_s, _streams, _gi, \ ++ BW2VBPS(_bw, 16329, 8164, 3902, 1944)) \ ++ } \ ++} ++ ++#define HE_GROUP_SHIFT(_streams, _gi, _bw) \ ++ GROUP_SHIFT(HE_DURATION(_streams, _gi, \ ++ BW2VBPS(_bw, 979, 489, 230, 115))) ++ ++#define HE_GROUP(_streams, _gi, _bw) \ ++ __HE_GROUP(_streams, _gi, _bw, \ ++ HE_GROUP_SHIFT(_streams, _gi, _bw)) ++struct mcs_group { ++ u8 shift; ++ u16 duration[MCS_GROUP_RATES]; ++}; ++ ++static const struct mcs_group airtime_mcs_groups[] = { ++ MCS_GROUP(1, 0, BW_20), ++ MCS_GROUP(2, 0, BW_20), ++ MCS_GROUP(3, 0, BW_20), ++ MCS_GROUP(4, 0, BW_20), ++ ++ MCS_GROUP(1, 1, BW_20), ++ MCS_GROUP(2, 1, BW_20), ++ MCS_GROUP(3, 1, BW_20), ++ MCS_GROUP(4, 1, BW_20), ++ ++ MCS_GROUP(1, 0, BW_40), ++ MCS_GROUP(2, 0, BW_40), ++ MCS_GROUP(3, 0, BW_40), ++ MCS_GROUP(4, 0, BW_40), ++ ++ MCS_GROUP(1, 1, BW_40), ++ MCS_GROUP(2, 1, BW_40), ++ MCS_GROUP(3, 1, BW_40), ++ MCS_GROUP(4, 1, BW_40), ++ ++ VHT_GROUP(1, 0, BW_20), ++ VHT_GROUP(2, 0, BW_20), ++ VHT_GROUP(3, 0, BW_20), ++ VHT_GROUP(4, 0, BW_20), ++ ++ VHT_GROUP(1, 1, BW_20), ++ VHT_GROUP(2, 1, BW_20), ++ VHT_GROUP(3, 1, BW_20), ++ VHT_GROUP(4, 1, BW_20), ++ ++ VHT_GROUP(1, 0, BW_40), ++ VHT_GROUP(2, 0, BW_40), ++ VHT_GROUP(3, 0, BW_40), ++ VHT_GROUP(4, 0, BW_40), ++ ++ VHT_GROUP(1, 1, BW_40), ++ VHT_GROUP(2, 1, BW_40), ++ VHT_GROUP(3, 1, BW_40), ++ VHT_GROUP(4, 1, BW_40), ++ ++ VHT_GROUP(1, 0, BW_80), ++ VHT_GROUP(2, 0, BW_80), ++ VHT_GROUP(3, 0, BW_80), ++ VHT_GROUP(4, 0, BW_80), ++ ++ VHT_GROUP(1, 1, BW_80), ++ VHT_GROUP(2, 1, BW_80), ++ VHT_GROUP(3, 1, BW_80), ++ VHT_GROUP(4, 1, BW_80), ++ ++ VHT_GROUP(1, 0, BW_160), ++ VHT_GROUP(2, 0, BW_160), ++ VHT_GROUP(3, 0, BW_160), ++ VHT_GROUP(4, 0, BW_160), ++ ++ VHT_GROUP(1, 1, BW_160), ++ VHT_GROUP(2, 1, BW_160), ++ VHT_GROUP(3, 1, BW_160), ++ VHT_GROUP(4, 1, BW_160), ++ ++ HE_GROUP(1, HE_GI_08, BW_20), ++ HE_GROUP(2, HE_GI_08, BW_20), ++ HE_GROUP(3, HE_GI_08, BW_20), ++ HE_GROUP(4, HE_GI_08, BW_20), ++ HE_GROUP(5, HE_GI_08, BW_20), ++ HE_GROUP(6, HE_GI_08, BW_20), ++ HE_GROUP(7, HE_GI_08, BW_20), ++ HE_GROUP(8, HE_GI_08, BW_20), ++ ++ HE_GROUP(1, HE_GI_16, BW_20), ++ HE_GROUP(2, HE_GI_16, BW_20), ++ HE_GROUP(3, HE_GI_16, BW_20), ++ HE_GROUP(4, HE_GI_16, BW_20), ++ HE_GROUP(5, HE_GI_16, BW_20), ++ HE_GROUP(6, HE_GI_16, BW_20), ++ HE_GROUP(7, HE_GI_16, BW_20), ++ HE_GROUP(8, HE_GI_16, BW_20), ++ ++ HE_GROUP(1, HE_GI_32, BW_20), ++ HE_GROUP(2, HE_GI_32, BW_20), ++ HE_GROUP(3, HE_GI_32, BW_20), ++ HE_GROUP(4, HE_GI_32, BW_20), ++ HE_GROUP(5, HE_GI_32, BW_20), ++ HE_GROUP(6, HE_GI_32, BW_20), ++ HE_GROUP(7, HE_GI_32, BW_20), ++ HE_GROUP(8, HE_GI_32, BW_20), ++ ++ HE_GROUP(1, HE_GI_08, BW_40), ++ HE_GROUP(2, HE_GI_08, BW_40), ++ HE_GROUP(3, HE_GI_08, BW_40), ++ HE_GROUP(4, HE_GI_08, BW_40), ++ HE_GROUP(5, HE_GI_08, BW_40), ++ HE_GROUP(6, HE_GI_08, BW_40), ++ HE_GROUP(7, HE_GI_08, BW_40), ++ HE_GROUP(8, HE_GI_08, BW_40), ++ ++ HE_GROUP(1, HE_GI_16, BW_40), ++ HE_GROUP(2, HE_GI_16, BW_40), ++ HE_GROUP(3, HE_GI_16, BW_40), ++ HE_GROUP(4, HE_GI_16, BW_40), ++ HE_GROUP(5, HE_GI_16, BW_40), ++ HE_GROUP(6, HE_GI_16, BW_40), ++ HE_GROUP(7, HE_GI_16, BW_40), ++ HE_GROUP(8, HE_GI_16, BW_40), ++ ++ HE_GROUP(1, HE_GI_32, BW_40), ++ HE_GROUP(2, HE_GI_32, BW_40), ++ HE_GROUP(3, HE_GI_32, BW_40), ++ HE_GROUP(4, HE_GI_32, BW_40), ++ HE_GROUP(5, HE_GI_32, BW_40), ++ HE_GROUP(6, HE_GI_32, BW_40), ++ HE_GROUP(7, HE_GI_32, BW_40), ++ HE_GROUP(8, HE_GI_32, BW_40), ++ ++ HE_GROUP(1, HE_GI_08, BW_80), ++ HE_GROUP(2, HE_GI_08, BW_80), ++ HE_GROUP(3, HE_GI_08, BW_80), ++ HE_GROUP(4, HE_GI_08, BW_80), ++ HE_GROUP(5, HE_GI_08, BW_80), ++ HE_GROUP(6, HE_GI_08, BW_80), ++ HE_GROUP(7, HE_GI_08, BW_80), ++ HE_GROUP(8, HE_GI_08, BW_80), ++ ++ HE_GROUP(1, HE_GI_16, BW_80), ++ HE_GROUP(2, HE_GI_16, BW_80), ++ HE_GROUP(3, HE_GI_16, BW_80), ++ HE_GROUP(4, HE_GI_16, BW_80), ++ HE_GROUP(5, HE_GI_16, BW_80), ++ HE_GROUP(6, HE_GI_16, BW_80), ++ HE_GROUP(7, HE_GI_16, BW_80), ++ HE_GROUP(8, HE_GI_16, BW_80), ++ ++ HE_GROUP(1, HE_GI_32, BW_80), ++ HE_GROUP(2, HE_GI_32, BW_80), ++ HE_GROUP(3, HE_GI_32, BW_80), ++ HE_GROUP(4, HE_GI_32, BW_80), ++ HE_GROUP(5, HE_GI_32, BW_80), ++ HE_GROUP(6, HE_GI_32, BW_80), ++ HE_GROUP(7, HE_GI_32, BW_80), ++ HE_GROUP(8, HE_GI_32, BW_80), ++ ++ HE_GROUP(1, HE_GI_08, BW_160), ++ HE_GROUP(2, HE_GI_08, BW_160), ++ HE_GROUP(3, HE_GI_08, BW_160), ++ HE_GROUP(4, HE_GI_08, BW_160), ++ HE_GROUP(5, HE_GI_08, BW_160), ++ HE_GROUP(6, HE_GI_08, BW_160), ++ HE_GROUP(7, HE_GI_08, BW_160), ++ HE_GROUP(8, HE_GI_08, BW_160), ++ ++ HE_GROUP(1, HE_GI_16, BW_160), ++ HE_GROUP(2, HE_GI_16, BW_160), ++ HE_GROUP(3, HE_GI_16, BW_160), ++ HE_GROUP(4, HE_GI_16, BW_160), ++ HE_GROUP(5, HE_GI_16, BW_160), ++ HE_GROUP(6, HE_GI_16, BW_160), ++ HE_GROUP(7, HE_GI_16, BW_160), ++ HE_GROUP(8, HE_GI_16, BW_160), ++ ++ HE_GROUP(1, HE_GI_32, BW_160), ++ HE_GROUP(2, HE_GI_32, BW_160), ++ HE_GROUP(3, HE_GI_32, BW_160), ++ HE_GROUP(4, HE_GI_32, BW_160), ++ HE_GROUP(5, HE_GI_32, BW_160), ++ HE_GROUP(6, HE_GI_32, BW_160), ++ HE_GROUP(7, HE_GI_32, BW_160), ++ HE_GROUP(8, HE_GI_32, BW_160), ++}; ++ ++static u32 ++ieee80211_calc_legacy_rate_duration(u16 bitrate, bool short_pre, ++ bool cck, int len) ++{ ++ u32 duration; ++ ++ if (cck) { ++ duration = 144 + 48; /* preamble + PLCP */ ++ if (short_pre) ++ duration >>= 1; ++ ++ duration += 10; /* SIFS */ ++ } else { ++ duration = 20 + 16; /* premable + SIFS */ ++ } ++ ++ len <<= 3; ++ duration += (len * 10) / bitrate; ++ ++ return duration; ++} ++ ++u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw, ++ struct ieee80211_rx_status *status, ++ int len) ++{ ++ struct ieee80211_supported_band *sband; ++ const struct ieee80211_rate *rate; ++ bool sgi = status->enc_flags & RX_ENC_FLAG_SHORT_GI; ++ bool sp = status->enc_flags & RX_ENC_FLAG_SHORTPRE; ++ int bw, streams; ++ int group, idx; ++ u32 duration; ++ bool cck; ++ ++ switch (status->bw) { ++ case RATE_INFO_BW_20: ++ bw = BW_20; ++ break; ++ case RATE_INFO_BW_40: ++ bw = BW_40; ++ break; ++ case RATE_INFO_BW_80: ++ bw = BW_80; ++ break; ++ case RATE_INFO_BW_160: ++ bw = BW_160; ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ return 0; ++ } ++ ++ switch (status->encoding) { ++ case RX_ENC_LEGACY: ++ if (WARN_ON_ONCE(status->band > NL80211_BAND_5GHZ)) ++ return 0; ++ ++ sband = hw->wiphy->bands[status->band]; ++ if (!sband || status->rate_idx >= sband->n_bitrates) ++ return 0; ++ ++ rate = &sband->bitrates[status->rate_idx]; ++ cck = rate->flags & IEEE80211_RATE_MANDATORY_B; ++ ++ return ieee80211_calc_legacy_rate_duration(rate->bitrate, sp, ++ cck, len); ++ ++ case RX_ENC_VHT: ++ streams = status->nss; ++ idx = status->rate_idx; ++ group = VHT_GROUP_IDX(streams, sgi, bw); ++ break; ++ case RX_ENC_HT: ++ streams = ((status->rate_idx >> 3) & 3) + 1; ++ idx = status->rate_idx & 7; ++ group = HT_GROUP_IDX(streams, sgi, bw); ++ break; ++ case RX_ENC_HE: ++ streams = status->nss; ++ idx = status->rate_idx; ++ group = HE_GROUP_IDX(streams, status->he_gi, bw); ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ return 0; ++ } ++ ++ if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) || ++ (status->encoding == RX_ENC_HE && streams > 8))) ++ return 0; ++ ++ duration = airtime_mcs_groups[group].duration[idx]; ++ duration <<= airtime_mcs_groups[group].shift; ++ duration *= len; ++ duration /= AVG_PKT_SIZE; ++ duration /= 1024; ++ ++ duration += 36 + (streams << 2); ++ ++ return duration; ++} ++EXPORT_SYMBOL_GPL(ieee80211_calc_rx_airtime); ++ ++static u32 ieee80211_calc_tx_airtime_rate(struct ieee80211_hw *hw, ++ struct ieee80211_tx_rate *rate, ++ u8 band, int len) ++{ ++ struct ieee80211_rx_status stat = { ++ .band = band, ++ }; ++ ++ if (rate->idx < 0 || !rate->count) ++ return 0; ++ ++ if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) ++ stat.bw = RATE_INFO_BW_80; ++ else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ++ stat.bw = RATE_INFO_BW_40; ++ else ++ stat.bw = RATE_INFO_BW_20; ++ ++ stat.enc_flags = 0; ++ if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ++ stat.enc_flags |= RX_ENC_FLAG_SHORTPRE; ++ if (rate->flags & IEEE80211_TX_RC_SHORT_GI) ++ stat.enc_flags |= RX_ENC_FLAG_SHORT_GI; ++ ++ stat.rate_idx = rate->idx; ++ if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { ++ stat.encoding = RX_ENC_VHT; ++ stat.rate_idx = ieee80211_rate_get_vht_mcs(rate); ++ stat.nss = ieee80211_rate_get_vht_nss(rate); ++ } else if (rate->flags & IEEE80211_TX_RC_MCS) { ++ stat.encoding = RX_ENC_HT; ++ } else { ++ stat.encoding = RX_ENC_LEGACY; ++ } ++ ++ return ieee80211_calc_rx_airtime(hw, &stat, len); ++} ++ ++u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw, ++ struct ieee80211_tx_info *info, ++ int len) ++{ ++ u32 duration = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(info->status.rates); i++) { ++ struct ieee80211_tx_rate *rate = &info->status.rates[i]; ++ u32 cur_duration; ++ ++ cur_duration = ieee80211_calc_tx_airtime_rate(hw, rate, ++ info->band, len); ++ if (!cur_duration) ++ break; ++ ++ duration += cur_duration * rate->count; ++ } ++ ++ return duration; ++} ++EXPORT_SYMBOL_GPL(ieee80211_calc_tx_airtime); ++ ++u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *pubsta, ++ int len) ++{ ++ struct ieee80211_supported_band *sband; ++ struct ieee80211_chanctx_conf *conf; ++ int rateidx, shift = 0; ++ bool cck, short_pream; ++ u32 basic_rates; ++ u8 band = 0; ++ u16 rate; ++ ++ len += 38; /* Ethernet header length */ ++ ++ conf = rcu_dereference(vif->chanctx_conf); ++ if (conf) { ++ band = conf->def.chan->band; ++ shift = ieee80211_chandef_get_shift(&conf->def); ++ } ++ ++ if (pubsta) { ++ struct sta_info *sta = container_of(pubsta, struct sta_info, ++ sta); ++ ++ return ieee80211_calc_tx_airtime_rate(hw, ++ &sta->tx_stats.last_rate, ++ band, len); ++ } ++ ++ if (!conf) ++ return 0; ++ ++ /* No station to get latest rate from, so calculate the worst-case ++ * duration using the lowest configured basic rate. ++ */ ++ sband = hw->wiphy->bands[band]; ++ ++ basic_rates = vif->bss_conf.basic_rates; ++ short_pream = vif->bss_conf.use_short_preamble; ++ ++ rateidx = basic_rates ? ffs(basic_rates) - 1 : 0; ++ rate = sband->bitrates[rateidx].bitrate << shift; ++ cck = sband->bitrates[rateidx].flags & IEEE80211_RATE_MANDATORY_B; ++ ++ return ieee80211_calc_legacy_rate_duration(rate, short_pream, cck, len); ++} +diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c +index f0e0d9e..993ee7b 100644 +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -1172,7 +1172,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); +- ieee80211_free_keys(sdata, true); + + sdata->vif.bss_conf.enable_beacon = false; + sdata->vif.bss_conf.ssid_len = 0; +@@ -2318,7 +2317,7 @@ static int ieee80211_scan(struct wiphy *wiphy, + * the frames sent while scanning on other channel will be + * lost) + */ +- if (sdata->u.ap.beacon && ++ if (0 && sdata->u.ap.beacon && + (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || + !(req->flags & NL80211_SCAN_FLAG_AP))) + return -EOPNOTSUPP; +@@ -2583,6 +2582,19 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, + return 0; + } + ++static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi) ++{ ++ struct ieee80211_local *local = wiphy_priv(wiphy); ++ ++ if (dbi < 0) ++ return -EINVAL; ++ ++ local->user_antenna_gain = dbi; ++ ieee80211_hw_config(local, 0); ++ ++ return 0; ++} ++ + static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr) + { +@@ -3450,7 +3462,7 @@ int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, + + spin_lock_irqsave(&local->ack_status_lock, spin_flags); + id = idr_alloc(&local->ack_status_frames, ack_skb, +- 1, 0x10000, GFP_ATOMIC); ++ 1, 0x2000, GFP_ATOMIC); + spin_unlock_irqrestore(&local->ack_status_lock, spin_flags); + + if (id < 0) { +@@ -4018,6 +4030,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) +diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c +index c20c6a6..30876d1 100644 +--- a/net/mac80211/debugfs.c ++++ b/net/mac80211/debugfs.c +@@ -148,6 +148,87 @@ static const struct file_operations aqm_ops = { + .llseek = default_llseek, + }; + ++static ssize_t aql_txq_limit_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 limit low AQL limit high\n" ++ "VO %u %u\n" ++ "VI %u %u\n" ++ "BE %u %u\n" ++ "BK %u %u\n", ++ local->aql_txq_limit_low[IEEE80211_AC_VO], ++ local->aql_txq_limit_high[IEEE80211_AC_VO], ++ local->aql_txq_limit_low[IEEE80211_AC_VI], ++ 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); ++} ++ ++static ssize_t aql_txq_limit_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, ++ loff_t *ppos) ++{ ++ struct ieee80211_local *local = file->private_data; ++ char buf[100]; ++ size_t len; ++ u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old; ++ struct sta_info *sta; ++ ++ if (count > sizeof(buf)) ++ return -EINVAL; ++ ++ if (copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ ++ buf[sizeof(buf) - 1] = 0; ++ len = strlen(buf); ++ if (len > 0 && buf[len - 1] == '\n') ++ buf[len - 1] = 0; ++ ++ if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) ++ return -EINVAL; ++ ++ if (ac >= IEEE80211_NUM_ACS) ++ return -EINVAL; ++ ++ q_limit_low_old = local->aql_txq_limit_low[ac]; ++ q_limit_high_old = local->aql_txq_limit_high[ac]; ++ ++ 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) { ++ /* If a sta has customized queue limits, keep it */ ++ if (sta->airtime[ac].aql_limit_low == q_limit_low_old && ++ sta->airtime[ac].aql_limit_high == q_limit_high_old) { ++ sta->airtime[ac].aql_limit_low = q_limit_low; ++ sta->airtime[ac].aql_limit_high = q_limit_high; ++ } ++ } ++ mutex_unlock(&local->sta_mtx); ++ return count; ++} ++ ++static const struct file_operations aql_txq_limit_ops = { ++ .write = aql_txq_limit_write, ++ .read = aql_txq_limit_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ + static ssize_t force_tx_status_read(struct file *file, + char __user *user_buf, + size_t count, +@@ -441,6 +522,10 @@ void debugfs_hw_add(struct ieee80211_local *local) + debugfs_create_u16("airtime_flags", 0600, + phyd, &local->airtime_flags); + ++ DEBUGFS_ADD(aql_txq_limit); ++ debugfs_create_u32("aql_threshold", 0600, ++ phyd, &local->aql_threshold); ++ + statsd = debugfs_create_dir("statistics", phyd); + + /* if the dir failed, don't put all the other things into the root! */ +diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c +index c8ad20c..e887d04 100644 +--- a/net/mac80211/debugfs_sta.c ++++ b/net/mac80211/debugfs_sta.c +@@ -5,7 +5,7 @@ + * Copyright 2007 Johannes Berg + * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH +- * Copyright (C) 2018 - 2019 Intel Corporation ++ * Copyright (C) 2018 - 2020 Intel Corporation + */ + + #include +@@ -78,6 +78,7 @@ static const char * const sta_flag_names[] = { + FLAG(MPSP_OWNER), + FLAG(MPSP_RECIPIENT), + FLAG(PS_DELIVER), ++ FLAG(USES_ENCRYPTION), + #undef FLAG + }; + +@@ -197,7 +198,7 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, + { + struct sta_info *sta = file->private_data; + struct ieee80211_local *local = sta->sdata->local; +- size_t bufsz = 200; ++ size_t bufsz = 400; + char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf; + u64 rx_airtime = 0, tx_airtime = 0; + s64 deficit[IEEE80211_NUM_ACS]; +@@ -218,13 +219,8 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, + p += scnprintf(p, bufsz + buf - p, + "RX: %llu us\nTX: %llu us\nWeight: %u\n" + "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n", +- rx_airtime, +- tx_airtime, +- sta->airtime_weight, +- deficit[0], +- deficit[1], +- deficit[2], +- deficit[3]); ++ 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); +@@ -250,6 +246,70 @@ static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf, + } + STA_OPS_RW(airtime); + ++static ssize_t sta_aql_read(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct sta_info *sta = file->private_data; ++ struct ieee80211_local *local = sta->sdata->local; ++ size_t bufsz = 400; ++ char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf; ++ u32 q_depth[IEEE80211_NUM_ACS]; ++ u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS]; ++ ssize_t rv; ++ int ac; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { ++ 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->active_txq_lock[ac]); ++ q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending); ++ } ++ ++ p += scnprintf(p, bufsz + buf - p, ++ "Q depth: VO: %u us VI: %u us BE: %u us BK: %u us\n" ++ "Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n", ++ q_depth[0], q_depth[1], q_depth[2], q_depth[3], ++ q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1], ++ q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]), ++ ++ rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); ++ kfree(buf); ++ return rv; ++} ++ ++static ssize_t sta_aql_write(struct file *file, const char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct sta_info *sta = file->private_data; ++ u32 ac, q_limit_l, q_limit_h; ++ char _buf[100] = {}, *buf = _buf; ++ ++ if (count > sizeof(_buf)) ++ return -EINVAL; ++ ++ if (copy_from_user(buf, userbuf, count)) ++ return -EFAULT; ++ ++ buf[sizeof(_buf) - 1] = '\0'; ++ if (sscanf(buf, "limit %u %u %u", &ac, &q_limit_l, &q_limit_h) ++ != 3) ++ return -EINVAL; ++ ++ if (ac >= IEEE80211_NUM_ACS) ++ return -EINVAL; ++ ++ sta->airtime[ac].aql_limit_low = q_limit_l; ++ sta->airtime[ac].aql_limit_high = q_limit_h; ++ ++ return count; ++} ++STA_OPS_RW(aql); ++ ++ + static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) + { +@@ -978,6 +1038,10 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) + DEBUGFS_ADD(airtime); + ++ if (wiphy_ext_feature_isset(local->hw.wiphy, ++ NL80211_EXT_FEATURE_AQL)) ++ DEBUGFS_ADD(aql); ++ + if (sizeof(sta->driver_buffered_tids) == sizeof(u32)) + debugfs_create_x32("driver_buffered_tids", 0400, + sta->debugfs_dir, +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 bacedb1..f75fb7d 100644 +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -1142,6 +1142,10 @@ struct ieee80211_local { + 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; + + const struct ieee80211_ops *ops; + +@@ -1372,6 +1376,7 @@ struct ieee80211_local { + int dynamic_ps_forced_timeout; + + int user_power_level; /* in dBm, for all interfaces */ ++ int user_antenna_gain; /* in dBi */ + + enum ieee80211_smps_mode smps_mode; + +@@ -1782,6 +1787,9 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, + const u8 *dest, __be16 proto, bool unencrypted); + int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev, + const u8 *buf, size_t len); ++int ieee80211_skb_resize(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, ++ struct sk_buff *skb, int hdrlen, int hdr_add); + + /* HT */ + void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, +@@ -2250,6 +2258,10 @@ const char *ieee80211_get_reason_code_string(u16 reason_code); + + extern const struct ethtool_ops ieee80211_ethtool_ops; + ++u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *pubsta, ++ int len); + #ifdef CPTCFG_MAC80211_NOINLINE + #define debug_noinline noinline + #else +diff --git a/net/mac80211/key.c b/net/mac80211/key.c +index 0f889b9..efc1acc 100644 +--- a/net/mac80211/key.c ++++ b/net/mac80211/key.c +@@ -6,7 +6,7 @@ + * Copyright 2007-2008 Johannes Berg + * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright 2015-2017 Intel Deutschland GmbH +- * Copyright 2018-2019 Intel Corporation ++ * Copyright 2018-2020 Intel Corporation + */ + + #include +@@ -262,22 +262,29 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) + sta ? sta->sta.addr : bcast_addr, ret); + } + +-int ieee80211_set_tx_key(struct ieee80211_key *key) ++static int _ieee80211_set_tx_key(struct ieee80211_key *key, bool force) + { + struct sta_info *sta = key->sta; + struct ieee80211_local *local = key->local; + + assert_key_lock(local); + ++ set_sta_flag(sta, WLAN_STA_USES_ENCRYPTION); ++ + sta->ptk_idx = key->conf.keyidx; + +- if (!ieee80211_hw_check(&local->hw, AMPDU_KEYBORDER_SUPPORT)) ++ if (force || !ieee80211_hw_check(&local->hw, AMPDU_KEYBORDER_SUPPORT)) + clear_sta_flag(sta, WLAN_STA_BLOCK_BA); + ieee80211_check_fast_xmit(sta); + + return 0; + } + ++int ieee80211_set_tx_key(struct ieee80211_key *key) ++{ ++ return _ieee80211_set_tx_key(key, false); ++} ++ + static void ieee80211_pairwise_rekey(struct ieee80211_key *old, + struct ieee80211_key *new) + { +@@ -441,11 +448,8 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, + if (pairwise) { + rcu_assign_pointer(sta->ptk[idx], new); + if (new && +- !(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)) { +- sta->ptk_idx = idx; +- clear_sta_flag(sta, WLAN_STA_BLOCK_BA); +- ieee80211_check_fast_xmit(sta); +- } ++ !(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)) ++ _ieee80211_set_tx_key(new, true); + } else { + rcu_assign_pointer(sta->gtk[idx], new); + } +diff --git a/net/mac80211/key.h b/net/mac80211/key.h +index a84465f..cbc219d 100644 +--- a/net/mac80211/key.h ++++ b/net/mac80211/key.h +@@ -88,12 +88,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; +diff --git a/net/mac80211/main.c b/net/mac80211/main.c +index 3028e1f..71dada6 100644 +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -93,7 +93,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) + struct ieee80211_sub_if_data *sdata; + struct cfg80211_chan_def chandef = {}; + u32 changed = 0; +- int power; ++ int power, max_power; + u32 offchannel_flag; + + offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; +@@ -150,6 +150,12 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) + } + rcu_read_unlock(); + ++ max_power = chandef.chan->max_reg_power; ++ if (local->user_antenna_gain > 0) { ++ max_power -= local->user_antenna_gain; ++ power = min(power, max_power); ++ } ++ + if (local->hw.conf.power_level != power) { + changed |= IEEE80211_CONF_CHANGE_POWER; + local->hw.conf.power_level = power; +@@ -314,7 +320,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) + } + EXPORT_SYMBOL(ieee80211_restart_hw); + +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + static int ieee80211_ifa_changed(struct notifier_block *nb, + unsigned long data, void *arg) + { +@@ -373,7 +379,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, + } + #endif + +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + static int ieee80211_ifa6_changed(struct notifier_block *nb, + unsigned long data, void *arg) + { +@@ -571,7 +577,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, +@@ -639,6 +645,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; ++ local->user_antenna_gain = 3; + 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; +@@ -669,8 +676,14 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + 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; + } ++ + local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX; ++ local->aql_threshold = IEEE80211_AQL_THRESHOLD; ++ atomic_set(&local->aql_total_pending_airtime, 0); + + INIT_LIST_HEAD(&local->chanctx_list); + mutex_init(&local->chanctx_mtx); +@@ -1264,14 +1277,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) + + rtnl_unlock(); + +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + local->ifa_notifier.notifier_call = ieee80211_ifa_changed; + result = register_inetaddr_notifier(&local->ifa_notifier); + if (result) + goto fail_ifa; + #endif + +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; + result = register_inet6addr_notifier(&local->ifa6_notifier); + if (result) +@@ -1280,13 +1293,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) + + return 0; + +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + fail_ifa6: +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + unregister_inetaddr_notifier(&local->ifa_notifier); + #endif + #endif +-#if defined(CONFIG_INET) || defined(CONFIG_IPV6) ++#if defined(__disabled__CONFIG_INET) || defined(__disabled__CONFIG_IPV6) + fail_ifa: + #endif + rtnl_lock(); +@@ -1314,10 +1327,10 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) + tasklet_kill(&local->tx_pending_tasklet); + tasklet_kill(&local->tasklet); + +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + unregister_inetaddr_notifier(&local->ifa_notifier); + #endif +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + unregister_inet6addr_notifier(&local->ifa6_notifier); + #endif + +diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c +index ceda2e9..9a0dc3b 100644 +--- a/net/mac80211/rc80211_minstrel.c ++++ b/net/mac80211/rc80211_minstrel.c +@@ -70,7 +70,7 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix) + } + + /* return current EMWA throughput */ +-int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma) ++int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_avg) + { + int usecs; + +@@ -79,13 +79,13 @@ int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma) + usecs = 1000000; + + /* reset thr. below 10% success */ +- if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100)) ++ if (mr->stats.prob_avg < MINSTREL_FRAC(10, 100)) + return 0; + +- if (prob_ewma > MINSTREL_FRAC(90, 100)) ++ if (prob_avg > MINSTREL_FRAC(90, 100)) + return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs)); + else +- return MINSTREL_TRUNC(100000 * (prob_ewma / usecs)); ++ return MINSTREL_TRUNC(100000 * (prob_avg / usecs)); + } + + /* find & sort topmost throughput rates */ +@@ -98,8 +98,8 @@ minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) + + 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_ewma) <= +- minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma)) ++ 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; + } + +@@ -157,20 +157,24 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi) + * Recalculate statistics and counters of a given rate + */ + void +-minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) ++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 (unlikely(!mrs->att_hist)) { +- mrs->prob_ewma = cur_prob; ++ 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_ewma = minstrel_ewma(mrs->prob_ewma, +- cur_prob, +- EWMA_LEVEL); ++ mrs->prob_avg = minstrel_ewma(mrs->prob_avg, ++ cur_prob, ++ EWMA_LEVEL); + } + mrs->att_hist += mrs->attempts; + mrs->succ_hist += mrs->success; +@@ -200,12 +204,12 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) + struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats; + + /* Update statistics of success probability per rate */ +- minstrel_calc_rate_stats(mrs); ++ 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_ewma > MINSTREL_FRAC(95, 100) || +- mrs->prob_ewma < MINSTREL_FRAC(10, 100)) { ++ 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; +@@ -225,14 +229,14 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) + * 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_ewma >= MINSTREL_FRAC(95, 100)) { +- tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_ewma); ++ 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_ewma); ++ tmp_mrs->prob_avg); + if (tmp_cur_tp >= tmp_prob_tp) + tmp_prob_rate = i; + } else { +- if (mrs->prob_ewma >= tmp_mrs->prob_ewma) ++ if (mrs->prob_avg >= tmp_mrs->prob_avg) + tmp_prob_rate = i; + } + } +@@ -290,7 +294,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, + mi->sample_deferred--; + + if (time_after(jiffies, mi->last_stats_update + +- (mp->update_interval * HZ) / 1000)) ++ mp->update_interval / (mp->new_avg ? 2 : 1))) + minstrel_update_stats(mp, mi); + } + +@@ -422,7 +426,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, + * 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_ewma > MINSTREL_FRAC(95, 100))) ++ (mi->r[ndx].stats.prob_avg > MINSTREL_FRAC(95, 100))) + return; + + mi->prev_sample = true; +@@ -573,7 +577,7 @@ static u32 minstrel_get_expected_throughput(void *priv_sta) + * computing cur_tp + */ + tmp_mrs = &mi->r[idx].stats; +- tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma) * 10; ++ 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; +diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h +index 9b6c433..f3fbea2 100644 +--- a/net/mac80211/rc80211_minstrel.h ++++ b/net/mac80211/rc80211_minstrel.h +@@ -19,6 +19,21 @@ + #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 +@@ -32,6 +47,37 @@ minstrel_ewma(int old, int new, int weight) + 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; +@@ -40,8 +86,9 @@ struct minstrel_rate_stats { + /* total attempts/success counters */ + u32 att_hist, succ_hist; + +- /* prob_ewma - exponential weighted moving average of prob */ +- u16 prob_ewma; ++ /* prob_avg - moving average of prob */ ++ u16 prob_avg; ++ u16 prob_avg_1; + + /* maximum retry counts */ + u8 retry_count; +@@ -95,6 +142,7 @@ struct minstrel_sta_info { + struct minstrel_priv { + struct ieee80211_hw *hw; + bool has_mrr; ++ bool new_avg; + u32 sample_switch; + unsigned int cw_min; + unsigned int cw_max; +@@ -126,8 +174,9 @@ 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_rate_stats *mrs); +-int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_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); +diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c +index c8afd85..9b8e0da 100644 +--- a/net/mac80211/rc80211_minstrel_debugfs.c ++++ b/net/mac80211/rc80211_minstrel_debugfs.c +@@ -90,8 +90,8 @@ minstrel_stats_open(struct inode *inode, struct file *file) + 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_ewma); +- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); ++ 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 " +@@ -147,8 +147,8 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file) + 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_ewma); +- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); ++ 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", +diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c +index 2888011..db5811c 100644 +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -346,12 +346,12 @@ minstrel_ht_avg_ampdu_len(struct minstrel_ht_sta *mi) + */ + int + minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, +- int prob_ewma) ++ int prob_avg) + { + unsigned int nsecs = 0; + + /* do not account throughput if sucess prob is below 10% */ +- if (prob_ewma < MINSTREL_FRAC(10, 100)) ++ if (prob_avg < MINSTREL_FRAC(10, 100)) + return 0; + + if (group != MINSTREL_CCK_GROUP) +@@ -365,11 +365,11 @@ minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, + * account for collision related packet error rate fluctuation + * (prob is scaled - see MINSTREL_FRAC above) + */ +- if (prob_ewma > MINSTREL_FRAC(90, 100)) ++ if (prob_avg > MINSTREL_FRAC(90, 100)) + return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000) + / nsecs)); + else +- return MINSTREL_TRUNC(100000 * ((prob_ewma * 1000) / nsecs)); ++ return MINSTREL_TRUNC(100000 * ((prob_avg * 1000) / nsecs)); + } + + /* +@@ -389,13 +389,13 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, + + cur_group = index / MCS_GROUP_RATES; + cur_idx = index % MCS_GROUP_RATES; +- cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma; ++ 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_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; ++ 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 (cur_tp_avg < tmp_tp_avg || +@@ -432,7 +432,7 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) + + tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; + tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; +- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; ++ 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 +@@ -444,11 +444,11 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) + + max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES; + max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; +- max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_ewma; ++ max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg; + +- if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) { ++ if (mrs->prob_avg > MINSTREL_FRAC(75, 100)) { + cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, +- mrs->prob_ewma); ++ mrs->prob_avg); + if (cur_tp_avg > tmp_tp_avg) + mi->max_prob_rate = index; + +@@ -458,9 +458,9 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) + if (cur_tp_avg > max_gpr_tp_avg) + mg->max_group_prob_rate = index; + } else { +- if (mrs->prob_ewma > tmp_prob) ++ if (mrs->prob_avg > tmp_prob) + mi->max_prob_rate = index; +- if (mrs->prob_ewma > max_gpr_prob) ++ if (mrs->prob_avg > max_gpr_prob) + mg->max_group_prob_rate = index; + } + } +@@ -482,12 +482,12 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, + + tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES; + tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES; +- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; ++ 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_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; ++ 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_rate && tmp_cck_tp > tmp_mcs_tp) { +@@ -518,7 +518,7 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) + continue; + + tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; +- tmp_prob = mi->groups[group].rates[tmp_idx].prob_ewma; ++ tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg; + + if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) && + (minstrel_mcs_groups[group].streams < tmp_max_streams)) { +@@ -623,7 +623,7 @@ minstrel_ht_rate_sample_switch(struct minstrel_priv *mp, + * 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_ewma > MINSTREL_FRAC(75, 100); ++ 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); +@@ -737,8 +737,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + + mrs = &mg->rates[i]; + mrs->retry_updated = false; +- minstrel_calc_rate_stats(mrs); +- cur_prob = mrs->prob_ewma; ++ minstrel_calc_rate_stats(mp, mrs); ++ cur_prob = mrs->prob_avg; + + if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0) + continue; +@@ -773,6 +773,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + + /* try to sample all available rates during each interval */ + mi->sample_count *= 8; ++ if (mp->new_avg) ++ mi->sample_count /= 2; + + if (sample) + minstrel_ht_rate_sample_switch(mp, mi); +@@ -889,6 +891,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_tx_rate *ar = info->status.rates; + struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL; + struct minstrel_priv *mp = priv; ++ u32 update_interval = mp->update_interval / 2; + bool last, update = false; + bool sample_status = false; + int i; +@@ -943,6 +946,10 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, + + 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: +@@ -970,23 +977,20 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, + */ + rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]); + if (rate->attempts > 30 && +- MINSTREL_FRAC(rate->success, rate->attempts) < +- MINSTREL_FRAC(20, 100)) { ++ rate->success < rate->attempts / 4) { + minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true); + update = true; + } + + rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]); + if (rate2->attempts > 30 && +- MINSTREL_FRAC(rate2->success, rate2->attempts) < +- MINSTREL_FRAC(20, 100)) { ++ rate2->success < rate2->attempts / 4) { + minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false); + update = true; + } + } + +- if (time_after(jiffies, mi->last_stats_update + +- (mp->update_interval / 2 * HZ) / 1000)) { ++ if (time_after(jiffies, mi->last_stats_update + update_interval)) { + update = true; + minstrel_ht_update_stats(mp, mi, true); + } +@@ -1008,7 +1012,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + unsigned int overhead = 0, overhead_rtscts = 0; + + mrs = minstrel_get_ratestats(mi, index); +- if (mrs->prob_ewma < MINSTREL_FRAC(1, 10)) { ++ if (mrs->prob_avg < MINSTREL_FRAC(1, 10)) { + mrs->retry_count = 1; + mrs->retry_count_rtscts = 1; + return; +@@ -1065,7 +1069,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + if (!mrs->retry_updated) + minstrel_calc_retransmit(mp, mi, index); + +- if (mrs->prob_ewma < MINSTREL_FRAC(20, 100) || !mrs->retry_count) { ++ if (mrs->prob_avg < MINSTREL_FRAC(20, 100) || !mrs->retry_count) { + ratetbl->rate[offset].count = 2; + ratetbl->rate[offset].count_rts = 2; + ratetbl->rate[offset].count_cts = 2; +@@ -1099,11 +1103,11 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + } + + static inline int +-minstrel_ht_get_prob_ewma(struct minstrel_ht_sta *mi, int rate) ++minstrel_ht_get_prob_avg(struct minstrel_ht_sta *mi, int rate) + { + int group = rate / MCS_GROUP_RATES; + rate %= MCS_GROUP_RATES; +- return mi->groups[group].rates[rate].prob_ewma; ++ return mi->groups[group].rates[rate].prob_avg; + } + + static int +@@ -1115,7 +1119,7 @@ minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi) + unsigned int duration; + + /* Disable A-MSDU if max_prob_rate is bad */ +- if (mi->groups[group].rates[rate].prob_ewma < MINSTREL_FRAC(50, 100)) ++ if (mi->groups[group].rates[rate].prob_avg < MINSTREL_FRAC(50, 100)) + return 1; + + duration = g->duration[rate]; +@@ -1138,7 +1142,7 @@ minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi) + * data packet size + */ + if (duration > MCS_DURATION(1, 0, 260) || +- (minstrel_ht_get_prob_ewma(mi, mi->max_tp_rate[0]) < ++ (minstrel_ht_get_prob_avg(mi, mi->max_tp_rate[0]) < + MINSTREL_FRAC(75, 100))) + return 3200; + +@@ -1243,7 +1247,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) + * rate, to avoid wasting airtime. + */ + sample_dur = minstrel_get_duration(sample_idx); +- if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) || ++ if (mrs->prob_avg > MINSTREL_FRAC(95, 100) || + minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur) + return -1; + +@@ -1666,7 +1670,8 @@ minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) + mp->has_mrr = true; + + mp->hw = hw; +- mp->update_interval = 100; ++ mp->update_interval = HZ / 10; ++ mp->new_avg = true; + + #ifdef CPTCFG_MAC80211_DEBUGFS + mp->fixed_rate_idx = (u32) -1; +@@ -1674,6 +1679,8 @@ minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *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 + + minstrel_ht_init_cck_rates(mp); +@@ -1698,7 +1705,7 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) + + i = mi->max_tp_rate[0] / MCS_GROUP_RATES; + j = mi->max_tp_rate[0] % MCS_GROUP_RATES; +- prob = mi->groups[i].rates[j].prob_ewma; ++ prob = mi->groups[i].rates[j].prob_avg; + + /* convert tp_avg from pkt per second in kbps */ + tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * 10; +diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h +index f938701..53ea3c2 100644 +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -119,6 +119,6 @@ struct minstrel_ht_sta_priv { + + 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_ewma); ++ int prob_avg); + + #endif +diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c +index 5a6e9f3..bebb719 100644 +--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c ++++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c +@@ -98,8 +98,8 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) + p += sprintf(p, "%6u ", tx_time); + + tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); +- tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); +- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); ++ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg); ++ eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000); + + p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u" + " %3u %3u %-3u " +@@ -243,8 +243,8 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) + p += sprintf(p, "%u,", tx_time); + + tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); +- tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); +- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); ++ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg); ++ eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000); + + p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u," + "%u,%llu,%llu,", +diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c +index c431722..ee72428 100644 +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -210,6 +210,20 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, + return NULL; + } + ++struct sta_info *sta_info_get_by_addrs(struct ieee80211_local *local, ++ const u8 *sta_addr, const u8 *vif_addr) ++{ ++ struct rhlist_head *tmp; ++ struct sta_info *sta; ++ ++ for_each_sta_info(local, sta_addr, sta, tmp) { ++ if (ether_addr_equal(vif_addr, sta->sdata->vif.addr)) ++ return sta; ++ } ++ ++ return NULL; ++} ++ + struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, + int idx) + { +@@ -324,6 +338,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames); + INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); + mutex_init(&sta->ampdu_mlme.mtx); ++ sta->ampdu_mlme.dialog_token_allocator = prandom_u32_max(U8_MAX); + #ifdef CPTCFG_MAC80211_MESH + if (ieee80211_vif_is_mesh(&sdata->vif)) { + sta->mesh = kzalloc(sizeof(*sta->mesh), gfp); +@@ -396,6 +411,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + skb_queue_head_init(&sta->ps_tx_buf[i]); + skb_queue_head_init(&sta->tx_filtered[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++) +@@ -1893,6 +1911,44 @@ void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid, + } + EXPORT_SYMBOL(ieee80211_sta_register_airtime); + ++void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, ++ struct sta_info *sta, u8 ac, ++ u16 tx_airtime, bool tx_completed) ++{ ++ int tx_pending; ++ ++ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) ++ return; ++ ++ if (!tx_completed) { ++ if (sta) ++ atomic_add(tx_airtime, ++ &sta->airtime[ac].aql_tx_pending); ++ ++ atomic_add(tx_airtime, &local->aql_total_pending_airtime); ++ return; ++ } ++ ++ if (sta) { ++ tx_pending = atomic_sub_return(tx_airtime, ++ &sta->airtime[ac].aql_tx_pending); ++ if (WARN_ONCE(tx_pending < 0, ++ "STA %pM AC %d txq pending airtime underflow: %u, %u", ++ sta->addr, ac, tx_pending, tx_airtime)) ++ atomic_cmpxchg(&sta->airtime[ac].aql_tx_pending, ++ tx_pending, 0); ++ } ++ ++ tx_pending = atomic_sub_return(tx_airtime, ++ &local->aql_total_pending_airtime); ++ 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_pending, 0); ++} ++ + int sta_info_move_state(struct sta_info *sta, + enum ieee80211_sta_state new_state) + { +diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h +index ad6c547..768000d 100644 +--- a/net/mac80211/sta_info.h ++++ b/net/mac80211/sta_info.h +@@ -98,6 +98,7 @@ enum ieee80211_sta_info_flags { + WLAN_STA_MPSP_OWNER, + WLAN_STA_MPSP_RECIPIENT, + WLAN_STA_PS_DELIVER, ++ WLAN_STA_USES_ENCRYPTION, + + NUM_WLAN_STA_FLAGS, + }; +@@ -132,8 +133,15 @@ struct airtime_info { + u64 rx_airtime; + u64 tx_airtime; + s64 deficit; ++ atomic_t aql_tx_pending; /* Estimated airtime for frames pending */ ++ u32 aql_limit_low; ++ u32 aql_limit_high; + }; + ++void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, ++ struct sta_info *sta, u8 ac, ++ u16 tx_airtime, bool tx_completed); ++ + struct sta_info; + + /** +@@ -725,6 +733,10 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, + const u8 *addr); + ++/* user must hold sta_mtx or be in RCU critical section */ ++struct sta_info *sta_info_get_by_addrs(struct ieee80211_local *local, ++ const u8 *sta_addr, const u8 *vif_addr); ++ + #define for_each_sta_info(local, _addr, _sta, _tmp) \ + rhl_for_each_entry_rcu(_sta, _tmp, \ + sta_info_hash_lookup(local, _addr), hash_node) +diff --git a/net/mac80211/status.c b/net/mac80211/status.c +index 351fd8e..2e3ae66 100644 +--- a/net/mac80211/status.c ++++ b/net/mac80211/status.c +@@ -670,12 +670,26 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, + struct sk_buff *skb, bool dropped) + { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ u16 tx_time_est = ieee80211_info_get_tx_time_est(info); + struct ieee80211_hdr *hdr = (void *)skb->data; + bool acked = info->flags & IEEE80211_TX_STAT_ACK; + + if (dropped) + acked = false; + ++ if (tx_time_est) { ++ struct sta_info *sta; ++ ++ rcu_read_lock(); ++ ++ sta = sta_info_get_by_addrs(local, hdr->addr1, hdr->addr2); ++ ieee80211_sta_update_pending_airtime(local, sta, ++ skb_get_queue_mapping(skb), ++ tx_time_est, ++ true); ++ rcu_read_unlock(); ++ } ++ + if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) { + struct ieee80211_sub_if_data *sdata; + +@@ -815,6 +829,11 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, + struct net_device *prev_dev = NULL; + int rtap_len; + ++ if (ieee80211_skb_resize(local, NULL, skb, 0, 0)) { ++ dev_kfree_skb(skb); ++ return; ++ } ++ + /* send frame to monitor interfaces now */ + rtap_len = ieee80211_tx_radiotap_len(info, status); + if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) { +@@ -876,9 +895,11 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, + int rates_idx; + bool send_to_cooked; + bool acked; ++ bool noack_success; + struct ieee80211_bar *bar; + int shift = 0; + int tid = IEEE80211_NUM_TIDS; ++ u16 tx_time_est; + + rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); + +@@ -893,6 +914,8 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, + clear_sta_flag(sta, WLAN_STA_SP); + + acked = !!(info->flags & IEEE80211_TX_STAT_ACK); ++ noack_success = !!(info->flags & ++ IEEE80211_TX_STAT_NOACK_TRANSMITTED); + + /* mesh Peer Service Period support */ + if (ieee80211_vif_is_mesh(&sta->sdata->vif) && +@@ -957,12 +980,12 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, + ieee80211_handle_filtered_frame(local, sta, skb); + return; + } else { +- if (!acked) ++ if (!acked && !noack_success) + sta->status_stats.retry_failed++; + sta->status_stats.retry_count += retry_count; + + if (ieee80211_is_data_present(fc)) { +- if (!acked) ++ if (!acked && !noack_success) + sta->status_stats.msdu_failed[tid]++; + + sta->status_stats.msdu_retries[tid] += +@@ -988,8 +1011,19 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, + ieee80211_sta_register_airtime(&sta->sta, tid, + info->status.tx_time, 0); + ++ if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) { ++ /* Do this here to avoid the expensive lookup of the sta ++ * in ieee80211_report_used_skb(). ++ */ ++ ieee80211_sta_update_pending_airtime(local, sta, ++ skb_get_queue_mapping(skb), ++ tx_time_est, ++ true); ++ ieee80211_info_set_tx_time_est(info, 0); ++ } ++ + if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) { +- if (info->flags & IEEE80211_TX_STAT_ACK) { ++ if (acked) { + if (sta->status_stats.lost_packets) + sta->status_stats.lost_packets = 0; + +@@ -997,6 +1031,8 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) + sta->status_stats.last_tdls_pkt_time = + jiffies; ++ } else if (noack_success) { ++ /* nothing to do here, do not account as lost */ + } else { + ieee80211_lost_packet(sta, info); + } +@@ -1076,19 +1112,13 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) + .skb = skb, + .info = IEEE80211_SKB_CB(skb), + }; +- struct rhlist_head *tmp; + struct sta_info *sta; + + rcu_read_lock(); + +- for_each_sta_info(local, hdr->addr1, sta, tmp) { +- /* skip wrong virtual interface */ +- if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) +- continue; +- ++ sta = sta_info_get_by_addrs(local, hdr->addr1, hdr->addr2); ++ if (sta) + status.sta = &sta->sta; +- break; +- } + + __ieee80211_tx_status(hw, &status); + rcu_read_unlock(); +@@ -1123,7 +1153,7 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, + + sta = container_of(pubsta, struct sta_info, sta); + +- if (!acked) ++ if (!acked && !noack_success) + sta->status_stats.retry_failed++; + sta->status_stats.retry_count += retry_count; + +@@ -1138,6 +1168,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, + sta->status_stats.last_tdls_pkt_time = jiffies; + } else if (test_sta_flag(sta, WLAN_STA_PS_STA)) { + return; ++ } else if (noack_success) { ++ /* nothing to do here, do not account as lost */ + } else { + ieee80211_lost_packet(sta, info); + } +diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c +index 535911b..9a04c48 100644 +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -590,10 +590,13 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; + +- if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) ++ if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) { + tx->key = NULL; +- else if (tx->sta && +- (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx]))) ++ return TX_CONTINUE; ++ } ++ ++ if (tx->sta && ++ (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx]))) + tx->key = key; + else if (ieee80211_is_group_privacy_action(tx->skb) && + (key = rcu_dereference(tx->sdata->default_multicast_key))) +@@ -654,6 +657,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) + if (!skip_hw && tx->key && + tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) + info->control.hw_key = &tx->key->conf; ++ } else if (!ieee80211_is_mgmt(hdr->frame_control) && tx->sta && ++ test_sta_flag(tx->sta, WLAN_STA_USES_ENCRYPTION)) { ++ return TX_DROP; + } + + return TX_CONTINUE; +@@ -1937,37 +1943,53 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, + } + + /* device xmit handlers */ +- +-static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, +- struct sk_buff *skb, +- int head_need, bool may_encrypt) ++int ieee80211_skb_resize(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, ++ struct sk_buff *skb, int hdr_len, int hdr_extra) + { +- struct ieee80211_local *local = sdata->local; ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr; +- bool enc_tailroom; +- int tail_need = 0; ++ int head_need, head_max; ++ int tail_need, tail_max; ++ bool enc_tailroom = false; + +- hdr = (struct ieee80211_hdr *) skb->data; +- enc_tailroom = may_encrypt && +- (sdata->crypto_tx_tailroom_needed_cnt || +- ieee80211_is_mgmt(hdr->frame_control)); +- +- if (enc_tailroom) { +- tail_need = IEEE80211_ENCRYPT_TAILROOM; +- tail_need -= skb_tailroom(skb); +- tail_need = max_t(int, tail_need, 0); ++ if (sdata && !hdr_len && ++ !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) { ++ hdr = (struct ieee80211_hdr *) skb->data; ++ enc_tailroom = (sdata->crypto_tx_tailroom_needed_cnt || ++ ieee80211_is_mgmt(hdr->frame_control)); ++ hdr_len += sdata->encrypt_headroom; ++ } ++ ++ head_need = head_max = hdr_len; ++ tail_need = tail_max = 0; ++ if (!sdata) { ++ head_need = head_max = local->tx_headroom; ++ } else { ++ head_max += hdr_extra; ++ head_max += max_t(int, local->tx_headroom, ++ local->hw.extra_tx_headroom); ++ head_need += local->hw.extra_tx_headroom; ++ ++ tail_max = IEEE80211_ENCRYPT_TAILROOM; ++ if (enc_tailroom) ++ tail_need = tail_max; + } + + if (skb_cloned(skb) && + (!ieee80211_hw_check(&local->hw, SUPPORTS_CLONED_SKBS) || + !skb_clone_writable(skb, ETH_HLEN) || enc_tailroom)) + I802_DEBUG_INC(local->tx_expand_skb_head_cloned); +- else if (head_need || tail_need) ++ else if (head_need > skb_headroom(skb) || ++ tail_need > skb_tailroom(skb)) + I802_DEBUG_INC(local->tx_expand_skb_head); + else + return 0; + +- if (pskb_expand_head(skb, head_need, tail_need, GFP_ATOMIC)) { ++ head_max = max_t(int, 0, head_max - skb_headroom(skb)); ++ tail_max = max_t(int, 0, tail_max - skb_tailroom(skb)); ++ ++ if (pskb_expand_head(skb, head_max, tail_max, GFP_ATOMIC)) { + wiphy_debug(local->hw.wiphy, + "failed to reallocate TX buffer\n"); + return -ENOMEM; +@@ -1983,18 +2005,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, + struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr; +- int headroom; +- bool may_encrypt; +- +- may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT); +- +- headroom = local->tx_headroom; +- if (may_encrypt) +- headroom += sdata->encrypt_headroom; +- headroom -= skb_headroom(skb); +- headroom = max_t(int, 0, headroom); + +- if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) { ++ if (ieee80211_skb_resize(local, sdata, skb, 0, 0)) { + ieee80211_free_txskb(&local->hw, skb); + return; + } +@@ -2433,6 +2445,33 @@ static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, + return 0; + } + ++static int ieee80211_store_ack_skb(struct ieee80211_local *local, ++ struct sk_buff *skb, ++ u32 *info_flags) ++{ ++ struct sk_buff *ack_skb = skb_clone_sk(skb); ++ u16 info_id = 0; ++ ++ if (ack_skb) { ++ unsigned long flags; ++ int id; ++ ++ spin_lock_irqsave(&local->ack_status_lock, flags); ++ id = idr_alloc(&local->ack_status_frames, ack_skb, ++ 1, 0x2000, GFP_ATOMIC); ++ spin_unlock_irqrestore(&local->ack_status_lock, flags); ++ ++ if (id >= 0) { ++ info_id = id; ++ *info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; ++ } else { ++ kfree_skb(ack_skb); ++ } ++ } ++ ++ return info_id; ++} ++ + /** + * ieee80211_build_hdr - build 802.11 header in the given frame + * @sdata: virtual interface to build the header for +@@ -2726,26 +2765,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, + } + + if (unlikely(!multicast && skb->sk && +- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { +- struct sk_buff *ack_skb = skb_clone_sk(skb); +- +- if (ack_skb) { +- unsigned long flags; +- int id; +- +- spin_lock_irqsave(&local->ack_status_lock, flags); +- id = idr_alloc(&local->ack_status_frames, ack_skb, +- 1, 0x10000, GFP_ATOMIC); +- spin_unlock_irqrestore(&local->ack_status_lock, flags); +- +- if (id >= 0) { +- info_id = id; +- info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; +- } else { +- kfree_skb(ack_skb); +- } +- } +- } ++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) ++ info_id = ieee80211_store_ack_skb(local, skb, &info_flags); + + /* + * If the skb is shared we need to obtain our own copy. +@@ -2784,29 +2805,13 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, + } + + skb_pull(skb, skip_header_bytes); +- head_need = hdrlen + encaps_len + meshhdrlen - skb_headroom(skb); ++ head_need = hdrlen + encaps_len + meshhdrlen; + +- /* +- * So we need to modify the skb header and hence need a copy of +- * that. The head_need variable above doesn't, so far, include +- * the needed header space that we don't need right away. If we +- * can, then we don't reallocate right now but only after the +- * frame arrives at the master device (if it does...) +- * +- * If we cannot, however, then we will reallocate to include all +- * the ever needed space. Also, if we need to reallocate it anyway, +- * make it big enough for everything we may ever need. +- */ +- +- if (head_need > 0 || skb_cloned(skb)) { +- head_need += sdata->encrypt_headroom; +- head_need += local->tx_headroom; +- head_need = max_t(int, 0, head_need); +- if (ieee80211_skb_resize(sdata, skb, head_need, true)) { +- ieee80211_free_txskb(&local->hw, skb); +- skb = NULL; +- return ERR_PTR(-ENOMEM); +- } ++ if (ieee80211_skb_resize(local, sdata, skb, head_need, ++ sdata->encrypt_headroom)) { ++ ieee80211_free_txskb(&local->hw, skb); ++ skb = NULL; ++ return ERR_PTR(-ENOMEM); + } + + if (encaps_data) +@@ -3421,7 +3426,6 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, + struct ieee80211_local *local = sdata->local; + u16 ethertype = (skb->data[12] << 8) | skb->data[13]; + int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); +- int hw_headroom = sdata->local->hw.extra_tx_headroom; + struct ethhdr eth; + struct ieee80211_tx_info *info; + struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; +@@ -3473,10 +3477,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, + * as the may-encrypt argument for the resize to not account for + * more room than we already have in 'extra_head' + */ +- if (unlikely(ieee80211_skb_resize(sdata, skb, +- max_t(int, extra_head + hw_headroom - +- skb_headroom(skb), 0), +- false))) { ++ if (unlikely(ieee80211_skb_resize(local, sdata, skb, extra_head, 0))) { + kfree_skb(skb); + return true; + } +@@ -3558,6 +3559,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, + + WARN_ON_ONCE(softirq_count() == 0); + ++ if (!ieee80211_txq_airtime_check(hw, txq)) ++ return NULL; ++ + begin: + spin_lock_bh(&fq->lock); + +@@ -3668,6 +3672,21 @@ begin: + } + + IEEE80211_SKB_CB(skb)->control.vif = vif; ++ ++ if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) { ++ u32 airtime; ++ ++ airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta, ++ skb->len); ++ if (airtime) { ++ airtime = ieee80211_info_set_tx_time_est(info, airtime); ++ ieee80211_sta_update_pending_airtime(local, tx.sta, ++ txq->ac, ++ airtime, ++ false); ++ } ++ } ++ + return skb; + + out: +@@ -3681,7 +3700,8 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) + { + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_txq *ret = NULL; +- struct txq_info *txqi = NULL; ++ struct txq_info *txqi = NULL, *head = NULL; ++ bool found_eligible_txq = false; + + spin_lock_bh(&local->active_txq_lock[ac]); + +@@ -3692,13 +3712,30 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) + if (!txqi) + goto out; + ++ if (txqi == head) { ++ if (!found_eligible_txq) ++ goto out; ++ else ++ found_eligible_txq = false; ++ } ++ ++ if (!head) ++ head = txqi; ++ + if (txqi->txq.sta) { + struct sta_info *sta = container_of(txqi->txq.sta, +- struct sta_info, sta); ++ struct sta_info, sta); ++ bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq); ++ s64 deficit = sta->airtime[txqi->txq.ac].deficit; ++ ++ if (aql_check) ++ found_eligible_txq = true; + +- if (sta->airtime[txqi->txq.ac].deficit < 0) { ++ if (deficit < 0) + sta->airtime[txqi->txq.ac].deficit += + sta->airtime_weight; ++ ++ if (deficit < 0 || !aql_check) { + list_move_tail(&txqi->schedule_order, + &local->active_txqs[txqi->txq.ac]); + goto begin; +@@ -3752,6 +3789,33 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw, + } + EXPORT_SYMBOL(__ieee80211_schedule_txq); + ++bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, ++ struct ieee80211_txq *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)) ++ return true; ++ ++ if (!txq->sta) ++ return true; ++ ++ 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(&sta->airtime[txq->ac].aql_tx_pending) < ++ sta->airtime[txq->ac].aql_limit_high) ++ return true; ++ ++ return false; ++} ++EXPORT_SYMBOL(ieee80211_txq_airtime_check); ++ + bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) + { +@@ -4024,6 +4088,12 @@ out: + netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev) + { ++#if defined(sk_pacing_shift) || LINUX_VERSION_IS_GEQ(4,15,0) ++ if (skb->sk && sk_fullsock(skb->sk) && ++ skb->sk->sk_pacing_shift != 6) ++ skb->sk->sk_pacing_shift = 6; ++#endif ++ + if (unlikely(ieee80211_multicast_to_unicast(skb, dev))) { + struct sk_buff_head queue; + +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) + } + + +-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) + { + __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; + +- /* 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; + } + + +@@ -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 (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; + } + +@@ -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]; + + if (info->control.hw_key && +@@ -700,8 +699,10 @@ static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) + + 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)); ++ ++ return 0; + } + + 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]; + + 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 (!ieee80211_is_mgmt(hdr->frame_control)) +diff --git a/net/wireless/core.c b/net/wireless/core.c +index 8bffbb6..c8a6c0e 100644 +--- a/net/wireless/core.c ++++ b/net/wireless/core.c +@@ -613,21 +613,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) + c->limits[j].max > 1)) + return -EINVAL; + +- /* +- * This isn't well-defined right now. If you have an +- * IBSS interface, then its beacon interval may change +- * by joining other networks, and nothing prevents it +- * from doing that. +- * So technically we probably shouldn't even allow AP +- * and IBSS in the same interface, but it seems that +- * some drivers support that, possibly only with fixed +- * beacon intervals for IBSS. +- */ +- if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) && +- c->beacon_int_min_gcd)) { +- return -EINVAL; +- } +- + cnt += c->limits[j].max; + /* + * Don't advertise an unsupported type +diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c +index 6b0bcec..c93a4e0 100644 +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -630,6 +630,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { + .len = SAE_PASSWORD_MAX_LEN }, + [NL80211_ATTR_TWT_RESPONDER] = { .type = NLA_FLAG }, + [NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy), ++ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, + }; + + /* policy for the key attributes */ +@@ -2995,6 +2996,20 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) + return result; + } + ++ if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_GAIN]) { ++ int idx, dbi = 0; ++ ++ if (!rdev->ops->set_antenna_gain) ++ return -EOPNOTSUPP; ++ ++ 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; ++ } ++ + if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && + info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { + u32 tx_ant, rx_ant; +diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c +index 93d663c..bd4227b 100644 +--- a/net/wireless/sysfs.c ++++ b/net/wireless/sysfs.c +@@ -23,18 +23,35 @@ static inline struct cfg80211_registered_device *dev_to_rdev( + return container_of(dev, struct cfg80211_registered_device, wiphy.dev); + } + +-#define SHOW_FMT(name, fmt, member) \ ++#define SHOW_FMT(name, fmt, member, mode) \ + static ssize_t name ## _show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \ + } \ +-static DEVICE_ATTR_RO(name) ++static DEVICE_ATTR_##mode(name) + +-SHOW_FMT(index, "%d", wiphy_idx); +-SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); +-SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); ++static ssize_t macaddress_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ u8 mac[ETH_ALEN]; ++ ++ if (!mac_pton(buf, mac)) ++ return -EINVAL; ++ ++ if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n') ++ return -EINVAL; ++ ++ memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN); ++ ++ return strnlen(buf, len); ++} ++ ++SHOW_FMT(index, "%d", wiphy_idx, RO); ++SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW); ++SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO); + + static ssize_t name_show(struct device *dev, + struct device_attribute *attr, 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 new file mode 100644 index 0000000..a29f9e9 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0003-backport-of-ath-patches-from-openwrt.patch @@ -0,0 +1,3995 @@ +From c4762b3c27cd17589da336df4d2b1b5954200f8d Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 27 May 2020 19:19:43 +0200 +Subject: [PATCH] FIX: [mac80211] 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 | 126 +++++-- + 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 | 72 +++- + drivers/net/wireless/ath/ath10k/wmi.h | 49 ++- + 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 | 34 +- + .../net/wireless/ath/ath9k/ath9k_pci_owl_loader.c | 17 + + drivers/net/wireless/ath/ath9k/channel.c | 8 + + 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 | 49 ++- + drivers/net/wireless/ath/ath9k/mac.c | 9 +- + drivers/net/wireless/ath/ath9k/main.c | 13 + + drivers/net/wireless/ath/ath9k/pci.c | 1 + + drivers/net/wireless/ath/ath9k/phy.h | 3 + + 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 | 9 + + local-symbols | 4 + + net/wireless/reg.c | 3 + + 54 files changed, 2137 insertions(+), 293 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 + +diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig +index b8a8688..53588af 100644 +--- a/drivers/net/wireless/ath/Kconfig ++++ b/drivers/net/wireless/ath/Kconfig +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: ISC + config ATH_COMMON +- tristate ++ tristate "ath.ko" + depends on m + + config WLAN_VENDOR_ATH +@@ -24,6 +24,9 @@ config WLAN_VENDOR_ATH + + if WLAN_VENDOR_ATH + ++config ATH_USER_REGD ++ bool "Do not enforce EEPROM regulatory restrictions" ++ + config ATH_DEBUG + bool "Atheros wireless debugging" + ---help--- +diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile +index 113420d..0a6a773 100644 +--- a/drivers/net/wireless/ath/Makefile ++++ b/drivers/net/wireless/ath/Makefile +@@ -14,10 +14,10 @@ ath-objs := main.o \ + regd.o \ + hw.o \ + key.o \ ++ debug.o \ + dfs_pattern_detector.o \ + dfs_pri_detector.o + +-ath-$(CPTCFG_ATH_DEBUG) += debug.o + ath-$(CPTCFG_ATH_TRACEPOINTS) += trace.o + + CFLAGS_trace.o := -I$(src) +diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h +index a547b2d..2a18e2e 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, + #endif /* CPTCFG_ATH_DEBUG */ + + /** Returns string describing opmode, or NULL if unknown mode. */ +-#ifdef CPTCFG_ATH_DEBUG + const char *ath_opmode_to_string(enum nl80211_iftype opmode); +-#else +-static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode) +-{ +- return "UNKNOWN"; +-} +-#endif + + extern const char *ath_bus_type_strings[]; + static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype) +diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig +index 56a25d7..754c92d 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 +@@ -87,6 +97,12 @@ config ATH10K_TRACING + ---help--- + Select this to ath10k use tracing infrastructure. + ++config ATH10K_THERMAL ++ bool "Atheros ath10k thermal monitoring support" ++ depends on THERMAL ++ ---help--- ++ Select this to ath10k use hwmon for thermal measurement. ++ + config ATH10K_DFS_CERTIFIED + bool "Atheros DFS support for certified platforms" + depends on ATH10K && CFG80211_CERTIFICATION_ONUS +diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile +index 24d846a..e040d84 100644 +--- a/drivers/net/wireless/ath/ath10k/Makefile ++++ b/drivers/net/wireless/ath/ath10k/Makefile +@@ -18,7 +18,8 @@ ath10k_core-y += mac.o \ + ath10k_core-$(CPTCFG_ATH10K_SPECTRAL) += spectral.o + ath10k_core-$(CPTCFG_NL80211_TESTMODE) += testmode.o + ath10k_core-$(CPTCFG_ATH10K_TRACING) += trace.o +-ath10k_core-$(CONFIG_THERMAL) += thermal.o ++ath10k_core-$(CPTCFG_ATH10K_THERMAL) += thermal.o ++ath10k_core-$(CPTCFG_ATH10K_LEDS) += leds.o + ath10k_core-$(CPTCFG_MAC80211_DEBUGFS) += debugfs_sta.o + ath10k_core-$(CONFIG_PM) += wow.o + ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o +diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c +index 383d4fa..9d5bede 100644 +--- a/drivers/net/wireless/ath/ath10k/core.c ++++ b/drivers/net/wireless/ath/ath10k/core.c +@@ -24,6 +24,7 @@ + #include "testmode.h" + #include "wmi-ops.h" + #include "coredump.h" ++#include "leds.h" + + unsigned int ath10k_debug_mask; + EXPORT_SYMBOL(ath10k_debug_mask); +@@ -60,6 +61,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, +@@ -337,6 +340,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, +@@ -378,6 +382,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, +@@ -426,6 +431,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, +@@ -2791,6 +2797,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: +@@ -3047,9 +3057,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: +@@ -3074,6 +3093,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); +@@ -3085,6 +3114,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 80d2f64..2dae928 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" +@@ -1170,6 +1171,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 fw_crash_counter; + u32 fw_warm_reset_counter; +@@ -1211,6 +1219,10 @@ struct ath10k { + struct ath10k_bus_params bus_param; + struct completion peer_delete_done; + ++#ifdef CPTCFG_MAC80211_LEDS ++ const char *led_default_trigger; ++#endif ++ + /* must be last */ + u8 drv_priv[0] __aligned(sizeof(void *)); + }; +diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h +index 4eaaa84..9c8ad11 100644 +--- a/drivers/net/wireless/ath/ath10k/htt.h ++++ b/drivers/net/wireless/ath/ath10k/htt.h +@@ -2219,7 +2219,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 6cb1845..4417204 100644 +--- a/drivers/net/wireless/ath/ath10k/hw.h ++++ b/drivers/net/wireless/ath/ath10k/hw.h +@@ -514,6 +514,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 aad1bda..7946d3b 100644 +--- a/drivers/net/wireless/ath/ath10k/mac.c ++++ b/drivers/net/wireless/ath/ath10k/mac.c +@@ -23,6 +23,7 @@ + #include "wmi-tlv.h" + #include "wmi-ops.h" + #include "wow.h" ++#include "leds.h" + + /*********/ + /* Rates */ +@@ -1008,6 +1009,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; +@@ -1040,7 +1075,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); +@@ -1486,7 +1522,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; +@@ -2515,7 +2552,7 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, + const u16 *vht_mcs_mask; + u8 ampdu_factor; + u8 max_nss, vht_mcs; +- int i; ++ int i, nss160; + + if (WARN_ON(ath10k_mac_vif_chan(vif, &def))) + return; +@@ -2575,23 +2612,45 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, + __le16_to_cpu(vht_cap->vht_mcs.tx_highest); + arg->peer_vht_rates.tx_mcs_set = ath10k_peer_assoc_h_vht_limit( + __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask); ++ arg->peer_bw_rxnss_override = 0; ++ nss160 = 1; /* 1x1 default config for VHT160 */ + +- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", +- sta->addr, arg->peer_max_mpdu, arg->peer_flags); ++ /* only local 4x4 configuration do support 2x2 for VHT160, ++ * everything else must use 1x1 ++ */ + +- if (arg->peer_vht_rates.rx_max_rate && +- (sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK)) { +- switch (arg->peer_vht_rates.rx_max_rate) { +- case 1560: +- /* Must be 2x2 at 160Mhz is all it can do. */ +- arg->peer_bw_rxnss_override = 2; +- break; +- case 780: +- /* Can only do 1x1 at 160Mhz (Long Guard Interval) */ +- arg->peer_bw_rxnss_override = 1; +- break; +- } ++ if (ar->cfg_rx_chainmask == 15) ++ nss160 = arg->peer_num_spatial_streams <= 2 ? 1 : 2; ++ ++ /* if peer provides 1x1 nss160 information using max rate ++ * vht information, we reduce local nss160 to 1x1. ++ * consider that it has been observed that some client ++ * devices provide zero here, no matter which transmission ++ * rate is possible. in that case the local nss configuration ++ * will be used at maxmimum configuration possible. (see above) ++ */ ++ ++ if (arg->peer_vht_rates.rx_max_rate == 780) ++ nss160 = 1; ++ ++ /* in case if peer is connected with vht160 or vht80+80, ++ * we need to properly adjust rxnss parameters otherwise ++ * firmware will raise a assert ++ */ ++ switch (arg->peer_phymode) { ++ case MODE_11AC_VHT80_80: ++ arg->peer_bw_rxnss_override = BW_NSS_FWCONF_80_80(nss160); ++ /* fall through */ ++ case MODE_11AC_VHT160: ++ arg->peer_bw_rxnss_override |= BW_NSS_FWCONF_160(nss160); ++ break; ++ default: ++ break; + } ++ ++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x peer_bw_rxnss_override 0x%x\n", ++ sta->addr, arg->peer_max_mpdu, arg->peer_flags, ++ arg->peer_bw_rxnss_override); + } + + static void ath10k_peer_assoc_h_qos(struct ath10k *ar, +@@ -2743,9 +2802,9 @@ static int ath10k_peer_assoc_prepare(struct ath10k *ar, + ath10k_peer_assoc_h_crypto(ar, vif, sta, arg); + ath10k_peer_assoc_h_rates(ar, vif, sta, arg); + ath10k_peer_assoc_h_ht(ar, vif, sta, arg); ++ ath10k_peer_assoc_h_phymode(ar, vif, sta, arg); + ath10k_peer_assoc_h_vht(ar, vif, sta, arg); + ath10k_peer_assoc_h_qos(ar, vif, sta, arg); +- ath10k_peer_assoc_h_phymode(ar, vif, sta, arg); + + return 0; + } +@@ -3145,7 +3204,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 +@@ -4553,13 +4613,6 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) + vht_cap.cap |= val; + } + +- /* Currently the firmware seems to be buggy, don't enable 80+80 +- * mode until that's resolved. +- */ +- if ((ar->vht_cap_info & IEEE80211_VHT_CAP_SHORT_GI_160) && +- (ar->vht_cap_info & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == 0) +- vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; +- + mcs_map = 0; + for (i = 0; i < 8; i++) { + if ((i < ar->num_rf_chains) && (ar->cfg_tx_chainmask & BIT(i))) +@@ -8673,6 +8726,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[] = { +@@ -9000,6 +9068,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 1491c25..a274dc4 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h ++++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h +@@ -216,7 +216,10 @@ struct wmi_ops { + struct sk_buff *(*gen_bb_timing) + (struct ath10k *ar, + const struct wmi_bb_timing_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); +@@ -1080,6 +1083,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 eb0c963..e0239f4 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c ++++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c +@@ -4367,6 +4367,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 8a2cee3..5d32e88 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.c ++++ b/drivers/net/wireless/ath/ath10k/wmi.c +@@ -1700,13 +1700,18 @@ void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, + flags |= WMI_CHAN_FLAG_HT40_PLUS; + if (arg->chan_radar) + flags |= WMI_CHAN_FLAG_DFS; +- ++ ch->band_center_freq2 = 0; + ch->mhz = __cpu_to_le32(arg->freq); + ch->band_center_freq1 = __cpu_to_le32(arg->band_center_freq1); + if (arg->mode == MODE_11AC_VHT80_80) + ch->band_center_freq2 = __cpu_to_le32(arg->band_center_freq2); +- else +- ch->band_center_freq2 = 0; ++ if (arg->mode == MODE_11AC_VHT160) { ++ if (arg->freq < arg->band_center_freq1) ++ ch->band_center_freq1 = __cpu_to_le32(arg->band_center_freq1 - 40); ++ else ++ ch->band_center_freq1 = __cpu_to_le32(arg->band_center_freq1 + 40); ++ ch->band_center_freq2 = __cpu_to_le32(arg->band_center_freq1); ++ } + ch->min_power = arg->min_power; + ch->max_power = arg->max_power; + ch->reg_power = arg->max_reg_power; +@@ -7367,6 +7372,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) +@@ -7552,12 +7600,7 @@ ath10k_wmi_peer_assoc_fill_10_4(struct ath10k *ar, void *buf, + struct wmi_10_4_peer_assoc_complete_cmd *cmd = buf; + + ath10k_wmi_peer_assoc_fill_10_2(ar, buf, arg); +- if (arg->peer_bw_rxnss_override) +- cmd->peer_bw_rxnss_override = +- __cpu_to_le32((arg->peer_bw_rxnss_override - 1) | +- BIT(PEER_BW_RXNSS_OVERRIDE_OFFSET)); +- else +- cmd->peer_bw_rxnss_override = 0; ++ cmd->peer_bw_rxnss_override = __cpu_to_le32(arg->peer_bw_rxnss_override); + } + + static int +@@ -9029,6 +9072,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 */ +@@ -9099,6 +9145,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 */ +@@ -9171,6 +9219,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 */ + }; + +@@ -9242,6 +9292,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 */ +@@ -9322,6 +9374,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 e80dbe7..1c819a5 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.h ++++ b/drivers/net/wireless/ath/ath10k/wmi.h +@@ -3005,6 +3005,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; +@@ -6478,7 +6513,19 @@ struct wmi_10_2_peer_assoc_complete_cmd { + __le32 info0; /* WMI_PEER_ASSOC_INFO0_ */ + } __packed; + +-#define PEER_BW_RXNSS_OVERRIDE_OFFSET 31 ++#define BW_NSS_FWCONF_MAP_ENABLE BIT(31) ++#define BW_NSS_FWCONF_MAP_160MHZ_LSB (0) ++#define BW_NSS_FWCONF_MAP_160MHZ_MASK (0x00000007) ++#define BW_NSS_FWCONF_MAP_80_80MHZ_LSB (3) ++#define BW_NSS_FWCONF_MAP_80_80MHZ_MASK (0x00000038) ++#define BW_NSS_FWCONF_MAP_MASK (0x0000003F) ++ ++#define GET_BW_NSS_FWCONF_160(x) (MS(x, BW_NSS_FWCONF_MAP_160MHZ) + 1) ++#define GET_BW_NSS_FWCONF_80_80(x) (MS(x, BW_NSS_FWCONF_MAP_80_80MHZ) + 1) ++ ++/* Values defined to set 160 MHz Bandwidth NSS Mapping into FW*/ ++#define BW_NSS_FWCONF_160(x) (BW_NSS_FWCONF_MAP_ENABLE | SM(x - 1, BW_NSS_FWCONF_MAP_160MHZ)) ++#define BW_NSS_FWCONF_80_80(x) (BW_NSS_FWCONF_MAP_ENABLE | SM(x - 1, BW_NSS_FWCONF_MAP_80_80MHZ)) + + struct wmi_10_4_peer_assoc_complete_cmd { + struct wmi_10_2_peer_assoc_complete_cmd cmd; +diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h +index f73c794..211a8d9 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 c15f4dc..d546e52 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 94f7004..c325081 100644 +--- a/drivers/net/wireless/ath/ath5k/debug.c ++++ b/drivers/net/wireless/ath/ath5k/debug.c +@@ -822,6 +822,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 */ + +@@ -1016,6 +1107,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 d5ee32c..9dcf379 100644 +--- a/drivers/net/wireless/ath/ath5k/pci.c ++++ b/drivers/net/wireless/ath/ath5k/pci.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #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) + } + + /* +- * Read from eeprom ++ * Read from eeprom or platform_data + */ + static bool + ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data) +@@ -79,6 +82,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; + ++ struct ath5k_platform_data *pdata = NULL; ++ ++ if (ah->pdev) ++ pdata = ah->pdev->dev.platform_data; ++ ++ if (pdata && pdata->eeprom_data && pdata->eeprom_data[61] == AR5K_EEPROM_MAGIC_VALUE) { ++ if (offset >= ATH5K_PLAT_EEP_MAX_WORDS) ++ return false; ++ ++ *data = pdata->eeprom_data[offset]; ++ return true; ++ } ++ + /* + * Initialize EEPROM access + */ +@@ -122,6 +138,16 @@ static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) + u16 data; + int octet; + ++ struct ath5k_platform_data *pdata = NULL; ++ ++ if (ah->pdev) ++ pdata = ah->pdev->dev.platform_data; ++ ++ if (pdata && pdata->macaddr) { ++ memcpy(mac, pdata->macaddr, ETH_ALEN); ++ return 0; ++ } ++ + AR5K_EEPROM_READ(0x20, data); + + for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { +diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c +index 56d7925..baf0b9d 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 ba8f1a0..3d8dfe3 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 63019c3..5ea040a 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 dae9540..8e903d8 100644 +--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c ++++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c +@@ -949,55 +949,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, +@@ -1320,9 +1271,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, +@@ -1337,6 +1309,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 6a35765..6601631 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 60731e0..e78699d 100644 +--- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c ++++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c +@@ -104,6 +104,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); +@@ -119,6 +120,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; + +@@ -138,8 +149,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 a03c7d4..2fd5309 100644 +--- a/drivers/net/wireless/ath/ath9k/channel.c ++++ b/drivers/net/wireless/ath/ath9k/channel.c +@@ -15,6 +15,8 @@ + */ + + #include "ath9k.h" ++#include ++#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 +24,7 @@ + */ + static int ath_set_channel(struct ath_softc *sc) + { ++ struct ath9k_platform_data *pdata = sc->dev->platform_data; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hw *hw = sc->hw; +@@ -42,6 +45,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 (pdata && pdata->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 9c1018a..85f65e0 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 6566400..015fe02 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 +@@ -520,6 +521,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; +@@ -715,6 +722,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 +@@ -737,6 +745,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); +@@ -808,6 +817,8 @@ struct ath_hw { + u32 ah_flags; + s16 nf_override; + ++ unsigned long diag; ++ + bool reset_power_on; + bool htc_reset_init; + +@@ -1073,6 +1084,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 e202d2f..0890e90 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) + { +@@ -752,6 +764,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; +@@ -819,7 +834,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[] = { +@@ -831,6 +847,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 +@@ -1014,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_AIRTIME_FAIRNESS); + } + ++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) + { +@@ -1054,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) +@@ -1142,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 1c81611..17418f9 100644 +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -16,8 +16,10 @@ + + #include + #include ++#include + #include "ath9k.h" + #include "btcoex.h" ++#include "hsr.h" + + u8 ath9k_parse_mpdudensity(u8 mpdudensity) + { +@@ -528,6 +530,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). +@@ -644,6 +651,7 @@ void ath_reset_work(struct work_struct *work) + static int ath9k_start(struct ieee80211_hw *hw) + { + struct ath_softc *sc = hw->priv; ++ struct ath9k_platform_data *pdata = sc->dev->platform_data; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan; +@@ -722,6 +730,11 @@ static int ath9k_start(struct ieee80211_hw *hw) + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + } + ++ if (pdata && pdata->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 caca0ac..119f1ab 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/regd.c b/drivers/net/wireless/ath/regd.c +index ca2392c..e263400 100644 +--- a/drivers/net/wireless/ath/regd.c ++++ b/drivers/net/wireless/ath/regd.c +@@ -24,6 +24,7 @@ + #include "regd_common.h" + + static int __ath_regd_init(struct ath_regulatory *reg); ++static struct reg_dmn_pair_mapping *ath_get_regpair(int regdmn); + + /* + * This is a set of common rules used by our world regulatory domains. +@@ -43,7 +44,8 @@ static int __ath_regd_init(struct ath_regulatory *reg); + NL80211_RRF_NO_OFDM) + + /* We allow IBSS on these on a case by case basis by regulatory domain */ +-#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\ ++#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5240+10, 80, 0, 30, 0),\ ++ REG_RULE(5260-10, 5350+10, 80, 0, 30,\ + NL80211_RRF_NO_IR) + #define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\ + NL80211_RRF_NO_IR) +@@ -61,64 +63,79 @@ static int __ath_regd_init(struct ath_regulatory *reg); + #define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \ + ATH9K_5GHZ_5725_5850 + ++#define REGD_RULES(...) \ ++ .reg_rules = { __VA_ARGS__ }, \ ++ .n_reg_rules = ARRAY_SIZE(((struct ieee80211_reg_rule[]) { __VA_ARGS__ })) ++ + /* Can be used for: + * 0x60, 0x61, 0x62 */ + static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = { +- .n_reg_rules = 5, + .alpha2 = "99", +- .reg_rules = { ++ REGD_RULES( + ATH9K_2GHZ_ALL, + ATH9K_5GHZ_ALL, +- } ++ ) + }; + + /* Can be used by 0x63 and 0x65 */ + static const struct ieee80211_regdomain ath_world_regdom_63_65 = { +- .n_reg_rules = 4, + .alpha2 = "99", +- .reg_rules = { ++ REGD_RULES( + ATH9K_2GHZ_CH01_11, + ATH9K_2GHZ_CH12_13, + ATH9K_5GHZ_NO_MIDBAND, +- } ++ ) + }; + + /* Can be used by 0x64 only */ + static const struct ieee80211_regdomain ath_world_regdom_64 = { +- .n_reg_rules = 3, + .alpha2 = "99", +- .reg_rules = { ++ REGD_RULES( + ATH9K_2GHZ_CH01_11, + ATH9K_5GHZ_NO_MIDBAND, +- } ++ ) + }; + + /* Can be used by 0x66 and 0x69 */ + static const struct ieee80211_regdomain ath_world_regdom_66_69 = { +- .n_reg_rules = 3, + .alpha2 = "99", +- .reg_rules = { ++ REGD_RULES( + ATH9K_2GHZ_CH01_11, + ATH9K_5GHZ_ALL, +- } ++ ) + }; + + /* Can be used by 0x67, 0x68, 0x6A and 0x6C */ + static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = { +- .n_reg_rules = 4, + .alpha2 = "99", +- .reg_rules = { ++ REGD_RULES( + ATH9K_2GHZ_CH01_11, + ATH9K_2GHZ_CH12_13, + ATH9K_5GHZ_ALL, +- } ++ ) + }; + ++static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg) ++{ ++ return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG; ++} ++ ++static bool is_default_regd(struct ath_regulatory *reg) ++{ ++ return ath_regd_get_eepromRD(reg) == CTRY_DEFAULT; ++} ++ + static bool dynamic_country_user_possible(struct ath_regulatory *reg) + { ++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD)) ++ return true; ++ + if (IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_CERT_TESTING)) + return true; + ++ if (is_default_regd(reg)) ++ return true; ++ + switch (reg->country_code) { + case CTRY_UNITED_STATES: + case CTRY_JAPAN1: +@@ -188,6 +205,8 @@ static bool dynamic_country_user_possible(struct ath_regulatory *reg) + + static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg) + { ++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD)) ++ return true; + if (!IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_REG_HINTS)) + return false; + if (!dynamic_country_user_possible(reg)) +@@ -202,11 +221,6 @@ static inline bool is_wwr_sku(u16 regd) + (regd == WORLD)); + } + +-static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg) +-{ +- return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG; +-} +- + bool ath_is_world_regd(struct ath_regulatory *reg) + { + return is_wwr_sku(ath_regd_get_eepromRD(reg)); +@@ -345,6 +359,9 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, + struct ieee80211_channel *ch; + unsigned int i; + ++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD)) ++ return; ++ + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!wiphy->bands[band]) + continue; +@@ -378,6 +395,9 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy, + { + struct ieee80211_supported_band *sband; + ++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD)) ++ return; ++ + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (!sband) + return; +@@ -407,6 +427,9 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy, + struct ieee80211_channel *ch; + unsigned int i; + ++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD)) ++ return; ++ + if (!wiphy->bands[NL80211_BAND_5GHZ]) + return; + +@@ -639,6 +662,13 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, + const struct ieee80211_regdomain *regd; + + wiphy->reg_notifier = reg_notifier; ++ ++ if (IS_ENABLED(CPTCFG_ATH_USER_REGD)) ++ return 0; ++ ++ if (is_default_regd(reg)) ++ return 0; ++ + wiphy->regulatory_flags |= REGULATORY_STRICT_REG | + REGULATORY_CUSTOM_REG; + +diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h +index c4bd26e..364011e 100644 +--- a/drivers/net/wireless/ath/regd_common.h ++++ b/drivers/net/wireless/ath/regd_common.h +@@ -32,6 +32,7 @@ enum EnumRd { + FCC2_WORLD = 0x21, + FCC2_ETSIC = 0x22, + FCC6_WORLD = 0x23, ++ FCC3_FCCA_2 = 0x2A, + FRANCE_RES = 0x31, + FCC3_FCCA = 0x3A, + FCC3_WORLD = 0x3B, +@@ -172,6 +173,7 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = { + {FCC2_WORLD, CTL_FCC, CTL_ETSI}, + {FCC2_ETSIC, CTL_FCC, CTL_ETSI}, + {FCC3_FCCA, CTL_FCC, CTL_FCC}, ++ {FCC3_FCCA_2, CTL_FCC, CTL_FCC}, + {FCC3_WORLD, CTL_FCC, CTL_ETSI}, + {FCC3_ETSIC, CTL_FCC, CTL_ETSI}, + {FCC4_FCCA, CTL_FCC, CTL_FCC}, +@@ -483,6 +485,7 @@ static struct country_code_to_enum_rd allCountries[] = { + {CTRY_UAE, NULL1_WORLD, "AE"}, + {CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"}, + {CTRY_UNITED_STATES, FCC3_FCCA, "US"}, ++ {CTRY_UNITED_STATES, FCC3_FCCA_2, "US"}, + {CTRY_UNITED_STATES2, FCC3_FCCA, "US"}, + {CTRY_UNITED_STATES3, FCC3_FCCA, "US"}, + /* This "PS" is for US public safety actually... to support this we +diff --git a/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..f1f2ad4 100644 +--- a/include/linux/ath9k_platform.h ++++ b/include/linux/ath9k_platform.h +@@ -46,6 +46,15 @@ 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; ++ ++ bool ubnt_hsr; + }; + + #endif /* _LINUX_ATH9K_PLATFORM_H */ +diff --git a/local-symbols b/local-symbols +index e7064fd..24b8388 100644 +--- a/local-symbols ++++ b/local-symbols +@@ -85,6 +85,7 @@ ADM8211= + ATH_COMMON= + WLAN_VENDOR_ATH= + ATH_DEBUG= ++ATH_USER_REGD= + ATH_TRACEPOINTS= + ATH_REG_DYNAMIC_USER_REG_HINTS= + ATH_REG_DYNAMIC_USER_CERT_TESTING= +@@ -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 b001608..a2908b8 100644 +--- a/net/wireless/reg.c ++++ b/net/wireless/reg.c +@@ -3039,6 +3039,8 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band, + enum environment_cap env = ENVIRON_ANY; + struct regulatory_request *request = NULL, *lr; + ++ return; ++ + /* IE len must be evenly divisible by 2 */ + if (country_ie_len & 0x01) + return; +@@ -3290,6 +3292,7 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag) + + void regulatory_hint_disconnect(void) + { ++ return; + /* Restore of regulatory settings is not required when wiphy(s) + * ignore IE from connected access point but clearance of beacon hints + * is required when wiphy(s) supports beacon hints. diff --git a/recipes-kernel/mac80211/mac80211/0004-backport-of-rt2x00-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0004-backport-of-rt2x00-patches-from-openwrt.patch new file mode 100644 index 0000000..bcb8c3a --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0004-backport-of-rt2x00-patches-from-openwrt.patch @@ -0,0 +1,2373 @@ +From 6811ae0fbf7e5bba287284b9d1f7e0ed9edc49e6 Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 27 May 2020 19:20:33 +0200 +Subject: [PATCH] FIX: [mac80211] 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 | 1 + + drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 1649 ++++++++++++++++++++- + drivers/net/wireless/ralink/rt2x00/rt2800lib.h | 21 + + drivers/net/wireless/ralink/rt2x00/rt2800soc.c | 21 +- + drivers/net/wireless/ralink/rt2x00/rt2x00.h | 8 + + drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 59 +- + drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c | 187 +++ + drivers/net/wireless/ralink/rt2x00/rt2x00leds.c | 3 + + drivers/net/wireless/ralink/rt2x00/rt2x00lib.h | 16 + + drivers/net/wireless/ralink/rt2x00/rt2x00soc.c | 1 + + include/linux/rt2x00_platform.h | 23 + + local-symbols | 1 + + 14 files changed, 1990 insertions(+), 24 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 0bf5243..c6a7701 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..dfe2542 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h +@@ -2739,6 +2739,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 dd7de57..3600b9e 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" +@@ -4358,6 +4359,45 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, + 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); ++ } ++ } ++ + bbp = rt2800_bbp_read(rt2x00dev, 4); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); + rt2800_bbp_write(rt2x00dev, 4, bbp); +@@ -8382,6 +8422,1584 @@ 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); ++ ++ 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, 0); ++ 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)) { ++ ph_rx = 0; ++ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); ++ } ++ ++ if ((g_imb > 12) || (g_imb < -12)) { ++ 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) { ++ 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 { ++ 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++) { ++ 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) + { +@@ -8989,8 +10607,13 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev) + rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C); + ++ 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); + } + + static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) +@@ -9531,6 +11154,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 */ + +@@ -9548,7 +11182,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, +@@ -9559,6 +11194,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..5a5391c 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); +@@ -135,6 +147,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); + } + +diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c +index e634621..5958a79 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c +@@ -90,19 +90,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) + { +@@ -166,7 +153,6 @@ 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, +@@ -237,10 +223,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/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h +index b8cf099..87c431d 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #include + +@@ -398,6 +399,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 +@@ -693,6 +695,7 @@ enum rt2x00_capability_flags { + REQUIRE_HT_TX_DESC, + REQUIRE_PS_AUTOWAKE, + REQUIRE_DELAYED_RFKILL, ++ REQUIRE_EEPROM_FILE, + + /* + * Capabilities +@@ -969,6 +972,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. + */ + DECLARE_KFIFO_PTR(txstatus_fifo, u32); +diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +index 77eb630..c477030 100644 +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +@@ -990,8 +990,13 @@ 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; + const char *mac_addr; + ++ pdata = rt2x00dev->dev->platform_data; ++ if (pdata && pdata->mac_address) ++ ether_addr_copy(eeprom_mac_addr, pdata->mac_address); ++ + mac_addr = of_get_mac_address(rt2x00dev->dev->of_node); + if (!IS_ERR(mac_addr)) + ether_addr_copy(eeprom_mac_addr, mac_addr); +@@ -1011,6 +1016,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) +@@ -1098,6 +1129,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; +@@ -1180,6 +1224,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. + */ +@@ -1325,7 +1373,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 +@@ -1418,6 +1466,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. + */ +@@ -1561,6 +1613,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 +@@ -286,6 +286,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. + */ + #ifdef CPTCFG_RT2X00_LIB_DEBUGFS +diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c +index 596b8a4..ec27a91 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); +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 24b8388..d6c55d2 100644 +--- a/local-symbols ++++ b/local-symbols +@@ -313,6 +313,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/0005-backport-of-mwl-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0005-backport-of-mwl-patches-from-openwrt.patch new file mode 100644 index 0000000..8bd3236 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0005-backport-of-mwl-patches-from-openwrt.patch @@ -0,0 +1,75 @@ +From a47e171c23025cb6b2f4afb6eccd59b3cb13c73c Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 27 May 2020 19:45:07 +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/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 2233b59..e0b8e36 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 c4db641..4b7d2cb 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-brcm-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0006-backport-of-brcm-patches-from-openwrt.patch new file mode 100644 index 0000000..abf7c18 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0006-backport-of-brcm-patches-from-openwrt.patch @@ -0,0 +1,1566 @@ +From 17567e991d152bf7ff700c460aed7425918085d4 Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Wed, 27 May 2020 19:45:36 +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 +- + .../wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 71 ++++-- + .../broadcom/brcm80211/brcmfmac/cfg80211.c | 267 ++++++++++++++++++--- + .../wireless/broadcom/brcm80211/brcmfmac/chip.c | 60 ++++- + .../wireless/broadcom/brcm80211/brcmfmac/chip.h | 1 + + .../wireless/broadcom/brcm80211/brcmfmac/common.c | 4 + + .../wireless/broadcom/brcm80211/brcmfmac/core.c | 88 ++++++- + .../wireless/broadcom/brcm80211/brcmfmac/core.h | 10 + + .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 1 + + .../wireless/broadcom/brcm80211/brcmfmac/feature.h | 2 + + .../broadcom/brcm80211/brcmfmac/firmware.c | 14 ++ + .../wireless/broadcom/brcm80211/brcmfmac/fwil.h | 2 + + .../broadcom/brcm80211/brcmfmac/fwsignal.c | 2 +- + .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 2 +- + .../net/wireless/broadcom/brcm80211/brcmfmac/of.c | 32 +++ + .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 2 +- + .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 4 + + .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 17 ++ + .../wireless/broadcom/brcm80211/brcmfmac/sdio.h | 1 - + .../wireless/broadcom/brcm80211/brcmsmac/channel.c | 19 +- + include/linux/mmc/sdio_ids.h | 2 + + 27 files changed, 641 insertions(+), 87 deletions(-) + +diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig +index b8f1262..428f40e 100644 +--- a/drivers/net/wireless/broadcom/b43/Kconfig ++++ b/drivers/net/wireless/broadcom/b43/Kconfig +@@ -100,7 +100,7 @@ config B43_BCMA_PIO + default y + + config B43_PIO +- bool ++ bool "Broadcom 43xx PIO support" + depends on B43 && B43_SSB + depends on SSB_BLOCKIO + default y +diff --git a/drivers/net/wireless/broadcom/b43/Makefile b/drivers/net/wireless/broadcom/b43/Makefile +index f20b692..48589b5 100644 +--- a/drivers/net/wireless/broadcom/b43/Makefile ++++ b/drivers/net/wireless/broadcom/b43/Makefile +@@ -18,7 +18,7 @@ b43-$(CPTCFG_B43_PHY_AC) += phy_ac.o + b43-y += sysfs.o + b43-y += xmit.o + b43-y += dma.o +-b43-y += pio.o ++b43-$(CPTCFG_B43_PIO) += pio.o + b43-y += rfkill.o + b43-y += ppr.o + b43-$(CPTCFG_B43_LEDS) += leds.o +diff --git a/drivers/net/wireless/broadcom/b43/b43.h b/drivers/net/wireless/broadcom/b43/b43.h +index 872ebd7..745736c 100644 +--- a/drivers/net/wireless/broadcom/b43/b43.h ++++ b/drivers/net/wireless/broadcom/b43/b43.h +@@ -840,6 +840,9 @@ struct b43_wldev { + bool qos_enabled; /* TRUE, if QoS is used. */ + bool hwcrypto_enabled; /* TRUE, if HW crypto acceleration is enabled. */ + bool use_pio; /* TRUE if next init should use PIO */ ++ int gpiomask; /* GPIO LED mask as a module parameter */ ++ int rx_antenna; /* Used RX antenna (B43_ANTENNAxxx) */ ++ int tx_antenna; /* Used TX antenna (B43_ANTENNAxxx) */ + + /* PHY/Radio device. */ + struct b43_phy phy; +diff --git a/drivers/net/wireless/broadcom/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h +index dfebc64..ec72414 100644 +--- a/drivers/net/wireless/broadcom/b43/dma.h ++++ b/drivers/net/wireless/broadcom/b43/dma.h +@@ -170,7 +170,7 @@ struct b43_dmadesc_generic { + + /* DMA engine tuning knobs */ + #define B43_TXRING_SLOTS 256 +-#define B43_RXRING_SLOTS 256 ++#define B43_RXRING_SLOTS 32 + #define B43_DMA0_RX_FW598_BUFSIZE (B43_DMA0_RX_FW598_FO + IEEE80211_MAX_FRAME_LEN) + #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 99d0b4a..df37945 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"); + MODULE_FIRMWARE("b43/ucode42.fw"); + MODULE_FIRMWARE("b43/ucode9.fw"); + ++static int modparam_gpiomask = 0x000F; ++module_param_named(gpiomask, modparam_gpiomask, int, 0444); ++MODULE_PARM_DESC(gpiomask, ++ "GPIO mask for LED control (default 0x000F)"); ++ + static int modparam_bad_frames_preempt; + module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); + MODULE_PARM_DESC(bad_frames_preempt, +@@ -109,7 +114,7 @@ static int b43_modparam_pio = 0; + module_param_named(pio, b43_modparam_pio, int, 0644); + MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO"); + +-static int modparam_allhwsupport = !IS_ENABLED(CPTCFG_BRCMSMAC); ++static int modparam_allhwsupport = 1; + module_param_named(allhwsupport, modparam_allhwsupport, int, 0444); + MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the brcmsmac driver)"); + +@@ -1637,7 +1642,7 @@ static void b43_write_beacon_template(struct b43_wldev *dev, + len, ram_offset, shm_size_offset, rate); + + /* Write the PHY TX control parameters. */ +- antenna = B43_ANTENNA_DEFAULT; ++ antenna = dev->tx_antenna; + antenna = b43_antenna_to_phyctl(antenna); + ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL); + /* We can't send beacons with short preamble. Would get PHY errors. */ +@@ -1995,10 +2000,12 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); ++#ifdef CPTCFG_B43_PIO + b43err(dev->wl, "This device does not support DMA " + "on your system. It will now be switched to PIO.\n"); + /* Fall back to PIO transfers if we get fatal DMA errors! */ + dev->use_pio = true; ++#endif + b43_controller_restart(dev, "DMA error"); + return; + } +@@ -2867,16 +2874,24 @@ static int b43_gpio_init(struct b43_wldev *dev) + u32 mask, set; + + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); +- b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xF); ++ b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, modparam_gpiomask); + + mask = 0x0000001F; +- set = 0x0000000F; ++ set = modparam_gpiomask; + if (dev->dev->chip_id == 0x4301) { + mask |= 0x0060; + set |= 0x0060; + } else if (dev->dev->chip_id == 0x5354) { + /* Don't allow overtaking buttons GPIOs */ + set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */ ++ } else if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || ++ dev->dev->chip_id == BCMA_CHIP_ID_BCM47162 || ++ dev->dev->chip_id == BCMA_CHIP_ID_BCM5356 || ++ dev->dev->chip_id == BCMA_CHIP_ID_BCM5357 || ++ dev->dev->chip_id == BCMA_CHIP_ID_BCM53572) { ++ /* just use gpio 0 and 1 for 2.4 GHz wifi led */ ++ set &= 0x3; ++ mask &= 0x3; + } + + if (0 /* FIXME: conditional unknown */ ) { +@@ -3275,8 +3290,8 @@ static int b43_chip_init(struct b43_wldev *dev) + + /* Select the antennae */ + if (phy->ops->set_rx_antenna) +- phy->ops->set_rx_antenna(dev, B43_ANTENNA_DEFAULT); +- b43_mgmtframe_txantenna(dev, B43_ANTENNA_DEFAULT); ++ phy->ops->set_rx_antenna(dev, dev->rx_antenna); ++ b43_mgmtframe_txantenna(dev, dev->tx_antenna); + + if (phy->type == B43_PHYTYPE_B) { + value16 = b43_read16(dev, 0x005E); +@@ -3976,7 +3991,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) + struct b43_wldev *dev = wl->current_dev; + struct b43_phy *phy = &dev->phy; + struct ieee80211_conf *conf = &hw->conf; +- int antenna; + int err = 0; + + mutex_lock(&wl->mutex); +@@ -4019,11 +4033,9 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) + } + + /* Antennas for RX and management frame TX. */ +- antenna = B43_ANTENNA_DEFAULT; +- b43_mgmtframe_txantenna(dev, antenna); +- antenna = B43_ANTENNA_DEFAULT; ++ b43_mgmtframe_txantenna(dev, dev->tx_antenna); + if (phy->ops->set_rx_antenna) +- phy->ops->set_rx_antenna(dev, antenna); ++ phy->ops->set_rx_antenna(dev, dev->rx_antenna); + + if (wl->radio_enabled != phy->radio_on) { + if (wl->radio_enabled) { +@@ -5167,6 +5179,47 @@ static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, + return 0; + } + ++static int b43_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) ++{ ++ struct b43_wl *wl = hw_to_b43_wl(hw); ++ struct b43_wldev *dev = wl->current_dev; ++ ++ if (tx_ant == 1 && rx_ant == 1) { ++ dev->tx_antenna = B43_ANTENNA0; ++ dev->rx_antenna = B43_ANTENNA0; ++ } ++ else if (tx_ant == 2 && rx_ant == 2) { ++ dev->tx_antenna = B43_ANTENNA1; ++ dev->rx_antenna = B43_ANTENNA1; ++ } ++ else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3) { ++ dev->tx_antenna = B43_ANTENNA_DEFAULT; ++ dev->rx_antenna = B43_ANTENNA_DEFAULT; ++ } ++ else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++ ++static int b43_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) ++{ ++ struct b43_wl *wl = hw_to_b43_wl(hw); ++ struct b43_wldev *dev = wl->current_dev; ++ ++ switch (dev->tx_antenna) { ++ case B43_ANTENNA0: ++ *tx_ant = 1; *rx_ant = 1; break; ++ case B43_ANTENNA1: ++ *tx_ant = 2; *rx_ant = 2; break; ++ case B43_ANTENNA_DEFAULT: ++ *tx_ant = 3; *rx_ant = 3; break; ++ } ++ return 0; ++} ++ + static const struct ieee80211_ops b43_hw_ops = { + .tx = b43_op_tx, + .conf_tx = b43_op_conf_tx, +@@ -5188,6 +5241,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, ++ .set_antenna = b43_op_set_antenna, ++ .get_antenna = b43_op_get_antenna, + }; + + /* Hard-reset the chip. Do not call this directly. +@@ -5489,6 +5544,8 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) + if (!wldev) + goto out; + ++ wldev->rx_antenna = B43_ANTENNA_DEFAULT; ++ wldev->tx_antenna = B43_ANTENNA_DEFAULT; + wldev->use_pio = b43_modparam_pio; + wldev->dev = dev; + wldev->wl = wl; +@@ -5583,6 +5640,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); + ++ hw->wiphy->available_antennas_rx = 0x3; ++ hw->wiphy->available_antennas_tx = 0x3; ++ + wl->hw_registered = false; + hw->max_rates = 2; + SET_IEEE80211_DEV(hw, dev->dev); +diff --git a/drivers/net/wireless/broadcom/b43/pio.h b/drivers/net/wireless/broadcom/b43/pio.h +index ffbfec6..a3742b7 100644 +--- a/drivers/net/wireless/broadcom/b43/pio.h ++++ b/drivers/net/wireless/broadcom/b43/pio.h +@@ -151,7 +151,7 @@ static inline void b43_piorx_write32(struct b43_pio_rxqueue *q, + b43_write32(q->dev, q->mmio_base + offset, value); + } + +- ++#ifdef CPTCFG_B43_PIO + int b43_pio_init(struct b43_wldev *dev); + void b43_pio_free(struct b43_wldev *dev); + +@@ -162,5 +162,37 @@ void b43_pio_rx(struct b43_pio_rxqueue *q); + + void b43_pio_tx_suspend(struct b43_wldev *dev); + void b43_pio_tx_resume(struct b43_wldev *dev); ++#else ++static inline int b43_pio_init(struct b43_wldev *dev) ++{ ++ return 0; ++} ++ ++static inline void b43_pio_free(struct b43_wldev *dev) ++{ ++} ++ ++static inline int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb) ++{ ++ return 0; ++} ++ ++static inline void b43_pio_handle_txstatus(struct b43_wldev *dev, ++ const struct b43_txstatus *status) ++{ ++} ++ ++static inline void b43_pio_rx(struct b43_pio_rxqueue *q) ++{ ++} ++ ++static inline void b43_pio_tx_suspend(struct b43_wldev *dev) ++{ ++} ++ ++static inline void b43_pio_tx_resume(struct b43_wldev *dev) ++{ ++} ++#endif /* CPTCFG_B43_PIO */ + + #endif /* B43_PIO_H_ */ +diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig +index 2c426b5..57e1ffc 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/Kconfig ++++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + config BRCMUTIL +- tristate ++ tristate "Broadcom 802.11 driver utility functions" + depends on m + + config BRCMSMAC +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +index 58fffb9..2add956 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +@@ -43,6 +43,7 @@ + + #define SDIO_FUNC1_BLOCKSIZE 64 + #define SDIO_FUNC2_BLOCKSIZE 512 ++#define SDIO_4359_FUNC2_BLOCKSIZE 256 + /* Maximum milliseconds to wait for F2 to come up */ + #define SDIO_WAIT_F2RDY 3000 + +@@ -119,7 +120,7 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) + brcmf_err("enable_irq_wake failed %d\n", ret); + return ret; + } +- sdiodev->irq_wake = true; ++ disable_irq_wake(pdata->oob_irq_nr); + + sdio_claim_host(sdiodev->func1); + +@@ -178,10 +179,6 @@ void brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) + sdio_release_host(sdiodev->func1); + + sdiodev->oob_irq_requested = false; +- if (sdiodev->irq_wake) { +- disable_irq_wake(pdata->oob_irq_nr); +- sdiodev->irq_wake = false; +- } + free_irq(pdata->oob_irq_nr, &sdiodev->func1->dev); + sdiodev->irq_en = false; + sdiodev->oob_irq_requested = false; +@@ -903,6 +900,7 @@ static void brcmf_sdiod_host_fixup(struct mmc_host *host) + static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) + { + int ret = 0; ++ unsigned int f2_blksz = SDIO_FUNC2_BLOCKSIZE; + + sdio_claim_host(sdiodev->func1); + +@@ -912,7 +910,9 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) + sdio_release_host(sdiodev->func1); + goto out; + } +- ret = sdio_set_block_size(sdiodev->func2, SDIO_FUNC2_BLOCKSIZE); ++ if (sdiodev->func2->device == SDIO_DEVICE_ID_BROADCOM_4359) ++ f2_blksz = SDIO_4359_FUNC2_BLOCKSIZE; ++ ret = sdio_set_block_size(sdiodev->func2, f2_blksz); + if (ret) { + brcmf_err("Failed to set F2 blocksize\n"); + sdio_release_host(sdiodev->func1); +@@ -969,8 +969,10 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356), ++ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012), ++ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_89359), + { /* end: all zeroes */ } + }; + MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); +@@ -1108,7 +1110,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev) + struct sdio_func *func; + struct brcmf_bus *bus_if; + struct brcmf_sdio_dev *sdiodev; +- mmc_pm_flag_t sdio_flags; ++ mmc_pm_flag_t pm_caps, sdio_flags; ++ int ret = 0; + + func = container_of(dev, struct sdio_func, dev); + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); +@@ -1119,19 +1122,33 @@ static int brcmf_ops_sdio_suspend(struct device *dev) + bus_if = dev_get_drvdata(dev); + sdiodev = bus_if->bus_priv.sdio; + +- brcmf_sdiod_freezer_on(sdiodev); +- brcmf_sdio_wd_timer(sdiodev->bus, 0); ++ pm_caps = sdio_get_host_pm_caps(func); ++ ++ if (pm_caps & MMC_PM_KEEP_POWER) { ++ /* preserve card power during suspend */ ++ brcmf_sdiod_freezer_on(sdiodev); ++ brcmf_sdio_wd_timer(sdiodev->bus, 0); ++ ++ sdio_flags = MMC_PM_KEEP_POWER; ++ if (sdiodev->wowl_enabled) { ++ if (sdiodev->settings->bus.sdio.oob_irq_supported) ++ enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); ++ else ++ sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; ++ } + +- sdio_flags = MMC_PM_KEEP_POWER; +- if (sdiodev->wowl_enabled) { +- if (sdiodev->settings->bus.sdio.oob_irq_supported) +- enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); +- else +- sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; ++ if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags)) ++ brcmf_err("Failed to set pm_flags %x\n", sdio_flags); ++ ++ } else { ++ /* power will be cut so remove device, probe again in resume */ ++ brcmf_sdiod_intr_unregister(sdiodev); ++ ret = brcmf_sdiod_remove(sdiodev); ++ if (ret) ++ brcmf_err("Failed to remove device on suspend\n"); + } +- if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags)) +- brcmf_err("Failed to set pm_flags %x\n", sdio_flags); +- return 0; ++ ++ return ret; + } + + static int brcmf_ops_sdio_resume(struct device *dev) +@@ -1139,13 +1156,27 @@ static int brcmf_ops_sdio_resume(struct device *dev) + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct sdio_func *func = container_of(dev, struct sdio_func, dev); ++ mmc_pm_flag_t pm_caps = sdio_get_host_pm_caps(func); ++ int ret = 0; + + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + if (func->num != 2) + return 0; + +- brcmf_sdiod_freezer_off(sdiodev); +- return 0; ++ if (!(pm_caps & MMC_PM_KEEP_POWER)) { ++ /* bus was powered off and device removed, probe again */ ++ ret = brcmf_sdiod_probe(sdiodev); ++ if (ret) ++ brcmf_err("Failed to probe device on resume\n"); ++ } else { ++ if (sdiodev->wowl_enabled && ++ sdiodev->settings->bus.sdio.oob_irq_supported) ++ disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); ++ ++ brcmf_sdiod_freezer_off(sdiodev); ++ } ++ ++ return ret; + } + + static const struct dev_pm_ops brcmf_sdio_pm_ops = { +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +index e3ebb7a..4412a63 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -619,6 +620,82 @@ static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) + return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; + } + ++/** ++ * brcmf_mon_add_vif() - create monitor mode virtual interface ++ * ++ * @wiphy: wiphy device of new interface. ++ * @name: name of the new interface. ++ */ ++static struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy, ++ const char *name) ++{ ++ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); ++ struct brcmf_cfg80211_vif *vif; ++ struct net_device *ndev; ++ struct brcmf_if *ifp; ++ int err; ++ ++ if (cfg->pub->mon_if) { ++ err = -EEXIST; ++ goto err_out; ++ } ++ ++ vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_MONITOR); ++ if (IS_ERR(vif)) { ++ err = PTR_ERR(vif); ++ goto err_out; ++ } ++ ++ ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup); ++ if (!ndev) { ++ err = -ENOMEM; ++ goto err_free_vif; ++ } ++ ndev->type = ARPHRD_IEEE80211_RADIOTAP; ++ ndev->ieee80211_ptr = &vif->wdev; ++ ndev->needs_free_netdev = true; ++ ndev->priv_destructor = brcmf_cfg80211_free_netdev; ++ SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); ++ ++ ifp = netdev_priv(ndev); ++ ifp->vif = vif; ++ ifp->ndev = ndev; ++ ifp->drvr = cfg->pub; ++ ++ vif->ifp = ifp; ++ vif->wdev.netdev = ndev; ++ ++ err = brcmf_net_mon_attach(ifp); ++ if (err) { ++ brcmf_err("Failed to attach %s device\n", ndev->name); ++ free_netdev(ndev); ++ goto err_free_vif; ++ } ++ ++ cfg->pub->mon_if = ifp; ++ ++ return &vif->wdev; ++ ++err_free_vif: ++ brcmf_free_vif(vif); ++err_out: ++ return ERR_PTR(err); ++} ++ ++static int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) ++{ ++ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); ++ struct net_device *ndev = wdev->netdev; ++ ++ ndev->netdev_ops->ndo_stop(ndev); ++ ++ brcmf_net_detach(ndev, true); ++ ++ cfg->pub->mon_if = NULL; ++ ++ return 0; ++} ++ + static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, +@@ -628,8 +705,36 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_pub *drvr = cfg->pub; + struct wireless_dev *wdev; ++ struct net_device *dev; + int err; + ++ /* ++ * There is a bug with in-firmware BSS management. When adding virtual ++ * interface brcmfmac first tells firmware to create new BSS and then ++ * it creates new struct net_device. ++ * ++ * If creating/registering netdev(ice) fails, BSS remains in some bugged ++ * state. It conflicts with existing BSSes by overtaking their auth ++ * requests. ++ * ++ * It results in one BSS (addresss X) sending beacons and another BSS ++ * (address Y) replying to authentication requests. This makes interface ++ * unusable as AP. ++ * ++ * To workaround this bug we may try to guess if register_netdev(ice) ++ * will fail. The most obvious case is using interface name that already ++ * exists. This is actually quite likely with brcmfmac & some user space ++ * scripts as brcmfmac doesn't allow deleting virtual interfaces. ++ * So this bug can be triggered even by something trivial like: ++ * iw dev wlan0 delete ++ * iw phy phy0 interface add wlan0 type __ap ++ */ ++ dev = dev_get_by_name(&init_net, name); ++ if (dev) { ++ dev_put(dev); ++ return ERR_PTR(-ENFILE); ++ } ++ + brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); + err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type); + if (err) { +@@ -641,9 +746,10 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: +- case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + return ERR_PTR(-EOPNOTSUPP); ++ case NL80211_IFTYPE_MONITOR: ++ return brcmf_mon_add_vif(wiphy, name); + case NL80211_IFTYPE_AP: + wdev = brcmf_ap_add_vif(wiphy, name, params); + break; +@@ -826,9 +932,10 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: +- case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + return -EOPNOTSUPP; ++ case NL80211_IFTYPE_MONITOR: ++ return brcmf_mon_del_vif(wiphy, wdev); + case NL80211_IFTYPE_AP: + return brcmf_cfg80211_del_ap_iface(wiphy, wdev); + case NL80211_IFTYPE_P2P_CLIENT: +@@ -2719,6 +2826,63 @@ done: + } + + static int ++brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, ++ int idx, struct survey_info *survey) ++{ ++ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); ++ struct brcmf_if *ifp = netdev_priv(ndev); ++ struct brcmu_chan ch; ++ enum nl80211_band band = 0; ++ s32 err = 0; ++ int noise; ++ u32 freq; ++ u32 chanspec; ++ ++ memset(survey, 0, sizeof(struct survey_info)); ++ if (idx != 0) { ++ if (idx >= cfg->pub->num_chan_stats || cfg->pub->chan_stats == NULL) ++ return -ENOENT; ++ if (cfg->pub->chan_stats[idx].freq == 0) ++ return -ENOENT; ++ survey->filled = SURVEY_INFO_NOISE_DBM; ++ survey->channel = ieee80211_get_channel(wiphy, cfg->pub->chan_stats[idx].freq); ++ survey->noise = cfg->pub->chan_stats[idx].noise; ++ return 0; ++ } ++ ++ err = brcmf_fil_iovar_int_get(ifp, "chanspec", &chanspec); ++ if (err) { ++ brcmf_err("chanspec failed (%d)\n", err); ++ return err; ++ } ++ ++ ch.chspec = chanspec; ++ cfg->d11inf.decchspec(&ch); ++ ++ switch (ch.band) { ++ case BRCMU_CHAN_BAND_2G: ++ band = NL80211_BAND_2GHZ; ++ break; ++ case BRCMU_CHAN_BAND_5G: ++ band = NL80211_BAND_5GHZ; ++ break; ++ } ++ ++ freq = ieee80211_channel_to_frequency(ch.control_ch_num, band); ++ survey->channel = ieee80211_get_channel(wiphy, freq); ++ ++ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); ++ if (err) { ++ brcmf_err("Could not get noise (%d)\n", err); ++ return err; ++ } ++ ++ survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_IN_USE; ++ survey->noise = le32_to_cpu(noise); ++ return 0; ++} ++ ++static int + brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev, + int idx, u8 *mac, struct station_info *sinfo) + { +@@ -2767,6 +2931,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 + */ ++#if defined(CONFIG_ARCH_BCM2835) ++ brcmf_dbg(INFO, "power management disabled\n"); ++ enabled = false; ++#endif + cfg->pwr_save = enabled; + if (!check_vif_up(ifp->vif)) { + +@@ -2804,6 +2972,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, + struct brcmu_chan ch; + u16 channel; + u32 freq; ++ int i; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; +@@ -2828,6 +2997,17 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, + band = NL80211_BAND_5GHZ; + + freq = ieee80211_channel_to_frequency(channel, band); ++ for (i = 0;i < cfg->pub->num_chan_stats;i++) { ++ if (freq == cfg->pub->chan_stats[i].freq) ++ break; ++ if (cfg->pub->chan_stats[i].freq == 0) ++ break; ++ } ++ if (i < cfg->pub->num_chan_stats) { ++ cfg->pub->chan_stats[i].freq = freq; ++ cfg->pub->chan_stats[i].noise = bi->phy_noise; ++ } ++ + 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()); +@@ -5245,6 +5425,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, ++ .dump_survey = brcmf_cfg80211_dump_survey, + .set_tx_power = brcmf_cfg80211_set_tx_power, + .get_tx_power = brcmf_cfg80211_get_tx_power, + .add_key = brcmf_cfg80211_add_key, +@@ -5301,6 +5482,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, + struct brcmf_cfg80211_vif *vif_walk; + struct brcmf_cfg80211_vif *vif; + bool mbss; ++ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); + + brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", + sizeof(*vif)); +@@ -5313,7 +5495,8 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, + + brcmf_init_prof(&vif->profile); + +- if (type == NL80211_IFTYPE_AP) { ++ if (type == NL80211_IFTYPE_AP && ++ brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) { + mbss = false; + list_for_each_entry(vif_walk, &cfg->vif_list, list) { + if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) { +@@ -5944,19 +6127,17 @@ static s32 brcmf_dongle_roam(struct brcmf_if *ifp) + roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER, + (void *)roamtrigger, sizeof(roamtrigger)); +- if (err) { ++ if (err) + bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err); +- goto roam_setup_done; +- } + + roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); + roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA, + (void *)roam_delta, sizeof(roam_delta)); +- if (err) { ++ if (err) + bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err); +- goto roam_setup_done; +- } ++ ++ return 0; + + roam_setup_done: + return err; +@@ -6454,6 +6635,9 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { + * #STA <= 1, #AP <= 1, channels = 1, 2 total + * #AP <= 4, matching BI, channels = 1, 4 total + * ++ * no p2p and rsdb: ++ * #STA <= 2, #AP <= 2, channels = 2, 4 total ++ * + * p2p, no mchan, and mbss: + * + * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total +@@ -6465,6 +6649,10 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { + * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total + * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total + * #AP <= 4, matching BI, channels = 1, 4 total ++ * ++ * p2p, rsdb, and no mbss: ++ * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2, ++ * channels = 2, 4 total + */ + static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) + { +@@ -6472,13 +6660,16 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) + struct ieee80211_iface_limit *c0_limits = NULL; + struct ieee80211_iface_limit *p2p_limits = NULL; + struct ieee80211_iface_limit *mbss_limits = NULL; +- bool mbss, p2p; +- int i, c, n_combos; ++ bool mon_flag, mbss, p2p, rsdb, mchan; ++ int i, c, n_combos, n_limits; + ++ mon_flag = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FLAG); + mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS); + p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P); ++ rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB); ++ mchan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN); + +- n_combos = 1 + !!p2p + !!mbss; ++ n_combos = 1 + !!(p2p && !rsdb) + !!mbss; + combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL); + if (!combo) + goto err; +@@ -6486,37 +6677,53 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); ++ if (mon_flag) ++ wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); ++ if (p2p) ++ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | ++ BIT(NL80211_IFTYPE_P2P_GO) | ++ BIT(NL80211_IFTYPE_P2P_DEVICE); + + c = 0; + i = 0; +- c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL); ++ n_limits = 1 + mon_flag + (p2p ? 2 : 0) + (rsdb || !p2p); ++ c0_limits = kcalloc(n_limits, sizeof(*c0_limits), GFP_KERNEL); + if (!c0_limits) + goto err; +- c0_limits[i].max = 1; ++ ++ combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan)); ++ c0_limits[i].max = 1 + rsdb; + c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); ++ if (mon_flag) { ++ c0_limits[i].max = 1; ++ c0_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR); ++ } + if (p2p) { +- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) +- combo[c].num_different_channels = 2; +- else +- combo[c].num_different_channels = 1; +- wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | +- BIT(NL80211_IFTYPE_P2P_GO) | +- BIT(NL80211_IFTYPE_P2P_DEVICE); + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); +- c0_limits[i].max = 1; ++ c0_limits[i].max = 1 + rsdb; + c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); ++ } ++ if (p2p && rsdb) { ++ c0_limits[i].max = 2; ++ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); ++ combo[c].max_interfaces = 5; ++ } else if (p2p) { ++ combo[c].max_interfaces = i; ++ } else if (rsdb) { ++ c0_limits[i].max = 2; ++ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); ++ combo[c].max_interfaces = 3; + } else { +- combo[c].num_different_channels = 1; + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); ++ combo[c].max_interfaces = i; + } +- combo[c].max_interfaces = i; + combo[c].n_limits = i; + combo[c].limits = c0_limits; + +- if (p2p) { ++ if (p2p && !rsdb) { + c++; + i = 0; + p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL); +@@ -6539,14 +6746,20 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) + if (mbss) { + c++; + i = 0; +- mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL); ++ n_limits = 1 + mon_flag; ++ mbss_limits = kcalloc(n_limits, sizeof(*mbss_limits), ++ GFP_KERNEL); + if (!mbss_limits) + goto err; + mbss_limits[i].max = 4; + mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP); ++ if (mon_flag) { ++ mbss_limits[i].max = 1; ++ mbss_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR); ++ } + combo[c].beacon_int_infra_match = true; + combo[c].num_different_channels = 1; +- combo[c].max_interfaces = 4; ++ combo[c].max_interfaces = 4 + mon_flag; + combo[c].n_limits = i; + combo[c].limits = mbss_limits; + } +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +index dd586a9..282d0bc 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +@@ -433,11 +433,25 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, + { + struct brcmf_chip_priv *ci; + int count; ++ struct brcmf_core *d11core2 = NULL; ++ struct brcmf_core_priv *d11priv2 = NULL; + + ci = core->chip; + ++ /* special handle two D11 cores reset */ ++ if (core->pub.id == BCMA_CORE_80211) { ++ d11core2 = brcmf_chip_get_d11core(&ci->pub, 1); ++ if (d11core2) { ++ brcmf_dbg(INFO, "found two d11 cores, reset both\n"); ++ d11priv2 = container_of(d11core2, ++ struct brcmf_core_priv, pub); ++ } ++ } ++ + /* must disable first to work for arbitrary current core state */ + brcmf_chip_ai_coredisable(core, prereset, reset); ++ if (d11priv2) ++ brcmf_chip_ai_coredisable(d11priv2, prereset, reset); + + count = 0; + while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) & +@@ -449,9 +463,30 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, + usleep_range(40, 60); + } + ++ if (d11priv2) { ++ count = 0; ++ while (ci->ops->read32(ci->ctx, ++ d11priv2->wrapbase + BCMA_RESET_CTL) & ++ BCMA_RESET_CTL_RESET) { ++ ci->ops->write32(ci->ctx, ++ d11priv2->wrapbase + BCMA_RESET_CTL, ++ 0); ++ count++; ++ if (count > 50) ++ break; ++ usleep_range(40, 60); ++ } ++ } ++ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + postreset | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); ++ ++ if (d11priv2) { ++ ci->ops->write32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL, ++ postreset | BCMA_IOCTL_CLK); ++ ci->ops->read32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL); ++ } + } + + char *brcmf_chip_name(u32 id, u32 rev, char *buf, uint len) +@@ -677,7 +712,6 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: + case BRCM_CC_4358_CHIP_ID: +- case BRCM_CC_4359_CHIP_ID: + case BRCM_CC_43602_CHIP_ID: + case BRCM_CC_4371_CHIP_ID: + return 0x180000; +@@ -687,6 +721,8 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) + case BRCM_CC_4366_CHIP_ID: + case BRCM_CC_43664_CHIP_ID: + return 0x200000; ++ case BRCM_CC_4359_CHIP_ID: ++ return (ci->pub.chiprev < 9) ? 0x180000 : 0x160000; + case CY_CC_4373_CHIP_ID: + return 0x160000; + default: +@@ -778,7 +814,6 @@ static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, + { + u8 desc; + u32 val, szdesc; +- u8 mpnum = 0; + u8 stype, sztype, wraptype; + + *regbase = 0; +@@ -786,7 +821,6 @@ static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, + + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + if (desc == DMP_DESC_MASTER_PORT) { +- mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S; + wraptype = DMP_SLAVE_TYPE_MWRAP; + } else if (desc == DMP_DESC_ADDRESS) { + /* revert erom address */ +@@ -854,7 +888,7 @@ int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) + u8 desc_type = 0; + u32 val; + u16 id; +- u8 nmp, nsp, nmw, nsw, rev; ++ u8 nmw, nsw, rev; + u32 base, wrap; + int err; + +@@ -880,8 +914,6 @@ int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) + return -EFAULT; + + /* only look at cores with master port(s) */ +- nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S; +- nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S; + nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S; + nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S; + rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; +@@ -1113,6 +1145,21 @@ void brcmf_chip_detach(struct brcmf_chip *pub) + kfree(chip); + } + ++struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit) ++{ ++ struct brcmf_chip_priv *chip; ++ struct brcmf_core_priv *core; ++ ++ chip = container_of(pub, struct brcmf_chip_priv, pub); ++ list_for_each_entry(core, &chip->cores, list) { ++ if (core->pub.id == BCMA_CORE_80211) { ++ if (unit-- == 0) ++ return &core->pub; ++ } ++ } ++ return NULL; ++} ++ + struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid) + { + struct brcmf_chip_priv *chip; +@@ -1361,6 +1408,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) + addr = CORE_CC_REG(base, sr_control0); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & CC_SR_CTL0_ENABLE_MASK) != 0; ++ case BRCM_CC_4359_CHIP_ID: + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(pmu->base, retention_ctl); + reg = chip->ops->read32(chip->ctx, addr); +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h +index 7b00f6a..8fa3865 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h +@@ -74,6 +74,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops); + void brcmf_chip_detach(struct brcmf_chip *chip); + struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid); ++struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit); + struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip); + struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub); + bool brcmf_chip_iscoreup(struct brcmf_core *core); +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +index 6d3c9ad..1d9c6f1 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; + module_param_named(fcmode, brcmf_fcmode, int, 0); + MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control"); + ++#if defined(CONFIG_ARCH_BCM2835) ++static int brcmf_roamoff = 1; ++#else + static int brcmf_roamoff; ++#endif + module_param_named(roamoff, brcmf_roamoff, int, 0400); + MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +index e688fd2..498f993 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +@@ -678,6 +678,8 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) + goto fail; + } + ++ netif_carrier_off(ndev); ++ + netdev_set_priv_destructor(ndev, brcmf_cfg80211_free_netdev); + brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); + return 0; +@@ -688,7 +690,7 @@ fail: + return -EBADE; + } + +-static void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked) ++void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked) + { + if (ndev->reg_state == NETREG_REGISTERED) { + if (rtnl_locked) +@@ -701,6 +703,81 @@ static void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked) + } + } + ++static int brcmf_net_mon_open(struct net_device *ndev) ++{ ++ struct brcmf_if *ifp = netdev_priv(ndev); ++ struct brcmf_pub *drvr = ifp->drvr; ++ u32 monitor; ++ int err; ++ ++ brcmf_dbg(TRACE, "Enter\n"); ++ ++ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_MONITOR, &monitor); ++ if (err) { ++ bphy_err(drvr, "BRCMF_C_GET_MONITOR error (%d)\n", err); ++ return err; ++ } else if (monitor) { ++ bphy_err(drvr, "Monitor mode is already enabled\n"); ++ return -EEXIST; ++ } ++ ++ monitor = 3; ++ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_MONITOR, monitor); ++ if (err) ++ bphy_err(drvr, "BRCMF_C_SET_MONITOR error (%d)\n", err); ++ ++ return err; ++} ++ ++static int brcmf_net_mon_stop(struct net_device *ndev) ++{ ++ struct brcmf_if *ifp = netdev_priv(ndev); ++ struct brcmf_pub *drvr = ifp->drvr; ++ u32 monitor; ++ int err; ++ ++ brcmf_dbg(TRACE, "Enter\n"); ++ ++ monitor = 0; ++ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_MONITOR, monitor); ++ if (err) ++ bphy_err(drvr, "BRCMF_C_SET_MONITOR error (%d)\n", err); ++ ++ return err; ++} ++ ++static netdev_tx_t brcmf_net_mon_start_xmit(struct sk_buff *skb, ++ struct net_device *ndev) ++{ ++ dev_kfree_skb_any(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++static const struct net_device_ops brcmf_netdev_ops_mon = { ++ .ndo_open = brcmf_net_mon_open, ++ .ndo_stop = brcmf_net_mon_stop, ++ .ndo_start_xmit = brcmf_net_mon_start_xmit, ++}; ++ ++int brcmf_net_mon_attach(struct brcmf_if *ifp) ++{ ++ struct brcmf_pub *drvr = ifp->drvr; ++ struct net_device *ndev; ++ int err; ++ ++ brcmf_dbg(TRACE, "Enter\n"); ++ ++ ndev = ifp->ndev; ++ ndev->netdev_ops = &brcmf_netdev_ops_mon; ++ ++ err = register_netdevice(ndev); ++ if (err) ++ bphy_err(drvr, "Failed to register %s device\n", ndev->name); ++ ++ return err; ++} ++ + void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on) + { + struct net_device *ndev; +@@ -1275,6 +1352,8 @@ int brcmf_attach(struct device *dev) + + /* Link to bus module */ + drvr->hdrlen = 0; ++ drvr->chan_stats = vzalloc(256 * sizeof(struct brcmf_chan_stats)); ++ drvr->num_chan_stats = 256; + + /* Attach and link in the protocol */ + ret = brcmf_proto_attach(drvr); +@@ -1357,6 +1436,12 @@ void brcmf_detach(struct device *dev) + if (drvr == NULL) + return; + ++ drvr->num_chan_stats = 0; ++ if (drvr->chan_stats) { ++ vfree(drvr->chan_stats); ++ drvr->chan_stats = NULL; ++ } ++ + #ifdef CONFIG_INET + unregister_inetaddr_notifier(&drvr->inetaddr_notifier); + #endif +@@ -1477,6 +1562,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 6699637..0df727e 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 { + u32 nvramrev; + }; + ++struct brcmf_chan_stats { ++ u32 freq; ++ int noise; ++}; ++ + /* Common structure for module and instance linkage */ + struct brcmf_pub { + /* Linkage ponters */ +@@ -100,6 +105,9 @@ struct brcmf_pub { + struct cfg80211_ops *ops; + struct brcmf_cfg80211_info *config; + ++ int num_chan_stats; ++ struct brcmf_chan_stats *chan_stats; ++ + /* Internal brcmf items */ + uint hdrlen; /* Total BRCMF header length (proto + bus) */ + +@@ -210,6 +218,8 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, + void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); + void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); + void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb); ++void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked); ++int brcmf_net_mon_attach(struct brcmf_if *ifp); + void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); + int __init brcmf_core_init(void); + void __exit brcmf_core_exit(void); +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +index 2c3526a..0901b4d 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +@@ -38,6 +38,7 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { + { BRCMF_FEAT_MCHAN, "mchan" }, + { BRCMF_FEAT_P2P, "p2p" }, + { BRCMF_FEAT_MONITOR, "monitor" }, ++ { BRCMF_FEAT_MONITOR_FLAG, "rtap" }, + { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" }, + { BRCMF_FEAT_DOT11H, "802.11h" } + }; +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +index 736a817..7a9f92a 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +@@ -23,6 +23,7 @@ + * GSCAN: enhanced scan offload feature. + * FWSUP: Firmware supplicant. + * MONITOR: firmware can pass monitor packets to host. ++ * MONITOR_FLAG: firmware flags monitor packets. + * MONITOR_FMT_RADIOTAP: firmware provides monitor packets with radiotap header + * MONITOR_FMT_HW_RX_HDR: firmware provides monitor packets with hw/ucode header + * DOT11H: firmware supports 802.11h +@@ -43,6 +44,7 @@ + BRCMF_FEAT_DEF(GSCAN) \ + BRCMF_FEAT_DEF(FWSUP) \ + BRCMF_FEAT_DEF(MONITOR) \ ++ BRCMF_FEAT_DEF(MONITOR_FLAG) \ + BRCMF_FEAT_DEF(MONITOR_FMT_RADIOTAP) \ + BRCMF_FEAT_DEF(MONITOR_FMT_HW_RX_HDR) \ + BRCMF_FEAT_DEF(DOT11H) +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +index 3aed4c4..c33ddd2 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 { + struct brcmf_fw_request *req; + u32 curpos; + void (*done)(struct device *dev, int err, struct brcmf_fw_request *req); ++ 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) + fwctx->req = NULL; + } + fwctx->done(fwctx->dev, ret, fwctx->req); ++ if (fwctx->completion) ++ complete(fwctx->completion); + kfree(fwctx); + } + +@@ -662,6 +665,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; + 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, + fwctx->dev = dev; + fwctx->req = req; + fwctx->done = fw_cb; ++ ++ 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, + if (ret < 0) + brcmf_fw_request_done(NULL, fwctx); + ++ ++ time_left = wait_for_completion_timeout(&completion, ++ msecs_to_jiffies(5000)); ++ if (!time_left && fwctx) ++ fwctx->completion = NULL; ++ + return 0; + } + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h +index 0ff6f52..ae4cf43 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h +@@ -49,6 +49,8 @@ + #define BRCMF_C_GET_PM 85 + #define BRCMF_C_SET_PM 86 + #define BRCMF_C_GET_REVINFO 98 ++#define BRCMF_C_GET_MONITOR 107 ++#define BRCMF_C_SET_MONITOR 108 + #define BRCMF_C_GET_CURR_RATESET 114 + #define BRCMF_C_GET_AP 117 + #define BRCMF_C_SET_AP 118 +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +index 2bd892d..5e1a11c 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +@@ -908,7 +908,7 @@ static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) + wlh += wlh[1] + 2; + + if (entry->send_tim_signal) { +- entry->send_tim_signal = 0; ++ entry->send_tim_signal = false; + wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; + wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + wlh[2] = entry->mac_handle; +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +index e3dd862..8bb4f1f 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +@@ -365,7 +365,7 @@ brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids, + struct brcmf_msgbuf_pktid *pktid; + struct sk_buff *skb; + +- if (idx < 0 || idx >= pktids->array_size) { ++ if (idx >= pktids->array_size) { + brcmf_err("Invalid packet id %d (max %d)\n", idx, + pktids->array_size); + return NULL; +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +index b886b56..ae1f436 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +@@ -12,6 +12,36 @@ + #include "common.h" + #include "of.h" + ++/* TODO: FIXME: Use DT */ ++static void brcmf_of_probe_cc(struct device *dev, ++ struct brcmf_mp_device *settings) ++{ ++ static struct brcmfmac_pd_cc_entry netgear_r8000_cc_ent[] = { ++ { "JP", "JP", 78 }, ++ { "US", "Q2", 86 }, ++ }; ++ struct brcmfmac_pd_cc_entry *cc_ent = NULL; ++ int table_size = 0; ++ ++ if (of_machine_is_compatible("netgear,r8000")) { ++ cc_ent = netgear_r8000_cc_ent; ++ table_size = ARRAY_SIZE(netgear_r8000_cc_ent); ++ } ++ ++ if (cc_ent && table_size) { ++ struct brcmfmac_pd_cc *cc; ++ size_t memsize; ++ ++ memsize = table_size * sizeof(struct brcmfmac_pd_cc_entry); ++ cc = devm_kzalloc(dev, sizeof(*cc) + memsize, GFP_KERNEL); ++ if (!cc) ++ return; ++ cc->table_size = table_size; ++ memcpy(cc->table, cc_ent, memsize); ++ settings->country_codes = cc; ++ } ++} ++ + void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, + struct brcmf_mp_device *settings) + { +@@ -30,6 +60,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")) + return; +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +index 759f27a..0da6845 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +@@ -78,7 +78,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { + BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), + }; + +-#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ ++#define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */ + + #define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +index 14e5306..fabfbb0 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +@@ -57,6 +57,10 @@ static int brcmf_pno_remove_request(struct brcmf_pno_info *pi, u64 reqid) + + mutex_lock(&pi->req_lock); + ++ /* Nothing to do if we have no requests */ ++ if (pi->n_reqs == 0) ++ goto done; ++ + /* find request */ + for (i = 0; i < pi->n_reqs; i++) { + if (pi->reqs[i]->reqid == reqid) +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +index a935993..f9047db 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +@@ -42,6 +42,8 @@ + #define DEFAULT_F2_WATERMARK 0x8 + #define CY_4373_F2_WATERMARK 0x40 + #define CY_43012_F2_WATERMARK 0x60 ++#define CY_4359_F2_WATERMARK 0x40 ++#define CY_4359_F1_MESBUSYCTRL (CY_4359_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB) + + #ifdef DEBUG + +@@ -614,6 +616,7 @@ BRCMF_FW_DEF(43455, "brcmfmac43455-sdio"); + BRCMF_FW_DEF(43456, "brcmfmac43456-sdio"); + BRCMF_FW_DEF(4354, "brcmfmac4354-sdio"); + BRCMF_FW_DEF(4356, "brcmfmac4356-sdio"); ++BRCMF_FW_DEF(4359, "brcmfmac4359-sdio"); + BRCMF_FW_DEF(4373, "brcmfmac4373-sdio"); + BRCMF_FW_DEF(43012, "brcmfmac43012-sdio"); + +@@ -636,6 +639,7 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { + BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455), + BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), + BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), ++ BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), + BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), + BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012) + }; +@@ -4206,6 +4210,19 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, + brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, + &err); + break; ++ case SDIO_DEVICE_ID_BROADCOM_4359: ++ brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", ++ CY_4359_F2_WATERMARK); ++ brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, ++ CY_4359_F2_WATERMARK, &err); ++ devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL, ++ &err); ++ devctl |= SBSDIO_DEVCTL_F2WM_ENAB; ++ brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, ++ &err); ++ brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, ++ CY_4359_F1_MESBUSYCTRL, &err); ++ break; + default: + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, + DEFAULT_F2_WATERMARK, &err); +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +index 0bd47c1..163fd66 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +@@ -178,7 +178,6 @@ struct brcmf_sdio_dev { + bool sd_irq_requested; + bool irq_en; /* irq enable flags */ + spinlock_t irq_en_lock; +- bool irq_wake; /* irq wake enable flags */ + bool sg_support; + uint max_request_size; + ushort max_segment_count; +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c +index db783e9..021383a 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c +@@ -58,19 +58,12 @@ + (((c) < 149) ? 3 : 4)))) + + #define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0) +-#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \ +- NL80211_RRF_NO_IR) +- +-#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \ +- NL80211_RRF_NO_IR) +-#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \ +- NL80211_RRF_DFS | \ +- NL80211_RRF_NO_IR) +-#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \ +- NL80211_RRF_DFS | \ +- NL80211_RRF_NO_IR) +-#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \ +- NL80211_RRF_NO_IR) ++#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, 0) ++ ++#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, 0) ++#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, 0) ++#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, 0) ++#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, 0) + + static const struct ieee80211_regdomain brcms_regdom_x2 = { + .n_reg_rules = 6, +diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h +index 08b25c0..2e9a6e4 100644 +--- a/include/linux/mmc/sdio_ids.h ++++ b/include/linux/mmc/sdio_ids.h +@@ -41,8 +41,10 @@ + #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf + #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 + #define SDIO_DEVICE_ID_BROADCOM_4356 0x4356 ++#define SDIO_DEVICE_ID_BROADCOM_4359 0x4359 + #define SDIO_DEVICE_ID_CYPRESS_4373 0x4373 + #define SDIO_DEVICE_ID_CYPRESS_43012 43012 ++#define SDIO_DEVICE_ID_CYPRESS_89359 0x4355 + + #define SDIO_VENDOR_ID_INTEL 0x0089 + #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 diff --git a/recipes-kernel/mac80211/mac80211/0007-backport-of-netmodule-patches-from-openwrt.patch b/recipes-kernel/mac80211/mac80211/0007-backport-of-netmodule-patches-from-openwrt.patch new file mode 100644 index 0000000..12c4974 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211/0007-backport-of-netmodule-patches-from-openwrt.patch @@ -0,0 +1,701 @@ +From 570a5113d096abd20168f93b3ab3ed3625fda840 Mon Sep 17 00:00:00 2001 +From: Patrick Walther +Date: Fri, 24 Jul 2020 18:56:44 +0200 +Subject: [PATCH] backport of netmodule patches from openwrt + +--- + drivers/net/wireless/ath/ath10k/mac.c | 2 +- + 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 | 13 +++----- + drivers/net/wireless/ti/wlcore/cmd.h | 2 +- + drivers/net/wireless/ti/wlcore/conf.h | 3 ++ + drivers/net/wireless/ti/wlcore/init.c | 22 ++++++++++--- + drivers/net/wireless/ti/wlcore/main.c | 54 +++++++++++++++++++------------ + drivers/net/wireless/ti/wlcore/wlcore_i.h | 1 + + include/net/cfg80211.h | 10 ++++-- + include/net/mac80211.h | 7 ++-- + include/uapi/linux/nl80211.h | 3 ++ + net/mac80211/cfg.c | 13 ++++++++ + net/mac80211/main.c | 4 +-- + net/wireless/core.c | 41 +++++++++++++++++++---- + net/wireless/nl80211.c | 13 ++++++++ + net/wireless/reg.c | 23 ++++++++++--- + 19 files changed, 162 insertions(+), 57 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c +index 7946d3b..ba289ad 100644 +--- a/drivers/net/wireless/ath/ath10k/mac.c ++++ b/drivers/net/wireless/ath/ath10k/mac.c +@@ -8419,7 +8419,7 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { + #endif + }, + { +- .max = 1, ++ .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) + }, + }; +diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c +index d7435c9..57ec879 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 { + ATH10K_PCI_RESET_WARM_ONLY = 1, + }; + +-static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO; ++static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_LEGACY; + 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/regd.c b/drivers/net/wireless/ath/regd.c +index e263400..c242f00 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) + regdmn == CTRY_DEFAULT) { + printk(KERN_DEBUG "ath: EEPROM indicates default " + "country code should be used\n"); +- reg->country_code = CTRY_UNITED_STATES; ++ reg->country_code = CTRY_EU; + } + + if (reg->country_code == CTRY_DEFAULT) { +diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h +index 8d5a16b..bb59c9d 100644 +--- a/drivers/net/wireless/ath/regd.h ++++ b/drivers/net/wireless/ath/regd.h +@@ -254,7 +254,8 @@ enum CountryCode { + CTRY_JAPAN59 = 4059, + CTRY_AUSTRALIA2 = 5000, + CTRY_CANADA2 = 5001, +- CTRY_BELGIUM2 = 5002 ++ CTRY_BELGIUM2 = 5002, ++ CTRY_EU = 500, + }; + + bool ath_is_world_regd(struct ath_regulatory *reg); +diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h +index 364011e..4db829c 100644 +--- a/drivers/net/wireless/ath/regd_common.h ++++ b/drivers/net/wireless/ath/regd_common.h +@@ -498,6 +498,7 @@ static struct country_code_to_enum_rd allCountries[] = { + {CTRY_VIET_NAM, NULL1_WORLD, "VN"}, + {CTRY_YEMEN, NULL1_WORLD, "YE"}, + {CTRY_ZIMBABWE, ETSI1_WORLD, "ZW"}, ++ {CTRY_EU, ETSI1_WORLD, "EU"}, + }; + + #endif +diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c +index 2a48fc6..888cfd7 100644 +--- a/drivers/net/wireless/ti/wlcore/cmd.c ++++ b/drivers/net/wireless/ti/wlcore/cmd.c +@@ -1429,7 +1429,7 @@ out: + int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, +- u16 tx_seq_16) ++ u16 tx_seq_16, bool is_pairwise) + { + struct wl1271_cmd_set_keys *cmd; + int ret = 0; +@@ -1444,8 +1444,10 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + lid_type = WEP_DEFAULT_LID_TYPE; + else + lid_type = BROADCAST_LID_TYPE; +- } else { ++ } else if (is_pairwise) { + lid_type = UNICAST_LID_TYPE; ++ } else { ++ lid_type = BROADCAST_LID_TYPE; + } + + wl1271_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d" +@@ -1565,13 +1567,6 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, + cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates, + wlvif->band)); + +- if (!cmd->supported_rates) { +- wl1271_debug(DEBUG_CMD, +- "peer has no supported rates yet, configuring basic rates: 0x%x", +- wlvif->basic_rate_set); +- cmd->supported_rates = cpu_to_le32(wlvif->basic_rate_set); +- } +- + wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x", + cmd->supported_rates, sta->uapsd_queues); + +diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h +index 084375b..bfad7b5 100644 +--- a/drivers/net/wireless/ti/wlcore/cmd.h ++++ b/drivers/net/wireless/ti/wlcore/cmd.h +@@ -65,7 +65,7 @@ int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, +- u16 tx_seq_16); ++ u16 tx_seq_16, bool is_pairwise); + int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid); + int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, +diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h +index 6116383..e33f577 100644 +--- a/drivers/net/wireless/ti/wlcore/conf.h ++++ b/drivers/net/wireless/ti/wlcore/conf.h +@@ -215,6 +215,9 @@ struct conf_rx_settings { + CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ + CONF_HW_BIT_RATE_11MBPS) + ++#define CONF_TX_OFDM_BASIC_RATES (CONF_HW_BIT_RATE_6MBPS | \ ++ CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS) ++ + #define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \ + CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \ + CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ +diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c +index 03b49ba..6334351 100644 +--- a/drivers/net/wireless/ti/wlcore/init.c ++++ b/drivers/net/wireless/ti/wlcore/init.c +@@ -425,6 +425,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) + int i, ret; + struct conf_tx_rate_class rc; + u32 supported_rates; ++ struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + + wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", + wlvif->basic_rate_set); +@@ -432,16 +433,27 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) + if (wlvif->basic_rate_set == 0) + return -EINVAL; + +- rc.enabled_rates = wlvif->basic_rate_set; +- rc.long_retry_limit = 10; +- rc.short_retry_limit = 10; +- rc.aflags = 0; ++ /*In order to handle mesh PREQ/PREP loss, increase retry limit ++ and use OFDM basic rates (mgmt policy)*/ ++ if (ieee80211_vif_is_mesh(vif)) { ++ rc.enabled_rates = wlvif->basic_rate_set; ++ rc.long_retry_limit = 30; ++ rc.short_retry_limit = 30; ++ rc.aflags = 0; ++ } ++ else { ++ rc.enabled_rates = wlvif->basic_rate_set; ++ rc.long_retry_limit = 10; ++ rc.short_retry_limit = 10; ++ rc.aflags = 0; ++ } ++ + ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx); + if (ret < 0) + return ret; + + /* use the min basic rate for AP broadcast/multicast */ +- rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ++ rc.enabled_rates = wlvif->basic_rate; + rc.short_retry_limit = 10; + 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 6d36cbf..355d186 100644 +--- a/drivers/net/wireless/ti/wlcore/main.c ++++ b/drivers/net/wireless/ti/wlcore/main.c +@@ -2213,12 +2213,13 @@ static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif) + static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) + { + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); ++ enum nl80211_iftype iftype = ieee80211_vif_type_p2p(vif); + int i; + + /* clear everything but the persistent data */ + memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent)); + +- switch (ieee80211_vif_type_p2p(vif)) { ++ switch (iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + wlvif->p2p = 1; + /* fall-through */ +@@ -2265,13 +2266,14 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) + for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++) + wl12xx_allocate_rate_policy(wl, + &wlvif->ap.ucast_rate_idx[i]); +- wlvif->basic_rate_set = CONF_TX_ENABLED_RATES; +- /* +- * TODO: check if basic_rate shouldn't be +- * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); +- * instead (the same thing for STA above). +- */ +- wlvif->basic_rate = CONF_TX_ENABLED_RATES; ++ /* For mesh set default basic rate set to OFDM basic rate set only*/ ++ if (iftype == NL80211_IFTYPE_MESH_POINT) ++ wlvif->basic_rate_set = CONF_TX_OFDM_BASIC_RATES; ++ else ++ wlvif->basic_rate_set = CONF_TX_ENABLED_RATES; ++ ++ wlvif->basic_rate = wl1271_tx_min_rate_get(wl, CONF_TX_ENABLED_RATES); ++ + /* TODO: this seems to be used only for STA, check it */ + wlvif->rate_set = CONF_TX_ENABLED_RATES; + } +@@ -3274,7 +3276,7 @@ out: + static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 id, u8 key_type, u8 key_size, + const u8 *key, u8 hlid, u32 tx_seq_32, +- u16 tx_seq_16) ++ u16 tx_seq_16, bool is_pairwise) + { + struct wl1271_ap_key *ap_key; + int i; +@@ -3312,6 +3314,7 @@ static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + ap_key->hlid = hlid; + ap_key->tx_seq_32 = tx_seq_32; + ap_key->tx_seq_16 = tx_seq_16; ++ ap_key->is_pairwise = is_pairwise; + + wlvif->ap.recorded_keys[i] = ap_key; + return 0; +@@ -3347,7 +3350,7 @@ static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif) + key->id, key->key_type, + key->key_size, key->key, + hlid, key->tx_seq_32, +- key->tx_seq_16); ++ key->tx_seq_16, key->is_pairwise); + if (ret < 0) + goto out; + +@@ -3370,7 +3373,8 @@ out: + static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, u32 tx_seq_32, +- u16 tx_seq_16, struct ieee80211_sta *sta) ++ u16 tx_seq_16, struct ieee80211_sta *sta, ++ bool is_pairwise) + { + int ret; + bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); +@@ -3397,12 +3401,12 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, + ret = wl1271_record_ap_key(wl, wlvif, id, + key_type, key_size, + key, hlid, tx_seq_32, +- tx_seq_16); ++ tx_seq_16, is_pairwise); + } else { + ret = wl1271_cmd_set_ap_key(wl, wlvif, action, + id, key_type, key_size, + key, hlid, tx_seq_32, +- tx_seq_16); ++ tx_seq_16, is_pairwise); + } + + if (ret < 0) +@@ -3502,6 +3506,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + u16 tx_seq_16 = 0; + u8 key_type; + u8 hlid; ++ bool is_pairwise; + + wl1271_debug(DEBUG_MAC80211, "mac80211 set key"); + +@@ -3551,12 +3556,14 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + return -EOPNOTSUPP; + } + ++ is_pairwise = key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE; ++ + switch (cmd) { + case SET_KEY: + ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE, + key_conf->keyidx, key_type, + key_conf->keylen, key_conf->key, +- tx_seq_32, tx_seq_16, sta); ++ tx_seq_32, tx_seq_16, sta, is_pairwise); + if (ret < 0) { + wl1271_error("Could not add or replace key"); + return ret; +@@ -3582,7 +3589,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + ret = wl1271_set_key(wl, wlvif, KEY_REMOVE, + key_conf->keyidx, key_type, + key_conf->keylen, key_conf->key, +- 0, 0, sta); ++ 0, 0, sta, is_pairwise); + if (ret < 0) { + wl1271_error("Could not remove key"); + return ret; +@@ -5216,11 +5223,6 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, + if (ret < 0) + return ret; + +- /* reconfigure rates */ +- ret = wl12xx_cmd_add_peer(wl, wlvif, sta, wl_sta->hlid); +- if (ret < 0) +- return ret; +- + ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, + wl_sta->hlid); + if (ret) +@@ -5794,9 +5796,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); ++ u8 role_type; + s8 rssi_dbm; + int ret; + ++ role_type = wl12xx_get_role_type(wl, wlvif); ++ if (role_type == WL1271_ROLE_AP || ++ role_type == WL1271_ROLE_MESH_POINT) { ++ return; ++ } ++ + wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi"); + + mutex_lock(&wl->mutex); +@@ -6224,6 +6233,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) + + ieee80211_hw_set(wl->hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(wl->hw, CHANCTX_STA_CSA); ++ ieee80211_hw_set(wl->hw, SUPPORTS_PER_STA_GTK); + ieee80211_hw_set(wl->hw, QUEUE_CONTROL); + ieee80211_hw_set(wl->hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(wl->hw, AMPDU_AGGREGATION); +@@ -6268,9 +6278,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) + + wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | +- WIPHY_FLAG_HAS_CHANNEL_SWITCH; ++ WIPHY_FLAG_HAS_CHANNEL_SWITCH | ++ WIPHY_FLAG_IBSS_RSN; + + wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; ++ wl->hw->wiphy->features &= ~NL80211_FEATURE_FULL_AP_CLIENT_STATE; + + /* make sure all our channels fit in the scanned_ch bitmask */ + BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + +diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h +index 6fab60b..eefae3f 100644 +--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h ++++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h +@@ -212,6 +212,7 @@ struct wl1271_ap_key { + u8 hlid; + u32 tx_seq_32; + u16 tx_seq_16; ++ bool is_pairwise; + }; + + enum wl12xx_flags { +diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h +index 8099b10..4849bab 100644 +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -112,6 +112,7 @@ enum ieee80211_channel_flags { + IEEE80211_CHAN_IR_CONCURRENT = 1<<10, + IEEE80211_CHAN_NO_20MHZ = 1<<11, + IEEE80211_CHAN_NO_10MHZ = 1<<12, ++ IEEE80211_CHAN_SRD = 1<<13, + }; + + #define IEEE80211_CHAN_NO_HT40 \ +@@ -3456,6 +3457,8 @@ struct cfg80211_update_owe_info { + * 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 +@@ -3771,6 +3774,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 (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr); +@@ -4771,6 +4775,8 @@ static inline const char *wiphy_name(const struct wiphy *wiphy) + * @sizeof_priv: The size of the private area to allocate + * @requested_name: Request a particular name. + * NULL is valid value, and means use the default phy%d naming. ++ * @requested_index: Request a particular index. ++ * -1 is valid value, and means use the default phy%d naming. + * + * Create a new wiphy and associate the given operations with it. + * @sizeof_priv bytes are allocated for private use. +@@ -4779,7 +4785,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); + + /** + * wiphy_new - create a new wiphy for use with cfg80211 +@@ -4796,7 +4802,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); + } + + /** +diff --git a/include/net/mac80211.h b/include/net/mac80211.h +index bca141e..ae3316f 100644 +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -4087,12 +4087,15 @@ struct ieee80211_ops { + * @ops: callbacks for this device + * @requested_name: Requested name for this device. + * NULL is valid value, and means use the default naming (phy%d) ++ * @requested_name: Requested index for this device. ++ * -1 is valid value, and means use the default naming (phy%d) + * + * Return: A pointer to the new hardware device, or %NULL on error. + */ + struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, + const struct ieee80211_ops *ops, +- const char *requested_name); ++ const char *requested_name, ++ int requested_index); + + /** + * ieee80211_alloc_hw - Allocate a new hardware device +@@ -4112,7 +4115,7 @@ static inline + struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, + const struct ieee80211_ops *ops) + { +- return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL); ++ return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL, -1); + } + + /** +diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h +index ef08258..2f41258 100644 +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -3559,6 +3559,8 @@ enum nl80211_wmm_rule { + * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations. + * This is a nested attribute that contains the wmm limitation per AC. + * (see &enum nl80211_wmm_rule) ++ * @NL80211_FREQUENCY_ATTR_SRD_CHANNEL: short range devices mode ++ * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number + * currently defined + * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use +@@ -3588,6 +3590,7 @@ enum nl80211_frequency_attr { + NL80211_FREQUENCY_ATTR_NO_20MHZ, + NL80211_FREQUENCY_ATTR_NO_10MHZ, + NL80211_FREQUENCY_ATTR_WMM, ++ 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 993ee7b..13bd5b2 100644 +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -2595,6 +2595,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) ++{ ++ struct ieee80211_local *local = wiphy_priv(wiphy); ++ ++ if (local) { ++ *dbi = local->user_antenna_gain; ++ } ++ ++ return 0; ++} ++ + static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr) + { +@@ -4031,6 +4043,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) +diff --git a/net/mac80211/main.c b/net/mac80211/main.c +index 71dada6..7bfa7bb 100644 +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -512,7 +512,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, +- const char *requested_name) ++ const char *requested_name, int requested_idx) + { + struct ieee80211_local *local; + int priv_size, i; +@@ -552,7 +552,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, + */ + priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; + +- wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name); ++ wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name, requested_idx); + + if (!wiphy) + return NULL; +diff --git a/net/wireless/core.c b/net/wireless/core.c +index c8a6c0e..847d4f6 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, + return 0; + } + ++static int cfg80211_dev_check_index(const int index) ++{ ++ struct cfg80211_registered_device *rdev; ++ ++ ASSERT_RTNL(); ++ ++ /* Ensure another device does not already have this name. */ ++ list_for_each_entry(rdev, &cfg80211_rdev_list, list) ++ if (rdev->wiphy_idx == index) ++ return -EINVAL; ++ return 0; ++} ++ + int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, + char *newname) + { +@@ -396,7 +409,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, +- const char *requested_name) ++ const char *requested_name, const int requested_index) + { + static atomic_t wiphy_counter = ATOMIC_INIT(0); + +@@ -439,17 +452,31 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, + + rdev->ops = ops; + +- rdev->wiphy_idx = atomic_inc_return(&wiphy_counter); ++ if (requested_index >= 0) { ++ int rv; + +- if (unlikely(rdev->wiphy_idx < 0)) { ++ rtnl_lock(); ++ rv = cfg80211_dev_check_index(requested_index); ++ ++ if (rv < 0) { ++ rtnl_unlock(); ++ goto use_default_index; ++ } ++ rdev->wiphy_idx = requested_index; ++ rtnl_unlock(); ++ } else { ++use_default_index: ++ rdev->wiphy_idx = atomic_inc_return(&wiphy_counter); ++ ++ if (unlikely(rdev->wiphy_idx < 0)) { + /* ugh, wrapped! */ + atomic_dec(&wiphy_counter); + kfree(rdev); + return NULL; +- } +- +- /* atomic_inc_return makes it start at 1, make it start at 0 */ +- rdev->wiphy_idx--; ++ } ++ /* atomic_inc_return makes it start at 1, make it start at 0 */ ++ rdev->wiphy_idx--; ++ } + + /* give it a proper name */ + if (requested_name && requested_name[0]) { +diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c +index c93a4e0..1b3c0d9 100644 +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -970,6 +970,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, + if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ)) + goto nla_put_failure; ++ if ((chan->flags & IEEE80211_CHAN_SRD) && ++ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_SRD_CHANNEL)) ++ goto nla_put_failure; + } + + if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, +@@ -3253,6 +3256,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag + goto nla_put_failure; + } + ++ if (rdev->ops->get_antenna_gain) { ++ int dbi = 0, ret; ++ ++ ret = rdev->ops->get_antenna_gain(&rdev->wiphy, &dbi); ++ if (ret == 0 && ++ nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_GAIN, ++ DBM_TO_MBM(dbi))) ++ goto nla_put_failure; ++ } ++ + wdev_lock(wdev); + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: +diff --git a/net/wireless/reg.c b/net/wireless/reg.c +index a2908b8..ed16782 100644 +--- a/net/wireless/reg.c ++++ b/net/wireless/reg.c +@@ -68,6 +68,7 @@ + * channels allowed by the current regulatory domain. + */ + #define REG_ENFORCE_GRACE_MS 60000 ++#define REG_CHANNEL_LOW_PWR_DBM 15 + + /** + * enum reg_request_treatment - regulatory request treatment +@@ -1778,11 +1779,21 @@ static void handle_channel(struct wiphy *wiphy, + MBI_TO_DBI(power_rule->max_antenna_gain)); + chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); + ++ if (chan->max_reg_power <= REG_CHANNEL_LOW_PWR_DBM) { ++ chan->flags |= IEEE80211_CHAN_SRD; ++ } ++ + if (chan->flags & IEEE80211_CHAN_RADAR) { +- if (reg_rule->dfs_cac_ms) +- chan->dfs_cac_ms = reg_rule->dfs_cac_ms; +- else +- chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; ++ if (reg_rule->dfs_cac_ms) ++ chan->dfs_cac_ms = reg_rule->dfs_cac_ms; ++ else if (regd->dfs_region == NL80211_DFS_ETSI && ++ chan->center_freq >= 5600 && ++ chan->center_freq <= 5640) { ++ /* TDWR channels require 10 min */ ++ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS * 10; ++ } else { ++ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; ++ } + } + + if (chan->orig_mpwr) { +@@ -2314,6 +2325,10 @@ static void handle_channel_custom(struct wiphy *wiphy, + } + + chan->max_power = chan->max_reg_power; ++ ++ if (chan->max_reg_power <= REG_CHANNEL_LOW_PWR_DBM) { ++ chan->flags |= IEEE80211_CHAN_SRD; ++ } + } + + static void handle_band_custom(struct wiphy *wiphy, diff --git a/recipes-kernel/mac80211/mac80211_5.4.27-1.bb b/recipes-kernel/mac80211/mac80211_5.4.27-1.bb new file mode 100644 index 0000000..839b0d9 --- /dev/null +++ b/recipes-kernel/mac80211/mac80211_5.4.27-1.bb @@ -0,0 +1,70 @@ +# Copyright (C) 2017 Khem Raj +# Released under the MIT license (see COPYING.MIT for the terms) + +DESCRIPTION = "Linux Backports" +HOMEPAGE = "https://backports.wiki.kernel.org" +SECTION = "kernel/modules" +LICENSE = "GPLv2" +LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814" + +inherit module cml1 + +SHRT_VER = "${@d.getVar('PV', True).split('-')[0]}" + +SRC_URI = "http://www.kernel.org/pub/linux/kernel/projects/backports/stable/v${SHRT_VER}/backports-${PV}.tar.gz \ + 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-netmodule-patches-from-openwrt.patch \ + " + +SRC_URI[sha256sum] = "2b060db29386c6f3fb178df33e0b8256703b7e90f7e3799e8b5a26330ca6fc1e" + + +S = "${WORKDIR}/backports-${PV}" + +DEPENDS += "coreutils-native flex-native bison-native" + +EXTRA_OEMAKE = "KLIB_BUILD=${STAGING_KERNEL_BUILDDIR} KLIB=${base_libdir}/modules/${KERNEL_VERSION}" +KCONFIG_CONFIG_COMMAND = "${EXTRA_OEMAKE} CC=${BUILD_CC} menuconfig" + +MAKE_TARGETS = "modules" + +PACKAGE_CONFIGFILE ?= "" +do_configure_prepend() { + # Make sure kconf is built to run on the host + unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS + oe_runmake -C kconf CC=${BUILD_CC} conf + # Copy our config as a defconfig + + if [ ! -n "${PACKAGE_CONFIGFILE}" ]; then + bbfatal "mac80211: no config file given" + fi + + cp ${WORKDIR}/${PACKAGE_CONFIGFILE} ${S}/.config + oe_runmake oldconfig + +} + +# Create a meta package with another name, in this way we can +# always build this receipe but only install the modules +# when requiered. +KERNEL_MODULES_META_PACKAGE = "kernel-module-mac80211" + +FILES_${KERNEL_MODULES_META_PACKAGE} = "" +ALLOW_EMPTY_${KERNEL_MODULES_META_PACKAGE} = "1" + +PACKAGES += "${KERNEL_MODULES_META_PACKAGE}" + +PACKAGES_DYNAMIC += "^${KERNEL_PACKAGE_NAME}-module-.*" + +KERNEL_MODULE_AUTOLOAD += "\ + compat \ + mac80211 \ + cfg80211 \ + " + +