mac80211: backport from trunk r48782 + required kernel patches and mt76, mwlwifi

Signed-off-by: Felix Fietkau <nbd@openwrt.org>

git-svn-id: svn://svn.openwrt.org/openwrt/branches/chaos_calmer@48822 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
Felix Fietkau 2016-02-29 17:19:04 +00:00
parent 5db86ba649
commit f3a1db04f0
333 changed files with 6016 additions and 20691 deletions

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@
@set -e ; test -f .local-symbols || ( \
echo "/--------------" ;\
echo "| You shouldn't run make in the backports tree, but only in" ;\
@@ -60,56 +62,60 @@ mrproper:
@@ -60,57 +62,61 @@ mrproper:
echo "| (that isn't currently running.)" ;\
echo "\\--" ;\
false)
@ -56,11 +56,12 @@
- done \
- ) > Kconfig.kernel ;\
- kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion | \
- sed 's/^\(\(3\|2\.6\)\.[0-9]\+\).*/\1/;t;d') ;\
- sed 's/^\(\([3-4]\|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 99 | sed 's/^/3./')" ;\
- kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\
- kvers="$$kvers $$(seq 0 99 | sed 's/^/4./')" ;\
- print=0 ;\
- for v in $$kvers ; do \
- if [ "$$print" = "1" ] ; then \
@ -111,11 +112,12 @@
+
+Kconfig.versions: Kconfig.kernel
+ @kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion | \
+ sed 's/^\(\(3\|2\.6\)\.[0-9]\+\).*/\1/;t;d') ;\
+ sed 's/^\(\([3-4]\|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 99 | sed 's/^/3./')" ;\
+ kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\
+ kvers="$$kvers $$(seq 0 99 | sed 's/^/4./')" ;\
+ print=0 ;\
+ for v in $$kvers ; do \
+ if [ "$$print" = "1" ] ; then \

View File

@ -1,6 +1,6 @@
--- a/kconf/conf.c
+++ b/kconf/conf.c
@@ -578,40 +578,12 @@ int main(int ac, char **av)
@@ -593,40 +593,12 @@ int main(int ac, char **av)
case oldconfig:
case listnewconfig:
case olddefconfig:
@ -42,3 +42,23 @@
break;
default:
break;
--- a/kconf/confdata.c
+++ b/kconf/confdata.c
@@ -1169,6 +1169,8 @@ bool conf_set_all_new_symbols(enum conf_
}
bool has_changed = false;
+ sym_clear_all_valid();
+
for_all_symbols(i, sym) {
if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
continue;
@@ -1212,8 +1214,6 @@ bool conf_set_all_new_symbols(enum conf_
}
- sym_clear_all_valid();
-
/*
* We have different type of choice blocks.
* If curr.tri equals to mod then we can select several

View File

@ -0,0 +1,21 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Thu, 28 Jan 2016 15:16:35 +0100
Subject: [PATCH] backports: add skb_free_frag()
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/backport-include/linux/skbuff.h
+++ b/backport-include/linux/skbuff.h
@@ -300,4 +300,11 @@ int skb_ensure_writable(struct sk_buff *
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
+static inline void skb_free_frag(void *data)
+{
+ put_page(virt_to_head_page(data));
+}
+#endif
+
#endif /* __BACKPORT_SKBUFF_H */

View File

@ -1,10 +0,0 @@
--- a/backport-include/linux/debugfs.h
+++ b/backport-include/linux/debugfs.h
@@ -3,6 +3,7 @@
#include_next <linux/debugfs.h>
#include <linux/version.h>
#include <generated/utsrelease.h>
+#include <linux/device.h>
#if defined(CONFIG_DEBUG_FS)
struct dentry *debugfs_create_devm_seqfile(struct device *dev, const char *name,

View File

@ -0,0 +1,20 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Thu, 28 Jan 2016 15:19:22 +0100
Subject: [PATCH] backports: add napi_alloc_frag
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/backport-include/linux/netdevice.h
+++ b/backport-include/linux/netdevice.h
@@ -232,6 +232,10 @@ static inline void backport_unregister_n
#define unregister_netdevice_many LINUX_BACKPORT(unregister_netdevice_many)
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#define napi_alloc_frag netdev_alloc_frag
+#endif
+
/*
* Complicated way of saying: We only backport netdev_rss_key stuff on kernels
* that either already have net_get_random_once() (>= 3.13) or where we've been

View File

@ -1,16 +0,0 @@
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1858,6 +1858,13 @@ void ieee80211_remove_interfaces(struct
}
mutex_unlock(&local->iflist_mtx);
unregister_netdevice_many(&unreg_list);
+#if (!(LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,45) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)) && \
+ !(LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,9) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)) && \
+ (LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)))
+ list_del(&unreg_list);
+#endif
list_for_each_entry_safe(sdata, tmp, &wdev_list, list) {
list_del(&sdata->list);

View File

@ -1,37 +0,0 @@
--- /dev/null
+++ b/include/uapi/linux/mpls.h
@@ -0,0 +1,34 @@
+#ifndef _UAPI_MPLS_H
+#define _UAPI_MPLS_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* Reference: RFC 5462, RFC 3032
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Label | TC |S| TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Label: Label Value, 20 bits
+ * TC: Traffic Class field, 3 bits
+ * S: Bottom of Stack, 1 bit
+ * TTL: Time to Live, 8 bits
+ */
+
+struct mpls_label {
+ __be32 entry;
+};
+
+#define MPLS_LS_LABEL_MASK 0xFFFFF000
+#define MPLS_LS_LABEL_SHIFT 12
+#define MPLS_LS_TC_MASK 0x00000E00
+#define MPLS_LS_TC_SHIFT 9
+#define MPLS_LS_S_MASK 0x00000100
+#define MPLS_LS_S_SHIFT 8
+#define MPLS_LS_TTL_MASK 0x000000FF
+#define MPLS_LS_TTL_SHIFT 0
+
+#endif /* _UAPI_MPLS_H */

View File

@ -1,5 +1,5 @@
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
--- a/drivers/net/wireless/ralink/rt2x00/Kconfig
+++ b/drivers/net/wireless/ralink/rt2x00/Kconfig
@@ -225,36 +225,37 @@ config RT2800SOC
@ -43,5 +43,5 @@
- tristate
+ tristate "RT2x00 support"
depends on m
select BPAUTO_AVERAGE
config RT2X00_LIB_FIRMWARE

View File

@ -1,5 +1,5 @@
--- a/drivers/net/wireless/brcm80211/Kconfig
+++ b/drivers/net/wireless/brcm80211/Kconfig
--- a/drivers/net/wireless/broadcom/brcm80211/Kconfig
+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
@@ -1,5 +1,5 @@
config BRCMUTIL
- tristate

View File

@ -1,104 +0,0 @@
--- a/drivers/bcma/driver_pci.c
+++ b/drivers/bcma/driver_pci.c
@@ -282,39 +282,6 @@ void bcma_core_pci_power_save(struct bcm
}
EXPORT_SYMBOL_GPL(bcma_core_pci_power_save);
-int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core,
- bool enable)
-{
- struct pci_dev *pdev;
- u32 coremask, tmp;
- int err = 0;
-
- if (bus->hosttype != BCMA_HOSTTYPE_PCI) {
- /* This bcma device is not on a PCI host-bus. So the IRQs are
- * not routed through the PCI core.
- * So we must not enable routing through the PCI core. */
- goto out;
- }
-
- pdev = bus->host_pci;
-
- err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
- if (err)
- goto out;
-
- coremask = BIT(core->core_index) << 8;
- if (enable)
- tmp |= coremask;
- else
- tmp &= ~coremask;
-
- err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
-
-out:
- return err;
-}
-EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
-
static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
{
u32 w;
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -351,3 +351,37 @@ void bcma_host_pci_down(struct bcma_bus
bcma_core_pci_down(&bus->drv_pci[0]);
}
EXPORT_SYMBOL_GPL(bcma_host_pci_down);
+
+/* See also si_pci_setup */
+int bcma_host_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core,
+ bool enable)
+{
+ struct pci_dev *pdev;
+ u32 coremask, tmp;
+ int err = 0;
+
+ if (bus->hosttype != BCMA_HOSTTYPE_PCI) {
+ /* This bcma device is not on a PCI host-bus. So the IRQs are
+ * not routed through the PCI core.
+ * So we must not enable routing through the PCI core. */
+ goto out;
+ }
+
+ pdev = bus->host_pci;
+
+ err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
+ if (err)
+ goto out;
+
+ coremask = BIT(core->core_index) << 8;
+ if (enable)
+ tmp |= coremask;
+ else
+ tmp &= ~coremask;
+
+ err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(bcma_host_pci_irq_ctl);
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4866,7 +4866,7 @@ static int b43_wireless_core_init(struct
switch (dev->dev->bus_type) {
#ifdef CPTCFG_B43_BCMA
case B43_BUS_BCMA:
- bcma_core_pci_irq_ctl(dev->dev->bdev->bus,
+ bcma_host_pci_irq_ctl(dev->dev->bdev->bus,
dev->dev->bdev, true);
bcma_host_pci_up(dev->dev->bdev->bus);
break;
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
@@ -4959,7 +4959,7 @@ static int brcms_b_up_prep(struct brcms_
* Configure pci/pcmcia here instead of in brcms_c_attach()
* to allow mfg hotswap: down, hotswap (chip power cycle), up.
*/
- bcma_core_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core,
+ bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core,
true);
/*

View File

@ -1,6 +1,6 @@
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -174,7 +174,7 @@ config CFG80211_WEXT_EXPORT
@@ -171,7 +171,7 @@ config CFG80211_WEXT_EXPORT
wext compatibility symbols to be exported.
config LIB80211
@ -9,7 +9,7 @@
depends on m
default n
help
@@ -184,15 +184,15 @@ config LIB80211
@@ -181,15 +181,15 @@ config LIB80211
Drivers should select this themselves if needed.
config LIB80211_CRYPT_WEP

View File

@ -1,9 +1,9 @@
--- a/.local-symbols
+++ b/.local-symbols
@@ -344,40 +344,3 @@ USB_CDC_PHONET=
USB_IPHETH=
@@ -476,44 +476,6 @@ USB_IPHETH=
USB_SIERRA_NET=
USB_VL600=
USB_NET_CH9200=
-SSB_POSSIBLE=
-SSB=
-SSB_SPROM=
@ -15,6 +15,7 @@
-SSB_PCMCIAHOST=
-SSB_SDIOHOST_POSSIBLE=
-SSB_SDIOHOST=
-SSB_HOST_SOC=
-SSB_SILENT=
-SSB_DEBUG=
-SSB_SERIAL=
@ -32,28 +33,20 @@
-BCMA_BLOCKIO=
-BCMA_HOST_PCI_POSSIBLE=
-BCMA_HOST_PCI=
-BCMA_DRIVER_PCI_HOSTMODE=
-BCMA_HOST_SOC=
-BCMA_DRIVER_PCI=
-BCMA_DRIVER_PCI_HOSTMODE=
-BCMA_DRIVER_MIPS=
-BCMA_SFLASH=
-BCMA_NFLASH=
-BCMA_DRIVER_GMAC_CMN=
-BCMA_DRIVER_GPIO=
-BCMA_DEBUG=
--- a/Makefile.kernel
+++ b/Makefile.kernel
@@ -38,8 +38,6 @@ obj-$(CPTCFG_MAC80211) += net/mac80211/
obj-$(CPTCFG_WLAN) += drivers/net/wireless/
#obj-$(CPTCFG_BT) += net/bluetooth/
#obj-$(CPTCFG_BT) += drivers/bluetooth/
-obj-$(CPTCFG_SSB) += drivers/ssb/
-obj-$(CPTCFG_BCMA) += drivers/bcma/
#obj-$(CPTCFG_ETHERNET) += drivers/net/ethernet/
obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/
#obj-$(CPTCFG_NFC) += net/nfc/
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
NFC=
NFC_DIGITAL=
NFC_NCI=
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -2866,7 +2866,7 @@ static struct ssb_device *b43_ssb_gpio_d
{
struct ssb_bus *bus = dev->dev->sdev->bus;
@ -63,7 +56,7 @@
return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev);
#else
return bus->chipco.dev;
@@ -4907,7 +4907,7 @@ static int b43_wireless_core_init(struct
@@ -4903,7 +4903,7 @@ static int b43_wireless_core_init(struct
}
if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
@ -72,8 +65,8 @@
if (dev->dev->bus_type == B43_BUS_SSB &&
dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
--- a/drivers/net/wireless/broadcom/b43legacy/main.c
+++ b/drivers/net/wireless/broadcom/b43legacy/main.c
@@ -1937,7 +1937,7 @@ static int b43legacy_gpio_init(struct b4
if (dev->dev->id.revision >= 2)
mask |= 0x0010; /* FIXME: This is redundant. */
@ -92,8 +85,8 @@
pcidev = bus->pcicore.dev;
#endif
gpiodev = bus->chipco.dev ? : pcidev;
--- a/drivers/net/wireless/brcm80211/brcmsmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmsmac/Makefile
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
@@ -43,6 +43,6 @@ brcmsmac-y := \
brcms_trace_events.o \
debug.o
@ -102,8 +95,8 @@
+brcmsmac-$(CONFIG_BCMA_DRIVER_GPIO) += led.o
obj-$(CPTCFG_BRCMSMAC) += brcmsmac.o
--- a/drivers/net/wireless/brcm80211/brcmsmac/led.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/led.h
--- 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;
};
@ -116,12 +109,23 @@
--- a/Kconfig.sources
+++ b/Kconfig.sources
@@ -9,9 +9,6 @@ source "$BACKPORT_DIR/drivers/net/wirele
#source "$BACKPORT_DIR/drivers/net/ethernet/Kconfig"
source "$BACKPORT_DIR/drivers/net/ethernet/Kconfig"
source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
-source "$BACKPORT_DIR/drivers/ssb/Kconfig"
-source "$BACKPORT_DIR/drivers/bcma/Kconfig"
-
#source "$BACKPORT_DIR/net/nfc/Kconfig"
source "$BACKPORT_DIR/net/nfc/Kconfig"
#source "$BACKPORT_DIR/drivers/media/Kconfig"
source "$BACKPORT_DIR/drivers/media/Kconfig"
--- a/Makefile.kernel
+++ b/Makefile.kernel
@@ -38,8 +38,6 @@ obj-$(CPTCFG_MAC80211) += net/mac80211/
obj-$(CPTCFG_WLAN) += drivers/net/wireless/
obj-$(CPTCFG_BT) += net/bluetooth/
obj-$(CPTCFG_BT) += drivers/bluetooth/
-obj-$(CPTCFG_SSB) += drivers/ssb/
-obj-$(CPTCFG_BCMA) += drivers/bcma/
obj-$(CPTCFG_ETHERNET) += drivers/net/ethernet/
obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/
obj-$(CPTCFG_NFC) += net/nfc/

View File

@ -1,10 +1,9 @@
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -6,6 +6,7 @@ menuconfig ATH_CARDS
tristate "Atheros Wireless Cards"
@@ -1,5 +1,5 @@
config ATH_COMMON
- tristate
+ tristate "ath.ko"
depends on m
depends on CFG80211 && (!UML || BROKEN)
+ select ATH_COMMON
---help---
This will enable the support for the Atheros wireless drivers.
ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
config WLAN_VENDOR_ATH

View File

@ -1,29 +0,0 @@
--- a/backport-include/linux/wait.h
+++ b/backport-include/linux/wait.h
@@ -23,7 +23,7 @@ backport_wait_on_bit_io(void *word, int
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,12)
#define WQ_FLAG_WOKEN 0x02
#define wait_woken LINUX_BACKPORT(wait_woken)
--- a/compat/backport-3.19.c
+++ b/compat/backport-3.19.c
@@ -15,6 +15,7 @@
#include <linux/netdevice.h>
#include <linux/debugfs.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,12)
static inline bool is_kthread_should_stop(void)
{
return (current->flags & PF_KTHREAD) && kthread_should_stop();
@@ -79,6 +80,7 @@ int woken_wake_function(wait_queue_t *wa
return default_wake_function(wait, mode, sync, key);
}
EXPORT_SYMBOL(woken_wake_function);
+#endif
#ifdef __BACKPORT_NETDEV_RSS_KEY_FILL
u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];

View File

@ -0,0 +1,376 @@
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -5,8 +5,6 @@ config MAC80211
depends on CRYPTO
depends on CRYPTO_ARC4
depends on CRYPTO_AES
- select BPAUTO_CRYPTO_CCM
- depends on CRYPTO_GCM
depends on CRC32
---help---
This option enables the hardware independent IEEE 802.11
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -16,9 +16,7 @@ mac80211-y := \
michael.o \
tkip.o \
aes_ccm.o \
- aes_gcm.o \
aes_cmac.o \
- aes_gmac.o \
cfg.o \
ethtool.o \
rx.o \
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -13,89 +13,132 @@
#include <linux/types.h>
#include <linux/err.h>
#include <crypto/aead.h>
+#include <crypto/aes.h>
#include <net/mac80211.h>
#include "key.h"
#include "aes_ccm.h"
-void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic,
- size_t mic_len)
+static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, u8 *s_0,
+ u8 *a, u8 *b)
{
- struct scatterlist sg[3];
+ 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);
- char aead_req_data[sizeof(struct aead_request) +
- crypto_aead_reqsize(tfm)]
- __aligned(__alignof__(struct aead_request));
- struct aead_request *aead_req = (void *) aead_req_data;
+ /* Mask out bits from auth-only-b_0 */
+ b_0[0] &= 0x07;
- memset(aead_req, 0, sizeof(aead_req_data));
+ /* 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);
+}
- sg_init_table(sg, 3);
- sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
- sg_set_buf(&sg[1], data, data_len);
- sg_set_buf(&sg[2], mic, mic_len);
- aead_request_set_tfm(aead_req, tfm);
- aead_request_set_crypt(aead_req, sg, sg, data_len, b_0);
- aead_request_set_ad(aead_req, sg[0].length);
+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];
+ }
- crypto_aead_encrypt(aead_req);
+ for (i = 0; i < mic_len; i++)
+ mic[i] = b[i] ^ s_0[i];
}
-int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+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)
{
- struct scatterlist sg[3];
- char aead_req_data[sizeof(struct aead_request) +
- crypto_aead_reqsize(tfm)]
- __aligned(__alignof__(struct aead_request));
- struct aead_request *aead_req = (void *) aead_req_data;
-
- if (data_len == 0)
- return -EINVAL;
-
- memset(aead_req, 0, sizeof(aead_req_data));
-
- sg_init_table(sg, 3);
- sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
- sg_set_buf(&sg[1], data, data_len);
- sg_set_buf(&sg[2], mic, mic_len);
-
- aead_request_set_tfm(aead_req, tfm);
- aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0);
- aead_request_set_ad(aead_req, sg[0].length);
+ 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 crypto_aead_decrypt(aead_req);
+ return 0;
}
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
- size_t key_len,
- size_t mic_len)
+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[],
+ size_t key_len,
+ size_t mic_len)
{
- struct crypto_aead *tfm;
- int err;
+ struct crypto_cipher *tfm;
- tfm = crypto_alloc_aead("ccm(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);
- if (err)
- goto free_aead;
+ tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (!IS_ERR(tfm))
+ crypto_cipher_setkey(tfm, key, key_len);
return tfm;
-
-free_aead:
- crypto_free_aead(tfm);
- return ERR_PTR(err);
}
-void ieee80211_aes_key_free(struct crypto_aead *tfm)
+
+void ieee80211_aes_key_free(struct crypto_cipher *tfm)
{
- crypto_free_aead(tfm);
+ crypto_free_cipher(tfm);
}
--- a/net/mac80211/aes_ccm.h
+++ b/net/mac80211/aes_ccm.h
@@ -12,15 +12,15 @@
#include <linux/crypto.h>
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
- size_t key_len,
- size_t mic_len);
-void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+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_aead *tfm, u8 *b_0, u8 *aad,
+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_aead *tfm);
+void ieee80211_aes_key_free(struct crypto_cipher *tfm);
#endif /* AES_CCM_H */
--- a/net/mac80211/aes_gcm.h
+++ b/net/mac80211/aes_gcm.h
@@ -11,12 +11,28 @@
#include <linux/crypto.h>
-void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
- 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);
-struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
- size_t key_len);
-void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm);
+static inline void
+ieee80211_aes_gcm_encrypt(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 -EOPNOTSUPP;
+}
+
+static inline struct crypto_aead *
+ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len)
+{
+ return NULL;
+}
+
+static inline void
+ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
+{
+}
#endif /* AES_GCM_H */
--- a/net/mac80211/aes_gmac.h
+++ b/net/mac80211/aes_gmac.h
@@ -11,10 +11,22 @@
#include <linux/crypto.h>
-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 */
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -84,7 +84,7 @@ 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 {
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -307,7 +307,8 @@ ieee80211_crypto_tkip_decrypt(struct iee
}
-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;
@@ -337,14 +338,8 @@ static void ccmp_special_blocks(struct s
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)
@@ -352,6 +347,8 @@ static void ccmp_special_blocks(struct s
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] */
@@ -463,7 +460,7 @@ static int ccmp_encrypt_skb(struct ieee8
return 0;
pos += IEEE80211_CCMP_HDR_LEN;
- ccmp_special_blocks(skb, pn, b_0, aad);
+ 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);
@@ -534,7 +531,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee
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,

View File

@ -2,7 +2,7 @@ Used for AP+STA support in OpenWrt - preserve AP mode keys across STA reconnects
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -856,7 +856,6 @@ static int ieee80211_stop_ap(struct wiph
@@ -846,7 +846,6 @@ static int ieee80211_stop_ap(struct wiph
sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF;
__sta_info_flush(sdata, true);

View File

@ -1,6 +1,6 @@
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -287,7 +287,7 @@ void ieee80211_restart_hw(struct ieee802
@@ -291,7 +291,7 @@ void ieee80211_restart_hw(struct ieee802
}
EXPORT_SYMBOL(ieee80211_restart_hw);
@ -9,7 +9,7 @@
static int ieee80211_ifa_changed(struct notifier_block *nb,
unsigned long data, void *arg)
{
@@ -346,7 +346,7 @@ static int ieee80211_ifa_changed(struct
@@ -350,7 +350,7 @@ static int ieee80211_ifa_changed(struct
}
#endif
@ -18,9 +18,9 @@
static int ieee80211_ifa6_changed(struct notifier_block *nb,
unsigned long data, void *arg)
{
@@ -1057,14 +1057,14 @@ int ieee80211_register_hw(struct ieee802
if (result)
goto fail_pm_qos;
@@ -1087,14 +1087,14 @@ int ieee80211_register_hw(struct ieee802
rtnl_unlock();
-#ifdef CONFIG_INET
+#ifdef __disabled__CONFIG_INET
@ -35,7 +35,7 @@
local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
result = register_inet6addr_notifier(&local->ifa6_notifier);
if (result)
@@ -1073,13 +1073,13 @@ int ieee80211_register_hw(struct ieee802
@@ -1103,13 +1103,13 @@ int ieee80211_register_hw(struct ieee802
return 0;
@ -50,12 +50,12 @@
-#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
+#if defined(__disabled__CONFIG_INET) || defined(__disabled__CONFIG_IPV6)
fail_ifa:
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
@@ -1124,10 +1124,10 @@ void ieee80211_unregister_hw(struct ieee
#endif
rtnl_lock();
@@ -1137,10 +1137,10 @@ void ieee80211_unregister_hw(struct ieee
tasklet_kill(&local->tx_pending_tasklet);
tasklet_kill(&local->tasklet);
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
-#ifdef CONFIG_INET
+#ifdef __disabled__CONFIG_INET
unregister_inetaddr_notifier(&local->ifa_notifier);

View File

@ -1,6 +1,6 @@
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1963,7 +1963,7 @@ static int ieee80211_scan(struct wiphy *
@@ -1999,7 +1999,7 @@ static int ieee80211_scan(struct wiphy *
* the frames sent while scanning on other channel will be
* lost)
*/

View File

@ -1,882 +0,0 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Tue, 18 Nov 2014 23:58:51 +0100
Subject: [PATCH] mac80211: add an intermediate software queue implementation
This allows drivers to request per-vif and per-sta-tid queues from which
they can pull frames. This makes it easier to keep the hardware queues
short, and to improve fairness between clients and vifs.
The task of scheduling packet transmission is left up to the driver -
queueing is controlled by mac80211. Drivers can only dequeue packets by
calling ieee80211_tx_dequeue. This makes it possible to add active queue
management later without changing drivers using this code.
This can also be used as a starting point to implement A-MSDU
aggregation in a way that does not add artificially induced latency.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -84,6 +84,39 @@
*
*/
+/**
+ * DOC: mac80211 software tx queueing
+ *
+ * mac80211 provides an optional intermediate queueing implementation designed
+ * to allow the driver to keep hardware queues short and provide some fairness
+ * between different stations/interfaces.
+ * In this model, the driver pulls data frames from the mac80211 queue instead
+ * of letting mac80211 push them via drv_tx().
+ * Other frames (e.g. control or management) are still pushed using drv_tx().
+ *
+ * Drivers indicate that they use this model by implementing the .wake_tx_queue
+ * driver operation.
+ *
+ * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with a
+ * single per-vif queue for multicast data frames.
+ *
+ * The driver is expected to initialize its private per-queue data for stations
+ * and interfaces in the .add_interface and .sta_add ops.
+ *
+ * The driver can't access the queue directly. To dequeue a frame, it calls
+ * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it
+ * calls the .wake_tx_queue driver op.
+ *
+ * For AP powersave TIM handling, the driver only needs to indicate if it has
+ * buffered packets in the driver specific data structures by calling
+ * ieee80211_sta_set_buffered(). For frames buffered in the ieee80211_txq
+ * struct, mac80211 sets the appropriate TIM PVB bits and calls
+ * .release_buffered_frames().
+ * In that callback the driver is therefore expected to release its own
+ * buffered frames and afterwards also frames from the ieee80211_txq (obtained
+ * via the usual ieee80211_tx_dequeue).
+ */
+
struct device;
/**
@@ -1246,6 +1279,7 @@ enum ieee80211_vif_flags {
* monitor interface (if that is requested.)
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *).
+ * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
*/
struct ieee80211_vif {
enum nl80211_iftype type;
@@ -1257,6 +1291,8 @@ struct ieee80211_vif {
u8 cab_queue;
u8 hw_queue[IEEE80211_NUM_ACS];
+ struct ieee80211_txq *txq;
+
struct ieee80211_chanctx_conf __rcu *chanctx_conf;
u32 driver_flags;
@@ -1501,6 +1537,7 @@ struct ieee80211_sta_rates {
* @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
* valid if the STA is a TDLS peer in the first place.
* @mfp: indicates whether the STA uses management frame protection or not.
+ * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
*/
struct ieee80211_sta {
u32 supp_rates[IEEE80211_NUM_BANDS];
@@ -1519,6 +1556,8 @@ struct ieee80211_sta {
bool tdls_initiator;
bool mfp;
+ struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
+
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
@@ -1547,6 +1586,27 @@ struct ieee80211_tx_control {
};
/**
+ * struct ieee80211_txq - Software intermediate tx queue
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @sta: station table entry, %NULL for per-vif queue
+ * @tid: the TID for this queue (unused for per-vif queue)
+ * @ac: the AC for this queue
+ *
+ * The driver can obtain packets from this queue by calling
+ * ieee80211_tx_dequeue().
+ */
+struct ieee80211_txq {
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ u8 tid;
+ u8 ac;
+
+ /* must be last */
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+/**
* enum ieee80211_hw_flags - hardware flags
*
* These flags are used to indicate hardware capabilities to
@@ -1770,6 +1830,8 @@ enum ieee80211_hw_flags {
* within &struct ieee80211_sta.
* @chanctx_data_size: size (in bytes) of the drv_priv data area
* within &struct ieee80211_chanctx_conf.
+ * @txq_data_size: size (in bytes) of the drv_priv data area
+ * within @struct ieee80211_txq.
*
* @max_rates: maximum number of alternate rate retry stages the hw
* can handle.
@@ -1818,6 +1880,9 @@ enum ieee80211_hw_flags {
* @n_cipher_schemes: a size of an array of cipher schemes definitions.
* @cipher_schemes: a pointer to an array of cipher scheme definitions
* supported by HW.
+ *
+ * @txq_ac_max_pending: maximum number of frames per AC pending in all txq
+ * entries for a vif.
*/
struct ieee80211_hw {
struct ieee80211_conf conf;
@@ -1830,6 +1895,7 @@ struct ieee80211_hw {
int vif_data_size;
int sta_data_size;
int chanctx_data_size;
+ int txq_data_size;
u16 queues;
u16 max_listen_interval;
s8 max_signal;
@@ -1846,6 +1912,7 @@ struct ieee80211_hw {
u8 uapsd_max_sp_len;
u8 n_cipher_schemes;
const struct ieee80211_cipher_scheme *cipher_schemes;
+ int txq_ac_max_pending;
};
/**
@@ -3007,6 +3074,8 @@ enum ieee80211_reconfig_type {
* response template is provided, together with the location of the
* switch-timing IE within the template. The skb can only be used within
* the function call.
+ *
+ * @wake_tx_queue: Called when new packets have been added to the queue.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -3238,6 +3307,9 @@ struct ieee80211_ops {
void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_tdls_ch_sw_params *params);
+
+ void (*wake_tx_queue)(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
};
/**
@@ -5249,4 +5321,15 @@ void ieee80211_unreserve_tid(struct ieee
*/
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
const u8 *ids, int n_ids, size_t offset);
+
+/**
+ * ieee80211_tx_dequeue - dequeue a packet from a software tx queue
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface
+ *
+ * Returns the skb if successful, %NULL if no frame was available.
+ */
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
#endif /* MAC80211_H */
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1367,4 +1367,16 @@ drv_tdls_recv_channel_switch(struct ieee
trace_drv_return_void(local);
}
+static inline void drv_wake_tx_queue(struct ieee80211_local *local,
+ struct txq_info *txq)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif);
+
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ trace_drv_wake_tx_queue(local, sdata, txq);
+ local->ops->wake_tx_queue(&local->hw, &txq->txq);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -809,6 +809,19 @@ struct mac80211_qos_map {
struct rcu_head rcu_head;
};
+enum txq_info_flags {
+ IEEE80211_TXQ_STOP,
+ IEEE80211_TXQ_AMPDU,
+};
+
+struct txq_info {
+ struct sk_buff_head queue;
+ unsigned long flags;
+
+ /* keep last! */
+ struct ieee80211_txq txq;
+};
+
struct ieee80211_sub_if_data {
struct list_head list;
@@ -853,6 +866,7 @@ struct ieee80211_sub_if_data {
bool control_port_no_encrypt;
int encrypt_headroom;
+ atomic_t txqs_len[IEEE80211_NUM_ACS];
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
struct mac80211_qos_map __rcu *qos_map;
@@ -1453,6 +1467,10 @@ static inline struct ieee80211_local *hw
return container_of(hw, struct ieee80211_local, hw);
}
+static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq)
+{
+ return container_of(txq, struct txq_info, txq);
+}
static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
{
@@ -1905,6 +1923,9 @@ static inline bool ieee80211_can_run_wor
return true;
}
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct txq_info *txq, int tid);
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
const u8 *extra, size_t extra_len, const u8 *bssid,
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -969,6 +969,13 @@ static void ieee80211_do_stop(struct iee
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ if (sdata->vif.txq) {
+ struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+
+ ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+ atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
+ }
+
if (local->open_count == 0)
ieee80211_clear_tx_pending(local);
@@ -1674,6 +1681,7 @@ int ieee80211_if_add(struct ieee80211_lo
{
struct net_device *ndev = NULL;
struct ieee80211_sub_if_data *sdata = NULL;
+ struct txq_info *txqi;
int ret, i;
int txqs = 1;
@@ -1693,10 +1701,18 @@ int ieee80211_if_add(struct ieee80211_lo
ieee80211_assign_perm_addr(local, wdev->address, type);
memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
} else {
+ int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
+ sizeof(void *));
+ int txq_size = 0;
+
+ if (local->ops->wake_tx_queue)
+ txq_size += sizeof(struct txq_info) +
+ local->hw.txq_data_size;
+
if (local->hw.queues >= IEEE80211_NUM_ACS)
txqs = IEEE80211_NUM_ACS;
- ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
+ ndev = alloc_netdev_mqs(size + txq_size,
name, NET_NAME_UNKNOWN,
ieee80211_if_setup, txqs, 1);
if (!ndev)
@@ -1731,6 +1747,11 @@ int ieee80211_if_add(struct ieee80211_lo
memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
memcpy(sdata->name, ndev->name, IFNAMSIZ);
+ if (txq_size) {
+ txqi = netdev_priv(ndev) + size;
+ ieee80211_init_tx_queue(sdata, NULL, txqi, 0);
+ }
+
sdata->dev = ndev;
}
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1019,6 +1019,9 @@ int ieee80211_register_hw(struct ieee802
local->dynamic_ps_forced_timeout = -1;
+ if (!local->hw.txq_ac_max_pending)
+ local->hw.txq_ac_max_pending = 64;
+
result = ieee80211_wep_init(local);
if (result < 0)
wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -118,6 +118,16 @@ static void __cleanup_single_sta(struct
atomic_dec(&ps->num_sta_ps);
}
+ if (sta->sta.txq[0]) {
+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+ int n = skb_queue_len(&txqi->queue);
+
+ ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+ atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]);
+ }
+ }
+
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
@@ -234,6 +244,8 @@ void sta_info_free(struct ieee80211_loca
sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
+ if (sta->sta.txq[0])
+ kfree(to_txq_info(sta->sta.txq[0]));
kfree(rcu_dereference_raw(sta->sta.rates));
kfree(sta);
}
@@ -285,11 +297,12 @@ struct sta_info *sta_info_alloc(struct i
const u8 *addr, gfp_t gfp)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
struct timespec uptime;
int i;
- sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
+ sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
if (!sta)
return NULL;
@@ -321,11 +334,25 @@ struct sta_info *sta_info_alloc(struct i
for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
ewma_init(&sta->chain_signal_avg[i], 1024, 8);
- if (sta_prepare_rate_control(local, sta, gfp)) {
- kfree(sta);
- return NULL;
+ if (local->ops->wake_tx_queue) {
+ void *txq_data;
+ int size = sizeof(struct txq_info) +
+ ALIGN(hw->txq_data_size, sizeof(void *));
+
+ txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
+ if (!txq_data)
+ goto free;
+
+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+ struct txq_info *txq = txq_data + i * size;
+
+ ieee80211_init_tx_queue(sdata, sta, txq, i);
+ }
}
+ if (sta_prepare_rate_control(local, sta, gfp))
+ goto free_txq;
+
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/*
* timer_to_tid must be initialized with identity mapping
@@ -346,7 +373,7 @@ struct sta_info *sta_info_alloc(struct i
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
struct ieee80211_supported_band *sband =
- local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+ hw->wiphy->bands[ieee80211_get_sdata_band(sdata)];
u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
IEEE80211_HT_CAP_SM_PS_SHIFT;
/*
@@ -371,6 +398,13 @@ struct sta_info *sta_info_alloc(struct i
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
return sta;
+
+free_txq:
+ if (sta->sta.txq[0])
+ kfree(to_txq_info(sta->sta.txq[0]));
+free:
+ kfree(sta);
+ return NULL;
}
static int sta_info_insert_check(struct sta_info *sta)
@@ -640,6 +674,8 @@ static void __sta_info_recalc_tim(struct
indicate_tim |=
sta->driver_buffered_tids & tids;
+ indicate_tim |=
+ sta->txq_buffered_tids & tids;
}
done:
@@ -1071,7 +1107,7 @@ void ieee80211_sta_ps_deliver_wakeup(str
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct sk_buff_head pending;
- int filtered = 0, buffered = 0, ac;
+ int filtered = 0, buffered = 0, ac, i;
unsigned long flags;
struct ps_data *ps;
@@ -1090,10 +1126,22 @@ void ieee80211_sta_ps_deliver_wakeup(str
BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
sta->driver_buffered_tids = 0;
+ sta->txq_buffered_tids = 0;
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
+ if (sta->sta.txq[0]) {
+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+
+ if (!skb_queue_len(&txqi->queue))
+ continue;
+
+ drv_wake_tx_queue(local, txqi);
+ }
+ }
+
skb_queue_head_init(&pending);
/* sync with ieee80211_tx_h_unicast_ps_buf */
@@ -1275,8 +1323,10 @@ ieee80211_sta_ps_deliver_response(struct
/* if we already have frames from software, then we can't also
* release from hardware queues
*/
- if (skb_queue_empty(&frames))
+ if (skb_queue_empty(&frames)) {
driver_release_tids |= sta->driver_buffered_tids & tids;
+ driver_release_tids |= sta->txq_buffered_tids & tids;
+ }
if (driver_release_tids) {
/* If the driver has data on more than one TID then
@@ -1447,6 +1497,9 @@ ieee80211_sta_ps_deliver_response(struct
sta_info_recalc_tim(sta);
} else {
+ unsigned long tids = sta->txq_buffered_tids & driver_release_tids;
+ int tid;
+
/*
* We need to release a frame that is buffered somewhere in the
* driver ... it'll have to handle that.
@@ -1466,8 +1519,22 @@ ieee80211_sta_ps_deliver_response(struct
* that the TID(s) became empty before returning here from the
* release function.
* Either way, however, when the driver tells us that the TID(s)
- * became empty we'll do the TIM recalculation.
+ * became empty or we find that a txq became empty, we'll do the
+ * TIM recalculation.
*/
+
+ if (!sta->sta.txq[0])
+ return;
+
+ for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+ if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue))
+ continue;
+
+ sta_info_recalc_tim(sta);
+ break;
+ }
}
}
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -274,6 +274,7 @@ struct sta_ampdu_mlme {
* entered power saving state, these are also delivered to
* the station when it leaves powersave or polls for frames
* @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
+ * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on
* @rx_packets: Number of MSDUs received from this STA
* @rx_bytes: Number of bytes received from this STA
* @last_rx: time (in jiffies) when last frame was received from this STA
@@ -368,6 +369,7 @@ struct sta_info {
struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
unsigned long driver_buffered_tids;
+ unsigned long txq_buffered_tids;
/* Updated from RX path only, no locking requirements */
unsigned long rx_packets;
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -2312,6 +2312,37 @@ TRACE_EVENT(drv_tdls_recv_channel_switch
)
);
+TRACE_EVENT(drv_wake_tx_queue,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct txq_info *txq),
+
+ TP_ARGS(local, sdata, txq),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u8, ac)
+ __field(u8, tid)
+ ),
+
+ TP_fast_assign(
+ struct ieee80211_sta *sta = txq->txq.sta;
+
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->ac = txq->txq.ac;
+ __entry->tid = txq->txq.tid;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ac:%d tid:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid
+ )
+);
+
#ifdef CPTCFG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mac80211_msg
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -776,12 +776,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
return TX_CONTINUE;
}
+static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid)
+{
+ u16 *seq = &sta->tid_seq[tid];
+ __le16 ret = cpu_to_le16(*seq);
+
+ /* Increase the sequence number. */
+ *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+
+ return ret;
+}
+
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_sequence(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;
- u16 *seq;
u8 *qc;
int tid;
@@ -832,13 +842,10 @@ ieee80211_tx_h_sequence(struct ieee80211
qc = ieee80211_get_qos_ctl(hdr);
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
- seq = &tx->sta->tid_seq[tid];
tx->sta->tx_msdu[tid]++;
- hdr->seq_ctrl = cpu_to_le16(*seq);
-
- /* Increase the sequence number. */
- *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+ if (!tx->sta->sta.txq[0])
+ hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
return TX_CONTINUE;
}
@@ -1067,7 +1074,7 @@ static bool ieee80211_tx_prep_agg(struct
* nothing -- this aggregation session is being started
* but that might still fail with the driver
*/
- } else {
+ } else if (!tx->sta->sta.txq[tid]) {
spin_lock(&tx->sta->lock);
/*
* Need to re-check now, because we may get here
@@ -1201,13 +1208,102 @@ ieee80211_tx_prepare(struct ieee80211_su
return TX_CONTINUE;
}
+static void ieee80211_drv_tx(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *pubsta,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_control control = {
+ .sta = pubsta,
+ };
+ struct ieee80211_txq *txq = NULL;
+ struct txq_info *txqi;
+ u8 ac;
+
+ if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)
+ goto tx_normal;
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ goto tx_normal;
+
+ if (pubsta) {
+ u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+ txq = pubsta->txq[tid];
+ } else if (vif) {
+ txq = vif->txq;
+ }
+
+ if (!txq)
+ goto tx_normal;
+
+ ac = txq->ac;
+ txqi = to_txq_info(txq);
+ atomic_inc(&sdata->txqs_len[ac]);
+ if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending)
+ netif_stop_subqueue(sdata->dev, ac);
+
+ skb_queue_tail(&txqi->queue, skb);
+ drv_wake_tx_queue(local, txqi);
+
+ return;
+
+tx_normal:
+ drv_tx(local, &control, skb);
+}
+
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
+ struct txq_info *txqi = container_of(txq, struct txq_info, txq);
+ struct ieee80211_hdr *hdr;
+ struct sk_buff *skb = NULL;
+ u8 ac = txq->ac;
+
+ spin_lock_bh(&txqi->queue.lock);
+
+ if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
+ goto out;
+
+ skb = __skb_dequeue(&txqi->queue);
+ if (!skb)
+ goto out;
+
+ atomic_dec(&sdata->txqs_len[ac]);
+ if (__netif_subqueue_stopped(sdata->dev, ac))
+ ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]);
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+ struct sta_info *sta = container_of(txq->sta, struct sta_info,
+ sta);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
+ if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
+ else
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ }
+
+out:
+ spin_unlock_bh(&txqi->queue.lock);
+
+ return skb;
+}
+EXPORT_SYMBOL(ieee80211_tx_dequeue);
+
static bool ieee80211_tx_frags(struct ieee80211_local *local,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct sk_buff_head *skbs,
bool txpending)
{
- struct ieee80211_tx_control control;
struct sk_buff *skb, *tmp;
unsigned long flags;
@@ -1265,10 +1361,9 @@ static bool ieee80211_tx_frags(struct ie
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
info->control.vif = vif;
- control.sta = sta;
__skb_unlink(skb, skbs);
- drv_tx(local, &control, skb);
+ ieee80211_drv_tx(local, vif, sta, skb);
}
return true;
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -308,6 +308,11 @@ void ieee80211_propagate_queue_wake(stru
for (ac = 0; ac < n_acs; ac++) {
int ac_queue = sdata->vif.hw_queue[ac];
+ if (local->ops->wake_tx_queue &&
+ (atomic_read(&sdata->txqs_len[ac]) >
+ local->hw.txq_ac_max_pending))
+ continue;
+
if (ac_queue == queue ||
(sdata->vif.cab_queue == queue &&
local->queue_stop_reasons[ac_queue] == 0 &&
@@ -3307,3 +3312,20 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u
return buf;
}
+
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct txq_info *txqi, int tid)
+{
+ skb_queue_head_init(&txqi->queue);
+ txqi->txq.vif = &sdata->vif;
+
+ if (sta) {
+ txqi->txq.sta = &sta->sta;
+ sta->sta.txq[tid] = &txqi->txq;
+ txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
+ } else {
+ sdata->vif.txq = &txqi->txq;
+ txqi->txq.ac = IEEE80211_AC_BE;
+ }
+}
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1176,6 +1176,7 @@ static void sta_ps_start(struct sta_info
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ps_data *ps;
+ int tid;
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1189,6 +1190,18 @@ static void sta_ps_start(struct sta_info
drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
ps_dbg(sdata, "STA %pM aid %d enters power save mode\n",
sta->sta.addr, sta->sta.aid);
+
+ if (!sta->sta.txq[0])
+ return;
+
+ for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+ if (!skb_queue_len(&txqi->queue))
+ set_bit(tid, &sta->txq_buffered_tids);
+ else
+ clear_bit(tid, &sta->txq_buffered_tids);
+ }
}
static void sta_ps_end(struct sta_info *sta)
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -188,6 +188,43 @@ ieee80211_wake_queue_agg(struct ieee8021
__release(agg_queue);
}
+static void
+ieee80211_agg_stop_txq(struct sta_info *sta, int tid)
+{
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
+ struct txq_info *txqi;
+
+ if (!txq)
+ return;
+
+ txqi = to_txq_info(txq);
+
+ /* Lock here to protect against further seqno updates on dequeue */
+ spin_lock_bh(&txqi->queue.lock);
+ set_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+ spin_unlock_bh(&txqi->queue.lock);
+}
+
+static void
+ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable)
+{
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
+ struct txq_info *txqi;
+
+ if (!txq)
+ return;
+
+ txqi = to_txq_info(txq);
+
+ if (enable)
+ set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+ else
+ clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+
+ clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+ drv_wake_tx_queue(sta->sdata->local, txqi);
+}
+
/*
* splice packets from the STA's pending to the local pending,
* requires a call to ieee80211_agg_splice_finish later
@@ -247,6 +284,7 @@ static void ieee80211_remove_tid_tx(stru
ieee80211_assign_tid_tx(sta, tid, NULL);
ieee80211_agg_splice_finish(sta->sdata, tid);
+ ieee80211_agg_start_txq(sta, tid, false);
kfree_rcu(tid_tx, rcu_head);
}
@@ -418,6 +456,8 @@ void ieee80211_tx_ba_session_handle_star
*/
clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
+ ieee80211_agg_stop_txq(sta, tid);
+
/*
* Make sure no packets are being processed. This ensures that
* we have a valid starting sequence number and that in-flight
@@ -440,6 +480,8 @@ void ieee80211_tx_ba_session_handle_star
ieee80211_agg_splice_finish(sdata, tid);
spin_unlock_bh(&sta->lock);
+ ieee80211_agg_start_txq(sta, tid, false);
+
kfree_rcu(tid_tx, rcu_head);
return;
}
@@ -666,6 +708,8 @@ static void ieee80211_agg_tx_operational
ieee80211_agg_splice_finish(sta->sdata, tid);
spin_unlock_bh(&sta->lock);
+
+ ieee80211_agg_start_txq(sta, tid, true);
}
void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)

View File

@ -0,0 +1,121 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Thu, 2 Jul 2015 15:20:56 +0200
Subject: [PATCH] ath9k: limit retries for powersave response frames
In some cases, the channel might be busy enough that an ath9k AP's
response to PS-Poll frames might be too slow and the station has already
gone to sleep. To avoid wasting too much airtime on this, limit the
number of retries on such frames and ensure that no sample rate gets
used.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -136,10 +136,25 @@ static void ath_send_bar(struct ath_atx_
}
static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct ath_buf *bf)
+ struct ath_buf *bf, bool ps)
{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(bf->bf_mpdu);
+
+ if (ps) {
+ /* Clear the first rate to avoid using a sample rate for PS frames */
+ info->control.rates[0].idx = -1;
+ info->control.rates[0].count = 0;
+ }
+
ieee80211_get_tx_rates(vif, sta, bf->bf_mpdu, bf->rates,
ARRAY_SIZE(bf->rates));
+ if (!ps)
+ return;
+
+ if (bf->rates[0].count > 2)
+ bf->rates[0].count = 2;
+
+ bf->rates[1].idx = -1;
}
static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
@@ -1419,7 +1434,7 @@ ath_tx_form_burst(struct ath_softc *sc,
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
break;
- ath_set_rates(tid->an->vif, tid->an->sta, bf);
+ ath_set_rates(tid->an->vif, tid->an->sta, bf, false);
} while (1);
}
@@ -1450,7 +1465,7 @@ static bool ath_tx_sched_aggr(struct ath
return false;
}
- ath_set_rates(tid->an->vif, tid->an->sta, bf);
+ ath_set_rates(tid->an->vif, tid->an->sta, bf, false);
if (aggr)
last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf,
tid_q, &aggr_len);
@@ -1647,7 +1662,7 @@ void ath9k_release_buffered_frames(struc
__skb_unlink(bf->bf_mpdu, tid_q);
list_add_tail(&bf->list, &bf_q);
- ath_set_rates(tid->an->vif, tid->an->sta, bf);
+ ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
if (bf_isampdu(bf)) {
ath_tx_addto_baw(sc, tid, bf);
bf->bf_state.bf_type &= ~BUF_AGGR;
@@ -2293,7 +2308,7 @@ int ath_tx_start(struct ieee80211_hw *hw
struct ath_txq *txq = txctl->txq;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf;
- bool queue, skip_uapsd = false, ps_resp;
+ bool queue, ps_resp;
int q, ret;
if (vif)
@@ -2346,13 +2361,13 @@ int ath_tx_start(struct ieee80211_hw *hw
if (!txctl->an)
txctl->an = &avp->mcast_node;
queue = true;
- skip_uapsd = true;
+ ps_resp = false;
}
if (txctl->an && queue)
tid = ath_get_skb_tid(sc, txctl->an, skb);
- if (!skip_uapsd && ps_resp) {
+ if (ps_resp) {
ath_txq_unlock(sc, txq);
txq = sc->tx.uapsdq;
ath_txq_lock(sc, txq);
@@ -2390,7 +2405,7 @@ int ath_tx_start(struct ieee80211_hw *hw
if (txctl->paprd)
bf->bf_state.bfs_paprd_timestamp = jiffies;
- ath_set_rates(vif, sta, bf);
+ ath_set_rates(vif, sta, bf, ps_resp);
ath_tx_send_normal(sc, txq, tid, skb);
out:
@@ -2429,7 +2444,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw
break;
bf->bf_lastbf = bf;
- ath_set_rates(vif, NULL, bf);
+ ath_set_rates(vif, NULL, bf, false);
ath_buf_set_rate(sc, bf, &info, fi->framelen, false);
duration += info.rates[0].PktDuration;
if (bf_tail)
@@ -2932,7 +2947,7 @@ int ath9k_tx99_send(struct ath_softc *sc
return -EINVAL;
}
- ath_set_rates(sc->tx99_vif, NULL, bf);
+ ath_set_rates(sc->tx99_vif, NULL, bf, false);
ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr);
ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum);

View File

@ -1,125 +0,0 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Wed, 11 Mar 2015 09:14:15 +0100
Subject: [PATCH] mac80211: lock rate control
Both minstrel (reported by Sven Eckelmann) and the iwlwifi rate
control aren't properly taking concurrency into account. It's
likely that the same is true for other rate control algorithms.
In the case of minstrel this manifests itself in crashes when an
update and other data access are run concurrently, for example
when the stations change bandwidth or similar. In iwlwifi, this
can cause firmware crashes.
Since fixing all rate control algorithms will be very difficult,
just provide locking for invocations. This protects the internal
data structures the algorithms maintain.
I've manipulated hostapd to test this, by having it change its
advertised bandwidth roughly ever 150ms. At the same time, I'm
running a flood ping between the client and the AP, which causes
this race of update vs. get_rate/status to easily happen on the
client. With this change, the system survives this test.
Reported-by: Sven Eckelmann <sven@open-mesh.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -683,7 +683,13 @@ void rate_control_get_rate(struct ieee80
if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
return;
- ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
+ if (ista) {
+ spin_lock_bh(&sta->rate_ctrl_lock);
+ ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
+ spin_unlock_bh(&sta->rate_ctrl_lock);
+ } else {
+ ref->ops->get_rate(ref->priv, NULL, NULL, txrc);
+ }
if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_RC_TABLE)
return;
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -42,10 +42,12 @@ static inline void rate_control_tx_statu
if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
return;
+ spin_lock_bh(&sta->rate_ctrl_lock);
if (ref->ops->tx_status)
ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
else
ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
+ spin_unlock_bh(&sta->rate_ctrl_lock);
}
static inline void
@@ -64,7 +66,9 @@ rate_control_tx_status_noskb(struct ieee
if (WARN_ON_ONCE(!ref->ops->tx_status_noskb))
return;
+ spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
+ spin_unlock_bh(&sta->rate_ctrl_lock);
}
static inline void rate_control_rate_init(struct sta_info *sta)
@@ -91,8 +95,10 @@ static inline void rate_control_rate_ini
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
+ spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
priv_sta);
+ spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
}
@@ -115,18 +121,20 @@ static inline void rate_control_rate_upd
return;
}
+ spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
ista, priv_sta, changed);
+ spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
}
drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
}
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
- struct ieee80211_sta *sta,
- gfp_t gfp)
+ struct sta_info *sta, gfp_t gfp)
{
- return ref->ops->alloc_sta(ref->priv, sta, gfp);
+ spin_lock_init(&sta->rate_ctrl_lock);
+ return ref->ops->alloc_sta(ref->priv, &sta->sta, gfp);
}
static inline void rate_control_free_sta(struct sta_info *sta)
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -286,7 +286,7 @@ static int sta_prepare_rate_control(stru
sta->rate_ctrl = local->rate_ctrl;
sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
- &sta->sta, gfp);
+ sta, gfp);
if (!sta->rate_ctrl_priv)
return -ENOMEM;
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -349,6 +349,7 @@ struct sta_info {
u8 ptk_idx;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
+ spinlock_t rate_ctrl_lock;
spinlock_t lock;
struct work_struct drv_deliver_wk;

View File

@ -1,21 +0,0 @@
From: Zefir Kurtisi <zefir.kurtisi@neratec.com>
Date: Tue, 10 Mar 2015 17:49:29 +0100
Subject: [PATCH] ath9k: restart only triggering DFS detector line
To support HT40 DFS mode, a triggering detector must
reset only itself but not other detector lines.
Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com>
---
--- a/drivers/net/wireless/ath/dfs_pattern_detector.c
+++ b/drivers/net/wireless/ath/dfs_pattern_detector.c
@@ -289,7 +289,7 @@ dpd_add_pulse(struct dfs_pattern_detecto
"count=%d, count_false=%d\n",
event->freq, pd->rs->type_id,
ps->pri, ps->count, ps->count_falses);
- channel_detector_reset(dpd, cd);
+ pd->reset(pd, dpd->last_pulse_ts);
return true;
}
}

View File

@ -0,0 +1,95 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Thu, 14 Jan 2016 03:14:03 +0100
Subject: [PATCH] ath9k_hw: add low power tx gain table for AR953x
Used in some newer TP-Link AR9533 devices.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -698,6 +698,9 @@ static void ar9003_tx_gain_table_mode2(s
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9340Modes_low_ob_db_tx_gain_table_1p0);
+ else if (AR_SREV_9531_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p1_modes_no_xpa_low_power_tx_gain_table);
else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_low_ob_db_tx_gain_1_1);
--- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
@@ -757,6 +757,71 @@ static const u32 qca953x_1p1_modes_xpa_t
{0x00016448, 0x6c927a70},
};
+static const u32 qca953x_1p1_modes_no_xpa_low_power_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xfff55592},
+ {0x0000a2e0, 0xfff99924},
+ {0x0000a2e4, 0xfffe1e00},
+ {0x0000a2e8, 0xffffe000},
+ {0x0000a410, 0x000050d6},
+ {0x0000a500, 0x00000069},
+ {0x0000a504, 0x0400006b},
+ {0x0000a508, 0x0800006d},
+ {0x0000a50c, 0x0c000269},
+ {0x0000a510, 0x1000026b},
+ {0x0000a514, 0x1400026d},
+ {0x0000a518, 0x18000669},
+ {0x0000a51c, 0x1c00066b},
+ {0x0000a520, 0x1d000a68},
+ {0x0000a524, 0x21000a6a},
+ {0x0000a528, 0x25000a6c},
+ {0x0000a52c, 0x29000a6e},
+ {0x0000a530, 0x2d0012a9},
+ {0x0000a534, 0x310012ab},
+ {0x0000a538, 0x350012ad},
+ {0x0000a53c, 0x39001b0a},
+ {0x0000a540, 0x3d001b0c},
+ {0x0000a544, 0x41001b0e},
+ {0x0000a548, 0x43001bae},
+ {0x0000a54c, 0x45001914},
+ {0x0000a550, 0x47001916},
+ {0x0000a554, 0x49001b96},
+ {0x0000a558, 0x49001b96},
+ {0x0000a55c, 0x49001b96},
+ {0x0000a560, 0x49001b96},
+ {0x0000a564, 0x49001b96},
+ {0x0000a568, 0x49001b96},
+ {0x0000a56c, 0x49001b96},
+ {0x0000a570, 0x49001b96},
+ {0x0000a574, 0x49001b96},
+ {0x0000a578, 0x49001b96},
+ {0x0000a57c, 0x49001b96},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x00000000},
+ {0x0000a610, 0x00000000},
+ {0x0000a614, 0x00000000},
+ {0x0000a618, 0x00804201},
+ {0x0000a61c, 0x01408201},
+ {0x0000a620, 0x01408502},
+ {0x0000a624, 0x01408502},
+ {0x0000a628, 0x01408502},
+ {0x0000a62c, 0x01408502},
+ {0x0000a630, 0x01408502},
+ {0x0000a634, 0x01408502},
+ {0x0000a638, 0x01408502},
+ {0x0000a63c, 0x01408502},
+ {0x0000b2dc, 0xfff55592},
+ {0x0000b2e0, 0xfff99924},
+ {0x0000b2e4, 0xfffe1e00},
+ {0x0000b2e8, 0xffffe000},
+ {0x00016044, 0x044922db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x044922db},
+ {0x00016448, 0x6c927a70},
+};
+
static const u32 qca953x_2p0_baseband_core[][2] = {
/* Addr allmodes */
{0x00009800, 0xafe68e30},

View File

@ -1,76 +0,0 @@
From: Zefir Kurtisi <zefir.kurtisi@neratec.com>
Date: Tue, 10 Mar 2015 17:49:30 +0100
Subject: [PATCH] ath9k: add DFS support for extension channel
In HT40 modes, pulse events on primary and extension
channel are processed individually. If valid, a pulse
event will be fed into the detector
* for primary frequency, or
* for extension frequency (+/-20MHz based on HT40-mode)
* or both
With that, a 40MHz radar will result in two individual
radar events.
Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com>
---
--- a/drivers/net/wireless/ath/ath9k/dfs.c
+++ b/drivers/net/wireless/ath/ath9k/dfs.c
@@ -126,8 +126,19 @@ ath9k_postprocess_radar_event(struct ath
DFS_STAT_INC(sc, pulses_detected);
return true;
}
-#undef PRI_CH_RADAR_FOUND
-#undef EXT_CH_RADAR_FOUND
+
+static void
+ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe)
+{
+ struct dfs_pattern_detector *pd = sc->dfs_detector;
+ DFS_STAT_INC(sc, pulses_processed);
+ if (pd == NULL)
+ return;
+ if (!pd->add_pulse(pd, pe))
+ return;
+ DFS_STAT_INC(sc, radar_detected);
+ ieee80211_radar_detected(sc->hw);
+}
/*
* DFS: check PHY-error for radar pulse and feed the detector
@@ -176,18 +187,21 @@ void ath9k_dfs_process_phyerr(struct ath
ard.pulse_length_pri = vdata_end[-3];
pe.freq = ah->curchan->channel;
pe.ts = mactime;
- if (ath9k_postprocess_radar_event(sc, &ard, &pe)) {
- struct dfs_pattern_detector *pd = sc->dfs_detector;
- ath_dbg(common, DFS,
- "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, "
- "width=%d, rssi=%d, delta_ts=%llu\n",
- pe.freq, pe.ts, pe.width, pe.rssi,
- pe.ts - sc->dfs_prev_pulse_ts);
- sc->dfs_prev_pulse_ts = pe.ts;
- DFS_STAT_INC(sc, pulses_processed);
- if (pd != NULL && pd->add_pulse(pd, &pe)) {
- DFS_STAT_INC(sc, radar_detected);
- ieee80211_radar_detected(sc->hw);
- }
+ if (!ath9k_postprocess_radar_event(sc, &ard, &pe))
+ return;
+
+ ath_dbg(common, DFS,
+ "ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
+ "width=%d, rssi=%d, delta_ts=%llu\n",
+ ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi,
+ pe.ts - sc->dfs_prev_pulse_ts);
+ sc->dfs_prev_pulse_ts = pe.ts;
+ if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND)
+ ath9k_dfs_process_radar_pulse(sc, &pe);
+ if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) {
+ pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20;
+ ath9k_dfs_process_radar_pulse(sc, &pe);
}
}
+#undef PRI_CH_RADAR_FOUND
+#undef EXT_CH_RADAR_FOUND

View File

@ -0,0 +1,156 @@
From: Eli Cooper <elicooper@gmx.com>
Date: Thu, 14 Jan 2016 00:07:12 +0800
Subject: [PATCH] rt2x00: fix monitor mode regression
Since commit df1404650ccbfeb76a84f301f22316be0d00a864 monitor mode for rt2x00
has been made effectively useless because the hardware filter is configured to
drop packets whose intended recipient is not the device, regardless of the
presence of monitor mode interfaces.
This patch fixes this regression by adding explicit monitor mode support, and
configuring the hardware filter accordingly.
Signed-off-by: Eli Cooper <elicooper@gmx.com>
---
--- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
@@ -273,8 +273,10 @@ static void rt2400pci_config_filter(stru
!(filter_flags & FIF_PLCPFAIL));
rt2x00_set_field32(&reg, RXCSR0_DROP_CONTROL,
!(filter_flags & FIF_CONTROL));
- rt2x00_set_field32(&reg, RXCSR0_DROP_NOT_TO_ME, 1);
+ rt2x00_set_field32(&reg, RXCSR0_DROP_NOT_TO_ME,
+ !rt2x00dev->is_monitoring);
rt2x00_set_field32(&reg, RXCSR0_DROP_TODS,
+ !rt2x00dev->is_monitoring &&
!rt2x00dev->intf_ap_count);
rt2x00_set_field32(&reg, RXCSR0_DROP_VERSION_ERROR, 1);
rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg);
--- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
@@ -274,8 +274,10 @@ static void rt2500pci_config_filter(stru
!(filter_flags & FIF_PLCPFAIL));
rt2x00_set_field32(&reg, RXCSR0_DROP_CONTROL,
!(filter_flags & FIF_CONTROL));
- rt2x00_set_field32(&reg, RXCSR0_DROP_NOT_TO_ME, 1);
+ rt2x00_set_field32(&reg, RXCSR0_DROP_NOT_TO_ME,
+ !rt2x00dev->is_monitoring);
rt2x00_set_field32(&reg, RXCSR0_DROP_TODS,
+ !rt2x00dev->is_monitoring &&
!rt2x00dev->intf_ap_count);
rt2x00_set_field32(&reg, RXCSR0_DROP_VERSION_ERROR, 1);
rt2x00_set_field32(&reg, RXCSR0_DROP_MCAST,
--- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
@@ -437,8 +437,10 @@ static void rt2500usb_config_filter(stru
!(filter_flags & FIF_PLCPFAIL));
rt2x00_set_field16(&reg, TXRX_CSR2_DROP_CONTROL,
!(filter_flags & FIF_CONTROL));
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_NOT_TO_ME, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_NOT_TO_ME,
+ !rt2x00dev->is_monitoring);
rt2x00_set_field16(&reg, TXRX_CSR2_DROP_TODS,
+ !rt2x00dev->is_monitoring &&
!rt2x00dev->intf_ap_count);
rt2x00_set_field16(&reg, TXRX_CSR2_DROP_VERSION_ERROR, 1);
rt2x00_set_field16(&reg, TXRX_CSR2_DROP_MULTICAST,
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -1490,7 +1490,8 @@ void rt2800_config_filter(struct rt2x00_
!(filter_flags & FIF_FCSFAIL));
rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_PHY_ERROR,
!(filter_flags & FIF_PLCPFAIL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_NOT_TO_ME, 1);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_NOT_TO_ME,
+ !rt2x00dev->is_monitoring);
rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0);
rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_VER_ERROR, 1);
rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_MULTICAST,
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -844,11 +844,13 @@ struct rt2x00_dev {
* - Open sta interface count.
* - Association count.
* - Beaconing enabled count.
+ * - Whether the device is monitoring.
*/
unsigned int intf_ap_count;
unsigned int intf_sta_count;
unsigned int intf_associated;
unsigned int intf_beaconing;
+ bool is_monitoring;
/*
* Interface combinations
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00config.c
@@ -244,6 +244,16 @@ void rt2x00lib_config(struct rt2x00_dev
(ieee80211_flags & IEEE80211_CONF_CHANGE_PS))
cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
+ if (ieee80211_flags & IEEE80211_CONF_CHANGE_MONITOR) {
+ if (conf->flags & IEEE80211_CONF_MONITOR) {
+ rt2x00_dbg(rt2x00dev, "Monitor mode is enabled\n");
+ rt2x00dev->is_monitoring = true;
+ } else {
+ rt2x00_dbg(rt2x00dev, "Monitor mode is disabled\n");
+ rt2x00dev->is_monitoring = false;
+ }
+ }
+
/*
* Start configuration.
*/
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -1204,6 +1204,7 @@ int rt2x00lib_start(struct rt2x00_dev *r
rt2x00dev->intf_ap_count = 0;
rt2x00dev->intf_sta_count = 0;
rt2x00dev->intf_associated = 0;
+ rt2x00dev->is_monitoring = false;
/* Enable the radio */
retval = rt2x00lib_enable_radio(rt2x00dev);
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
@@ -385,11 +385,6 @@ void rt2x00mac_configure_filter(struct i
*total_flags |= FIF_PSPOLL;
}
- /*
- * Check if there is any work left for us.
- */
- if (rt2x00dev->packet_filter == *total_flags)
- return;
rt2x00dev->packet_filter = *total_flags;
rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags);
--- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
@@ -530,8 +530,10 @@ static void rt61pci_config_filter(struct
!(filter_flags & FIF_PLCPFAIL));
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_CONTROL,
!(filter_flags & (FIF_CONTROL | FIF_PSPOLL)));
- rt2x00_set_field32(&reg, TXRX_CSR0_DROP_NOT_TO_ME, 1);
+ rt2x00_set_field32(&reg, TXRX_CSR0_DROP_NOT_TO_ME,
+ !rt2x00dev->is_monitoring);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_TO_DS,
+ !rt2x00dev->is_monitoring &&
!rt2x00dev->intf_ap_count);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_VERSION_ERROR, 1);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_MULTICAST,
--- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
@@ -480,8 +480,10 @@ static void rt73usb_config_filter(struct
!(filter_flags & FIF_PLCPFAIL));
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_CONTROL,
!(filter_flags & (FIF_CONTROL | FIF_PSPOLL)));
- rt2x00_set_field32(&reg, TXRX_CSR0_DROP_NOT_TO_ME, 1);
+ rt2x00_set_field32(&reg, TXRX_CSR0_DROP_NOT_TO_ME,
+ !rt2x00dev->is_monitoring);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_TO_DS,
+ !rt2x00dev->is_monitoring &&
!rt2x00dev->intf_ap_count);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_VERSION_ERROR, 1);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_MULTICAST,

View File

@ -1,19 +0,0 @@
From: Zefir Kurtisi <zefir.kurtisi@neratec.com>
Date: Tue, 10 Mar 2015 17:49:31 +0100
Subject: [PATCH] ath9k: allow 40MHz radar detection width
Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com>
---
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -763,7 +763,8 @@ static const struct ieee80211_iface_comb
.num_different_channels = 1,
.beacon_int_infra_match = true,
.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- BIT(NL80211_CHAN_WIDTH_20),
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40),
}
#endif
};

View File

@ -0,0 +1,32 @@
From: Miaoqing Pan <miaoqing@codeaurora.org>
Date: Fri, 15 Jan 2016 18:17:17 +0800
Subject: [PATCH] ath9k: avoid ANI restart if no trigger
Fixes commit 54da20d83f0e ("ath9k_hw: improve ANI processing and rx desensitizing parameters")
Call ath9k_ani_restart() only when the phy error rate reach the
ANI immunity threshold. Sync the logic with internal code base.
Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -444,14 +444,16 @@ void ath9k_hw_ani_monitor(struct ath_hw
ofdmPhyErrRate < ah->config.ofdm_trig_low) {
ath9k_hw_ani_lower_immunity(ah);
aniState->ofdmsTurn = !aniState->ofdmsTurn;
+ ath9k_ani_restart(ah);
} else if (ofdmPhyErrRate > ah->config.ofdm_trig_high) {
ath9k_hw_ani_ofdm_err_trigger(ah);
aniState->ofdmsTurn = false;
+ ath9k_ani_restart(ah);
} else if (cckPhyErrRate > ah->config.cck_trig_high) {
ath9k_hw_ani_cck_err_trigger(ah);
aniState->ofdmsTurn = true;
+ ath9k_ani_restart(ah);
}
- ath9k_ani_restart(ah);
}
}
EXPORT_SYMBOL(ath9k_hw_ani_monitor);

View File

@ -1,137 +0,0 @@
From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Date: Wed, 4 Mar 2015 05:12:10 +0300
Subject: [PATCH] ath5k: channel change fix
ath5k updates the channel pointer and after that it stops the Rx logic
and apply channel to HW. In case of channel switch, such sequence
creates a small window when a frame, which is received on the old
channel is considered as a frame received on the new one.
The most notable consequence of this situation occurs during the switch
from 2 GHz band (CCK+OFDM) to the 5GHz band (OFDM-only). Frame received
with CCK rate, e.g. beacon received at the 1mbps, causes the following
warning:
WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]()
invalid hw_rix: 1a
[..]
Call Trace:
[<802656a8>] show_stack+0x48/0x70
[<802dd92c>] warn_slowpath_common+0x88/0xbc
[<802dd98c>] warn_slowpath_fmt+0x2c/0x38
[<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k]
[<8028ac64>] tasklet_action+0x8c/0xf0
[<80075804>] __do_softirq+0x180/0x32c
[<80196ce8>] irq_exit+0x54/0x70
[<80041848>] ret_from_irq+0x0/0x4
[<80182fdc>] ioread32+0x4/0xc
[<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k]
[<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k]
[<81b50900>] ath5k_reset+0xd4/0x310 [ath5k]
[<81b557e8>] ath5k_config+0x4c/0x104 [ath5k]
[<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211]
[<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211]
[<8022c3f4>] process_one_work+0x28c/0x400
[<802df8f8>] worker_thread+0x258/0x3c0
[<801b5710>] kthread+0xe0/0xec
[<800418a8>] ret_from_kernel_thread+0x14/0x1c
The easiest way to reproduce this warning is to run scan with dualband
NIC in noisy environments, when the channel 11 runs multiple APs. In my
tests if the APs num >= 12, the warning appears in the first few
seconds of scanning.
In order to fix this, the Rx disable code moved to a higher level and
placed before the channel pointer update. This is also makes the code a
bit more symmetrical, since we disable and enable the Rx in the same
function.
In fact, at the pointer update time new frames should not appear,
because interrupt generation at this point should already be disabled.
The next patch should address this issue.
CC: Jiri Slaby <jirislaby@gmail.com>
CC: Nick Kossifidis <mickflemm@gmail.com>
CC: Luis R. Rodriguez <mcgrof@do-not-panic.com>
Reported-by: Christophe Prevotaux <cprevotaux@nltinc.com>
Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com>
Tested-by: Eric Bree <ebree@nltinc.com>
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -2858,7 +2858,7 @@ ath5k_reset(struct ath5k_hw *ah, struct
{
struct ath_common *common = ath5k_hw_common(ah);
int ret, ani_mode;
- bool fast;
+ bool fast = chan && modparam_fastchanswitch ? 1 : 0;
ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
@@ -2876,11 +2876,29 @@ ath5k_reset(struct ath5k_hw *ah, struct
* so we should also free any remaining
* tx buffers */
ath5k_drain_tx_buffs(ah);
+
+ /* Stop PCU */
+ ath5k_hw_stop_rx_pcu(ah);
+
+ /* Stop DMA
+ *
+ * Note: If DMA didn't stop continue
+ * since only a reset will fix it.
+ */
+ ret = ath5k_hw_dma_stop(ah);
+
+ /* RF Bus grant won't work if we have pending
+ * frames
+ */
+ if (ret && fast) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "DMA didn't stop, falling back to normal reset\n");
+ fast = false;
+ }
+
if (chan)
ah->curchan = chan;
- fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
-
ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
if (ret) {
ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
--- a/drivers/net/wireless/ath/ath5k/reset.c
+++ b/drivers/net/wireless/ath/ath5k/reset.c
@@ -1169,30 +1169,6 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum
if (ah->ah_version == AR5K_AR5212)
ath5k_hw_set_sleep_clock(ah, false);
- /*
- * Stop PCU
- */
- ath5k_hw_stop_rx_pcu(ah);
-
- /*
- * Stop DMA
- *
- * Note: If DMA didn't stop continue
- * since only a reset will fix it.
- */
- ret = ath5k_hw_dma_stop(ah);
-
- /* RF Bus grant won't work if we have pending
- * frames */
- if (ret && fast) {
- ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
- "DMA didn't stop, falling back to normal reset\n");
- fast = false;
- /* Non fatal, just continue with
- * normal reset */
- ret = 0;
- }
-
mode = channel->hw_value;
switch (mode) {
case AR5K_MODE_11A:

View File

@ -0,0 +1,91 @@
From: Miaoqing Pan <miaoqing@codeaurora.org>
Date: Fri, 15 Jan 2016 18:17:18 +0800
Subject: [PATCH] ath9k: clean up ANI per-channel pointer checking
commit c24bd3620c50 ("ath9k: Do not maintain ANI state per-channel")
removed per-channel handling, the code to check 'curchan' also
should be removed as never used.
Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -126,12 +126,8 @@ static void ath9k_hw_update_mibstats(str
static void ath9k_ani_restart(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
-
- if (!ah->curchan)
- return;
+ struct ar5416AniState *aniState = &ah->ani;
- aniState = &ah->ani;
aniState->listenTime = 0;
ENABLE_REGWRITE_BUFFER(ah);
@@ -221,12 +217,7 @@ static void ath9k_hw_set_ofdm_nil(struct
static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
-
- if (!ah->curchan)
- return;
-
- aniState = &ah->ani;
+ struct ar5416AniState *aniState = &ah->ani;
if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);
@@ -281,12 +272,7 @@ static void ath9k_hw_set_cck_nil(struct
static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
-
- if (!ah->curchan)
- return;
-
- aniState = &ah->ani;
+ struct ar5416AniState *aniState = &ah->ani;
if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,
@@ -299,9 +285,7 @@ static void ath9k_hw_ani_cck_err_trigger
*/
static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
-
- aniState = &ah->ani;
+ struct ar5416AniState *aniState = &ah->ani;
/* lower OFDM noise immunity */
if (aniState->ofdmNoiseImmunityLevel > 0 &&
@@ -329,7 +313,7 @@ void ath9k_ani_reset(struct ath_hw *ah,
struct ath_common *common = ath9k_hw_common(ah);
int ofdm_nil, cck_nil;
- if (!ah->curchan)
+ if (!chan)
return;
BUG_ON(aniState == NULL);
@@ -416,14 +400,10 @@ static bool ath9k_hw_ani_read_counters(s
void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
{
- struct ar5416AniState *aniState;
+ struct ar5416AniState *aniState = &ah->ani;
struct ath_common *common = ath9k_hw_common(ah);
u32 ofdmPhyErrRate, cckPhyErrRate;
- if (!ah->curchan)
- return;
-
- aniState = &ah->ani;
if (!ath9k_hw_ani_read_counters(ah))
return;

View File

@ -1,96 +0,0 @@
From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Date: Wed, 4 Mar 2015 05:12:11 +0300
Subject: [PATCH] ath5k: fix reset race
To prepare for reset ath5k should finish all asynchronous tasks. At
first, it disables the interrupt generation, then it waits for the
interrupt handler and tasklets completion, and then proceeds to the HW
configuration update. But it does not consider that the interrupt
handler or tasklet re-enables the interrupt generation. And we fall in a
situation when ath5k assumes that interrupts are disabled, but it is
not.
This can lead to different consequences, such as reception of the frame,
when we do not expect it. Under certain circumstances, this can lead to
the following warning:
WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]()
invalid hw_rix: 1a
[..]
Call Trace:
[<802656a8>] show_stack+0x48/0x70
[<802dd92c>] warn_slowpath_common+0x88/0xbc
[<802dd98c>] warn_slowpath_fmt+0x2c/0x38
[<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k]
[<8028ac64>] tasklet_action+0x8c/0xf0
[<80075804>] __do_softirq+0x180/0x32c
[<80196ce8>] irq_exit+0x54/0x70
[<80041848>] ret_from_irq+0x0/0x4
[<80182fdc>] ioread32+0x4/0xc
[<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k]
[<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k]
[<81b50900>] ath5k_reset+0xd4/0x310 [ath5k]
[<81b557e8>] ath5k_config+0x4c/0x104 [ath5k]
[<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211]
[<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211]
[<8022c3f4>] process_one_work+0x28c/0x400
[<802df8f8>] worker_thread+0x258/0x3c0
[<801b5710>] kthread+0xe0/0xec
[<800418a8>] ret_from_kernel_thread+0x14/0x1c
Fix this issue by adding a new status flag, which forbids to re-enable
the interrupt generation until the HW configuration is completed.
Note: previous patch, which reorders the Rx disable code helps to avoid
the above warning, but not fixes the root cause of unexpected frame
receiving.
CC: Jiri Slaby <jirislaby@gmail.com>
CC: Nick Kossifidis <mickflemm@gmail.com>
CC: Luis R. Rodriguez <mcgrof@do-not-panic.com>
Reported-by: Christophe Prevotaux <cprevotaux@nltinc.com>
Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com>
Tested-by: Eric Bree <ebree@nltinc.com>
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1283,6 +1283,7 @@ struct ath5k_hw {
#define ATH_STAT_PROMISC 1
#define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */
#define ATH_STAT_STARTED 3 /* opened & irqs enabled */
+#define ATH_STAT_RESET 4 /* hw reset */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int fif_filter_flags; /* Current FIF_* filter flags */
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1523,6 +1523,9 @@ ath5k_set_current_imask(struct ath5k_hw
enum ath5k_int imask;
unsigned long flags;
+ if (test_bit(ATH_STAT_RESET, ah->status))
+ return;
+
spin_lock_irqsave(&ah->irqlock, flags);
imask = ah->imask;
if (ah->rx_pending)
@@ -2862,6 +2865,8 @@ ath5k_reset(struct ath5k_hw *ah, struct
ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
+ __set_bit(ATH_STAT_RESET, ah->status);
+
ath5k_hw_set_imr(ah, 0);
synchronize_irq(ah->irq);
ath5k_stop_tasklets(ah);
@@ -2952,6 +2957,8 @@ ath5k_reset(struct ath5k_hw *ah, struct
*/
/* ath5k_chan_change(ah, c); */
+ __clear_bit(ATH_STAT_RESET, ah->status);
+
ath5k_beacon_config(ah);
/* intrs are enabled by ath5k_beacon_config */

View File

@ -0,0 +1,31 @@
From: Miaoqing Pan <miaoqing@codeaurora.org>
Date: Fri, 15 Jan 2016 18:17:19 +0800
Subject: [PATCH] ath9k: do not reset while BB panic(0x4000409) on ar9561
BB panic(0x4000409) observed while AP enabling/disabling
bursting.
Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -2071,7 +2071,8 @@ void ar9003_hw_attach_phy_ops(struct ath
* to be disabled.
*
* 0x04000409: Packet stuck on receive.
- * Full chip reset is required for all chips except AR9340.
+ * Full chip reset is required for all chips except
+ * AR9340, AR9531 and AR9561.
*/
/*
@@ -2100,7 +2101,7 @@ bool ar9003_hw_bb_watchdog_check(struct
case 0x04000b09:
return true;
case 0x04000409:
- if (AR_SREV_9340(ah) || AR_SREV_9531(ah))
+ if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah))
return false;
else
return true;

View File

@ -0,0 +1,27 @@
From: Miaoqing Pan <miaoqing@codeaurora.org>
Date: Fri, 15 Jan 2016 18:17:20 +0800
Subject: [PATCH] ath9k: fix inconsistent use of tab and space in
indentation
Minor changes for indenting.
Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -5485,11 +5485,11 @@ unsigned int ar9003_get_paprd_scale_fact
AR9300_PAPRD_SCALE_1);
else {
if (chan->channel >= 5700)
- return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20),
- AR9300_PAPRD_SCALE_1);
+ return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20),
+ AR9300_PAPRD_SCALE_1);
else if (chan->channel >= 5400)
return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40),
- AR9300_PAPRD_SCALE_2);
+ AR9300_PAPRD_SCALE_2);
else
return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40),
AR9300_PAPRD_SCALE_1);

View File

@ -1,76 +0,0 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Thu, 12 Mar 2015 17:10:50 +0100
Subject: [PATCH] ath9k: fix tracking of enabled AP beacons
sc->nbcnvifs tracks assigned beacon slots, not enabled beacons.
Therefore, it cannot be used to decide if cur_conf->enable_beacon (bool)
should be updated, or if beacons have been enabled already.
With the current code (depending on the order of calls), beacons often
do not get enabled in an AP+STA setup.
To fix tracking of enabled beacons, convert cur_conf->enable_beacon to a
bitmask of enabled beacon slots.
Cc: stable@vger.kernel.org
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -219,12 +219,15 @@ void ath9k_beacon_remove_slot(struct ath
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)vif->drv_priv;
struct ath_buf *bf = avp->av_bcbuf;
+ struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
avp->av_bslot);
tasklet_disable(&sc->bcon_tasklet);
+ cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+
if (bf && bf->bf_mpdu) {
struct sk_buff *skb = bf->bf_mpdu;
dma_unmap_single(sc->dev, bf->bf_buf_addr,
@@ -521,8 +524,7 @@ static bool ath9k_allow_beacon_config(st
}
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
- if ((vif->type != NL80211_IFTYPE_AP) ||
- (sc->nbcnvifs > 1)) {
+ if (vif->type != NL80211_IFTYPE_AP) {
ath_dbg(common, CONFIG,
"An AP interface is already present !\n");
return false;
@@ -616,12 +618,14 @@ void ath9k_beacon_config(struct ath_soft
* enabling/disabling SWBA.
*/
if (changed & BSS_CHANGED_BEACON_ENABLED) {
- if (!bss_conf->enable_beacon &&
- (sc->nbcnvifs <= 1)) {
- cur_conf->enable_beacon = false;
- } else if (bss_conf->enable_beacon) {
- cur_conf->enable_beacon = true;
- ath9k_cache_beacon_config(sc, ctx, bss_conf);
+ bool enabled = cur_conf->enable_beacon;
+
+ if (!bss_conf->enable_beacon) {
+ cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+ } else {
+ cur_conf->enable_beacon |= BIT(avp->av_bslot);
+ if (!enabled)
+ ath9k_cache_beacon_config(sc, ctx, bss_conf);
}
}
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -54,7 +54,7 @@ struct ath_beacon_config {
u16 dtim_period;
u16 bmiss_timeout;
u8 dtim_count;
- bool enable_beacon;
+ u8 enable_beacon;
bool ibss_creator;
u32 nexttbtt;
u32 intval;

View File

@ -0,0 +1,65 @@
From: Miaoqing Pan <miaoqing@codeaurora.org>
Date: Fri, 15 Jan 2016 18:17:21 +0800
Subject: [PATCH] ath9k: fix data bus error on ar9300 and ar9580
One crash issue be found on ar9300: RTC_RC reg read leads crash, leading
the data bus error, due to RTC_RC reg write not happen properly.
Warm Reset trigger in continuous beacon stuck for one of the customer for
other chip, noticed the MAC was stuck in RTC reset. After analysis noticed
DMA did not complete when RTC was put in reset.
So, before resetting the MAC need to make sure there are no pending DMA
transactions because this reset does not reset all parts of the chip.
The 12th and 11th bit of MAC _DMA_CFG register used to do that.
12 cfg_halt_ack 0x0
0 DMA has not yet halted
1 DMA has halted
11 cfg_halt_req 0x0
0 DMA logic operates normally
1 Request DMA logic to stop so software can reset the MAC
The Bit [12] of this register indicates when the halt has taken effect or
not. the DMA halt IS NOT recoverable; once software sets bit [11] to
request a DMA halt, software must wait for bit [12] to be set and reset
the MAC.
So, the same thing we implemented for ar9580 chip.
Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1368,6 +1368,16 @@ static bool ath9k_hw_set_reset(struct at
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
+ */
+ if (AR_SREV_9300(ah) || AR_SREV_9580(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);
+ }
+
REG_WRITE(ah, AR_RTC_RC, rst_flags);
REGWRITE_BUFFER_FLUSH(ah);
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -34,8 +34,10 @@
#define AR_CFG_SWRG 0x00000010
#define AR_CFG_AP_ADHOC_INDICATION 0x00000020
#define AR_CFG_PHOK 0x00000100
-#define AR_CFG_CLK_GATE_DIS 0x00000400
#define AR_CFG_EEBS 0x00000200
+#define AR_CFG_CLK_GATE_DIS 0x00000400
+#define AR_CFG_HALT_REQ 0x00000800
+#define AR_CFG_HALT_ACK 0x00001000
#define AR_CFG_PCI_MASTER_REQ_Q_THRESH 0x00060000
#define AR_CFG_PCI_MASTER_REQ_Q_THRESH_S 17

View File

@ -1,43 +0,0 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Fri, 13 Mar 2015 10:49:40 +0100
Subject: [PATCH] mac80211: minstrel_ht: fix rounding issue in MCS duration
calculation
On very high MCS bitrates, the calculated duration of rates that are
next to each other can be very imprecise, due to the small packet size
used as reference (1200 bytes).
This is most visible in VHT80 nss=2 MCS8/9, for which minstrel shows the
same throughput when the probability is also the same. This leads to a
bad rate selection for such rates.
Fix this issue by introducing an average A-MPDU size factor into the
calculation.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -17,10 +17,11 @@
#include "rc80211_minstrel.h"
#include "rc80211_minstrel_ht.h"
+#define AVG_AMPDU_SIZE 16
#define AVG_PKT_SIZE 1200
/* Number of bits for an average sized packet */
-#define MCS_NBITS (AVG_PKT_SIZE << 3)
+#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
/* Number of symbols for a packet with (bps) bits per symbol */
#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
@@ -33,7 +34,8 @@
)
/* Transmit duration for the raw data part of an average sized packet */
-#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
+#define MCS_DURATION(streams, sgi, bps) \
+ (MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE)
#define BW_20 0
#define BW_40 1

View File

@ -0,0 +1,19 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Fri, 15 Jan 2016 15:59:45 +0100
Subject: [PATCH] brcmfmac: add missing include
linux/module.h is required for defining module parameters
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/netdevice.h>
+#include <linux/module.h>
#include <brcmu_wifi.h>
#include <brcmu_utils.h>
#include "core.h"

View File

@ -0,0 +1,118 @@
From: Hante Meuleman <meuleman@broadcom.com>
Date: Tue, 19 Jan 2016 12:39:24 +0100
Subject: [PATCH] brcmfmac: fix sdio sg table alloc crash
With commit 7d34b0560567 ("brcmfmac: Move all module parameters to
one place") a bug was introduced causing a null pointer exception.
This patch fixes the bug by initializing the sg table till after
the settings have been initialized.
Fixes: 7d34b0560567 ("brcmfmac: Move all module parameters to one place")
Reported-by: Marc Zyngier <marc.zyngier@arm.com>
Tested-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -879,11 +879,24 @@ int brcmf_sdiod_abort(struct brcmf_sdio_
return 0;
}
-static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
+void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
{
+ struct sdio_func *func;
+ struct mmc_host *host;
+ uint max_blocks;
uint nents;
int err;
+ func = sdiodev->func[2];
+ host = func->card->host;
+ sdiodev->sg_support = host->max_segs > 1;
+ max_blocks = min_t(uint, host->max_blk_count, 511u);
+ sdiodev->max_request_size = min_t(uint, host->max_req_size,
+ max_blocks * func->cur_blksize);
+ sdiodev->max_segment_count = min_t(uint, host->max_segs,
+ SG_MAX_SINGLE_ALLOC);
+ sdiodev->max_segment_size = host->max_seg_size;
+
if (!sdiodev->sg_support)
return;
@@ -1021,9 +1034,6 @@ static void brcmf_sdiod_host_fixup(struc
static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
{
- struct sdio_func *func;
- struct mmc_host *host;
- uint max_blocks;
int ret = 0;
sdiodev->num_funcs = 2;
@@ -1054,26 +1064,6 @@ static int brcmf_sdiod_probe(struct brcm
goto out;
}
- /*
- * determine host related variables after brcmf_sdiod_probe()
- * as func->cur_blksize is properly set and F2 init has been
- * completed successfully.
- */
- func = sdiodev->func[2];
- host = func->card->host;
- sdiodev->sg_support = host->max_segs > 1;
- max_blocks = min_t(uint, host->max_blk_count, 511u);
- sdiodev->max_request_size = min_t(uint, host->max_req_size,
- max_blocks * func->cur_blksize);
- sdiodev->max_segment_count = min_t(uint, host->max_segs,
- SG_MAX_SINGLE_ALLOC);
- sdiodev->max_segment_size = host->max_seg_size;
-
- /* allocate scatter-gather table. sg support
- * will be disabled upon allocation failure.
- */
- brcmf_sdiod_sgtable_alloc(sdiodev);
-
ret = brcmf_sdiod_freezer_attach(sdiodev);
if (ret)
goto out;
@@ -1084,7 +1074,7 @@ static int brcmf_sdiod_probe(struct brcm
ret = -ENODEV;
goto out;
}
- brcmf_sdiod_host_fixup(host);
+ brcmf_sdiod_host_fixup(sdiodev->func[2]->card->host);
out:
if (ret)
brcmf_sdiod_remove(sdiodev);
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -4114,6 +4114,11 @@ struct brcmf_sdio *brcmf_sdio_probe(stru
goto fail;
}
+ /* allocate scatter-gather table. sg support
+ * will be disabled upon allocation failure.
+ */
+ brcmf_sdiod_sgtable_alloc(bus->sdiodev);
+
/* Query the F2 block size, set roundup accordingly */
bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
bus->roundup = min(max_roundup, bus->blocksize);
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -342,6 +342,7 @@ int brcmf_sdiod_ramrw(struct brcmf_sdio_
/* Issue an abort to the specified function */
int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
+void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev);
void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
enum brcmf_sdiod_state state);
#ifdef CONFIG_PM_SLEEP

View File

@ -1,22 +0,0 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Sun, 15 Mar 2015 08:02:37 +0100
Subject: [PATCH] ath9k: disable TPC support again (for now)
TPC support has been observed to cause some tx power fluctuations on
some devices with at least AR934x and AR938x chips.
Disable it for now until the bugs have been found and fixed
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -424,7 +424,7 @@ static void ath9k_hw_init_defaults(struc
ah->power_mode = ATH9K_PM_UNDEFINED;
ah->htc_reset_init = true;
- ah->tpc_enabled = true;
+ ah->tpc_enabled = false;
ah->ani_function = ATH9K_ANI_ALL;
if (!AR_SREV_9300_20_OR_LATER(ah))

View File

@ -0,0 +1,38 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Thu, 21 Jan 2016 16:28:44 +0100
Subject: [PATCH] ath9k_hw: ignore eeprom magic mismatch on flash based devices
Many AR913x based devices (maybe others too) do not have a valid EEPROM
magic in their calibration data partition.
Fixes: 6fa658fd5ab2 ("ath9k: Simplify and fix eeprom endianness swapping")
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -150,18 +150,18 @@ int ath9k_hw_nvram_swap_data(struct ath_
return -EIO;
}
- if (magic == AR5416_EEPROM_MAGIC) {
- *swap_needed = false;
- } else if (swab16(magic) == AR5416_EEPROM_MAGIC) {
+ *swap_needed = false;
+ if (swab16(magic) == AR5416_EEPROM_MAGIC) {
if (ah->ah_flags & AH_NO_EEP_SWAP) {
ath_info(common,
"Ignoring endianness difference in EEPROM magic bytes.\n");
-
- *swap_needed = false;
} else {
*swap_needed = true;
}
- } else {
+ } else if (magic != AR5416_EEPROM_MAGIC) {
+ if (ath9k_hw_use_flash(ah))
+ return 0;
+
ath_err(common,
"Invalid EEPROM Magic (0x%04x).\n", magic);
return -EINVAL;

View File

@ -1,21 +0,0 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Tue, 24 Feb 2015 00:28:18 +0100
Subject: [PATCH] mac80211: don't look up stations for multicast addresses
Since multicast addresses don't exist as stations, don't attempt
to look them up in the hashtable on TX.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1161,7 +1161,7 @@ ieee80211_tx_prepare(struct ieee80211_su
tx->sdata->control_port_protocol == tx->skb->protocol) {
tx->sta = sta_info_get_bss(sdata, hdr->addr1);
}
- if (!tx->sta)
+ if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
tx->sta = sta_info_get(sdata, hdr->addr1);
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&

View File

@ -0,0 +1,55 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Fri, 22 Jan 2016 01:05:56 +0100
Subject: [PATCH] ath9k: do not limit the number of DFS interfaces to 1
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -751,14 +751,6 @@ static const struct ieee80211_iface_comb
#endif /* CPTCFG_ATH9K_CHANNEL_CONTEXT */
-static const struct ieee80211_iface_limit if_dfs_limits[] = {
- { .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
-#ifdef CPTCFG_MAC80211_MESH
- BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
- BIT(NL80211_IFTYPE_ADHOC) },
-};
-
static const struct ieee80211_iface_combination if_comb[] = {
{
.limits = if_limits,
@@ -766,6 +758,11 @@ static const struct ieee80211_iface_comb
.max_interfaces = 2048,
.num_different_channels = 1,
.beacon_int_infra_match = true,
+#ifdef CPTCFG_ATH9K_DFS_CERTIFIED
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40),
+#endif
},
{
.limits = wds_limits,
@@ -774,18 +771,6 @@ static const struct ieee80211_iface_comb
.num_different_channels = 1,
.beacon_int_infra_match = true,
},
-#ifdef CPTCFG_ATH9K_DFS_CERTIFIED
- {
- .limits = if_dfs_limits,
- .n_limits = ARRAY_SIZE(if_dfs_limits),
- .max_interfaces = 1,
- .num_different_channels = 1,
- .beacon_int_infra_match = true,
- .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- BIT(NL80211_CHAN_WIDTH_20) |
- BIT(NL80211_CHAN_WIDTH_40),
- }
-#endif
};
#ifdef CPTCFG_ATH9K_CHANNEL_CONTEXT

View File

@ -1,130 +0,0 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Fri, 20 Mar 2015 11:41:58 +0100
Subject: [PATCH] mac80211: remove drop_unencrypted code
This mechanism was historic, and only ever used by IBSS, which
also doesn't need to have it as it properly manages station's
802.1X PAE state (or, with WEP, always has a key.)
Remove the mechanism to clean up the code.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -274,8 +274,6 @@ void debugfs_hw_add(struct ieee80211_loc
#ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop);
DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued);
- DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted,
- local->tx_handlers_drop_unencrypted);
DEBUGFS_STATS_ADD(tx_handlers_drop_fragment,
local->tx_handlers_drop_fragment);
DEBUGFS_STATS_ADD(tx_handlers_drop_wep,
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -177,7 +177,6 @@ static ssize_t ieee80211_if_write_##name
IEEE80211_IF_FILE_R(name)
/* common attributes */
-IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ],
HEX);
IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ],
@@ -562,7 +561,6 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDu
static void add_common_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(drop_unencrypted);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -249,8 +249,6 @@ static void __ieee80211_sta_join_ibss(st
if (presp)
kfree_rcu(presp, rcu_head);
- sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
-
/* make a copy of the chandef, it could be modified below. */
chandef = *req_chandef;
chan = chandef.chan;
@@ -1289,8 +1287,6 @@ static void ieee80211_sta_create_ibss(st
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
- else
- sdata->drop_unencrypted = 0;
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
&ifibss->chandef, ifibss->basic_rates,
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -842,8 +842,6 @@ struct ieee80211_sub_if_data {
unsigned long state;
- int drop_unencrypted;
-
char name[IFNAMSIZ];
/* Fragment table for host-based reassembly */
@@ -1289,7 +1287,6 @@ struct ieee80211_local {
/* TX/RX handler statistics */
unsigned int tx_handlers_drop;
unsigned int tx_handlers_queued;
- unsigned int tx_handlers_drop_unencrypted;
unsigned int tx_handlers_drop_fragment;
unsigned int tx_handlers_drop_wep;
unsigned int tx_handlers_drop_not_assoc;
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1535,7 +1535,6 @@ int ieee80211_if_change_type(struct ieee
}
/* reset some values that shouldn't be kept across type changes */
- sdata->drop_unencrypted = 0;
if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = false;
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1897,8 +1897,7 @@ static int ieee80211_drop_unencrypted(st
/* Drop unencrypted frames if key is set. */
if (unlikely(!ieee80211_has_protected(fc) &&
!ieee80211_is_nullfunc(fc) &&
- ieee80211_is_data(fc) &&
- (rx->key || rx->sdata->drop_unencrypted)))
+ ieee80211_is_data(fc) && rx->key))
return -EACCES;
return 0;
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -594,23 +594,8 @@ ieee80211_tx_h_select_key(struct ieee802
else if (!is_multicast_ether_addr(hdr->addr1) &&
(key = rcu_dereference(tx->sdata->default_unicast_key)))
tx->key = key;
- else if (info->flags & IEEE80211_TX_CTL_INJECTED)
+ else
tx->key = NULL;
- else if (!tx->sdata->drop_unencrypted)
- tx->key = NULL;
- else if (tx->skb->protocol == tx->sdata->control_port_protocol)
- tx->key = NULL;
- else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
- !(ieee80211_is_action(hdr->frame_control) &&
- tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
- tx->key = NULL;
- else if (ieee80211_is_mgmt(hdr->frame_control) &&
- !ieee80211_is_robust_mgmt_frame(tx->skb))
- tx->key = NULL;
- else {
- I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
- return TX_DROP;
- }
if (tx->key) {
bool skip_hw = false;

View File

@ -1,71 +0,0 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Fri, 20 Mar 2015 16:24:21 +0100
Subject: [PATCH] mac80211: don't look up destination station twice
There's no need to look up the destination station twice while
building the 802.11 header for a given frame if the frame will
actually be transmitted to the station we initially looked up.
This happens for 4-addr VLAN interfaces and TDLS connections, which
both directly send the frame to the station they looked up, though
in the case of TDLS some station conditions need to be checked.
To avoid that, add a variable indicating that we've looked up the
station that the frame is going to be transmitted to, and avoid the
lookup/flag checking if it already has been done.
In the TDLS case, also move the authorized/wme_sta flag assignment
to the correct place, i.e. only when that station is really used.
Before this change, the new lookup should always have succeeded so
that the potentially erroneous data would be overwritten.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1894,6 +1894,7 @@ static struct sk_buff *ieee80211_build_h
bool wme_sta = false, authorized = false, tdls_auth = false;
bool tdls_peer = false, tdls_setup_frame = false;
bool multicast;
+ bool have_station = false;
u16 info_id = 0;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_sub_if_data *ap_sdata;
@@ -1918,6 +1919,7 @@ static struct sk_buff *ieee80211_build_h
hdrlen = 30;
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = sta->sta.wme;
+ have_station = true;
}
ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
u.ap);
@@ -2034,9 +2036,6 @@ static struct sk_buff *ieee80211_build_h
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
sta = sta_info_get(sdata, skb->data);
if (sta) {
- authorized = test_sta_flag(sta,
- WLAN_STA_AUTHORIZED);
- wme_sta = sta->sta.wme;
tdls_peer = test_sta_flag(sta,
WLAN_STA_TDLS_PEER);
tdls_auth = test_sta_flag(sta,
@@ -2068,6 +2067,9 @@ static struct sk_buff *ieee80211_build_h
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN);
hdrlen = 24;
+ have_station = true;
+ authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+ wme_sta = sta->sta.wme;
} else if (sdata->u.mgd.use_4addr &&
cpu_to_be16(ethertype) != sdata->control_port_protocol) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
@@ -2130,7 +2132,7 @@ static struct sk_buff *ieee80211_build_h
* in AP mode)
*/
multicast = is_multicast_ether_addr(hdr.addr1);
- if (!multicast) {
+ if (!multicast && !have_station) {
sta = sta_info_get(sdata, hdr.addr1);
if (sta) {
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);

View File

@ -0,0 +1,27 @@
From: Michal Kazior <michal.kazior@tieto.com>
Date: Thu, 21 Jan 2016 14:23:07 +0100
Subject: [PATCH] mac80211: fix txq queue related crashes
The driver can access the queue simultanously
while mac80211 tears down the interface. Without
spinlock protection this could lead to corrupting
sk_buff_head and subsequently to an invalid
pointer dereference.
Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation")
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -977,7 +977,10 @@ static void ieee80211_do_stop(struct iee
if (sdata->vif.txq) {
struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+ spin_lock_bh(&txqi->queue.lock);
ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+ spin_unlock_bh(&txqi->queue.lock);
+
atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
}

View File

@ -1,27 +0,0 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Fri, 20 Mar 2015 16:24:22 +0100
Subject: [PATCH] mac80211: drop 4-addr VLAN frames earlier if not
connected
If there's no station on the 4-addr VLAN interface, then frames
cannot be transmitted. Drop such frames earlier, before setting
up all the information for them.
We should keep the old check though since that code might be used
for other internally-generated frames.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1920,6 +1920,9 @@ static struct sk_buff *ieee80211_build_h
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = sta->sta.wme;
have_station = true;
+ } else if (sdata->wdev.use_4addr) {
+ ret = -ENOLINK;
+ goto free;
}
ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
u.ap);

View File

@ -0,0 +1,57 @@
From: Michal Kazior <michal.kazior@tieto.com>
Date: Mon, 25 Jan 2016 14:43:24 +0100
Subject: [PATCH] mac80211: fix unnecessary frame drops in mesh fwding
The ieee80211_queue_stopped() expects hw queue
number but it was given raw WMM AC number instead.
This could cause frame drops and problems with
traffic in some cases - most notably if driver
doesn't map AC numbers to queue numbers 1:1 and
uses ieee80211_stop_queues() and
ieee80211_wake_queue() only without ever calling
ieee80211_wake_queues().
On ath10k it was possible to hit this problem in
the following case:
1. wlan0 uses queue 0
(ath10k maps queues per vif)
2. offchannel uses queue 15
3. queues 1-14 are unused
4. ieee80211_stop_queues()
5. ieee80211_wake_queue(q=0)
6. ieee80211_wake_queue(q=15)
(other queues are not woken up because both
driver and mac80211 know other queues are
unused)
7. ieee80211_rx_h_mesh_fwding()
8. ieee80211_select_queue_80211() returns 2
9. ieee80211_queue_stopped(q=2) returns true
10. frame is dropped (oops!)
Fixes: d3c1597b8d1b ("mac80211: fix forwarded mesh frame queue mapping")
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2235,7 +2235,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u16 q, hdrlen;
+ u16 ac, q, hdrlen;
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -2304,7 +2304,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
ether_addr_equal(sdata->vif.addr, hdr->addr3))
return RX_CONTINUE;
- q = ieee80211_select_queue_80211(sdata, skb, hdr);
+ ac = ieee80211_select_queue_80211(sdata, skb, hdr);
+ q = sdata->vif.hw_queue[ac];
if (ieee80211_queue_stopped(&local->hw, q)) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
return RX_DROP_MONITOR;

View File

@ -0,0 +1,103 @@
From: Sachin Kulkarni <Sachin.Kulkarni@imgtec.com>
Date: Tue, 12 Jan 2016 14:30:19 +0530
Subject: [PATCH] mac80211: Requeue work after scan complete for all VIF
types.
During a sw scan ieee80211_iface_work ignores work items for all vifs.
However after the scan complete work is requeued only for STA, ADHOC
and MESH iftypes.
This occasionally results in event processing getting delayed/not
processed for iftype AP when it coexists with a STA. This can result
in data halt and eventually disconnection on the AP interface.
Signed-off-by: Sachin Kulkarni <Sachin.Kulkarni@imgtec.com>
Cc: linux-wireless@vger.kernel.org
Cc: johannes@sipsolutions.net
---
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -1731,7 +1731,6 @@ void ieee80211_ibss_notify_scan_complete
if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
continue;
sdata->u.ibss.last_scan_completed = jiffies;
- ieee80211_queue_work(&local->hw, &sdata->work);
}
mutex_unlock(&local->iflist_mtx);
}
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -1369,17 +1369,6 @@ out:
sdata_unlock(sdata);
}
-void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
-{
- struct ieee80211_sub_if_data *sdata;
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list)
- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- ieee80211_sdata_running(sdata))
- ieee80211_queue_work(&local->hw, &sdata->work);
- rcu_read_unlock();
-}
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
{
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -362,14 +362,10 @@ static inline bool mesh_path_sel_is_hwmp
return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP;
}
-void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
-
void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata);
void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata);
void ieee80211s_stop(void);
#else
-static inline void
-ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
{ return false; }
static inline void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3978,8 +3978,6 @@ static void ieee80211_restart_sta_timer(
if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.monitor_work);
- /* and do all the other regular work too */
- ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
}
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -314,6 +314,7 @@ static void __ieee80211_scan_completed(s
bool was_scanning = local->scanning;
struct cfg80211_scan_request *scan_req;
struct ieee80211_sub_if_data *scan_sdata;
+ struct ieee80211_sub_if_data *sdata;
lockdep_assert_held(&local->mtx);
@@ -373,7 +374,15 @@ static void __ieee80211_scan_completed(s
ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local);
- ieee80211_mesh_notify_scan_completed(local);
+
+ /* Requeue all the work that might have been ignored while
+ * the scan was in progress
+ */
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (ieee80211_sdata_running(sdata))
+ ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+ }
+
if (was_scanning)
ieee80211_start_next_roc(local);
}

View File

@ -1,33 +0,0 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Fri, 20 Mar 2015 16:24:23 +0100
Subject: [PATCH] mac80211: mesh: avoid pointless station lookup
In ieee80211_build_hdr(), the station is looked up to build the
header correctly (QoS field) and to check for authorization. For
mesh, authorization isn't checked here, and QoS capability is
mandatory, so the station lookup can be avoided.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2130,12 +2130,14 @@ static struct sk_buff *ieee80211_build_h
}
/*
- * There's no need to try to look up the destination
- * if it is a multicast address (which can only happen
- * in AP mode)
+ * There's no need to try to look up the destination station
+ * if it is a multicast address. In mesh, there's no need to
+ * look up the station at all as it always must be QoS capable
+ * and mesh mode checks authorization later.
*/
multicast = is_multicast_ether_addr(hdr.addr1);
- if (!multicast && !have_station) {
+ if (!multicast && !have_station &&
+ !ieee80211_vif_is_mesh(&sdata->vif)) {
sta = sta_info_get(sdata, hdr.addr1);
if (sta) {
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);

View File

@ -1,267 +0,0 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Fri, 20 Mar 2015 14:18:27 +0100
Subject: [PATCH] mac80211: avoid duplicate TX path station lookup
Instead of looking up the destination station twice in the TX path
(first to build the header, and then for control processing), save
it when building the header and use it later in the TX path.
To avoid having to look up the station in the many callers, allow
those to pass %NULL which keeps the existing lookup.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3565,7 +3565,7 @@ static int ieee80211_probe_client(struct
nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, sta, skb);
local_bh_enable();
rcu_read_unlock();
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1775,7 +1775,8 @@ void mac80211_ev_michael_mic_failure(str
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, struct sk_buff *skb);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1279,7 +1279,7 @@ static void ieee80211_send_null_response
}
info->band = chanctx_conf->def.chan->band;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, sta, skb);
rcu_read_unlock();
}
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1110,11 +1110,13 @@ static bool ieee80211_tx_prep_agg(struct
/*
* initialises @tx
+ * pass %NULL for the station if unknown, a valid pointer if known
+ * or an ERR_PTR() if the station is known not to exist
*/
static ieee80211_tx_result
ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_data *tx,
- struct sk_buff *skb)
+ struct sta_info *sta, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_hdr *hdr;
@@ -1137,17 +1139,22 @@ ieee80211_tx_prepare(struct ieee80211_su
hdr = (struct ieee80211_hdr *) skb->data;
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
- tx->sta = rcu_dereference(sdata->u.vlan.sta);
- if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
- return TX_DROP;
- } else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
- IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
- tx->sdata->control_port_protocol == tx->skb->protocol) {
- tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+ if (likely(sta)) {
+ if (!IS_ERR(sta))
+ tx->sta = sta;
+ } else {
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ tx->sta = rcu_dereference(sdata->u.vlan.sta);
+ if (!tx->sta && sdata->wdev.use_4addr)
+ return TX_DROP;
+ } else if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_CTL_INJECTED) ||
+ tx->sdata->control_port_protocol == tx->skb->protocol) {
+ tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+ }
+ if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
+ tx->sta = sta_info_get(sdata, hdr->addr1);
}
- if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
- tx->sta = sta_info_get(sdata, hdr->addr1);
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
!ieee80211_is_qos_nullfunc(hdr->frame_control) &&
@@ -1485,7 +1492,7 @@ bool ieee80211_tx_prepare_skb(struct iee
struct ieee80211_tx_data tx;
struct sk_buff *skb2;
- if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+ if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
return false;
info->band = band;
@@ -1518,7 +1525,8 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending)
+ struct sta_info *sta, struct sk_buff *skb,
+ bool txpending)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
@@ -1534,7 +1542,7 @@ static bool ieee80211_tx(struct ieee8021
/* initialises tx */
led_len = skb->len;
- res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
+ res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb);
if (unlikely(res_prepare == TX_DROP)) {
ieee80211_free_txskb(&local->hw, skb);
@@ -1590,7 +1598,8 @@ static int ieee80211_skb_resize(struct i
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1625,7 +1634,7 @@ void ieee80211_xmit(struct ieee80211_sub
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false);
+ ieee80211_tx(sdata, sta, skb, false);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1846,7 +1855,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
goto fail_rcu;
info->band = chandef->chan->band;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, NULL, skb);
rcu_read_unlock();
return NETDEV_TX_OK;
@@ -1877,7 +1886,8 @@ fail:
* Returns: the (possibly reallocated) skb or an ERR_PTR() code
*/
static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u32 info_flags)
+ struct sk_buff *skb, u32 info_flags,
+ struct sta_info **sta_out)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info;
@@ -1920,6 +1930,7 @@ static struct sk_buff *ieee80211_build_h
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = sta->sta.wme;
have_station = true;
+ *sta_out = sta;
} else if (sdata->wdev.use_4addr) {
ret = -ENOLINK;
goto free;
@@ -2073,6 +2084,7 @@ static struct sk_buff *ieee80211_build_h
have_station = true;
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = sta->sta.wme;
+ *sta_out = sta;
} else if (sdata->u.mgd.use_4addr &&
cpu_to_be16(ethertype) != sdata->control_port_protocol) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
@@ -2136,13 +2148,18 @@ static struct sk_buff *ieee80211_build_h
* and mesh mode checks authorization later.
*/
multicast = is_multicast_ether_addr(hdr.addr1);
- if (!multicast && !have_station &&
- !ieee80211_vif_is_mesh(&sdata->vif)) {
- sta = sta_info_get(sdata, hdr.addr1);
+ if (multicast) {
+ *sta_out = ERR_PTR(-ENOENT);
+ } else if (!have_station && !ieee80211_vif_is_mesh(&sdata->vif)) {
+ if (sdata->control_port_protocol == skb->protocol)
+ sta = sta_info_get_bss(sdata, hdr.addr1);
+ else
+ sta = sta_info_get(sdata, hdr.addr1);
if (sta) {
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = sta->sta.wme;
}
+ *sta_out = sta ?: ERR_PTR(-ENOENT);
}
/* For mesh, the use of the QoS header is mandatory */
@@ -2320,6 +2337,7 @@ void __ieee80211_subif_start_xmit(struct
u32 info_flags)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct sta_info *sta = NULL;
if (unlikely(skb->len < ETH_HLEN)) {
kfree_skb(skb);
@@ -2328,7 +2346,7 @@ void __ieee80211_subif_start_xmit(struct
rcu_read_lock();
- skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta);
if (IS_ERR(skb))
goto out;
@@ -2336,7 +2354,7 @@ void __ieee80211_subif_start_xmit(struct
dev->stats.tx_bytes += skb->len;
dev->trans_start = jiffies;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, sta, skb);
out:
rcu_read_unlock();
}
@@ -2364,10 +2382,11 @@ ieee80211_build_data_template(struct iee
.local = sdata->local,
.sdata = sdata,
};
+ struct sta_info *sta_ignore;
rcu_read_lock();
- skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta_ignore);
if (IS_ERR(skb))
goto out;
@@ -2425,7 +2444,7 @@ static bool ieee80211_tx_pending_skb(str
return true;
}
info->band = chanctx_conf->def.chan->band;
- result = ieee80211_tx(sdata, skb, true);
+ result = ieee80211_tx(sdata, NULL, skb, true);
} else {
struct sk_buff_head skbs;
@@ -3163,7 +3182,7 @@ ieee80211_get_buffered_bc(struct ieee802
if (sdata->vif.type == NL80211_IFTYPE_AP)
sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
- if (!ieee80211_tx_prepare(sdata, &tx, skb))
+ if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb))
break;
dev_kfree_skb_any(skb);
}
@@ -3295,6 +3314,6 @@ void __ieee80211_tx_skb_tid_band(struct
*/
local_bh_disable();
IEEE80211_SKB_CB(skb)->band = band;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, NULL, skb);
local_bh_enable();
}

View File

@ -0,0 +1,57 @@
From: Sara Sharon <sara.sharon@intel.com>
Date: Mon, 25 Jan 2016 15:46:35 +0200
Subject: [PATCH] mac80211: fix ibss scan parameters
When joining IBSS a full scan should be initiated in order to search
for existing cell, unless the fixed_channel parameter was set.
A default channel to create the IBSS on if no cell was found is
provided as well.
However - a scan is initiated only on the default channel provided
regardless of whether ifibss->fixed_channel is set or not, with the
obvious result of the cell not joining existing IBSS cell that is
on another channel.
Fixes: 76bed0f43b27 ("mac80211: IBSS fix scan request")
Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
---
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -7,6 +7,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* 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
@@ -1483,14 +1484,21 @@ static void ieee80211_sta_find_ibss(stru
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
- num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
- &ifibss->chandef,
- channels,
- ARRAY_SIZE(channels));
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
- ieee80211_request_ibss_scan(sdata, ifibss->ssid,
- ifibss->ssid_len, channels, num,
- scan_width);
+
+ if (ifibss->fixed_channel) {
+ num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
+ &ifibss->chandef,
+ channels,
+ ARRAY_SIZE(channels));
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, channels,
+ num, scan_width);
+ } else {
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, NULL,
+ 0, scan_width);
+ }
} else {
int interval = IEEE80211_SCAN_INTERVAL;

View File

@ -1,38 +0,0 @@
From: John Linville <linville@tuxdriver.com>
Date: Tue, 31 Mar 2015 10:49:14 -0400
Subject: [PATCH] mac80211: reduce log spam from ieee80211_handle_pwr_constr
This changes a couple of messages from sdata_info to sdata_dbg.
This should reduce some log spam, as reported here:
https://bugzilla.redhat.com/show_bug.cgi?id=1206468
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1347,15 +1347,15 @@ static u32 ieee80211_handle_pwr_constr(s
*/
if (has_80211h_pwr &&
(!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
- sdata_info(sdata,
- "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
- pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
- sdata->u.mgd.bssid);
+ sdata_dbg(sdata,
+ "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+ pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
+ sdata->u.mgd.bssid);
new_ap_level = pwr_level_80211h;
} else { /* has_cisco_pwr is always true here. */
- sdata_info(sdata,
- "Limiting TX power to %d dBm as advertised by %pM\n",
- pwr_level_cisco, sdata->u.mgd.bssid);
+ sdata_dbg(sdata,
+ "Limiting TX power to %d dBm as advertised by %pM\n",
+ pwr_level_cisco, sdata->u.mgd.bssid);
new_ap_level = pwr_level_cisco;
}

View File

@ -0,0 +1,50 @@
From: Chris Bainbridge <chris.bainbridge@gmail.com>
Date: Wed, 27 Jan 2016 15:46:18 +0000
Subject: [PATCH] net/mac80211/agg-rx.c: fix use of uninitialised values
Use kzalloc instead of kmalloc for struct tid_ampdu_rx. Fixes:
[ 7.976605] UBSAN: Undefined behaviour in net/mac80211/rx.c:932:29
[ 7.976608] load of value 2 is not a valid value for type '_Bool'
[ 7.976611] CPU: 3 PID: 1134 Comm: kworker/u16:7 Not tainted 4.5.0-rc1+ #265
[ 7.976613] Hardware name: Apple Inc. MacBookPro10,2/Mac-AFD8A9D944EA4843, BIOS MBP102.88Z.0106.B0A.1509130955 09/13/2015
[ 7.976616] Workqueue: phy0 rt2x00usb_work_rxdone
[ 7.976619] 0000000000000004 ffff880254a7ba50 ffffffff8181d866 0000000000000007
[ 7.976622] ffff880254a7ba78 ffff880254a7ba68 ffffffff8188422d ffffffff8379b500
[ 7.976626] ffff880254a7bab8 ffffffff81884747 0000000000000202 0000000348620032
[ 7.976629] Call Trace:
[ 7.976633] [<ffffffff8181d866>] dump_stack+0x45/0x5f
[ 7.976637] [<ffffffff8188422d>] ubsan_epilogue+0xd/0x40
[ 7.976642] [<ffffffff81884747>] __ubsan_handle_load_invalid_value+0x67/0x70
[ 7.976646] [<ffffffff82227b4d>] ieee80211_sta_reorder_release.isra.16+0x5ed/0x730
[ 7.976650] [<ffffffff8222ca14>] ieee80211_prepare_and_rx_handle+0xd04/0x1c00
[ 7.976654] [<ffffffff81cb27ce>] ? usb_hcd_map_urb_for_dma+0x65e/0x960
[ 7.976659] [<ffffffff8222db03>] __ieee80211_rx_handle_packet+0x1f3/0x750
[ 7.976663] [<ffffffff8222e4a7>] ieee80211_rx_napi+0x447/0x990
[ 7.976667] [<ffffffff81c5fb85>] rt2x00lib_rxdone+0x305/0xbd0
[ 7.976670] [<ffffffff811ac23f>] ? dequeue_task_fair+0x64f/0x1de0
[ 7.976674] [<ffffffff811a1516>] ? sched_clock_cpu+0xe6/0x150
[ 7.976678] [<ffffffff81c6c45c>] rt2x00usb_work_rxdone+0x7c/0x140
[ 7.976682] [<ffffffff8117aef6>] process_one_work+0x226/0x860
[ 7.976686] [<ffffffff8117b58c>] worker_thread+0x5c/0x680
[ 7.976690] [<ffffffff8117b530>] ? process_one_work+0x860/0x860
[ 7.976693] [<ffffffff81184f86>] kthread+0xf6/0x150
[ 7.976697] [<ffffffff81184e90>] ? kthread_worker_fn+0x310/0x310
[ 7.976700] [<ffffffff822a94df>] ret_from_fork+0x3f/0x70
[ 7.976703] [<ffffffff81184e90>] ? kthread_worker_fn+0x310/0x310
Link: https://lkml.org/lkml/2016/1/26/230
Signed-off-by: Chris Bainbridge <chris.bainbridge@gmail.com>
---
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -327,7 +327,7 @@ void __ieee80211_start_rx_ba_session(str
}
/* prepare A-MPDU MLME for Rx aggregation */
- tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_KERNEL);
+ tid_agg_rx = kzalloc(sizeof(struct tid_ampdu_rx), GFP_KERNEL);
if (!tid_agg_rx)
goto end;

View File

@ -1,35 +0,0 @@
From: Hante Meuleman <meuleman@broadcom.com>
Date: Fri, 6 Mar 2015 18:40:41 +0100
Subject: [PATCH] brcmfmac: Fix race condition in msgbuf ioctl processing.
Msgbuf is using a wait_event_timeout to wait for the response on
an ioctl. The wakeup routine uses waitqueue_active to see if
wait_event_timeout has been called. There is a chance that the
response arrives before wait_event_timeout is called, this
will result in situation that wait_event_timeout never gets
woken again and assumed result will be a timeout. This patch
removes that errornous situation by always setting the
ctl_completed var before checking for queue active.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
@@ -481,10 +481,9 @@ static int brcmf_msgbuf_ioctl_resp_wait(
static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
{
- if (waitqueue_active(&msgbuf->ioctl_resp_wait)) {
- msgbuf->ctl_completed = true;
+ msgbuf->ctl_completed = true;
+ if (waitqueue_active(&msgbuf->ioctl_resp_wait))
wake_up(&msgbuf->ioctl_resp_wait);
- }
}

View File

@ -0,0 +1,45 @@
From: Konstantin Khlebnikov <koct9i@gmail.com>
Date: Fri, 29 Jan 2016 11:35:12 +0300
Subject: [PATCH] mac80211: minstrel_ht: fix out-of-bound in
minstrel_ht_set_best_prob_rate
Patch fixes this splat
BUG: KASAN: slab-out-of-bounds in minstrel_ht_update_stats.isra.7+0x6e1/0x9e0
[mac80211] at addr ffff8800cee640f4 Read of size 4 by task swapper/3/0
Signed-off-by: Konstantin Khlebnikov <koct9i@gmail.com>
Link: http://lkml.kernel.org/r/CALYGNiNyJhSaVnE35qS6UCGaSb2Dx1_i5HcRavuOX14oTz2P+w@mail.gmail.com
---
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -414,15 +414,16 @@ minstrel_ht_set_best_prob_rate(struct mi
(max_tp_group != MINSTREL_CCK_GROUP))
return;
+ max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES;
+ max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
+ max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_ewma;
+
if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) {
cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx,
mrs->prob_ewma);
if (cur_tp_avg > tmp_tp_avg)
mi->max_prob_rate = 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_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group,
max_gpr_idx,
max_gpr_prob);
@@ -431,7 +432,7 @@ minstrel_ht_set_best_prob_rate(struct mi
} else {
if (mrs->prob_ewma > tmp_prob)
mi->max_prob_rate = index;
- if (mrs->prob_ewma > mg->rates[mg->max_group_prob_rate].prob_ewma)
+ if (mrs->prob_ewma > max_gpr_prob)
mg->max_group_prob_rate = index;
}
}

View File

@ -1,30 +0,0 @@
From: Hante Meuleman <meuleman@broadcom.com>
Date: Wed, 18 Mar 2015 13:25:23 +0100
Subject: [PATCH] brcmfmac: Update msgbuf commonring size for improved
throughput.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h
@@ -17,11 +17,11 @@
#ifdef CPTCFG_BRCMFMAC_PROTO_MSGBUF
-#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20
-#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256
-#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 20
+#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 512
+#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64
#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024
-#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 256
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 512
#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512
#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40

View File

@ -0,0 +1,35 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Tue, 2 Feb 2016 14:39:08 +0100
Subject: [PATCH] mac80211: move A-MSDU skb_linearize call to
ieee80211_amsdu_to_8023s
Prepararation for zero-copy A-MSDU support with page fragment SKBs
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2203,9 +2203,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
skb->dev = dev;
__skb_queue_head_init(&frame_list);
- if (skb_linearize(skb))
- return RX_DROP_UNUSABLE;
-
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
rx->sdata->vif.type,
rx->local->hw.extra_tx_headroom, true);
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -657,6 +657,9 @@ void ieee80211_amsdu_to_8023s(struct sk_
int remaining, err;
u8 dst[ETH_ALEN], src[ETH_ALEN];
+ if (skb_linearize(skb))
+ goto out;
+
if (has_80211_header) {
err = ieee80211_data_to_8023(skb, addr, iftype);
if (err)

View File

@ -1,307 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:46 +0100
Subject: [PATCH] ath9k_htc: add new WMI_REG_RMW_CMDID command
Since usb bus add extra delay on each request, a command
with read + write requests is too expensive. We can dramtically
reduce usb load by moving this command to firmware.
In my tests, this patch will reduce channel scan time
for about 5-10 seconds.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -131,6 +131,9 @@ struct ath_ops {
void (*enable_write_buffer)(void *);
void (*write_flush) (void *);
u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr);
+ void (*enable_rmw_buffer)(void *);
+ void (*rmw_flush) (void *);
+
};
struct ath_common;
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -444,6 +444,10 @@ static inline void ath9k_htc_stop_btcoex
#define OP_BT_SCAN BIT(4)
#define OP_TSF_RESET BIT(6)
+enum htc_op_flags {
+ HTC_FWFLAG_NO_RMW,
+};
+
struct ath9k_htc_priv {
struct device *dev;
struct ieee80211_hw *hw;
@@ -482,6 +486,7 @@ struct ath9k_htc_priv {
bool reconfig_beacon;
unsigned int rxfilter;
unsigned long op_flags;
+ unsigned long fw_flags;
struct ath9k_hw_cal_data caldata;
struct ath_spec_scan_priv spec_priv;
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -376,17 +376,139 @@ static void ath9k_regwrite_flush(void *h
mutex_unlock(&priv->wmi->multi_write_mutex);
}
-static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
+static void ath9k_reg_rmw_buffer(void *hw_priv,
+ u32 reg_offset, u32 set, u32 clr)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+ u32 rsp_status;
+ int r;
+
+ mutex_lock(&priv->wmi->multi_rmw_mutex);
+
+ /* Store the register/value */
+ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg =
+ cpu_to_be32(reg_offset);
+ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set =
+ cpu_to_be32(set);
+ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr =
+ cpu_to_be32(clr);
+
+ priv->wmi->multi_rmw_idx++;
+
+ /* If the buffer is full, send it out. */
+ if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) {
+ r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+ (u8 *) &priv->wmi->multi_rmw,
+ sizeof(struct register_write) * priv->wmi->multi_rmw_idx,
+ (u8 *) &rsp_status, sizeof(rsp_status),
+ 100);
+ if (unlikely(r)) {
+ ath_dbg(common, WMI,
+ "REGISTER RMW FAILED, multi len: %d\n",
+ priv->wmi->multi_rmw_idx);
+ }
+ priv->wmi->multi_rmw_idx = 0;
+ }
+
+ mutex_unlock(&priv->wmi->multi_rmw_mutex);
+}
+
+static void ath9k_reg_rmw_flush(void *hw_priv)
{
- u32 val;
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+ u32 rsp_status;
+ int r;
+
+ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
+ return;
+
+ atomic_dec(&priv->wmi->m_rmw_cnt);
+
+ mutex_lock(&priv->wmi->multi_rmw_mutex);
+
+ if (priv->wmi->multi_rmw_idx) {
+ r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+ (u8 *) &priv->wmi->multi_rmw,
+ sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx,
+ (u8 *) &rsp_status, sizeof(rsp_status),
+ 100);
+ if (unlikely(r)) {
+ ath_dbg(common, WMI,
+ "REGISTER RMW FAILED, multi len: %d\n",
+ priv->wmi->multi_rmw_idx);
+ }
+ priv->wmi->multi_rmw_idx = 0;
+ }
- val = ath9k_regread(hw_priv, reg_offset);
- val &= ~clr;
- val |= set;
- ath9k_regwrite(hw_priv, val, reg_offset);
+ mutex_unlock(&priv->wmi->multi_rmw_mutex);
+}
+
+static void ath9k_enable_rmw_buffer(void *hw_priv)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
+ return;
+
+ atomic_inc(&priv->wmi->m_rmw_cnt);
+}
+
+static u32 ath9k_reg_rmw_single(void *hw_priv,
+ u32 reg_offset, u32 set, u32 clr)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+ struct register_rmw buf, buf_ret;
+ int ret;
+ u32 val = 0;
+
+ buf.reg = cpu_to_be32(reg_offset);
+ buf.set = cpu_to_be32(set);
+ buf.clr = cpu_to_be32(clr);
+
+ ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+ (u8 *) &buf, sizeof(buf),
+ (u8 *) &buf_ret, sizeof(buf_ret),
+ 100);
+ if (unlikely(ret)) {
+ ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n",
+ reg_offset, ret);
+ }
return val;
}
+static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) {
+ u32 val;
+
+ val = REG_READ(ah, reg_offset);
+ val &= ~clr;
+ val |= set;
+ REG_WRITE(ah, reg_offset, val);
+
+ return 0;
+ }
+
+ if (atomic_read(&priv->wmi->m_rmw_cnt))
+ ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr);
+ else
+ ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr);
+
+ return 0;
+}
+
static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
{
*csz = L1_CACHE_BYTES >> 2;
@@ -501,6 +623,8 @@ static int ath9k_init_priv(struct ath9k_
ah->reg_ops.write = ath9k_regwrite;
ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer;
ah->reg_ops.write_flush = ath9k_regwrite_flush;
+ ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer;
+ ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush;
ah->reg_ops.rmw = ath9k_reg_rmw;
priv->ah = ah;
@@ -686,6 +810,12 @@ static int ath9k_init_firmware_version(s
return -EINVAL;
}
+ if (priv->fw_version_major == 1 && priv->fw_version_minor < 4)
+ set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags);
+
+ dev_info(priv->dev, "FW RMW support: %s\n",
+ test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On");
+
return 0;
}
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -100,6 +100,18 @@
(_ah)->reg_ops.write_flush((_ah)); \
} while (0)
+#define ENABLE_REG_RMW_BUFFER(_ah) \
+ do { \
+ if ((_ah)->reg_ops.enable_rmw_buffer) \
+ (_ah)->reg_ops.enable_rmw_buffer((_ah)); \
+ } while (0)
+
+#define REG_RMW_BUFFER_FLUSH(_ah) \
+ do { \
+ if ((_ah)->reg_ops.rmw_flush) \
+ (_ah)->reg_ops.rmw_flush((_ah)); \
+ } while (0)
+
#define PR_EEP(_s, _val) \
do { \
len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -61,6 +61,8 @@ static const char *wmi_cmd_to_name(enum
return "WMI_REG_READ_CMDID";
case WMI_REG_WRITE_CMDID:
return "WMI_REG_WRITE_CMDID";
+ case WMI_REG_RMW_CMDID:
+ return "WMI_REG_RMW_CMDID";
case WMI_RC_STATE_CHANGE_CMDID:
return "WMI_RC_STATE_CHANGE_CMDID";
case WMI_RC_RATE_UPDATE_CMDID:
@@ -101,6 +103,7 @@ struct wmi *ath9k_init_wmi(struct ath9k_
spin_lock_init(&wmi->event_lock);
mutex_init(&wmi->op_mutex);
mutex_init(&wmi->multi_write_mutex);
+ mutex_init(&wmi->multi_rmw_mutex);
init_completion(&wmi->cmd_wait);
INIT_LIST_HEAD(&wmi->pending_tx_events);
tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -112,6 +112,7 @@ enum wmi_cmd_id {
WMI_TX_STATS_CMDID,
WMI_RX_STATS_CMDID,
WMI_BITRATE_MASK_CMDID,
+ WMI_REG_RMW_CMDID,
};
enum wmi_event_id {
@@ -125,12 +126,19 @@ enum wmi_event_id {
};
#define MAX_CMD_NUMBER 62
+#define MAX_RMW_CMD_NUMBER 15
struct register_write {
__be32 reg;
__be32 val;
};
+struct register_rmw {
+ __be32 reg;
+ __be32 set;
+ __be32 clr;
+} __packed;
+
struct ath9k_htc_tx_event {
int count;
struct __wmi_event_txstatus txs;
@@ -156,10 +164,18 @@ struct wmi {
spinlock_t wmi_lock;
+ /* multi write section */
atomic_t mwrite_cnt;
struct register_write multi_write[MAX_CMD_NUMBER];
u32 multi_write_idx;
struct mutex multi_write_mutex;
+
+ /* multi rmw section */
+ atomic_t m_rmw_cnt;
+ struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER];
+ u32 multi_rmw_idx;
+ struct mutex multi_rmw_mutex;
+
};
struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);

View File

@ -0,0 +1,186 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Tue, 2 Feb 2016 14:39:09 +0100
Subject: [PATCH] cfg80211: add function for 802.3 conversion with separate
output buffer
Use skb_copy_bits in preparation for allowing fragmented skbs
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -393,9 +393,9 @@ unsigned int ieee80211_get_hdrlen_from_s
}
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
-unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+static unsigned int __ieee80211_get_mesh_hdrlen(u8 flags)
{
- int ae = meshhdr->flags & MESH_FLAGS_AE;
+ int ae = flags & MESH_FLAGS_AE;
/* 802.11-2012, 8.2.4.7.3 */
switch (ae) {
default:
@@ -407,21 +407,31 @@ unsigned int ieee80211_get_mesh_hdrlen(s
return 18;
}
}
+
+unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+{
+ return __ieee80211_get_mesh_hdrlen(meshhdr->flags);
+}
EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
-int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
- enum nl80211_iftype iftype)
+static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr,
+ const u8 *addr, enum nl80211_iftype iftype)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 hdrlen, ethertype;
- u8 *payload;
- u8 dst[ETH_ALEN];
- u8 src[ETH_ALEN] __aligned(2);
+ struct {
+ u8 hdr[ETH_ALEN] __aligned(2);
+ __be16 proto;
+ } payload;
+ struct ethhdr tmp;
+ u16 hdrlen;
+ u8 mesh_flags = 0;
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return -1;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (skb->len < hdrlen + 8)
+ return -1;
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
@@ -432,8 +442,11 @@ int ieee80211_data_to_8023(struct sk_buf
* 1 0 BSSID SA DA n/a
* 1 1 RA TA DA SA
*/
- memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
- memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
+ memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
+ memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
+
+ if (iftype == NL80211_IFTYPE_MESH_POINT)
+ skb_copy_bits(skb, hdrlen, &mesh_flags, 1);
switch (hdr->frame_control &
cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
@@ -450,44 +463,31 @@ int ieee80211_data_to_8023(struct sk_buf
iftype != NL80211_IFTYPE_STATION))
return -1;
if (iftype == NL80211_IFTYPE_MESH_POINT) {
- struct ieee80211s_hdr *meshdr =
- (struct ieee80211s_hdr *) (skb->data + hdrlen);
- /* make sure meshdr->flags is on the linear part */
- if (!pskb_may_pull(skb, hdrlen + 1))
- return -1;
- if (meshdr->flags & MESH_FLAGS_AE_A4)
+ if (mesh_flags & MESH_FLAGS_AE_A4)
return -1;
- if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
+ if (mesh_flags & MESH_FLAGS_AE_A5_A6) {
skb_copy_bits(skb, hdrlen +
offsetof(struct ieee80211s_hdr, eaddr1),
- dst, ETH_ALEN);
- skb_copy_bits(skb, hdrlen +
- offsetof(struct ieee80211s_hdr, eaddr2),
- src, ETH_ALEN);
+ tmp.h_dest, 2 * ETH_ALEN);
}
- hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+ hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
}
break;
case cpu_to_le16(IEEE80211_FCTL_FROMDS):
if ((iftype != NL80211_IFTYPE_STATION &&
iftype != NL80211_IFTYPE_P2P_CLIENT &&
iftype != NL80211_IFTYPE_MESH_POINT) ||
- (is_multicast_ether_addr(dst) &&
- ether_addr_equal(src, addr)))
+ (is_multicast_ether_addr(tmp.h_dest) &&
+ ether_addr_equal(tmp.h_source, addr)))
return -1;
if (iftype == NL80211_IFTYPE_MESH_POINT) {
- struct ieee80211s_hdr *meshdr =
- (struct ieee80211s_hdr *) (skb->data + hdrlen);
- /* make sure meshdr->flags is on the linear part */
- if (!pskb_may_pull(skb, hdrlen + 1))
- return -1;
- if (meshdr->flags & MESH_FLAGS_AE_A5_A6)
+ if (mesh_flags & MESH_FLAGS_AE_A5_A6)
return -1;
- if (meshdr->flags & MESH_FLAGS_AE_A4)
+ if (mesh_flags & MESH_FLAGS_AE_A4)
skb_copy_bits(skb, hdrlen +
offsetof(struct ieee80211s_hdr, eaddr1),
- src, ETH_ALEN);
- hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+ tmp.h_source, ETH_ALEN);
+ hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
}
break;
case cpu_to_le16(0):
@@ -498,33 +498,33 @@ int ieee80211_data_to_8023(struct sk_buf
break;
}
- if (!pskb_may_pull(skb, hdrlen + 8))
- return -1;
-
- payload = skb->data + hdrlen;
- ethertype = (payload[6] << 8) | payload[7];
+ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload));
+ tmp.h_proto = payload.proto;
- if (likely((ether_addr_equal(payload, rfc1042_header) &&
- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
- ether_addr_equal(payload, bridge_tunnel_header))) {
+ if (likely((ether_addr_equal(payload.hdr, rfc1042_header) &&
+ tmp.h_proto != htons(ETH_P_AARP) &&
+ tmp.h_proto != htons(ETH_P_IPX)) ||
+ ether_addr_equal(payload.hdr, bridge_tunnel_header)))
/* remove RFC1042 or Bridge-Tunnel encapsulation and
* replace EtherType */
- skb_pull(skb, hdrlen + 6);
- memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
- } else {
- struct ethhdr *ehdr;
- __be16 len;
+ hdrlen += ETH_ALEN + 2;
+ else
+ tmp.h_proto = htons(skb->len);
- skb_pull(skb, hdrlen);
- len = htons(skb->len);
+ pskb_pull(skb, hdrlen);
+
+ if (!ehdr)
ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
- memcpy(ehdr->h_dest, dst, ETH_ALEN);
- memcpy(ehdr->h_source, src, ETH_ALEN);
- ehdr->h_proto = len;
- }
+ memcpy(ehdr, &tmp, sizeof(tmp));
+
return 0;
}
+
+int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
+ enum nl80211_iftype iftype)
+{
+ return __ieee80211_data_to_8023(skb, NULL, addr, iftype);
+}
EXPORT_SYMBOL(ieee80211_data_to_8023);
int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,

View File

@ -1,89 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:47 +0100
Subject: [PATCH] ath9k: ar9271_hw_pa_cal - use defs instead of magin
numbers
This function uses mixed styles for register names/numbers which
is make harder reading and optimisation.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -430,22 +430,22 @@ static void ar9271_hw_pa_cal(struct ath_
u32 regVal;
unsigned int i;
u32 regList[][2] = {
- { 0x786c, 0 },
- { 0x7854, 0 },
- { 0x7820, 0 },
- { 0x7824, 0 },
- { 0x7868, 0 },
- { 0x783c, 0 },
- { 0x7838, 0 } ,
- { 0x7828, 0 } ,
+ { AR9285_AN_TOP3, 0 },
+ { AR9285_AN_RXTXBB1, 0 },
+ { AR9285_AN_RF2G1, 0 },
+ { AR9285_AN_RF2G2, 0 },
+ { AR9285_AN_TOP2, 0 },
+ { AR9285_AN_RF2G8, 0 },
+ { AR9285_AN_RF2G7, 0 } ,
+ { AR9285_AN_RF2G3, 0 } ,
};
for (i = 0; i < ARRAY_SIZE(regList); i++)
regList[i][1] = REG_READ(ah, regList[i][0]);
- regVal = REG_READ(ah, 0x7834);
+ regVal = REG_READ(ah, AR9285_AN_RF2G6);
regVal &= (~(0x1));
- REG_WRITE(ah, 0x7834, regVal);
+ REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
regVal = REG_READ(ah, 0x9808);
regVal |= (0x1 << 27);
REG_WRITE(ah, 0x9808, regVal);
@@ -477,7 +477,7 @@ static void ar9271_hw_pa_cal(struct ath_
* does not matter since we turn it off
*/
REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
-
+ /* 7828, b0-11, ccom=fff */
REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff);
/* Set:
@@ -490,15 +490,16 @@ static void ar9271_hw_pa_cal(struct ath_
/* find off_6_1; */
for (i = 6; i > 0; i--) {
- regVal = REG_READ(ah, 0x7834);
+ regVal = REG_READ(ah, AR9285_AN_RF2G6);
regVal |= (1 << (20 + i));
- REG_WRITE(ah, 0x7834, regVal);
+ REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
udelay(1);
/* regVal = REG_READ(ah, 0x7834); */
regVal &= (~(0x1 << (20 + i)));
- regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9)
+ regVal |= (MS(REG_READ(ah, AR9285_AN_RF2G9),
+ AR9285_AN_RXTXBB1_SPARE9)
<< (20 + i));
- REG_WRITE(ah, 0x7834, regVal);
+ REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
}
regVal = (regVal >> 20) & 0x7f;
@@ -517,9 +518,9 @@ static void ar9271_hw_pa_cal(struct ath_
ENABLE_REGWRITE_BUFFER(ah);
- regVal = REG_READ(ah, 0x7834);
+ regVal = REG_READ(ah, AR_AN_RF2G1_CH1);
regVal |= 0x1;
- REG_WRITE(ah, 0x7834, regVal);
+ REG_WRITE(ah, AR_AN_RF2G1_CH1, regVal);
regVal = REG_READ(ah, 0x9808);
regVal &= (~(0x1 << 27));
REG_WRITE(ah, 0x9808, regVal);

View File

@ -0,0 +1,159 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Tue, 2 Feb 2016 14:39:10 +0100
Subject: [PATCH] cfg80211: add support for non-linear skbs in
ieee80211_amsdu_to_8023s
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -644,73 +644,75 @@ int ieee80211_data_from_8023(struct sk_b
}
EXPORT_SYMBOL(ieee80211_data_from_8023);
+static struct sk_buff *
+__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
+ int offset, int len)
+{
+ struct sk_buff *frame;
+
+ if (skb->len - offset < len)
+ return NULL;
+
+ /*
+ * Allocate and reserve two bytes more for payload
+ * alignment since sizeof(struct ethhdr) is 14.
+ */
+ frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
+
+ skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
+ skb_copy_bits(skb, offset, skb_put(frame, len), len);
+
+ return frame;
+}
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
const u8 *addr, enum nl80211_iftype iftype,
const unsigned int extra_headroom,
bool has_80211_header)
{
+ unsigned int hlen = ALIGN(extra_headroom, 4);
struct sk_buff *frame = NULL;
u16 ethertype;
u8 *payload;
- const struct ethhdr *eth;
- int remaining, err;
- u8 dst[ETH_ALEN], src[ETH_ALEN];
-
- if (skb_linearize(skb))
- goto out;
+ int offset = 0, remaining, err;
+ struct ethhdr eth;
+ bool reuse_skb = true;
+ bool last = false;
if (has_80211_header) {
- err = ieee80211_data_to_8023(skb, addr, iftype);
+ err = __ieee80211_data_to_8023(skb, &eth, addr, iftype);
if (err)
goto out;
-
- /* skip the wrapping header */
- eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
- if (!eth)
- goto out;
- } else {
- eth = (struct ethhdr *) skb->data;
}
- while (skb != frame) {
+ while (!last) {
+ unsigned int subframe_len;
+ int len;
u8 padding;
- __be16 len = eth->h_proto;
- unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
-
- remaining = skb->len;
- memcpy(dst, eth->h_dest, ETH_ALEN);
- memcpy(src, eth->h_source, ETH_ALEN);
+ skb_copy_bits(skb, offset, &eth, sizeof(eth));
+ len = ntohs(eth.h_proto);
+ subframe_len = sizeof(struct ethhdr) + len;
padding = (4 - subframe_len) & 0x3;
+
/* the last MSDU has no padding */
+ remaining = skb->len - offset;
if (subframe_len > remaining)
goto purge;
- skb_pull(skb, sizeof(struct ethhdr));
+ offset += sizeof(struct ethhdr);
/* reuse skb for the last subframe */
- if (remaining <= subframe_len + padding)
+ last = remaining <= subframe_len + padding;
+ if (!skb_is_nonlinear(skb) && last) {
+ skb_pull(skb, offset);
frame = skb;
- else {
- unsigned int hlen = ALIGN(extra_headroom, 4);
- /*
- * Allocate and reserve two bytes more for payload
- * alignment since sizeof(struct ethhdr) is 14.
- */
- frame = dev_alloc_skb(hlen + subframe_len + 2);
+ reuse_skb = true;
+ } else {
+ frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
if (!frame)
goto purge;
- skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
- memcpy(skb_put(frame, ntohs(len)), skb->data,
- ntohs(len));
-
- eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
- padding);
- if (!eth) {
- dev_kfree_skb(frame);
- goto purge;
- }
+ offset += len + padding;
}
skb_reset_network_header(frame);
@@ -719,24 +721,20 @@ void ieee80211_amsdu_to_8023s(struct sk_
payload = frame->data;
ethertype = (payload[6] << 8) | payload[7];
-
if (likely((ether_addr_equal(payload, rfc1042_header) &&
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
ether_addr_equal(payload, bridge_tunnel_header))) {
- /* remove RFC1042 or Bridge-Tunnel
- * encapsulation and replace EtherType */
- skb_pull(frame, 6);
- memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
- } else {
- memcpy(skb_push(frame, sizeof(__be16)), &len,
- sizeof(__be16));
- memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+ eth.h_proto = htons(ethertype);
+ skb_pull(frame, ETH_ALEN + 2);
}
+
+ memcpy(skb_push(frame, sizeof(eth)), &eth, sizeof(eth));
__skb_queue_tail(list, frame);
}
+ if (!reuse_skb)
+ dev_kfree_skb(skb);
+
return;
purge:

View File

@ -1,79 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:48 +0100
Subject: [PATCH] ath9k: ar9271_hw_pa_cal: use proper makroses.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -443,33 +443,30 @@ static void ar9271_hw_pa_cal(struct ath_
for (i = 0; i < ARRAY_SIZE(regList); i++)
regList[i][1] = REG_READ(ah, regList[i][0]);
- regVal = REG_READ(ah, AR9285_AN_RF2G6);
- regVal &= (~(0x1));
- REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
- regVal = REG_READ(ah, 0x9808);
- regVal |= (0x1 << 27);
- REG_WRITE(ah, 0x9808, regVal);
-
+ /* 7834, b1=0 */
+ REG_CLR_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
+ /* 9808, b27=1 */
+ REG_SET_BIT(ah, 0x9808, 1 << 27);
/* 786c,b23,1, pwddac=1 */
- REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1);
+ REG_SET_BIT(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC);
/* 7854, b5,1, pdrxtxbb=1 */
- REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1);
+ REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1);
/* 7854, b7,1, pdv2i=1 */
- REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1);
+ REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I);
/* 7854, b8,1, pddacinterface=1 */
- REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1);
+ REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF);
/* 7824,b12,0, offcal=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL);
/* 7838, b1,0, pwddb=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB);
/* 7820,b11,0, enpacal=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL);
/* 7820,b25,1, pdpadrv1=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1);
/* 7820,b24,0, pdpadrv2=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2);
/* 7820,b23,0, pdpaout=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT);
/* 783c,b14-16,7, padrvgn2tab_0=7 */
REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7);
/*
@@ -516,15 +513,13 @@ static void ar9271_hw_pa_cal(struct ath_
ah->pacal_info.prev_offset = regVal;
}
- ENABLE_REGWRITE_BUFFER(ah);
- regVal = REG_READ(ah, AR_AN_RF2G1_CH1);
- regVal |= 0x1;
- REG_WRITE(ah, AR_AN_RF2G1_CH1, regVal);
- regVal = REG_READ(ah, 0x9808);
- regVal &= (~(0x1 << 27));
- REG_WRITE(ah, 0x9808, regVal);
+ /* 7834, b1=1 */
+ REG_SET_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
+ /* 9808, b27=0 */
+ REG_CLR_BIT(ah, 0x9808, 1 << 27);
+ ENABLE_REGWRITE_BUFFER(ah);
for (i = 0; i < ARRAY_SIZE(regList); i++)
REG_WRITE(ah, regList[i][0], regList[i][1]);

View File

@ -0,0 +1,155 @@
From: Sven Eckelmann <sven@narfation.org>
Date: Tue, 26 Jan 2016 17:11:13 +0100
Subject: [PATCH] mac80211: Parse legacy and HT rate in injected frames
Drivers/devices without their own rate control algorithm can get the
information what rates they should use from either the radiotap header of
injected frames or from the rate control algorithm. But the parsing of the
legacy rate information from the radiotap header was removed in commit
e6a9854b05c1 ("mac80211/drivers: rewrite the rate control API").
The removal of this feature heavily reduced the usefulness of frame
injection when wanting to simulate specific transmission behavior. Having
rate parsing together with MCS rates and retry support allows a fine
grained selection of the tx behavior of injected frames for these kind of
tests.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Cc: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -708,12 +708,14 @@ enum mac80211_tx_info_flags {
* protocol frame (e.g. EAP)
* @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
* frame (PS-Poll or uAPSD).
+ * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
*
* These flags are used in tx_info->control.flags.
*/
enum mac80211_tx_control_flags {
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
+ IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
};
/*
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
info->control.short_preamble = txrc.short_preamble;
+ /* don't ask rate control when rate already injected via radiotap */
+ if (info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)
+ return TX_CONTINUE;
+
if (tx->sta)
assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
@@ -1665,15 +1669,24 @@ void ieee80211_xmit(struct ieee80211_sub
ieee80211_tx(sdata, sta, skb, false);
}
-static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
+static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
+ struct sk_buff *skb)
{
struct ieee80211_radiotap_iterator iterator;
struct ieee80211_radiotap_header *rthdr =
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_supported_band *sband =
+ local->hw.wiphy->bands[info->band];
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
NULL);
u16 txflags;
+ u16 rate = 0;
+ bool rate_found = false;
+ u8 rate_retries = 0;
+ u16 rate_flags = 0;
+ u8 mcs_known, mcs_flags;
+ int i;
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_DONTFRAG;
@@ -1724,6 +1737,35 @@ static bool ieee80211_parse_tx_radiotap(
info->flags |= IEEE80211_TX_CTL_NO_ACK;
break;
+ case IEEE80211_RADIOTAP_RATE:
+ rate = *iterator.this_arg;
+ rate_flags = 0;
+ rate_found = true;
+ break;
+
+ case IEEE80211_RADIOTAP_DATA_RETRIES:
+ rate_retries = *iterator.this_arg;
+ break;
+
+ case IEEE80211_RADIOTAP_MCS:
+ mcs_known = iterator.this_arg[0];
+ mcs_flags = iterator.this_arg[1];
+ if (!(mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_MCS))
+ break;
+
+ rate_found = true;
+ rate = iterator.this_arg[2];
+ rate_flags = IEEE80211_TX_RC_MCS;
+
+ if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI &&
+ mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
+ rate_flags |= IEEE80211_TX_RC_SHORT_GI;
+
+ if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
+ mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40)
+ rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ break;
+
/*
* Please update the file
* Documentation/networking/mac80211-injection.txt
@@ -1738,6 +1780,32 @@ static bool ieee80211_parse_tx_radiotap(
if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
return false;
+ if (rate_found) {
+ info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->control.rates[i].idx = -1;
+ info->control.rates[i].flags = 0;
+ info->control.rates[i].count = 0;
+ }
+
+ if (rate_flags & IEEE80211_TX_RC_MCS) {
+ info->control.rates[0].idx = rate;
+ } else {
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (rate * 5 != sband->bitrates[i].bitrate)
+ continue;
+
+ info->control.rates[0].idx = i;
+ break;
+ }
+ }
+
+ info->control.rates[0].flags = rate_flags;
+ info->control.rates[0].count = min_t(u8, rate_retries + 1,
+ local->hw.max_rate_tries);
+ }
+
/*
* remove the radiotap header
* iterator->_max_length was sanity-checked against
@@ -1819,7 +1887,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
IEEE80211_TX_CTL_INJECTED;
/* process and remove the injection radiotap header */
- if (!ieee80211_parse_tx_radiotap(skb))
+ if (!ieee80211_parse_tx_radiotap(local, skb))
goto fail;
rcu_read_lock();

View File

@ -1,48 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:49 +0100
Subject: [PATCH] ath9k: ar9271_hw_pa_cal: use RMW buffer
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -436,13 +436,14 @@ static void ar9271_hw_pa_cal(struct ath_
{ AR9285_AN_RF2G2, 0 },
{ AR9285_AN_TOP2, 0 },
{ AR9285_AN_RF2G8, 0 },
- { AR9285_AN_RF2G7, 0 } ,
- { AR9285_AN_RF2G3, 0 } ,
+ { AR9285_AN_RF2G7, 0 },
+ { AR9285_AN_RF2G3, 0 },
};
for (i = 0; i < ARRAY_SIZE(regList); i++)
regList[i][1] = REG_READ(ah, regList[i][0]);
+ ENABLE_REG_RMW_BUFFER(ah);
/* 7834, b1=0 */
REG_CLR_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
/* 9808, b27=1 */
@@ -476,6 +477,7 @@ static void ar9271_hw_pa_cal(struct ath_
REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
/* 7828, b0-11, ccom=fff */
REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff);
+ REG_RMW_BUFFER_FLUSH(ah);
/* Set:
* localmode=1,bmode=1,bmoderxtx=1,synthon=1,
@@ -514,10 +516,12 @@ static void ar9271_hw_pa_cal(struct ath_
}
+ ENABLE_REG_RMW_BUFFER(ah);
/* 7834, b1=1 */
REG_SET_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
/* 9808, b27=0 */
REG_CLR_BIT(ah, 0x9808, 1 << 27);
+ REG_RMW_BUFFER_FLUSH(ah);
ENABLE_REGWRITE_BUFFER(ah);
for (i = 0; i < ARRAY_SIZE(regList); i++)

View File

@ -0,0 +1,317 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Fri, 5 Feb 2016 01:38:51 +0100
Subject: [PATCH] mac80211: add A-MSDU tx support
Requires software tx queueing support. frag_list support (for zero-copy)
is optional.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -709,6 +709,7 @@ enum mac80211_tx_info_flags {
* @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
* frame (PS-Poll or uAPSD).
* @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
+ * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
*
* These flags are used in tx_info->control.flags.
*/
@@ -716,6 +717,7 @@ enum mac80211_tx_control_flags {
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
+ IEEE80211_TX_CTRL_AMSDU = BIT(3),
};
/*
@@ -1728,6 +1730,7 @@ struct ieee80211_sta_rates {
* size is min(max_amsdu_len, 7935) bytes.
* Both additional HT limits must be enforced by the low level driver.
* This is defined by the spec (IEEE 802.11-2012 section 8.3.2.2 NOTE 2).
+ * @max_rc_amsdu_len: Maximum A-MSDU size in bytes recommended by rate control.
* @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
*/
struct ieee80211_sta {
@@ -1748,6 +1751,7 @@ struct ieee80211_sta {
bool mfp;
u8 max_amsdu_subframes;
u16 max_amsdu_len;
+ u16 max_rc_amsdu_len;
struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
@@ -1961,6 +1965,15 @@ struct ieee80211_txq {
* order and does not need to manage its own reorder buffer or BA session
* timeout.
*
+ * @IEEE80211_HW_TX_AMSDU: Hardware (or driver) supports software aggregated
+ * A-MSDU frames. Requires software tx queueing and fast-xmit support.
+ * When not using minstrel/minstrel_ht rate control, the driver should
+ * limit the maximum A-MSDU size based on the current tx rate by setting
+ * max_rc_amsdu_len in struct ieee80211_sta.
+ *
+ * @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
+ * skbs, needed for zero-copy software A-MSDU.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -1998,6 +2011,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_BEACON_TX_STATUS,
IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
IEEE80211_HW_SUPPORTS_REORDERING_BUFFER,
+ IEEE80211_HW_TX_AMSDU,
+ IEEE80211_HW_TX_FRAG_LIST,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -2070,6 +2085,9 @@ enum ieee80211_hw_flags {
* size is smaller (an example is LinkSys WRT120N with FW v1.0.07
* build 002 Jun 18 2012).
*
+ * @max_tx_fragments: maximum number of tx buffers per (A)-MSDU, sum
+ * of 1 + skb_shinfo(skb)->nr_frags for each skb in the frag_list.
+ *
* @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
* (if %IEEE80211_HW_QUEUE_CONTROL is set)
*
@@ -2124,6 +2142,7 @@ struct ieee80211_hw {
u8 max_rate_tries;
u8 max_rx_aggregation_subframes;
u8 max_tx_aggregation_subframes;
+ u8 max_tx_fragments;
u8 offchannel_tx_hw_queue;
u8 radiotap_mcs_details;
u16 radiotap_vht_details;
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -935,6 +935,7 @@ void ieee80211_process_addba_resp(struct
size_t len)
{
struct tid_ampdu_tx *tid_tx;
+ struct ieee80211_txq *txq;
u16 capab, tid;
u8 buf_size;
bool amsdu;
@@ -945,6 +946,10 @@ void ieee80211_process_addba_resp(struct
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
+ txq = sta->sta.txq[tid];
+ if (!amsdu && txq)
+ set_bit(IEEE80211_TXQ_NO_AMSDU, &to_txq_info(txq)->flags);
+
mutex_lock(&sta->ampdu_mlme.mtx);
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -127,6 +127,8 @@ static const char *hw_flag_names[NUM_IEE
FLAG(BEACON_TX_STATUS),
FLAG(NEEDS_UNIQUE_STA_ADDR),
FLAG(SUPPORTS_REORDERING_BUFFER),
+ FLAG(TX_AMSDU),
+ FLAG(TX_FRAG_LIST),
/* keep last for the build bug below */
(void *)0x1
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -799,6 +799,7 @@ struct mac80211_qos_map {
enum txq_info_flags {
IEEE80211_TXQ_STOP,
IEEE80211_TXQ_AMPDU,
+ IEEE80211_TXQ_NO_AMSDU,
};
struct txq_info {
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1318,6 +1318,10 @@ struct sk_buff *ieee80211_tx_dequeue(str
out:
spin_unlock_bh(&txqi->queue.lock);
+ if (skb && skb_has_frag_list(skb) &&
+ !ieee80211_hw_check(&local->hw, TX_FRAG_LIST))
+ skb_linearize(skb);
+
return skb;
}
EXPORT_SYMBOL(ieee80211_tx_dequeue);
@@ -2757,6 +2761,163 @@ void ieee80211_clear_fast_xmit(struct st
kfree_rcu(fast_tx, rcu_head);
}
+static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
+ struct sk_buff *skb, int headroom,
+ int *subframe_len)
+{
+ int amsdu_len = *subframe_len + sizeof(struct ethhdr);
+ int padding = (4 - amsdu_len) & 3;
+
+ if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
+ I802_DEBUG_INC(local->tx_expand_skb_head);
+
+ if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
+ wiphy_debug(local->hw.wiphy,
+ "failed to reallocate TX buffer\n");
+ return false;
+ }
+ }
+
+ if (padding) {
+ *subframe_len += padding;
+ memset(skb_put(skb, padding), 0, padding);
+ }
+
+ return true;
+}
+
+static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_fast_tx *fast_tx,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr;
+ struct ethhdr amsdu_hdr;
+ int hdr_len = fast_tx->hdr_len - sizeof(rfc1042_header);
+ int subframe_len = skb->len - hdr_len;
+ void *data;
+ u8 *qc;
+
+ if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
+ return false;
+
+ if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
+ return true;
+
+ if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(amsdu_hdr),
+ &subframe_len))
+ return false;
+
+ amsdu_hdr.h_proto = cpu_to_be16(subframe_len);
+ memcpy(amsdu_hdr.h_source, skb->data + fast_tx->sa_offs, ETH_ALEN);
+ memcpy(amsdu_hdr.h_dest, skb->data + fast_tx->da_offs, ETH_ALEN);
+
+ data = skb_push(skb, sizeof(amsdu_hdr));
+ memmove(data, data + sizeof(amsdu_hdr), hdr_len);
+ memcpy(data + hdr_len, &amsdu_hdr, sizeof(amsdu_hdr));
+
+ hdr = data;
+ qc = ieee80211_get_qos_ctl(hdr);
+ *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+ info->control.flags |= IEEE80211_TX_CTRL_AMSDU;
+
+ return true;
+}
+
+static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_fast_tx *fast_tx,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = sdata->local;
+ u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
+ struct txq_info *txqi;
+ struct sk_buff **frag_tail, *head;
+ int subframe_len = skb->len - ETH_ALEN;
+ u8 max_subframes = sta->sta.max_amsdu_subframes;
+ int max_frags = local->hw.max_tx_fragments;
+ int max_amsdu_len = sta->sta.max_amsdu_len;
+ __be16 len;
+ void *data;
+ bool ret = false;
+ int n = 1, nfrags;
+
+ if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
+ return false;
+
+ if (!txq)
+ return false;
+
+ txqi = to_txq_info(txq);
+ if (test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags))
+ return false;
+
+ if (sta->sta.max_rc_amsdu_len)
+ max_amsdu_len = min_t(int, max_amsdu_len,
+ sta->sta.max_rc_amsdu_len);
+
+ spin_lock_bh(&txqi->queue.lock);
+
+ head = skb_peek_tail(&txqi->queue);
+ if (!head)
+ goto out;
+
+ if (skb->len + head->len > max_amsdu_len)
+ goto out;
+
+ /*
+ * HT A-MPDU limits maximum MPDU size to 4095 bytes. Since aggregation
+ * sessions are started/stopped without txq flush, use the limit here
+ * to avoid having to de-aggregate later.
+ */
+ if (skb->len + head->len > 4095 &&
+ !sta->sta.vht_cap.vht_supported)
+ goto out;
+
+ if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
+ goto out;
+
+ nfrags = 1 + skb_shinfo(skb)->nr_frags;
+ nfrags += 1 + skb_shinfo(head)->nr_frags;
+ frag_tail = &skb_shinfo(head)->frag_list;
+ while (*frag_tail) {
+ nfrags += 1 + skb_shinfo(*frag_tail)->nr_frags;
+ frag_tail = &(*frag_tail)->next;
+ n++;
+ }
+
+ if (max_subframes && n > max_subframes)
+ goto out;
+
+ if (max_frags && nfrags > max_frags)
+ goto out;
+
+ if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
+ &subframe_len))
+ return false;
+
+ ret = true;
+ data = skb_push(skb, ETH_ALEN + 2);
+ memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);
+
+ data += 2 * ETH_ALEN;
+ len = cpu_to_be16(subframe_len);
+ memcpy(data, &len, 2);
+ memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
+
+ head->len += skb->len;
+ head->data_len += skb->len;
+ *frag_tail = skb;
+
+out:
+ spin_unlock_bh(&txqi->queue.lock);
+
+ return ret;
+}
+
static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
struct net_device *dev, struct sta_info *sta,
struct ieee80211_fast_tx *fast_tx,
@@ -2811,6 +2972,10 @@ static bool ieee80211_xmit_fast(struct i
ieee80211_tx_stats(dev, skb->len + extra_head);
+ if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
+ ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
+ return true;
+
/* will not be crypto-handled beyond what we do here, so use false
* as the may-encrypt argument for the resize to not account for
* more room than we already have in 'extra_head'

View File

@ -1,5 +1,5 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Wed, 20 Jan 2016 16:35:12 +0100
Date: Wed, 20 Jan 2016 16:46:04 +0100
Subject: [PATCH] brcmfmac: fix setting primary channel for 80 MHz width
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
@ -14,13 +14,12 @@ offset in MHz against -2 or 2 which was resulting in picking a wrong
primary channel.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
.../brcm80211/brcmfmac/cfg80211.c | 23 ++++++++++------------
1 file changed, 10 insertions(+), 13 deletions(-)
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -246,7 +246,7 @@ static u16 chandef_to_chanspec(struct br
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -247,7 +247,7 @@ static u16 chandef_to_chanspec(struct br
brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
ch->chan->center_freq, ch->center_freq1, ch->width);
ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
@ -29,7 +28,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
switch (ch->width) {
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
@@ -255,24 +255,21 @@ static u16 chandef_to_chanspec(struct br
@@ -256,24 +256,21 @@ static u16 chandef_to_chanspec(struct br
break;
case NL80211_CHAN_WIDTH_40:
ch_inf.bw = BRCMU_CHAN_BW_40;

View File

@ -0,0 +1,51 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Tue, 26 Jan 2016 17:57:01 +0100
Subject: [PATCH] brcmfmac: analyze descriptors of current component only
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
So far we were looking for address descriptors without a check for
crossing current component border. In case of dealing with unsupported
descriptor or descriptor missing at all the code would incorrectly get
data from another component.
Consider this binary-described component from BCM4366 EROM:
4bf83b01 TAG==CI CID==0x83b
20080201 TAG==CI PORTS==0+1 WRAPPERS==0+1
18400035 TAG==ADDR SZ_SZD TYPE_SLAVE
00050000
18107085 TAG==ADDR SZ_4K TYPE_SWRAP
Driver was assigning invalid base address to this core:
brcmfmac: [6 ] core 0x83b:32 base 0x18109000 wrap 0x18107000
which came from totally different component defined in EROM:
43b36701 TAG==CI CID==0x367
00000201 TAG==CI PORTS==0+1 WRAPPERS==0+0
18109005 TAG==ADDR SZ_4K TYPE_SLAVE
This change will also allow us to support components without wrapper
address in the future.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -803,7 +803,14 @@ static int brcmf_chip_dmp_get_regaddr(st
*eromaddr -= 4;
return -EFAULT;
}
- } while (desc != DMP_DESC_ADDRESS);
+ } while (desc != DMP_DESC_ADDRESS &&
+ desc != DMP_DESC_COMPONENT);
+
+ /* stop if we crossed current component border */
+ if (desc == DMP_DESC_COMPONENT) {
+ *eromaddr -= 4;
+ return 0;
+ }
/* skip upper 32-bit address descriptor */
if (val & DMP_DESC_ADDRSIZE_GT32)

View File

@ -0,0 +1,28 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Tue, 26 Jan 2016 17:57:02 +0100
Subject: [PATCH] brcmfmac: allow storing PMU core without wrapper address
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Separated PMU core can be found in new devices and should be used for
accessing PMU registers (which were routed through ChipCommon so far).
This core is one of exceptions that doesn't have or need wrapper address
to be still safely accessible.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -883,7 +883,8 @@ int brcmf_chip_dmp_erom_scan(struct brcm
rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
/* need core with ports */
- if (nmw + nsw == 0)
+ if (nmw + nsw == 0 &&
+ id != BCMA_CORE_PMU)
continue;
/* try to obtain register address info */

View File

@ -0,0 +1,43 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Tue, 26 Jan 2016 17:57:03 +0100
Subject: [PATCH] brcmfmac: read extended capabilities of ChipCommon core
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is an extra bitfield with info about some present hardware.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -1025,6 +1025,9 @@ static int brcmf_chip_setup(struct brcmf
/* get chipcommon capabilites */
pub->cc_caps = chip->ops->read32(chip->ctx,
CORE_CC_REG(base, capabilities));
+ pub->cc_caps_ext = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(base,
+ capabilities_ext));
/* get pmu caps & rev */
if (pub->cc_caps & CC_CAP_PMU) {
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
@@ -27,6 +27,7 @@
* @chip: chip identifier.
* @chiprev: chip revision.
* @cc_caps: chipcommon core capabilities.
+ * @cc_caps_ext: chipcommon core extended capabilities.
* @pmucaps: PMU capabilities.
* @pmurev: PMU revision.
* @rambase: RAM base address (only applicable for ARM CR4 chips).
@@ -38,6 +39,7 @@ struct brcmf_chip {
u32 chip;
u32 chiprev;
u32 cc_caps;
+ u32 cc_caps_ext;
u32 pmucaps;
u32 pmurev;
u32 rambase;

View File

@ -0,0 +1,148 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Tue, 26 Jan 2016 17:57:04 +0100
Subject: [PATCH] brcmfmac: access PMU registers using standalone PMU core if
available
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On recent Broadcom chipsets PMU is present as separated core and it
can't be accessed using ChipCommon anymore as it fails with e.g.:
[ 18.198412] Unhandled fault: imprecise external abort (0x1406) at 0xb6da200f
Add a new helper function that will return a proper core that should be
used for accessing PMU registers.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -1014,6 +1014,7 @@ static int brcmf_chip_setup(struct brcmf
{
struct brcmf_chip *pub;
struct brcmf_core_priv *cc;
+ struct brcmf_core *pmu;
u32 base;
u32 val;
int ret = 0;
@@ -1030,9 +1031,10 @@ static int brcmf_chip_setup(struct brcmf
capabilities_ext));
/* get pmu caps & rev */
+ pmu = brcmf_chip_get_pmu(pub); /* after reading cc_caps_ext */
if (pub->cc_caps & CC_CAP_PMU) {
val = chip->ops->read32(chip->ctx,
- CORE_CC_REG(base, pmucapabilities));
+ CORE_CC_REG(pmu->base, pmucapabilities));
pub->pmurev = val & PCAP_REV_MASK;
pub->pmucaps = val;
}
@@ -1131,6 +1133,23 @@ struct brcmf_core *brcmf_chip_get_chipco
return &cc->pub;
}
+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub)
+{
+ struct brcmf_core *cc = brcmf_chip_get_chipcommon(pub);
+ struct brcmf_core *pmu;
+
+ /* See if there is separated PMU core available */
+ if (cc->rev >= 35 &&
+ pub->cc_caps_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) {
+ pmu = brcmf_chip_get_core(pub, BCMA_CORE_PMU);
+ if (pmu)
+ return pmu;
+ }
+
+ /* Fallback to ChipCommon core for older hardware */
+ return cc;
+}
+
bool brcmf_chip_iscoreup(struct brcmf_core *pub)
{
struct brcmf_core_priv *core;
@@ -1301,6 +1320,7 @@ bool brcmf_chip_sr_capable(struct brcmf_
{
u32 base, addr, reg, pmu_cc3_mask = ~0;
struct brcmf_chip_priv *chip;
+ struct brcmf_core *pmu = brcmf_chip_get_pmu(pub);
brcmf_dbg(TRACE, "Enter\n");
@@ -1320,9 +1340,9 @@ bool brcmf_chip_sr_capable(struct brcmf_
case BRCM_CC_4335_CHIP_ID:
case BRCM_CC_4339_CHIP_ID:
/* read PMU chipcontrol register 3 */
- addr = CORE_CC_REG(base, chipcontrol_addr);
+ addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
chip->ops->write32(chip->ctx, addr, 3);
- addr = CORE_CC_REG(base, chipcontrol_data);
+ addr = CORE_CC_REG(pmu->base, chipcontrol_data);
reg = chip->ops->read32(chip->ctx, addr);
return (reg & pmu_cc3_mask) != 0;
case BRCM_CC_43430_CHIP_ID:
@@ -1330,12 +1350,12 @@ bool brcmf_chip_sr_capable(struct brcmf_
reg = chip->ops->read32(chip->ctx, addr);
return reg != 0;
default:
- addr = CORE_CC_REG(base, pmucapabilities_ext);
+ addr = CORE_CC_REG(pmu->base, pmucapabilities_ext);
reg = chip->ops->read32(chip->ctx, addr);
if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
return false;
- addr = CORE_CC_REG(base, retention_ctl);
+ addr = CORE_CC_REG(pmu->base, retention_ctl);
reg = chip->ops->read32(chip->ctx, addr);
return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
@@ -85,6 +85,7 @@ struct brcmf_chip *brcmf_chip_attach(voi
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_chipcommon(struct brcmf_chip *chip);
+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub);
bool brcmf_chip_iscoreup(struct brcmf_core *core);
void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -3615,7 +3615,6 @@ brcmf_sdio_drivestrengthinit(struct brcm
const struct sdiod_drive_str *str_tab = NULL;
u32 str_mask;
u32 str_shift;
- u32 base;
u32 i;
u32 drivestrength_sel = 0;
u32 cc_data_temp;
@@ -3658,14 +3657,15 @@ brcmf_sdio_drivestrengthinit(struct brcm
}
if (str_tab != NULL) {
+ struct brcmf_core *pmu = brcmf_chip_get_pmu(ci);
+
for (i = 0; str_tab[i].strength != 0; i++) {
if (drivestrength >= str_tab[i].strength) {
drivestrength_sel = str_tab[i].sel;
break;
}
}
- base = brcmf_chip_get_chipcommon(ci)->base;
- addr = CORE_CC_REG(base, chipcontrol_addr);
+ addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
cc_data_temp &= ~str_mask;
@@ -3835,8 +3835,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
goto fail;
/* set PMUControl so a backplane reset does PMU state reload */
- reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
- pmucontrol);
+ reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
if (err)
goto fail;

View File

@ -0,0 +1,38 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Tue, 26 Jan 2016 17:57:05 +0100
Subject: [PATCH] brcmfmac: add support for 14e4:4365 PCI ID with BCM4366
chipset
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On Broadcom ARM routers BCM4366 cards are available with 14e4:4365 ID.
Unfortunately this ID was already used by Broadcom for cards with
BCM43142, a totally different chipset requiring SoftMAC driver. To avoid
a conflict between brcmfmac and bcma use more specific ID entry with
subvendor and subdevice specified.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -1951,6 +1951,9 @@ static const struct dev_pm_ops brcmf_pci
#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
+#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev) { \
+ BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
+ subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
static struct pci_device_id brcmf_pcie_devid_table[] = {
BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
@@ -1966,6 +1969,7 @@ static struct pci_device_id brcmf_pcie_d
BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE_SUB(0x4365, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4365),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID),

View File

@ -1,35 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:50 +0100
Subject: [PATCH] ath9k: add multi_read to be compatible with ath9k_htc
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -141,6 +141,16 @@ static unsigned int ath9k_ioread32(void
return val;
}
+static void ath9k_multi_ioread32(void *hw_priv, u32 *addr,
+ u32 *val, u16 count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ val[i] = ath9k_ioread32(hw_priv, addr[i]);
+}
+
+
static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset,
u32 set, u32 clr)
{
@@ -530,6 +540,7 @@ static int ath9k_init_softc(u16 devid, s
ah->hw = sc->hw;
ah->hw_version.devid = devid;
ah->reg_ops.read = ath9k_ioread32;
+ ah->reg_ops.multi_read = ath9k_multi_ioread32;
ah->reg_ops.write = ath9k_iowrite32;
ah->reg_ops.rmw = ath9k_reg_rmw;
pCap = &ah->caps;

View File

@ -1,69 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:51 +0100
Subject: [PATCH] ath9k: add new function ath9k_hw_read_array
REG_READ generate most overhead on usb bus. It send and read micro packages
and reduce usb bandwidth. To reduce this overhead we should read in batches.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -121,6 +121,36 @@ void ath9k_hw_write_array(struct ath_hw
REGWRITE_BUFFER_FLUSH(ah);
}
+void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size)
+{
+ u32 *tmp_reg_list, *tmp_data;
+ int i;
+
+ tmp_reg_list = kmalloc(size * sizeof(u32), GFP_KERNEL);
+ if (!tmp_reg_list) {
+ dev_err(ah->dev, "%s: tmp_reg_list: alloc filed\n", __func__);
+ return;
+ }
+
+ tmp_data = kmalloc(size * sizeof(u32), GFP_KERNEL);
+ if (!tmp_data) {
+ dev_err(ah->dev, "%s tmp_data: alloc filed\n", __func__);
+ goto error_tmp_data;
+ }
+
+ for (i = 0; i < size; i++)
+ tmp_reg_list[i] = array[i][0];
+
+ REG_READ_MULTI(ah, tmp_reg_list, tmp_data, size);
+
+ for (i = 0; i < size; i++)
+ array[i][1] = tmp_data[i];
+
+ kfree(tmp_data);
+error_tmp_data:
+ kfree(tmp_reg_list);
+}
+
u32 ath9k_hw_reverse_bits(u32 val, u32 n)
{
u32 retval;
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -138,6 +138,8 @@
#define REG_WRITE_ARRAY(iniarray, column, regWr) \
ath9k_hw_write_array(ah, iniarray, column, &(regWr))
+#define REG_READ_ARRAY(ah, array, size) \
+ ath9k_hw_read_array(ah, array, size)
#define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0
#define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1
@@ -1020,6 +1022,7 @@ void ath9k_hw_synth_delay(struct ath_hw
bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array,
int column, unsigned int *writecnt);
+void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size);
u32 ath9k_hw_reverse_bits(u32 val, u32 n);
u16 ath9k_hw_computetxtime(struct ath_hw *ah,
u8 phy, int kbps,

View File

@ -0,0 +1,32 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Sun, 31 Jan 2016 12:14:34 +0100
Subject: [PATCH] brcmfmac: treat NULL character in NVRAM as separator
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Platform NVRAM (stored on a flash partition) has entries separated by a
NULL (\0) char. Our parsing code switches from VALUE state to IDLE
whenever it meets a NULL (\0). When that happens our IDLE handler should
simply consume it and analyze whatever is placed ahead.
This fixes harmless warnings spamming debugging output:
[ 155.165624] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=20: ignoring invalid character
[ 155.180806] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=44: ignoring invalid character
[ 155.195971] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=63: ignoring invalid character
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -93,7 +93,7 @@ static enum nvram_parser_state brcmf_nvr
c = nvp->data[nvp->pos];
if (c == '\n')
return COMMENT;
- if (is_whitespace(c))
+ if (is_whitespace(c) || c == '\0')
goto proceed;
if (c == '#')
return COMMENT;

View File

@ -1,24 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:52 +0100
Subject: [PATCH] ath9k: ar9271_hw_pa_cal: use REG_READ_ARRAY
insted of reading each register separatly
and waste 4ms on each operation, we can
use one shot read.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -440,8 +440,7 @@ static void ar9271_hw_pa_cal(struct ath_
{ AR9285_AN_RF2G3, 0 },
};
- for (i = 0; i < ARRAY_SIZE(regList); i++)
- regList[i][1] = REG_READ(ah, regList[i][0]);
+ REG_READ_ARRAY(ah, regList, ARRAY_SIZE(regList));
ENABLE_REG_RMW_BUFFER(ah);
/* 7834, b1=0 */

View File

@ -0,0 +1,41 @@
From: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
Date: Mon, 25 Jan 2016 11:47:29 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
the card responds very quickly most of the time, unfortunately during
initialisation it sometimes seems to take just a bit over 2 seconds to
respond.
This results intialization failing with message like:
brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
brcmf_bus_start: failed: -52
brcmf_sdio_firmware_callback: dongle is not responding
Increasing the timeout to allow for a bit more headroom allows the
card to initialize reliably.
A quick search online after diagnosing/fixing this showed that Google
has a similar patch in their ChromeOS tree, so this doesn't seem
specific to the board I'm using.
Signed-off-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
Reviewed-by: Julian Calaby <julian.calaby@gmail.com>
Acked-by: Arend van Spriel <arend@broadcom.com>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -45,8 +45,8 @@
#include "chip.h"
#include "firmware.h"
-#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2000)
-#define CTL_DONE_TIMEOUT msecs_to_jiffies(2000)
+#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500)
+#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500)
#ifdef DEBUG

View File

@ -0,0 +1,87 @@
From: Miaoqing Pan <miaoqing@codeaurora.org>
Date: Fri, 5 Feb 2016 09:45:50 +0800
Subject: [PATCH] ath9k: make NF load complete quickly and reliably
Make NF load complete quickly and reliably. NF load execution
is delayed by HW to end of frame if frame Rx or Tx is ongoing.
Increasing timeout to max frame duration. If NF cal is ongoing
before NF load, stop it before load, and restart it afterwards.
Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -241,6 +241,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
struct ath_common *common = ath9k_hw_common(ah);
s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
+ u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL);
if (ah->caldata)
h = ah->caldata->nfCalHist;
@@ -264,6 +265,16 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
}
/*
+ * stop NF cal if ongoing to ensure NF load completes immediately
+ * (or after end rx/tx frame if ongoing)
+ */
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
+ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+ REG_RMW_BUFFER_FLUSH(ah);
+ ENABLE_REG_RMW_BUFFER(ah);
+ }
+
+ /*
* Load software filtered NF value into baseband internal minCCApwr
* variable.
*/
@@ -276,18 +287,33 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
/*
* Wait for load to complete, should be fast, a few 10s of us.
- * The max delay was changed from an original 250us to 10000us
- * since 250us often results in NF load timeout and causes deaf
- * condition during stress testing 12/12/2009
+ * The max delay was changed from an original 250us to 22.2 msec.
+ * This would increase timeout to the longest possible frame
+ * (11n max length 22.1 msec)
*/
- for (j = 0; j < 10000; j++) {
+ for (j = 0; j < 22200; j++) {
if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
- AR_PHY_AGC_CONTROL_NF) == 0)
+ AR_PHY_AGC_CONTROL_NF) == 0)
break;
udelay(10);
}
/*
+ * Restart NF so it can continue.
+ */
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
+ ENABLE_REG_RMW_BUFFER(ah);
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF)
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_ENABLE_NF);
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF)
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+ REG_RMW_BUFFER_FLUSH(ah);
+ }
+
+ /*
* We timed out waiting for the noisefloor to load, probably due to an
* in-progress rx. Simply return here and allow the load plenty of time
* to complete before the next calibration interval. We need to avoid
@@ -296,7 +322,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
* here, the baseband nf cal will just be capped by our present
* noisefloor until the next calibration timer.
*/
- if (j == 10000) {
+ if (j == 22200) {
ath_dbg(common, ANY,
"Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
REG_READ(ah, AR_PHY_AGC_CONTROL));

View File

@ -1,39 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:53 +0100
Subject: [PATCH] ath9k: use one shot read in ath9k_hw_update_mibstats
this will reduce some overhead on usb bus.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -107,11 +107,21 @@ static const struct ani_cck_level_entry
static void ath9k_hw_update_mibstats(struct ath_hw *ah,
struct ath9k_mib_stats *stats)
{
- stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
- stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
- stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
- stats->rts_good += REG_READ(ah, AR_RTS_OK);
- stats->beacons += REG_READ(ah, AR_BEACON_CNT);
+ u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL,
+ AR_FCS_FAIL, AR_BEACON_CNT};
+ u32 data[5];
+
+ REG_READ_MULTI(ah, &addr[0], &data[0], 5);
+ /* AR_RTS_OK */
+ stats->rts_good += data[0];
+ /* AR_RTS_FAIL */
+ stats->rts_bad += data[1];
+ /* AR_ACK_FAIL */
+ stats->ackrcv_bad += data[2];
+ /* AR_FCS_FAIL */
+ stats->fcs_bad += data[3];
+ /* AR_BEACON_CNT */
+ stats->beacons += data[4];
}
static void ath9k_ani_restart(struct ath_hw *ah)

View File

@ -1,71 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:54 +0100
Subject: [PATCH] ath9k: ath9k_hw_loadnf: use REG_RMW
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -238,7 +238,6 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
{
struct ath9k_nfcal_hist *h = NULL;
unsigned i, j;
- int32_t val;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
struct ath_common *common = ath9k_hw_common(ah);
s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
@@ -246,6 +245,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
if (ah->caldata)
h = ah->caldata->nfCalHist;
+ ENABLE_REG_RMW_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
s16 nfval;
@@ -258,10 +258,8 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
else
nfval = default_nf;
- val = REG_READ(ah, ah->nf_regs[i]);
- val &= 0xFFFFFE00;
- val |= (((u32) nfval << 1) & 0x1ff);
- REG_WRITE(ah, ah->nf_regs[i], val);
+ REG_RMW(ah, ah->nf_regs[i],
+ (((u32) nfval << 1) & 0x1ff), 0x1ff);
}
}
@@ -274,6 +272,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+ REG_RMW_BUFFER_FLUSH(ah);
/*
* Wait for load to complete, should be fast, a few 10s of us.
@@ -309,19 +308,17 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
* by the median we just loaded. This will be initial (and max) value
* of next noise floor calibration the baseband does.
*/
- ENABLE_REGWRITE_BUFFER(ah);
+ ENABLE_REG_RMW_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
continue;
- val = REG_READ(ah, ah->nf_regs[i]);
- val &= 0xFFFFFE00;
- val |= (((u32) (-50) << 1) & 0x1ff);
- REG_WRITE(ah, ah->nf_regs[i], val);
+ REG_RMW(ah, ah->nf_regs[i],
+ (((u32) (-50) << 1) & 0x1ff), 0x1ff);
}
}
- REGWRITE_BUFFER_FLUSH(ah);
+ REG_RMW_BUFFER_FLUSH(ah);
return 0;
}

View File

@ -0,0 +1,54 @@
From: Henning Rogge <hrogge@gmail.com>
Date: Wed, 3 Feb 2016 13:58:36 +0100
Subject: [PATCH] mac80211: Remove MPP table entries with MPath
Make the mesh_path_del() function remove all mpp table entries
that are proxied by the removed mesh path.
Acked-by: Bob Copeland <me@bobcopeland.com>
Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -835,6 +835,29 @@ void mesh_path_flush_by_nexthop(struct s
rcu_read_unlock();
}
+static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata,
+ const u8 *proxy)
+{
+ struct mesh_table *tbl;
+ struct mesh_path *mpp;
+ struct mpath_node *node;
+ int i;
+
+ rcu_read_lock();
+ read_lock_bh(&pathtbl_resize_lock);
+ tbl = resize_dereference_mpp_paths();
+ for_each_mesh_entry(tbl, node, i) {
+ mpp = node->mpath;
+ if (ether_addr_equal(mpp->mpp, proxy)) {
+ spin_lock(&tbl->hashwlock[i]);
+ __mesh_path_del(tbl, node);
+ spin_unlock(&tbl->hashwlock[i]);
+ }
+ }
+ read_unlock_bh(&pathtbl_resize_lock);
+ rcu_read_unlock();
+}
+
static void table_flush_by_iface(struct mesh_table *tbl,
struct ieee80211_sub_if_data *sdata)
{
@@ -892,6 +915,9 @@ int mesh_path_del(struct ieee80211_sub_i
int hash_idx;
int err = 0;
+ /* flush relevant mpp entries first */
+ mpp_flush_by_proxy(sdata, addr);
+
read_lock_bh(&pathtbl_resize_lock);
tbl = resize_dereference_mesh_paths();
hash_idx = mesh_table_hash(addr, sdata, tbl);

View File

@ -1,27 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:55 +0100
Subject: [PATCH] ath9k: write buffer related optimisation in
ar5008_hw_set_channel_regs
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -681,12 +681,13 @@ static void ar5008_hw_set_channel_regs(s
phymode |= AR_PHY_FC_DYN2040_PRI_CH;
}
+ ENABLE_REGWRITE_BUFFER(ah);
REG_WRITE(ah, AR_PHY_TURBO, phymode);
+ /* This function do only REG_WRITE, so
+ * we can include it to REGWRITE_BUFFER. */
ath9k_hw_set11nmac2040(ah, chan);
- ENABLE_REGWRITE_BUFFER(ah);
-
REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S);

View File

@ -0,0 +1,104 @@
From: Henning Rogge <hrogge@gmail.com>
Date: Wed, 3 Feb 2016 13:58:37 +0100
Subject: [PATCH] mac80211: let unused MPP table entries timeout
Remember the last time when a mpp table entry is used for
rx or tx and remove them after MESH_PATH_EXPIRE time.
Acked-by: Bob Copeland <me@bobcopeland.com>
Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -942,6 +942,46 @@ enddel:
}
/**
+ * mpp_path_del - delete a mesh proxy path from the table
+ *
+ * @addr: addr address (ETH_ALEN length)
+ * @sdata: local subif
+ *
+ * Returns: 0 if successful
+ */
+static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+{
+ struct mesh_table *tbl;
+ struct mesh_path *mpath;
+ struct mpath_node *node;
+ struct hlist_head *bucket;
+ int hash_idx;
+ int err = 0;
+
+ read_lock_bh(&pathtbl_resize_lock);
+ tbl = resize_dereference_mpp_paths();
+ hash_idx = mesh_table_hash(addr, sdata, tbl);
+ bucket = &tbl->hash_buckets[hash_idx];
+
+ spin_lock(&tbl->hashwlock[hash_idx]);
+ hlist_for_each_entry(node, bucket, list) {
+ mpath = node->mpath;
+ if (mpath->sdata == sdata &&
+ ether_addr_equal(addr, mpath->dst)) {
+ __mesh_path_del(tbl, node);
+ goto enddel;
+ }
+ }
+
+ err = -ENXIO;
+enddel:
+ mesh_paths_generation++;
+ spin_unlock(&tbl->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
+ return err;
+}
+
+/**
* mesh_path_tx_pending - sends pending frames in a mesh path queue
*
* @mpath: mesh path to activate
@@ -1157,6 +1197,17 @@ void mesh_path_expire(struct ieee80211_s
time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
mesh_path_del(mpath->sdata, mpath->dst);
}
+
+ tbl = rcu_dereference(mpp_paths);
+ for_each_mesh_entry(tbl, node, i) {
+ if (node->mpath->sdata != sdata)
+ continue;
+ mpath = node->mpath;
+ if ((!(mpath->flags & MESH_PATH_FIXED)) &&
+ time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
+ mpp_path_del(mpath->sdata, mpath->dst);
+ }
+
rcu_read_unlock();
}
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2291,6 +2291,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
spin_lock_bh(&mppath->state_lock);
if (!ether_addr_equal(mppath->mpp, mpp_addr))
memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
+ mppath->exp_time = jiffies;
spin_unlock_bh(&mppath->state_lock);
}
rcu_read_unlock();
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2171,8 +2171,11 @@ static struct sk_buff *ieee80211_build_h
mpp_lookup = true;
}
- if (mpp_lookup)
+ if (mpp_lookup) {
mppath = mpp_path_lookup(sdata, skb->data);
+ if (mppath)
+ mppath->exp_time = jiffies;
+ }
if (mppath && mpath)
mesh_path_del(mpath->sdata, mpath->dst);

View File

@ -1,26 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:56 +0100
Subject: [PATCH] ath9k: ath9k_hw_set_4k_power_cal_tabl: use rmw buffer
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -389,6 +389,7 @@ static void ath9k_hw_set_4k_power_cal_ta
}
}
+ ENABLE_REG_RMW_BUFFER(ah);
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
(numXpdGain - 1) & 0x3);
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
@@ -396,6 +397,7 @@ static void ath9k_hw_set_4k_power_cal_ta
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
xpdGainValues[1]);
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0);
+ REG_RMW_BUFFER_FLUSH(ah);
for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
regChainOffset = i * 0x1000;

View File

@ -0,0 +1,143 @@
From: Henning Rogge <hrogge@gmail.com>
Date: Wed, 3 Feb 2016 13:58:38 +0100
Subject: [PATCH] mac80211: Unify mesh and mpp path removal function
mpp_path_del() and mesh_path_del() are mostly the same function.
Move common code into a new static function.
Acked-by: Bob Copeland <me@bobcopeland.com>
Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -55,16 +55,21 @@ int mpp_paths_generation;
static DEFINE_RWLOCK(pathtbl_resize_lock);
+static inline struct mesh_table *resize_dereference_paths(
+ struct mesh_table __rcu *table)
+{
+ return rcu_dereference_protected(table,
+ lockdep_is_held(&pathtbl_resize_lock));
+}
+
static inline struct mesh_table *resize_dereference_mesh_paths(void)
{
- return rcu_dereference_protected(mesh_paths,
- lockdep_is_held(&pathtbl_resize_lock));
+ return resize_dereference_paths(mesh_paths);
}
static inline struct mesh_table *resize_dereference_mpp_paths(void)
{
- return rcu_dereference_protected(mpp_paths,
- lockdep_is_held(&pathtbl_resize_lock));
+ return resize_dereference_paths(mpp_paths);
}
/*
@@ -899,14 +904,17 @@ void mesh_path_flush_by_iface(struct iee
}
/**
- * mesh_path_del - delete a mesh path from the table
+ * table_path_del - delete a path from the mesh or mpp table
*
- * @addr: dst address (ETH_ALEN length)
+ * @tbl: mesh or mpp path table
* @sdata: local subif
+ * @addr: dst address (ETH_ALEN length)
*
* Returns: 0 if successful
*/
-int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+static int table_path_del(struct mesh_table __rcu *rcu_tbl,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *addr)
{
struct mesh_table *tbl;
struct mesh_path *mpath;
@@ -915,11 +923,7 @@ int mesh_path_del(struct ieee80211_sub_i
int hash_idx;
int err = 0;
- /* flush relevant mpp entries first */
- mpp_flush_by_proxy(sdata, addr);
-
- read_lock_bh(&pathtbl_resize_lock);
- tbl = resize_dereference_mesh_paths();
+ tbl = resize_dereference_paths(rcu_tbl);
hash_idx = mesh_table_hash(addr, sdata, tbl);
bucket = &tbl->hash_buckets[hash_idx];
@@ -935,9 +939,30 @@ int mesh_path_del(struct ieee80211_sub_i
err = -ENXIO;
enddel:
- mesh_paths_generation++;
spin_unlock(&tbl->hashwlock[hash_idx]);
+ return err;
+}
+
+/**
+ * mesh_path_del - delete a mesh path from the table
+ *
+ * @addr: dst address (ETH_ALEN length)
+ * @sdata: local subif
+ *
+ * Returns: 0 if successful
+ */
+int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+{
+ int err = 0;
+
+ /* flush relevant mpp entries first */
+ mpp_flush_by_proxy(sdata, addr);
+
+ read_lock_bh(&pathtbl_resize_lock);
+ err = table_path_del(mesh_paths, sdata, addr);
+ mesh_paths_generation++;
read_unlock_bh(&pathtbl_resize_lock);
+
return err;
}
@@ -951,33 +976,13 @@ enddel:
*/
static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
{
- struct mesh_table *tbl;
- struct mesh_path *mpath;
- struct mpath_node *node;
- struct hlist_head *bucket;
- int hash_idx;
int err = 0;
read_lock_bh(&pathtbl_resize_lock);
- tbl = resize_dereference_mpp_paths();
- hash_idx = mesh_table_hash(addr, sdata, tbl);
- bucket = &tbl->hash_buckets[hash_idx];
-
- spin_lock(&tbl->hashwlock[hash_idx]);
- hlist_for_each_entry(node, bucket, list) {
- mpath = node->mpath;
- if (mpath->sdata == sdata &&
- ether_addr_equal(addr, mpath->dst)) {
- __mesh_path_del(tbl, node);
- goto enddel;
- }
- }
-
- err = -ENXIO;
-enddel:
- mesh_paths_generation++;
- spin_unlock(&tbl->hashwlock[hash_idx]);
+ err = table_path_del(mpp_paths, sdata, addr);
+ mpp_paths_generation++;
read_unlock_bh(&pathtbl_resize_lock);
+
return err;
}

View File

@ -1,43 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:57 +0100
Subject: [PATCH] ath9k: use rmw buffer in ath9k_hw_set_operating_mode
and ath9k_hw_reset
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1227,6 +1227,7 @@ static void ath9k_hw_set_operating_mode(
u32 mask = AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC;
u32 set = AR_STA_ID1_KSRCH_MODE;
+ ENABLE_REG_RMW_BUFFER(ah);
switch (opmode) {
case NL80211_IFTYPE_ADHOC:
if (!AR_SREV_9340_13(ah)) {
@@ -1248,6 +1249,7 @@ static void ath9k_hw_set_operating_mode(
break;
}
REG_RMW(ah, AR_STA_ID1, set, mask);
+ REG_RMW_BUFFER_FLUSH(ah);
}
void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
@@ -1960,6 +1962,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st
if (!ath9k_hw_mci_is_enabled(ah))
REG_WRITE(ah, AR_OBS, 8);
+ ENABLE_REG_RMW_BUFFER(ah);
if (ah->config.rx_intr_mitigation) {
REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, ah->config.rimt_last);
REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, ah->config.rimt_first);
@@ -1969,6 +1972,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st
REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300);
REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750);
}
+ REG_RMW_BUFFER_FLUSH(ah);
ath9k_hw_init_bb(ah, chan);

View File

@ -0,0 +1,51 @@
From: Sven Eckelmann <sven.eckelmann@open-mesh.com>
Date: Tue, 2 Feb 2016 08:12:26 +0100
Subject: [PATCH] mac80211: minstrel: Change expected throughput unit back to
Kbps
The change from cur_tp to the function
minstrel_get_tp_avg/minstrel_ht_get_tp_avg changed the unit used for the
current throughput. For example in minstrel_ht the correct
conversion between them would be:
mrs->cur_tp / 10 == minstrel_ht_get_tp_avg(..).
This factor 10 must also be included in the calculation of
minstrel_get_expected_throughput and minstrel_ht_get_expected_throughput to
return values with the unit [Kbps] instead of [10Kbps]. Otherwise routing
algorithms like B.A.T.M.A.N. V will make incorrect decision based on these
values. Its kernel based implementation expects expected_throughput always
to have the unit [Kbps] and not sometimes [10Kbps] and sometimes [Kbps].
The same requirement has iw or olsrdv2's nl80211 based statistics module
which retrieve the same data via NL80211_STA_INFO_TX_BITRATE.
Cc: stable@vger.kernel.org
Fixes: 6a27b2c40b48 ("mac80211: restructure per-rate throughput calculation into function")
Signed-off-by: Sven Eckelmann <sven@open-mesh.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -711,7 +711,7 @@ static u32 minstrel_get_expected_through
* computing cur_tp
*/
tmp_mrs = &mi->r[idx].stats;
- tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma);
+ tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma) * 10;
tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
return tmp_cur_tp;
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -1335,7 +1335,8 @@ static u32 minstrel_ht_get_expected_thro
prob = mi->groups[i].rates[j].prob_ewma;
/* convert tp_avg from pkt per second in kbps */
- tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024;
+ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * 10;
+ tp_avg = tp_avg * AVG_PKT_SIZE * 8 / 1024;
return tp_avg;
}

View File

@ -1,26 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:58 +0100
Subject: [PATCH] ath9k: ath9k_hw_4k_set_board_values: use rmw buffer
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -1082,6 +1082,7 @@ static void ath9k_hw_4k_set_board_values
mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25);
pwrctrl = mask * bb_desired_scale;
clr = mask * 0x1f;
+ ENABLE_REG_RMW_BUFFER(ah);
REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr);
REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr);
REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr);
@@ -1096,6 +1097,7 @@ static void ath9k_hw_4k_set_board_values
clr = mask * 0x1f;
REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr);
REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr);
+ REG_RMW_BUFFER_FLUSH(ah);
}
}

View File

@ -0,0 +1,307 @@
From: Hante Meuleman <meuleman@broadcom.com>
Date: Sun, 7 Feb 2016 18:08:24 +0100
Subject: [PATCH] brcmfmac: Increase nr of supported flowrings.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
New generation devices have firmware which has more than 256 flowrings.
E.g. following debugging message comes from 14e4:4365 BCM4366:
[ 194.606245] brcmfmac: brcmf_pcie_init_ringbuffers Nr of flowrings is 264
At various code places (related to flowrings) we were using u8 which
could lead to storing wrong number or infinite loops when indexing with
this type. This issue was quite easy to spot in brcmf_flowring_detach
where it led to infinite loop e.g. on failed initialization.
This patch switches code to proper types and increases the maximum
number of supported flowrings to 512.
Originally this change was sent in September 2015, but back it was
causing a regression on BCM43602 resulting in:
Unable to handle kernel NULL pointer dereference at virtual address ...
The reason for this regression was missing update (s/u8/u16) of struct
brcmf_flowring_ring. This problem was handled in 9f64df9 ("brcmfmac: Fix
bug in flowring management."). Starting with that it's safe to apply
this original patch as it doesn't cause a regression anymore.
This patch fixes an infinite loop on BCM4366 which is supported since
4.4 so it makes sense to apply it to stable 4.4+.
Cc: <stable@vger.kernel.org> # 4.4+
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
@@ -32,7 +32,7 @@
#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256)
#define BRCMF_FLOWRING_INVALID_IFIDX 0xff
-#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16)
+#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] * 2 + fifo + ifidx * 16)
#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16)
static const u8 brcmf_flowring_prio2fifo[] = {
@@ -68,7 +68,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
u8 prio, u8 ifidx)
{
struct brcmf_flowring_hash *hash;
- u8 hash_idx;
+ u16 hash_idx;
u32 i;
bool found;
bool sta;
@@ -88,6 +88,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
}
hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
found = false;
hash = flow->hash;
for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
@@ -98,6 +99,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
break;
}
hash_idx++;
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
}
if (found)
return hash[hash_idx].flowid;
@@ -111,7 +113,7 @@ u32 brcmf_flowring_create(struct brcmf_f
{
struct brcmf_flowring_ring *ring;
struct brcmf_flowring_hash *hash;
- u8 hash_idx;
+ u16 hash_idx;
u32 i;
bool found;
u8 fifo;
@@ -131,6 +133,7 @@ u32 brcmf_flowring_create(struct brcmf_f
}
hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
found = false;
hash = flow->hash;
for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
@@ -140,6 +143,7 @@ u32 brcmf_flowring_create(struct brcmf_f
break;
}
hash_idx++;
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
}
if (found) {
for (i = 0; i < flow->nrofrings; i++) {
@@ -169,7 +173,7 @@ u32 brcmf_flowring_create(struct brcmf_f
}
-u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid)
+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
@@ -179,7 +183,7 @@ u8 brcmf_flowring_tid(struct brcmf_flowr
}
-static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid,
+static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,
bool blocked)
{
struct brcmf_flowring_ring *ring;
@@ -228,10 +232,10 @@ static void brcmf_flowring_block(struct
}
-void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid)
+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
- u8 hash_idx;
+ u16 hash_idx;
struct sk_buff *skb;
ring = flow->rings[flowid];
@@ -253,7 +257,7 @@ void brcmf_flowring_delete(struct brcmf_
}
-u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb)
{
struct brcmf_flowring_ring *ring;
@@ -279,7 +283,7 @@ u32 brcmf_flowring_enqueue(struct brcmf_
}
-struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid)
+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
struct sk_buff *skb;
@@ -300,7 +304,7 @@ struct sk_buff *brcmf_flowring_dequeue(s
}
-void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb)
{
struct brcmf_flowring_ring *ring;
@@ -311,7 +315,7 @@ void brcmf_flowring_reinsert(struct brcm
}
-u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid)
+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
@@ -326,7 +330,7 @@ u32 brcmf_flowring_qlen(struct brcmf_flo
}
-void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid)
+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
@@ -340,10 +344,10 @@ void brcmf_flowring_open(struct brcmf_fl
}
-u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid)
+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
- u8 hash_idx;
+ u16 hash_idx;
ring = flow->rings[flowid];
hash_idx = ring->hash_id;
@@ -384,7 +388,7 @@ void brcmf_flowring_detach(struct brcmf_
struct brcmf_pub *drvr = bus_if->drvr;
struct brcmf_flowring_tdls_entry *search;
struct brcmf_flowring_tdls_entry *remove;
- u8 flowid;
+ u16 flowid;
for (flowid = 0; flowid < flow->nrofrings; flowid++) {
if (flow->rings[flowid])
@@ -408,7 +412,7 @@ void brcmf_flowring_configure_addr_mode(
struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
struct brcmf_pub *drvr = bus_if->drvr;
u32 i;
- u8 flowid;
+ u16 flowid;
if (flow->addr_mode[ifidx] != addr_mode) {
for (i = 0; i < ARRAY_SIZE(flow->hash); i++) {
@@ -434,7 +438,7 @@ void brcmf_flowring_delete_peer(struct b
struct brcmf_flowring_tdls_entry *prev;
struct brcmf_flowring_tdls_entry *search;
u32 i;
- u8 flowid;
+ u16 flowid;
bool sta;
sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
@@ -16,7 +16,7 @@
#define BRCMFMAC_FLOWRING_H
-#define BRCMF_FLOWRING_HASHSIZE 256
+#define BRCMF_FLOWRING_HASHSIZE 512 /* has to be 2^x */
#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF
@@ -24,7 +24,7 @@ struct brcmf_flowring_hash {
u8 mac[ETH_ALEN];
u8 fifo;
u8 ifidx;
- u8 flowid;
+ u16 flowid;
};
enum ring_status {
@@ -61,16 +61,16 @@ u32 brcmf_flowring_lookup(struct brcmf_f
u8 prio, u8 ifidx);
u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
u8 prio, u8 ifidx);
-void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid);
-void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid);
-u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid);
-u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid);
+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid);
+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid);
+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb);
-struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid);
-void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid);
+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb);
-u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid);
-u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid);
+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid);
+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid);
struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings);
void brcmf_flowring_detach(struct brcmf_flowring *flow);
void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -677,7 +677,7 @@ static u32 brcmf_msgbuf_flowring_create(
}
-static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
+static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid)
{
struct brcmf_flowring *flow = msgbuf->flow;
struct brcmf_commonring *commonring;
@@ -1310,7 +1310,7 @@ int brcmf_proto_msgbuf_rx_trigger(struct
}
-void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid)
+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
{
struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
struct msgbuf_tx_flowring_delete_req *delete;
@@ -1415,6 +1415,13 @@ int brcmf_proto_msgbuf_attach(struct brc
u32 count;
if_msgbuf = drvr->bus_if->msgbuf;
+
+ if (if_msgbuf->nrof_flowrings >= BRCMF_FLOWRING_HASHSIZE) {
+ brcmf_err("driver not configured for this many flowrings %d\n",
+ if_msgbuf->nrof_flowrings);
+ if_msgbuf->nrof_flowrings = BRCMF_FLOWRING_HASHSIZE - 1;
+ }
+
msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
if (!msgbuf)
goto fail;
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
@@ -33,7 +33,7 @@
int brcmf_proto_msgbuf_rx_trigger(struct device *dev);
-void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid);
+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid);
int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr);
void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr);
#else

View File

@ -1,27 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:29:59 +0100
Subject: [PATCH] ath9k: ath9k_hw_analog_shift_rmw: use REG_RMW
use REG_RMW in ath9k_hw_analog_shift_rmw.
It will double execution speed on usb bus.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -27,12 +27,7 @@ void ath9k_hw_analog_shift_regwrite(stru
void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask,
u32 shift, u32 val)
{
- u32 regVal;
-
- regVal = REG_READ(ah, reg) & ~mask;
- regVal |= (val << shift) & mask;
-
- REG_WRITE(ah, reg, regVal);
+ REG_RMW(ah, reg, ((val << shift) & mask), mask);
if (ah->config.analog_shiftreg)
udelay(100);

View File

@ -0,0 +1,22 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Mon, 8 Feb 2016 14:24:36 +0100
Subject: [PATCH] cfg80211: fix faulty variable initialization in
ieee80211_amsdu_to_8023s
reuse_skb is set to true if the code decides to use the last segment.
Fixes a memory leak
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -676,7 +676,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
u8 *payload;
int offset = 0, remaining, err;
struct ethhdr eth;
- bool reuse_skb = true;
+ bool reuse_skb = false;
bool last = false;
if (has_80211_header) {

View File

@ -1,47 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:30:01 +0100
Subject: [PATCH] ath9k: use REG_RMW and rmw buffer in
ath9k_hw_4k_set_gain
it is possible to reduce time needed for this function
by rplacing REG_WRITE with REG_RMW (plus dummy 0) and putt all commands
in same buffer.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -772,15 +772,14 @@ static void ath9k_hw_4k_set_gain(struct
struct ar5416_eeprom_4k *eep,
u8 txRxAttenLocal)
{
- REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0,
- pModal->antCtrlChain[0]);
-
- REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0),
- (REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) &
- ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
- AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
- SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
- SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+ ENABLE_REG_RMW_BUFFER(ah);
+ REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0,
+ pModal->antCtrlChain[0], 0);
+
+ REG_RMW(ah, AR_PHY_TIMING_CTRL4(0),
+ SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+ SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF),
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF);
if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
AR5416_EEP_MINOR_VER_3) {
@@ -819,6 +818,7 @@ static void ath9k_hw_4k_set_gain(struct
AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000,
AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
+ REG_RMW_BUFFER_FLUSH(ah);
}
/*

View File

@ -0,0 +1,132 @@
From: Felix Fietkau <nbd@openwrt.org>
Date: Mon, 8 Feb 2016 14:33:19 +0100
Subject: [PATCH] cfg80211: reuse existing page fragments in A-MSDU rx
This massively reduces data copying and thus improves rx performance
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -644,23 +644,93 @@ int ieee80211_data_from_8023(struct sk_b
}
EXPORT_SYMBOL(ieee80211_data_from_8023);
+static void
+__frame_add_frag(struct sk_buff *skb, struct page *page,
+ void *ptr, int len, int size)
+{
+ struct skb_shared_info *sh = skb_shinfo(skb);
+ int page_offset;
+
+ atomic_inc(&page->_count);
+ page_offset = ptr - page_address(page);
+ skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size);
+}
+
+static void
+__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame,
+ int offset, int len)
+{
+ struct skb_shared_info *sh = skb_shinfo(skb);
+ const skb_frag_t *frag = &sh->frags[-1];
+ struct page *frag_page;
+ void *frag_ptr;
+ int frag_len, frag_size;
+ int head_size = skb->len - skb->data_len;
+ int cur_len;
+
+ frag_page = virt_to_head_page(skb->head);
+ frag_ptr = skb->data;
+ frag_size = head_size;
+
+ while (offset >= frag_size) {
+ offset -= frag_size;
+ frag++;
+ frag_page = skb_frag_page(frag);
+ frag_ptr = skb_frag_address(frag);
+ frag_size = skb_frag_size(frag);
+ }
+
+ frag_ptr += offset;
+ frag_len = frag_size - offset;
+
+ cur_len = min(len, frag_len);
+
+ __frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size);
+ len -= cur_len;
+
+ while (len > 0) {
+ frag++;
+ frag_len = skb_frag_size(frag);
+ cur_len = min(len, frag_len);
+ __frame_add_frag(frame, skb_frag_page(frag),
+ skb_frag_address(frag), cur_len, frag_len);
+ len -= cur_len;
+ }
+}
+
static struct sk_buff *
__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
- int offset, int len)
+ int offset, int len, bool reuse_frag)
{
struct sk_buff *frame;
+ int cur_len = len;
if (skb->len - offset < len)
return NULL;
/*
+ * When reusing framents, copy some data to the head to simplify
+ * ethernet header handling and speed up protocol header processing
+ * in the stack later.
+ */
+ if (reuse_frag)
+ cur_len = min_t(int, len, 32);
+
+ /*
* Allocate and reserve two bytes more for payload
* alignment since sizeof(struct ethhdr) is 14.
*/
- frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
+ frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
- skb_copy_bits(skb, offset, skb_put(frame, len), len);
+ skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
+
+ len -= cur_len;
+ if (!len)
+ return frame;
+
+ offset += cur_len;
+ __ieee80211_amsdu_copy_frag(skb, frame, offset, len);
return frame;
}
@@ -676,6 +746,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
u8 *payload;
int offset = 0, remaining, err;
struct ethhdr eth;
+ bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
bool reuse_skb = false;
bool last = false;
@@ -703,12 +774,13 @@ void ieee80211_amsdu_to_8023s(struct sk_
offset += sizeof(struct ethhdr);
/* reuse skb for the last subframe */
last = remaining <= subframe_len + padding;
- if (!skb_is_nonlinear(skb) && last) {
+ if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
skb_pull(skb, offset);
frame = skb;
reuse_skb = true;
} else {
- frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
+ frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
+ reuse_frag);
if (!frame)
goto purge;

View File

@ -1,67 +0,0 @@
From: Oleksij Rempel <linux@rempel-privat.de>
Date: Sun, 22 Mar 2015 19:30:03 +0100
Subject: [PATCH] ath9k: use REG_RMW and rmw buffer in
ath9k_hw_def_set_gain
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -466,6 +466,7 @@ static void ath9k_hw_def_set_gain(struct
struct ar5416_eeprom_def *eep,
u8 txRxAttenLocal, int regChainOffset, int i)
{
+ ENABLE_REG_RMW_BUFFER(ah);
if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) {
txRxAttenLocal = pModal->txRxAttenCh[i];
@@ -483,16 +484,12 @@ static void ath9k_hw_def_set_gain(struct
AR_PHY_GAIN_2GHZ_XATTEN2_DB,
pModal->xatten2Db[i]);
} else {
- REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
- | SM(pModal-> bswMargin[i],
- AR_PHY_GAIN_2GHZ_BSW_MARGIN));
- REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
- | SM(pModal->bswAtten[i],
- AR_PHY_GAIN_2GHZ_BSW_ATTEN));
+ REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ SM(pModal-> bswMargin[i], AR_PHY_GAIN_2GHZ_BSW_MARGIN),
+ AR_PHY_GAIN_2GHZ_BSW_MARGIN);
+ REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ SM(pModal->bswAtten[i], AR_PHY_GAIN_2GHZ_BSW_ATTEN),
+ AR_PHY_GAIN_2GHZ_BSW_ATTEN);
}
}
@@ -504,17 +501,14 @@ static void ath9k_hw_def_set_gain(struct
AR_PHY_RXGAIN + regChainOffset,
AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]);
} else {
- REG_WRITE(ah,
- AR_PHY_RXGAIN + regChainOffset,
- (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) &
- ~AR_PHY_RXGAIN_TXRX_ATTEN)
- | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN));
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ + regChainOffset,
- (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
- SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
+ REG_RMW(ah, AR_PHY_RXGAIN + regChainOffset,
+ SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN),
+ AR_PHY_RXGAIN_TXRX_ATTEN);
+ REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN),
+ AR_PHY_GAIN_2GHZ_RXTX_MARGIN);
}
+ REG_RMW_BUFFER_FLUSH(ah);
}
static void ath9k_hw_def_set_board_values(struct ath_hw *ah,

View File

@ -0,0 +1,36 @@
From: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
Date: Wed, 10 Feb 2016 16:08:17 +0100
Subject: [PATCH] mac80211: fix wiphy supported_band access
Fix wiphy supported_band access in tx radiotap parsing. In particular,
info->band is always set to 0 (IEEE80211_BAND_2GHZ) since it has not
assigned yet. This cause a kernel crash on 5GHz only devices.
Move ieee80211_parse_tx_radiotap() after info->band assignment
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
---
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1890,10 +1890,6 @@ netdev_tx_t ieee80211_monitor_start_xmit
info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
IEEE80211_TX_CTL_INJECTED;
- /* process and remove the injection radiotap header */
- if (!ieee80211_parse_tx_radiotap(local, skb))
- goto fail;
-
rcu_read_lock();
/*
@@ -1955,6 +1951,10 @@ netdev_tx_t ieee80211_monitor_start_xmit
goto fail_rcu;
info->band = chandef->chan->band;
+ /* process and remove the injection radiotap header */
+ if (!ieee80211_parse_tx_radiotap(local, skb))
+ goto fail_rcu;
+
ieee80211_xmit(sdata, NULL, skb);
rcu_read_unlock();

View File

@ -1,44 +0,0 @@
From: Hante Meuleman <meuleman@broadcom.com>
Date: Fri, 6 Mar 2015 18:40:38 +0100
Subject: [PATCH] brcmfmac: Fix oops when SDIO device is removed.
On removal of SDIO card both functions of card will be getting
a remove call. When the first is hanging in ctrl frame xmit then
the second will cause oops. This patch fixes the xmit ctrl
handling in case of serious errors and also limits the handling
for remove to function 1 only.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -1194,7 +1194,7 @@ static void brcmf_ops_sdio_remove(struct
brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
brcmf_dbg(SDIO, "Function: %d\n", func->num);
- if (func->num != 1 && func->num != 2)
+ if (func->num != 1)
return;
bus_if = dev_get_drvdata(&func->dev);
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
@@ -2740,6 +2740,11 @@ static void brcmf_sdio_dpc(struct brcmf_
if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) {
brcmf_err("failed backplane access over SDIO, halting operation\n");
atomic_set(&bus->intstatus, 0);
+ if (bus->ctrl_frame_stat) {
+ bus->ctrl_frame_err = -ENODEV;
+ bus->ctrl_frame_stat = false;
+ brcmf_sdio_wait_event_wakeup(bus);
+ }
} else if (atomic_read(&bus->intstatus) ||
atomic_read(&bus->ipend) > 0 ||
(!atomic_read(&bus->fcstate) &&

View File

@ -1,157 +0,0 @@
From: Hante Meuleman <meuleman@broadcom.com>
Date: Fri, 6 Mar 2015 18:40:39 +0100
Subject: [PATCH] brcmfmac: Simplify watchdog sleep.
The watchdog thread is used to put the SDIO bus to sleep when the
system is idling. This patch simplifies the way it is determined
when sleep can be entered.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
@@ -485,10 +485,9 @@ struct brcmf_sdio {
#endif /* DEBUG */
uint clkstate; /* State of sd and backplane clock(s) */
- bool activity; /* Activity flag for clock down */
s32 idletime; /* Control for activity timeout */
- s32 idlecount; /* Activity timeout counter */
- s32 idleclock; /* How to set bus driver when idle */
+ s32 idlecount; /* Activity timeout counter */
+ s32 idleclock; /* How to set bus driver when idle */
bool rxflow_mode; /* Rx flow control mode */
bool rxflow; /* Is rx flow control on */
bool alp_only; /* Don't use HT clock (ALP only) */
@@ -511,6 +510,7 @@ struct brcmf_sdio {
struct workqueue_struct *brcmf_wq;
struct work_struct datawork;
atomic_t dpc_tskcnt;
+ atomic_t dpc_running;
bool txoff; /* Transmit flow-controlled */
struct brcmf_sdio_count sdcnt;
@@ -959,13 +959,8 @@ static int brcmf_sdio_clkctl(struct brcm
brcmf_dbg(SDIO, "Enter\n");
/* Early exit if we're already there */
- if (bus->clkstate == target) {
- if (target == CLK_AVAIL) {
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
- bus->activity = true;
- }
+ if (bus->clkstate == target)
return 0;
- }
switch (target) {
case CLK_AVAIL:
@@ -975,7 +970,6 @@ static int brcmf_sdio_clkctl(struct brcm
/* Now request HT Avail on the backplane */
brcmf_sdio_htclk(bus, true, pendok);
brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
- bus->activity = true;
break;
case CLK_SDONLY:
@@ -1024,17 +1018,6 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *
/* Going to sleep */
if (sleep) {
- /* Don't sleep if something is pending */
- if (atomic_read(&bus->intstatus) ||
- atomic_read(&bus->ipend) > 0 ||
- bus->ctrl_frame_stat ||
- (!atomic_read(&bus->fcstate) &&
- brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
- data_ok(bus))) {
- err = -EBUSY;
- goto done;
- }
-
clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
&err);
@@ -1045,11 +1028,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *
SBSDIO_ALP_AVAIL_REQ, &err);
}
err = brcmf_sdio_kso_control(bus, false);
- /* disable watchdog */
- if (!err)
- brcmf_sdio_wd_timer(bus, 0);
} else {
- bus->idlecount = 0;
err = brcmf_sdio_kso_control(bus, true);
}
if (err) {
@@ -3566,7 +3545,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *b
queue_work(bus->brcmf_wq, &bus->datawork);
}
-static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
+static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
{
brcmf_dbg(TIMER, "Enter\n");
@@ -3627,22 +3606,21 @@ static bool brcmf_sdio_bus_watchdog(stru
#endif /* DEBUG */
/* On idle timeout clear activity flag and/or turn off clock */
- if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
- if (++bus->idlecount >= bus->idletime) {
+ if ((atomic_read(&bus->dpc_tskcnt) == 0) &&
+ (atomic_read(&bus->dpc_running) == 0) &&
+ (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
+ bus->idlecount++;
+ if (bus->idlecount > bus->idletime) {
+ brcmf_dbg(SDIO, "idle\n");
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wd_timer(bus, 0);
bus->idlecount = 0;
- if (bus->activity) {
- bus->activity = false;
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
- } else {
- brcmf_dbg(SDIO, "idle\n");
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_bus_sleep(bus, true, false);
- sdio_release_host(bus->sdiodev->func[1]);
- }
+ brcmf_sdio_bus_sleep(bus, true, false);
+ sdio_release_host(bus->sdiodev->func[1]);
}
+ } else {
+ bus->idlecount = 0;
}
-
- return (atomic_read(&bus->ipend) > 0);
}
static void brcmf_sdio_dataworker(struct work_struct *work)
@@ -3651,8 +3629,11 @@ static void brcmf_sdio_dataworker(struct
datawork);
while (atomic_read(&bus->dpc_tskcnt)) {
+ atomic_set(&bus->dpc_running, 1);
atomic_set(&bus->dpc_tskcnt, 0);
brcmf_sdio_dpc(bus);
+ bus->idlecount = 0;
+ atomic_set(&bus->dpc_running, 0);
}
if (brcmf_sdiod_freezing(bus->sdiodev)) {
brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN);
@@ -4154,6 +4135,7 @@ struct brcmf_sdio *brcmf_sdio_probe(stru
}
/* Initialize DPC thread */
atomic_set(&bus->dpc_tskcnt, 0);
+ atomic_set(&bus->dpc_running, 0);
/* Assign bus interface call back */
bus->sdiodev->bus_if->dev = bus->sdiodev->dev;

View File

@ -1,83 +0,0 @@
From: Hante Meuleman <meuleman@broadcom.com>
Date: Fri, 6 Mar 2015 18:40:40 +0100
Subject: [PATCH] brcmfmac: Fix possible race-condition.
SDIO is using a "shared" variable to handoff ctl frames to DPC
and to see when they are done. In a timeout situation this can
lead to erroneous situation where DPC started to handle the ctl
frame while the timeout expired. This patch will fix this by
adding locking around the shared variable.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
@@ -2700,11 +2700,13 @@ static void brcmf_sdio_dpc(struct brcmf_
if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
data_ok(bus)) {
sdio_claim_host(bus->sdiodev->func[1]);
- err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
- bus->ctrl_frame_len);
+ if (bus->ctrl_frame_stat) {
+ err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
+ bus->ctrl_frame_len);
+ bus->ctrl_frame_err = err;
+ bus->ctrl_frame_stat = false;
+ }
sdio_release_host(bus->sdiodev->func[1]);
- bus->ctrl_frame_err = err;
- bus->ctrl_frame_stat = false;
brcmf_sdio_wait_event_wakeup(bus);
}
/* Send queued frames (limit 1 if rx may still be pending) */
@@ -2720,9 +2722,13 @@ static void brcmf_sdio_dpc(struct brcmf_
brcmf_err("failed backplane access over SDIO, halting operation\n");
atomic_set(&bus->intstatus, 0);
if (bus->ctrl_frame_stat) {
- bus->ctrl_frame_err = -ENODEV;
- bus->ctrl_frame_stat = false;
- brcmf_sdio_wait_event_wakeup(bus);
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ bus->ctrl_frame_err = -ENODEV;
+ bus->ctrl_frame_stat = false;
+ brcmf_sdio_wait_event_wakeup(bus);
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
}
} else if (atomic_read(&bus->intstatus) ||
atomic_read(&bus->ipend) > 0 ||
@@ -2930,15 +2936,20 @@ brcmf_sdio_bus_txctl(struct device *dev,
brcmf_sdio_trigger_dpc(bus);
wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
msecs_to_jiffies(CTL_DONE_TIMEOUT));
-
- if (!bus->ctrl_frame_stat) {
+ ret = 0;
+ if (bus->ctrl_frame_stat) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ brcmf_dbg(SDIO, "ctrl_frame timeout\n");
+ bus->ctrl_frame_stat = false;
+ ret = -ETIMEDOUT;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ if (!ret) {
brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n",
bus->ctrl_frame_err);
ret = bus->ctrl_frame_err;
- } else {
- brcmf_dbg(SDIO, "ctrl_frame timeout\n");
- bus->ctrl_frame_stat = false;
- ret = -ETIMEDOUT;
}
if (ret)

View File

@ -1,86 +0,0 @@
From: Syed Asifful Dayyan <syedd@broadcom.com>
Date: Fri, 6 Mar 2015 18:40:42 +0100
Subject: [PATCH] brcmfmac: Add support for BCM4345 SDIO chipset.
These changes add support for BCM4345 SDIO chipset.
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: Syed Asifful Dayyan <syedd@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -1096,6 +1096,7 @@ static const struct sdio_device_id brcmf
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
{ /* end: all zeroes */ }
};
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
@@ -491,6 +491,10 @@ static void brcmf_chip_get_raminfo(struc
case BRCM_CC_43362_CHIP_ID:
ci->pub.ramsize = 0x3c000;
break;
+ case BRCM_CC_4345_CHIP_ID:
+ ci->pub.ramsize = 0xc8000;
+ ci->pub.rambase = 0x198000;
+ break;
case BRCM_CC_4339_CHIP_ID:
case BRCM_CC_4354_CHIP_ID:
case BRCM_CC_4356_CHIP_ID:
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
@@ -617,6 +617,8 @@ static const struct sdiod_drive_str sdio
#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt"
#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin"
#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt"
+#define BCM4345_FIRMWARE_NAME "brcm/brcmfmac4345-sdio.bin"
+#define BCM4345_NVRAM_NAME "brcm/brcmfmac4345-sdio.txt"
#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin"
#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt"
@@ -640,6 +642,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME);
MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
+MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM4345_NVRAM_NAME);
MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME);
MODULE_FIRMWARE(BCM4354_NVRAM_NAME);
@@ -669,6 +673,7 @@ static const struct brcmf_firmware_names
{ BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
{ BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
{ BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
+ { BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) },
{ BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
};
--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
@@ -37,6 +37,7 @@
#define BRCM_CC_43362_CHIP_ID 43362
#define BRCM_CC_4335_CHIP_ID 0x4335
#define BRCM_CC_4339_CHIP_ID 0x4339
+#define BRCM_CC_4345_CHIP_ID 0x4345
#define BRCM_CC_4354_CHIP_ID 0x4354
#define BRCM_CC_4356_CHIP_ID 0x4356
#define BRCM_CC_43566_CHIP_ID 43566
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -33,6 +33,7 @@
#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d
#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962
+#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
#define SDIO_VENDOR_ID_INTEL 0x0089

Some files were not shown because too many files have changed in this diff Show More